protected static List<LayoutDecision> maximalDecisionValues(Map<Element, LayoutDecisionMap> alreadyDecided, LayoutDecision.Kind k, Comparator<LayoutDecision> cmp) { ArrayList<LayoutDecision> currentMax = null; for (Map.Entry<Element, LayoutDecisionMap> eOuter : alreadyDecided.entrySet()) { LayoutDecision decisionToCompare = eOuter.getValue().getDecision(k); Integer compareResult = currentMax == null ? null : cmp.compare(decisionToCompare, currentMax.get(0)); if (currentMax == null || compareResult > 0) { // replace the current max with a new equivalence class currentMax = new ArrayList<>(1); currentMax.add(decisionToCompare); } else if (compareResult == 0) { // extend current max equivalence class currentMax.add(decisionToCompare); } // else it's less than the current max, so do nothing } return currentMax; }
@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; } }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this); // our content (but not our size) depends on the offset and size // of the corresponding element LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT); LayoutDecision elOffset = decisions.get(el).getDecision(LayoutDecision.Kind.OFFSET); LayoutDecision elSize = decisions.get(el).getDecision(LayoutDecision.Kind.SIZE); deps.add(BuildDependency.createOrGet(ourContent, elOffset)); deps.add(BuildDependency.createOrGet(ourContent, elSize)); return deps; }
public static HashSet<BuildDependency> basicDependencies(Map<Element, LayoutDecisionMap> decisions, Element el, boolean sizeOnContent, boolean vaddrOnOffset) { HashSet<BuildDependency> deps = new HashSet<>(); /* * As a minimum, we specify that the offset and vaddr of an element depend on its size. This * is so that once we assign an offset or vaddr, the "next available" offset/vaddr can * always be computed -- using the size which we require to have already been decided. */ deps.add(BuildDependency.createOrGet(decisions.get(el).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(el).getDecision(LayoutDecision.Kind.SIZE))); if (decisions.get(el).getDecision(LayoutDecision.Kind.VADDR) != null) { deps.add(BuildDependency.createOrGet(decisions.get(el).getDecision(LayoutDecision.Kind.VADDR), decisions.get(el).getDecision(LayoutDecision.Kind.SIZE))); } if (sizeOnContent) { deps.add(BuildDependency.createOrGet(decisions.get(el).getDecision(LayoutDecision.Kind.SIZE), decisions.get(el).getDecision(LayoutDecision.Kind.CONTENT))); } // if we have a vaddr, by default it depends on our offset if (vaddrOnOffset && decisions.get(el).getDecision(LayoutDecision.Kind.VADDR) != null) { deps.add(BuildDependency.createOrGet(decisions.get(el).getDecision(LayoutDecision.Kind.VADDR), decisions.get(el).getDecision(LayoutDecision.Kind.OFFSET))); } return deps; }
public Object getDecidedValue(LayoutDecision.Kind key) { return getDecision(key).getValue(); }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this); // our size is fixed; // our content depends on the offset and size of the ExportTrieElement LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT); deps.add(BuildDependency.createOrGet(ourContent, decisions.get(export).getDecision(LayoutDecision.Kind.OFFSET))); deps.add(BuildDependency.createOrGet(ourContent, decisions.get(export).getDecision(LayoutDecision.Kind.SIZE))); return deps; }
@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); deps.add(BuildDependency.createOrGet(ourContent, decisions.get(el).getDecision(LayoutDecision.Kind.OFFSET))); deps.add(BuildDependency.createOrGet(ourContent, decisions.get(el).getDecision(LayoutDecision.Kind.SIZE))); return deps; }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this); // our content depends on the offset and size of strtab, and offset of symtab LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT); LayoutDecision strtabSize = decisions.get(symtab.strtab).getDecision(LayoutDecision.Kind.SIZE); LayoutDecision strtabOffset = decisions.get(symtab.strtab).getDecision(LayoutDecision.Kind.OFFSET); LayoutDecision symtabOffset = decisions.get(symtab).getDecision(LayoutDecision.Kind.OFFSET); deps.add(BuildDependency.createOrGet(ourContent, strtabSize)); deps.add(BuildDependency.createOrGet(ourContent, strtabOffset)); deps.add(BuildDependency.createOrGet(ourContent, symtabOffset)); return deps; } }
protected static List<LayoutDecision> minimalDecisionValues(Map<Element, LayoutDecisionMap> alreadyDecided, LayoutDecision.Kind k, Comparator<LayoutDecision> cmp) { ArrayList<LayoutDecision> currentMin = null; for (Map.Entry<Element, LayoutDecisionMap> eOuter : alreadyDecided.entrySet()) { LayoutDecision decisionToCompare = eOuter.getValue().getDecision(k); // if an element has no decision of the requested kind, just skip it if (decisionToCompare == null) { continue; } Integer compareResult = currentMin == null ? null : cmp.compare(decisionToCompare, currentMin.get(0)); if (currentMin == null || compareResult < 0) { // replace current min with new equivalence class currentMin = new ArrayList<>(1); currentMin.add(decisionToCompare); } else if (compareResult == 0) { currentMin.add(decisionToCompare); } } return currentMin; }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { // The Header depends on the section count and symbol table size and offset. // We don't use the default dependencies, because our offset mustn't depend on anything. HashSet<BuildDependency> dependencies = new HashSet<>(); LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT); LayoutDecision ourOffset = decisions.get(this).getDecision(LayoutDecision.Kind.OFFSET); LayoutDecision ourSize = decisions.get(this).getDecision(LayoutDecision.Kind.SIZE); LayoutDecision shtSize = decisions.get(sht).getDecision(LayoutDecision.Kind.SIZE); LayoutDecision symtOffset = decisions.get(symtab).getDecision(LayoutDecision.Kind.OFFSET); LayoutDecision symtSize = decisions.get(symtab).getDecision(LayoutDecision.Kind.SIZE); // Mark that our offset depends on our size. dependencies.add(BuildDependency.createOrGet(ourOffset, ourSize)); dependencies.add(BuildDependency.createOrGet(ourContent, shtSize)); dependencies.add(BuildDependency.createOrGet(ourContent, symtOffset)); dependencies.add(BuildDependency.createOrGet(ourContent, symtSize)); return dependencies; }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this); // our content (but not our size) depends on the offsets and sizes of every text section LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT); for (Section s : getSections()) { MachOSection ms = (MachOSection) s; if (ms.flags.contains(SectionFlag.SOME_INSTRUCTIONS)) { deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.OFFSET))); deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.SIZE))); } } return deps; }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { ArrayList<BuildDependency> ourDeps = new ArrayList<>(ObjectFile.defaultDependencies(decisions, this)); // we depend on the contents of our strtab ourDeps.add(BuildDependency.createOrGet(decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT), decisions.get(strtab).getDecision(LayoutDecision.Kind.CONTENT))); // if we're dynamic, we also depend on vaddrs of any sections into which our symbols refer if (isDynamic()) { Set<ELFSection> referencedSections = new HashSet<>(); for (Entry ent : entries) { ELFSection es = ent.getReferencedSection(); if (es != null) { referencedSections.add(es); } } for (ELFSection es : referencedSections) { ourDeps.add(BuildDependency.createOrGet(decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT), decisions.get(es).getDecision(LayoutDecision.Kind.VADDR))); } } return ourDeps; }
@Override public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this); // our content depends on strtab content LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT); LayoutDecision strtabContent = decisions.get(strtab).getDecision(LayoutDecision.Kind.CONTENT); deps.add(BuildDependency.createOrGet(ourContent, strtabContent)); /* * We also depend on the vaddr of any referenced defined symbol. It doesn't matter whether * we're dynamic! Every Mach-O section has a vaddr, even in a relocatable file. */ for (Entry e : entries) { Section s = e.getDefinedSection(); if (s != null) { deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.VADDR))); } } return deps; }
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 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 Set<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) { HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, getElement()); LayoutDecision ourContent = decisions.get(getElement()).getDecision(LayoutDecision.Kind.CONTENT); LayoutDecision ourVaddr = decisions.get(getElement()).getDecision(LayoutDecision.Kind.VADDR); LayoutDecision rodataVaddr = decisions.get(getRodataSection()).getDecision(LayoutDecision.Kind.VADDR); deps.add(BuildDependency.createOrGet(ourContent, ourVaddr)); deps.add(BuildDependency.createOrGet(ourContent, rodataVaddr)); return deps; }
@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); } }
@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; } } }
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; }
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; }