package com.oracle.objectfile.macho;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.oracle.objectfile.BasicProgbitsSectionImpl;
import com.oracle.objectfile.BuildDependency;
import com.oracle.objectfile.ElementImpl;
import com.oracle.objectfile.ElementList;
import com.oracle.objectfile.LayoutDecision;
import com.oracle.objectfile.LayoutDecisionMap;
import com.oracle.objectfile.ObjectFile;
import com.oracle.objectfile.SymbolTable;
import com.oracle.objectfile.io.AssemblyBuffer;
import com.oracle.objectfile.io.OutputAssembler;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
public final class MachOObjectFile extends ObjectFile {
private static final int MAGIC = 0xfeedfacf;
private static final int CIGAM = 0xcffaedfe;
private static final ByteOrder nativeOrder = ByteOrder.nativeOrder();
private static final ByteOrder oppositeOrder = (nativeOrder == ByteOrder.BIG_ENDIAN) ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
final MachOCpuType cpuType;
final int cpuSubType;
private final MachOHeader ;
private ByteOrder fileByteOrder;
MachORelocationElement relocs;
public MachOObjectFile(int pageSize) {
this(pageSize, MachOCpuType.from(ImageSingletons.lookup(Platform.class).getArchitecture()));
}
public MachOObjectFile(int pageSize, MachOCpuType cpuType) {
super(pageSize);
this.cpuType = cpuType;
switch (cpuType) {
case X86_64:
cpuSubType = 3;
break;
default:
cpuSubType = 0;
}
header = new MachOHeader("MachOHeader");
setByteOrder(ByteOrder.nativeOrder());
Segment64Command segment = new Segment64Command("MachOUnnamedSegment", getUnnamedSegmentName());
segment.initprot = EnumSet.of(VMProt.READ, VMProt.WRITE, VMProt.EXECUTE);
segment.maxprot = EnumSet.of(VMProt.READ, VMProt.WRITE, VMProt.EXECUTE);
createSymbolTable();
assert getSymbolTable() != null;
LoadCommand functionStarts = new FunctionStartsCommand("MachOFunctionStartsCommand");
assert loadCommands.otherCommands.contains(functionStarts);
}
@Override
public Format getFormat() {
return Format.MACH_O;
}
protected static String getUnnamedSegmentName() {
return "";
}
@Override
protected ElementList createElementList() {
return new MachOElementList();
}
@Override
public ByteOrder getByteOrder() {
return fileByteOrder;
}
@Override
public void setByteOrder(ByteOrder byteOrder) {
this.fileByteOrder = byteOrder;
}
@Override
protected int initialVaddr() {
return super.initialVaddr();
}
@Override
public int getWordSizeInBytes() {
return 8;
}
@Override
public boolean shouldRecordDebugRelocations() {
return false;
}
@Override
public Symbol createDefinedSymbol(String name, Element baseSection, long position, int size, boolean isCode, boolean isGlobal) {
MachOSymtab symtab = (MachOSymtab) getOrCreateSymbolTable();
return symtab.newDefinedEntry(name, (MachOSection) baseSection, position, size, isGlobal, isCode);
}
@Override
public Symbol createUndefinedSymbol(String name, int size, boolean isCode) {
MachOSymtab symtab = (MachOSymtab) getOrCreateSymbolTable();
return symtab.newUndefinedEntry(name, isCode);
}
@Override
protected Segment64Command getOrCreateSegment(String segmentNameOrNull, String sectionName, boolean writable, boolean executable) {
final String segmentName = (segmentNameOrNull != null) ? segmentNameOrNull : getUnnamedSegmentName();
Segment64Command nonNullSegment = (Segment64Command) findSegmentByName(segmentName);
if (nonNullSegment != null) {
if (nonNullSegment.isWritable() != writable) {
nonNullSegment.initprot.add(VMProt.WRITE);
nonNullSegment.maxprot.add(VMProt.WRITE);
}
if (nonNullSegment.isExecutable() != executable) {
nonNullSegment.initprot.add(VMProt.EXECUTE);
nonNullSegment.maxprot.add(VMProt.EXECUTE);
}
} else {
nonNullSegment = new Segment64Command(sectionName, segmentName);
nonNullSegment.initprot = EnumSet.of(VMProt.READ);
if (writable) {
nonNullSegment.initprot.add(VMProt.WRITE);
}
if (executable) {
nonNullSegment.initprot.add(VMProt.EXECUTE);
}
nonNullSegment.maxprot = nonNullSegment.initprot;
assert loadCommands.otherCommands.contains(nonNullSegment);
}
assert nonNullSegment != null;
return nonNullSegment;
}
@Override
public MachOZeroFillSection newNobitsSection(Segment segment, String name, NobitsSectionImpl impl) {
assert segment != null && impl != null;
MachOZeroFillSection zeroFill = new MachOZeroFillSection(this, name, (Segment64Command) segment, impl);
impl.setElement(zeroFill);
return zeroFill;
}
@Override
public MachORegularSection newProgbitsSection(Segment segment, String name, int alignment, boolean writable, boolean executable, ProgbitsSectionImpl impl) {
assert segment != null;
EnumSet<SectionFlag> sectionFlags = EnumSet.noneOf(SectionFlag.class);
if (executable) {
sectionFlags.add(SectionFlag.SOME_INSTRUCTIONS);
}
MachORegularSection regular = new MachORegularSection(this, name, alignment, (Segment64Command) segment, impl, sectionFlags);
impl.setElement(regular);
if (executable) {
((Segment64Command) segment).initprot.add(VMProt.EXECUTE);
}
if (writable) {
((Segment64Command) segment).initprot.add(VMProt.WRITE);
}
return regular;
}
@Override
public MachOUserDefinedSection newUserDefinedSection(Segment segment, String name, int alignment, ElementImpl impl) {
assert segment != null;
ElementImpl ourImpl;
if (impl == null) {
ourImpl = new BasicProgbitsSectionImpl((Section) null);
} else {
ourImpl = impl;
}
MachOUserDefinedSection userDefined = new MachOUserDefinedSection(this, name, alignment, (Segment64Command) segment, SectionType.REGULAR, ourImpl);
ourImpl.setElement(userDefined);
return userDefined;
}
private final class MachOElementList extends ElementList {
@Override
public int sectionIndexToElementIndex(int sectionIndex) {
int i = 0;
Iterator<Section> it = sectionsIterator();
while (it.hasNext()) {
Section s = it.next();
if (sectionIndex == i) {
return elements.indexOf(s);
}
++i;
}
return -1;
}
@Override
public Iterator<Section> sectionsIterator() {
return getSegments().stream()
.flatMap(segment -> ((Segment64Command) segment).elementsInSegment.stream())
.filter(element -> element instanceof Section)
.map(element -> (Section) element).iterator();
}
}
public enum FileType {
OBJECT(0x1),
EXECUTE(0x2),
FVMLIB(0x3),
PRELOAD(0x5),
DYLIB(0x6),
DYLINKER(0x7),
BUNDLE(0x8),
DYLIB_STUB(0x9),
DSYM(0xa),
KEXT_BUNDLE(0xb);
final int value;
FileType(int value) {
this.value = value;
}
}
enum Flag implements ValueEnum {
NOUNDEFS(0x1),
INCRLINK(0x2),
DYLDLINK(0x4),
BINDATLOAD(0x8),
PREBOUND(0x10),
SPLIT_SEGS(0x20),
LAZY_INIT(0x40),
TWOLEVEL(0x80),
FORCE_FLAT(0x100),
NOMULTIDEFS(0x200),
NOFIXPREBINDING(0x400),
PREBINDABLE(0x800),
ALLMODSBOUND(0x1000),
SUBSECTIONS_VIA_SYMBOLS(0x2000),
CANONICAL(0x4000);
private final int value;
Flag(int value) {
this.value = value;
}
@Override
public long value() {
return value;
}
}
static class {
(int magic, MachOCpuType cpuType, int cpuSubtype, int ncmds, int sizeOfCmds, int flags) {
this.magic = magic;
this.cpuType = cpuType;
this.cpuSubtype = cpuSubtype;
this.ncmds = ncmds;
this.sizeOfCmds = sizeOfCmds;
this.flags = flags;
}
() {
}
private int ;
private MachOCpuType ;
private int ;
private int ;
private int ;
private int ;
public void (OutputAssembler out) {
int startPos = out.pos();
assert this.magic == MAGIC || this.magic == CIGAM;
out.setByteOrder(ByteOrder.nativeOrder());
out.write4Byte(this.magic);
out.setByteOrder(this.magic == MAGIC ? nativeOrder : oppositeOrder);
out.write4Byte(cpuType.toInt());
out.write4Byte(cpuSubtype);
out.write4Byte(FileType.OBJECT.value);
out.write4Byte(ncmds);
out.write4Byte(sizeOfCmds);
out.write4Byte(flags);
out.write4Byte(0);
assert out.pos() - startPos == HEADER_SIZE;
}
public static final int = 32;
public int () {
return HEADER_SIZE;
}
}
private LoadCommandList loadCommands = new LoadCommandList();
private class LoadCommandList implements Iterable<LoadCommand> {
LinkEditSegment64Command linkEditCommand;
List<LoadCommand> otherCommands = new ArrayList<>();
private int size() {
return otherCommands.size() + ((linkEditCommand == null) ? 0 : 1);
}
@Override
public Iterator<LoadCommand> iterator() {
return stream().iterator();
}
private Stream<LoadCommand> stream() {
if (linkEditCommand != null) {
return Stream.concat(otherCommands.stream(), Stream.of(linkEditCommand));
}
return otherCommands.stream();
}
private void add(LoadCommand arg) {
if (arg instanceof LinkEditSegment64Command) {
assert linkEditCommand == null : "cannot have more than one __LINKEDIT segment";
linkEditCommand = (LinkEditSegment64Command) arg;
} else {
otherCommands.add(arg);
}
}
}
public LoadCommand getLoadCommand(LoadCommandKind k) {
if (k == LoadCommandKind.SEGMENT_64) {
throw new IllegalArgumentException("use getSegments() to get segments");
}
for (LoadCommand cmd : loadCommands) {
if (cmd.cmdKind == k) {
return cmd;
}
}
return null;
}
public Segment64Command getLinkEditSegment() {
final Segment64Command result = (Segment64Command) findSegmentByName(getUnnamedSegmentName());
return result;
}
public MachORelocationElement getRelocationElement() {
return relocs;
}
public MachORelocationElement getOrCreateRelocationElement(@SuppressWarnings("unused") boolean useImplicitAddend) {
if (relocs == null) {
final Segment64Command containingSegment = getOrCreateSegment(getUnnamedSegmentName(), null, false, false);
relocs = new MachORelocationElement(containingSegment);
}
return relocs;
}
@Override
public Set<Segment> getSegments() {
return loadCommands.stream().filter(loadCmd -> loadCmd instanceof Segment).map(loadCmd -> (Segment) loadCmd).collect(Collectors.toSet());
}
class extends Header {
(String name) {
super(name);
}
@Override
public byte[] (Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
OutputAssembler out = AssemblyBuffer.createOutputAssembler(ByteBuffer.allocate(65536).order(getOwner().getByteOrder()));
out.skip((new HeaderStruct()).getWrittenSize());
out.align(8);
int loadCommandsSizeInBytes = 0;
for (LoadCommand cmd : loadCommands) {
loadCommandsSizeInBytes += (int) alreadyDecided.get(cmd).getDecidedValue(LayoutDecision.Kind.SIZE);
}
out.pushSeek(0);
new HeaderStruct(getOwner().getByteOrder() == nativeOrder ? MAGIC : CIGAM, cpuType, MachOObjectFile.this.cpuSubType, loadCommands.size(),
loadCommandsSizeInBytes, (int) ObjectFile.flagSetAsLong(MachOObjectFile.this.flags)).write(out);
out.pop();
return out.getBlob();
}
@Override
public int (Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return HeaderStruct.HEADER_SIZE;
}
@Override
public Iterable<BuildDependency> (Map<Element, LayoutDecisionMap> decisions) {
HashSet<BuildDependency> deps = new HashSet<>();
Segment prevNonEmptySegment = null;
for (Segment s : getSegments()) {
Element prev = null;
for (Element e : s) {
assert !(e instanceof Header);
if (prev != null) {
assert e != prev;
deps.add(BuildDependency.createOrGet(decisions.get(e).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(prev).getDecision(LayoutDecision.Kind.OFFSET)));
}
prev = e;
}
if (s.size() > 0 && prevNonEmptySegment != null) {
assert prevNonEmptySegment != s;
deps.add(BuildDependency.createOrGet(decisions.get(s.get(0)).getDecision(LayoutDecision.Kind.OFFSET),
decisions.get(prevNonEmptySegment.get(prevNonEmptySegment.size() - 1)).getDecision(LayoutDecision.Kind.OFFSET)));
if (s.get(0).isReferenceable() && prevNonEmptySegment.get(prevNonEmptySegment.size() - 1).isReferenceable()) {
deps.add(BuildDependency.createOrGet(decisions.get(s.get(0)).getDecision(LayoutDecision.Kind.VADDR),
decisions.get(prevNonEmptySegment.get(prevNonEmptySegment.size() - 1)).getDecision(LayoutDecision.Kind.VADDR)));
}
}
if (s.size() > 0) {
prevNonEmptySegment = s;
}
}
for (LoadCommand cmd : loadCommands) {
deps.add(BuildDependency.createOrGet(decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT), decisions.get(cmd).getDecision(LayoutDecision.Kind.SIZE)));
}
deps.add(BuildDependency.createOrGet(decisions.get(this).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(this).getDecision(LayoutDecision.Kind.SIZE)));
for (Segment seg : getSegments()) {
for (Element el : seg) {
for (LoadCommand cmd : loadCommands) {
deps.add(BuildDependency.createOrGet(decisions.get(el).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(cmd).getDecision(LayoutDecision.Kind.OFFSET)));
}
}
}
Segment64Command previousSegCmd = null;
for (Segment seg : getSegments()) {
Segment64Command segCmd = (Segment64Command) seg;
if (previousSegCmd != null) {
deps.add(BuildDependency.createOrGet(decisions.get(segCmd).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(previousSegCmd).getDecision(LayoutDecision.Kind.OFFSET)));
}
previousSegCmd = segCmd;
}
LoadCommand firstNonSegmentCmd = null;
LoadCommand previousCmd = null;
for (LoadCommand cmd : loadCommands) {
if (!(cmd instanceof Segment64Command)) {
if (previousCmd != null) {
deps.add(BuildDependency.createOrGet(decisions.get(cmd).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(previousCmd).getDecision(LayoutDecision.Kind.OFFSET)));
} else {
firstNonSegmentCmd = cmd;
}
previousCmd = cmd;
}
}
if (firstNonSegmentCmd != null) {
assert previousSegCmd != null;
deps.add(BuildDependency.createOrGet(decisions.get(firstNonSegmentCmd).getDecision(LayoutDecision.Kind.OFFSET), decisions.get(previousSegCmd).getDecision(LayoutDecision.Kind.OFFSET)));
}
return deps;
}
}
static Map<Integer, LoadCommandKind> loadCommandKindsByValue = new HashMap<>();
static {
for (LoadCommandKind k : LoadCommandKind.values()) {
loadCommandKindsByValue.put((int) k.getValue(), k);
}
}
public enum LoadCommandKind {
unused0,
SEGMENT {
{
assert getValue() == 0x1;
}
},
SYMTAB {
{
assert getValue() == 0x2;
}
},
SYMSEG {
{
assert getValue() == 0x3;
}
},
THREAD {
{
assert getValue() == 0x4;
}
},
UNIXTHREAD {
{
assert getValue() == 0x5;
}
},
unused6 {
{
assert getValue() == 0x6;
}
},
unused7 {
{
assert getValue() == 0x7;
}
},
unused8 {
{
assert getValue() == 0x8;
}
},
unused9 {
{
assert getValue() == 0x9;
}
},
unuseda {
{
assert getValue() == 0xa;
}
},
DYSYMTAB {
{
assert getValue() == 0xb;
}
},
LOAD_DYLIB {
{
assert getValue() == 0xc;
}
},
ID_DYLIB {
{
assert getValue() == 0xd;
}
},
LOAD_DYLINKER {
{
assert getValue() == 0xe;
}
},
ID_DYLINKER {
{
assert getValue() == 0xf;
}
},
PREBOUND_DYLIB {
{
assert getValue() == 0x10;
}
},
ROUTINES {
{
assert getValue() == 0x11;
}
},
SUB_FRAMEWORK {
{
assert getValue() == 0x12;
}
},
SUB_UMBRELLA {
{
assert getValue() == 0x13;
}
},
SUB_CLIENT {
{
assert getValue() == 0x14;
}
},
SUB_LIBRARY {
{
assert getValue() == 0x15;
}
},
TWOLEVEL_HINTS {
{
assert getValue() == 0x16;
}
},
unused17 {
{
assert getValue() == 0x17;
}
},
unused18 {
{
assert getValue() == (0x18 | 0x80000000L);
}
@Override
public long getValue() {
return (super.getValue() | 0x80000000L);
}
},
SEGMENT_64 {
{
assert getValue() == 0x19;
}
},
ROUTINES_64 {
{
assert getValue() == 0x1a;
}
},
UUID {
{
assert getValue() == 0x1b;
}
},
RPATH {
{
assert getValue() == (0x1c | 0x80000000L);
}
@Override
public long getValue() {
return super.getValue() | 0x80000000L;
}
},
unused1d {
{
assert getValue() == 0x1d;
}
},
unused1e {
{
assert getValue() == 0x1e;
}
},
unused1f {
{
assert getValue() == (0x1f | 0x80000000L);
}
@Override
public long getValue() {
return super.getValue() | 0x80000000L;
}
},
unused20 {
{
assert getValue() == 0x20;
}
},
unused21 {
{
assert getValue() == 0x21;
}
},
DYLD_INFO {
{
assert getValue() == 0x22;
}
},
unused23 {
{
assert getValue() == 0x23;
}
},
VERSION_MIN_MACOS {
{
assert getValue() == 0x24;
}
},
unused25 {
{
assert getValue() == 0x25;
}
},
FUNCTION_STARTS {
{
assert getValue() == 0x26;
}
},
unused27 {
{
assert getValue() == 0x27;
}
},
unused28 {
{
assert getValue() == (0x28 | 0x80000000L);
}
@Override
public long getValue() {
return super.getValue() | 0x80000000L;
}
},
DATA_IN_CODE {
{
assert getValue() == 0x29;
}
},
unused2a {
{
assert getValue() == 0x2a;
}
},
unused2b {
{
assert getValue() == 0x2b;
}
},
REQ_DYLD {
@Override
public long getValue() {
return 0x80000000L;
}
},
DYLD_INFO_ONLY {
@Override
public long getValue() {
return DYLD_INFO.getValue() | 0x80000000L;
}
};
public long getValue() {
return ordinal();
}
}
public abstract class LoadCommand extends Header {
LoadCommandKind cmdKind;
public LoadCommand(String name, LoadCommandKind k) {
super(name);
this.cmdKind = k;
loadCommands.add(this);
}
protected abstract void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided);
@Override
public MachOObjectFile getOwner() {
return (MachOObjectFile) super.getOwner();
}
@Override
public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
OutputAssembler out = AssemblyBuffer.createOutputAssembler(getByteOrder());
int startPos = out.pos();
out.write4Byte((int) cmdKind.getValue());
int sizePos = out.pos();
out.write4Byte(0);
writePayload(out, alreadyDecided);
out.align(8);
int cmdSize = out.pos() - startPos;
out.pushSeek(sizePos);
out.write4Byte(cmdSize);
out.pop();
assert out.pos() == startPos + cmdSize;
return out.getBlob();
}
int sizeBeforePayload() {
return getWrittenSize(0);
}
protected int getWrittenSize(int payloadSize) {
OutputAssembler out = AssemblyBuffer.createOutputAssembler(getByteOrder());
int startPos = out.pos();
out.write4Byte((int) cmdKind.getValue());
out.write4Byte(0);
out.skip(payloadSize);
out.align(8);
return out.pos() - startPos;
}
}
@Override
public Header () {
return header;
}
public class UUIDCommand extends LoadCommand {
byte[] uuidbytes;
public UUIDCommand(String name) {
super(name, LoadCommandKind.UUID);
UUID randomUUID = UUID.randomUUID();
OutputAssembler oa = AssemblyBuffer.createOutputAssembler(ByteOrder.BIG_ENDIAN);
oa.write8Byte(randomUUID.getMostSignificantBits());
oa.write8Byte(randomUUID.getLeastSignificantBits());
assert oa.pos() == 16;
uuidbytes = oa.getBlob();
assert uuidbytes.length == 16;
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
out.writeBlob(uuidbytes);
}
}
static class DylibStruct {
int stroff;
int timestamp;
int currentVersion;
int compatibilityVersion;
public void write(OutputAssembler out) {
out.write4Byte(stroff);
out.write4Byte(timestamp);
out.write4Byte(currentVersion);
out.write4Byte(compatibilityVersion);
}
DylibStruct(int stroff, int timestamp, int currentVersion, int compatibilityVersion) {
this.stroff = stroff;
this.timestamp = timestamp;
this.currentVersion = currentVersion;
this.compatibilityVersion = compatibilityVersion;
}
public int getWrittenSize() {
OutputAssembler oa = AssemblyBuffer.createOutputAssembler();
write(oa);
return oa.pos();
}
}
public abstract class AbstractDylibCommand extends LoadCommand {
String libName = "blah.dylib";
public AbstractDylibCommand(String name, LoadCommandKind k, String libName) {
super(name, k);
this.libName = libName;
}
public AbstractDylibCommand(String name, LoadCommandKind k) {
super(name, k);
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
int loadCommandHeaderLength = getWrittenSize(0);
DylibStruct s = new DylibStruct(0, 0, 0, 0);
s.stroff = loadCommandHeaderLength + s.getWrittenSize();
s.write(out);
out.writeString(libName);
}
public void setLibName(String libName) {
this.libName = libName;
}
}
public class IDDylibCommand extends AbstractDylibCommand {
public IDDylibCommand(String name, String libName) {
super(name, LoadCommandKind.ID_DYLIB);
this.libName = libName;
}
public IDDylibCommand(String name) {
super(name, LoadCommandKind.ID_DYLIB);
}
}
static int encodedLengthLEB128(long value) {
OutputAssembler dummy = AssemblyBuffer.createOutputAssembler();
dummy.writeLEB128(value);
return dummy.pos();
}
class VersionMinMacOSCommand extends LoadCommand {
VersionMinMacOSCommand(String name) {
super(name, LoadCommandKind.VERSION_MIN_MACOS);
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
out.writeByte((byte) 0);
out.writeByte((byte) 7);
out.writeByte((byte) 10);
out.writeByte((byte) 0);
out.write4Byte(0);
}
}
public class LoadDylibCommand extends AbstractDylibCommand {
int timestamp;
int currentVersion;
int compatVersion;
public LoadDylibCommand(String name, String libname, int timestamp, int currentVersion, int compatVersion) {
super(name, LoadCommandKind.LOAD_DYLIB);
this.libName = libname;
this.timestamp = timestamp;
this.currentVersion = currentVersion;
this.compatVersion = compatVersion;
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
int loadCommandHeaderSize = getWrittenSize(0);
DylibStruct s = new DylibStruct(0, timestamp, currentVersion, compatVersion);
s.stroff = loadCommandHeaderSize + s.getWrittenSize();
s.write(out);
out.writeString(libName);
}
}
public class RPathCommand extends LoadCommand {
String dirname;
public RPathCommand(String name, String dirname) {
super(name, LoadCommandKind.RPATH);
this.dirname = dirname;
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
int loadCommandHeaderSize = getWrittenSize(0);
int stroff = loadCommandHeaderSize + 4;
out.write4Byte(stroff);
out.writeString(dirname);
}
}
class FunctionStartsCommand extends LoadCommand {
FunctionStartsElement el;
FunctionStartsCommand(String name) {
super(name, LoadCommandKind.FUNCTION_STARTS);
el = new FunctionStartsElement("MachOFunctionStartsElement");
}
@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
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
int elOffset = (int) alreadyDecided.get(el).getDecidedValue(LayoutDecision.Kind.OFFSET);
int elSize = (int) alreadyDecided.get(el).getDecidedValue(LayoutDecision.Kind.SIZE);
out.write4Byte(elOffset);
out.write4Byte(elSize);
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return getWrittenSize(8);
}
}
class FunctionStartsElement extends LinkEditElement {
FunctionStartsElement(String name) {
super(name, getLinkEditSegment());
}
@Override
public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
OutputAssembler out = AssemblyBuffer.createOutputAssembler(getOwner().getByteOrder());
TreeSet<Integer> fileOffsets = new TreeSet<>();
for (Symbol sym : symbolsOfInterest()) {
Section s = sym.getDefinedSection();
assert s != null;
int sectionOffset = (int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.OFFSET);
fileOffsets.add(sectionOffset + (int) sym.getDefinedOffset());
}
Integer previousOffset = null;
for (Integer i : fileOffsets) {
if (previousOffset == null) {
Segment textSegment = null;
for (Segment s : getSegments()) {
if (s.getName().equals("__TEXT")) {
textSegment = s;
break;
}
}
if (textSegment == null) {
break;
}
out.writeLEB128(i);
} else {
out.writeLEB128(i - previousOffset);
}
}
out.writeLEB128(0);
int overapproximation = overapproximateSize();
assert out.pos() <= overapproximation;
out.skip(overapproximation - out.pos());
return out.getBlob();
}
private static final int BIGGEST_INTER_FUNCTION_GAP = 65536;
private int overapproximateSize() {
int size = 1;
for (Symbol sym : symbolsOfInterest()) {
Section s = sym.getDefinedSection();
assert s != null;
int offsetEncodedLength = MachOObjectFile.encodedLengthLEB128(BIGGEST_INTER_FUNCTION_GAP);
size += offsetEncodedLength;
}
return size;
}
@Override
public int getOrDecideSize(java.util.Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
Object decidedContent = alreadyDecided.get(this).getDecidedValue(LayoutDecision.Kind.CONTENT);
assert decidedContent != null;
return ((byte[]) decidedContent).length;
}
@Override
public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) {
HashSet<BuildDependency> deps = ObjectFile.defaultDependencies(decisions, this);
ArrayList<Section> requiredOffsets = new ArrayList<>();
for (LoadCommand c : loadCommands) {
if (c instanceof SymtabCommand) {
SymtabCommand syms = (SymtabCommand) c;
for (Symbol sym : syms.symtab) {
if (sym.isDefined() && sym.isFunction() && !sym.isAbsolute()) {
Section s = sym.getDefinedSection();
assert s != null;
requiredOffsets.add(s);
}
}
}
}
LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT);
for (Section s : requiredOffsets) {
deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.OFFSET)));
}
return deps;
}
private List<Symbol> symbolsOfInterest() {
List<Symbol> ofInterest = new ArrayList<>();
for (LoadCommand c : loadCommands) {
if (c instanceof SymtabCommand) {
SymtabCommand syms = (SymtabCommand) c;
for (Symbol sym : syms.symtab) {
if (sym.isDefined() && sym.isFunction() && !sym.isAbsolute()) {
ofInterest.add(sym);
}
}
}
}
return ofInterest;
}
}
class DataInCodeElement extends LinkEditElement {
class EntryStruct {
int fileOffset;
short length;
short entryKind;
int getWrittenSize() {
return 8;
}
void write(OutputAssembler oa) {
oa.write4Byte(fileOffset);
oa.write2Byte(length);
oa.write2Byte(entryKind);
}
}
DataInCodeElement(String name) {
super(name, getLinkEditSegment());
}
@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);
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 int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
int count = 0;
for (Section s : getSections()) {
MachOSection ms = (MachOSection) s;
if (ms.flags.contains(SectionFlag.SOME_INSTRUCTIONS)) {
++count;
}
}
return count * (new EntryStruct()).getWrittenSize();
}
@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));
}
}
Collections.sort(decisionsOfInterest, new IntegerDecisionComparator(false));
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;
ent.write(out);
}
return out.getBlob();
}
}
class DataInCodeCommand extends LoadCommand {
DataInCodeElement el;
DataInCodeCommand(String name) {
super(name, LoadCommandKind.DATA_IN_CODE);
this.el = new DataInCodeElement(name);
}
@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);
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;
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
out.write4Byte((int) alreadyDecided.get(el).getDecidedValue(LayoutDecision.Kind.OFFSET));
out.write4Byte((int) alreadyDecided.get(el).getDecidedValue(LayoutDecision.Kind.SIZE));
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return getWrittenSize(8);
}
}
public enum SectionType {
REGULAR,
ZEROFILL,
LITERALS_CSTRING,
LITERALS_4BYTE,
LITERALS_8BYTE,
LITERALS_POINTER,
NON_LAZY_SYMBOL_POINTERS,
LAZY_SYMBOL_POINTERS,
SYMBOL_STUBS,
MOD_INIT_FUNC_POINTERS,
MOD_TERM_FUNC_POINTERS,
COALESCED,
GB_ZEROFILL;
static SectionType fromFlags(int flags) {
return values()[flags & 0xff];
}
int getValue() {
return ordinal();
}
}
public enum SectionFlag implements ValueEnum {
LOC_RELOC(0x00000100),
EXT_RELOC(0x00000200),
SOME_INSTRUCTIONS(0x00000400),
DEBUG(0x02000000),
SELF_MODIFYING_CODE(0x04000000),
LIVE_SUPPORT(0x08000000),
NO_DEAD_STRIP(0x10000000),
STRIP_STATIC_SYMS(0x20000000),
NO_TOC(0x40000000),
PURE_INSTRUCTIONS(0x80000000);
private final int value;
SectionFlag(int value) {
this.value = value;
}
@Override
public long value() {
return value;
}
}
public abstract class MachOSection extends ObjectFile.Section {
SectionType type;
EnumSet<SectionFlag> flags;
Segment64Command segment;
String destinationSegmentName;
@Override
public boolean isLoadable() {
if (getImpl() == this) {
return true;
}
return getImpl().isLoadable();
}
@Override
public boolean isReferenceable() {
if (getImpl() == this) {
return isLoadable();
}
return getImpl().isReferenceable();
}
public MachOSection(String name, int alignment, Segment64Command segment, SectionType t, EnumSet<SectionFlag> flags) {
super(name, alignment);
if (name.length() > 16) {
throw new IllegalArgumentException("Mach-O section names may not be longer than 16 characters");
}
this.type = SectionType.REGULAR;
assert t.equals(this.type);
this.flags = flags;
int firstNonSectionPosition = 0;
while (firstNonSectionPosition < segment.size() && segment.get(firstNonSectionPosition) instanceof Section) {
++firstNonSectionPosition;
}
segment.add(firstNonSectionPosition, this);
this.segment = segment;
if (name.contains("debug")) {
destinationSegmentName = "__DWARF";
} else if (flags.contains(
SectionFlag.SOME_INSTRUCTIONS) ) {
destinationSegmentName = "__TEXT";
} else {
destinationSegmentName = "__DATA";
}
}
public Segment getSegment() {
return segment;
}
public void setDestinationSegmentName(String dest) {
this.destinationSegmentName = dest;
}
@Override
public MachOObjectFile getOwner() {
return (MachOObjectFile) super.getOwner();
}
}
public class SymtabCommand extends LoadCommand {
MachOSymtab symtab;
public SymtabCommand(String name, MachOSymtab symtab) {
super(name, LoadCommandKind.SYMTAB);
this.symtab = symtab;
}
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
int symtabOffset = (int) alreadyDecided.get(symtab).getDecidedValue(LayoutDecision.Kind.OFFSET);
int symtabEntriesCount = symtab.getEntryCount();
int strtabOffset = (int) alreadyDecided.get(symtab.strtab).getDecidedValue(LayoutDecision.Kind.OFFSET);
int strtabSize = (int) alreadyDecided.get(symtab.strtab).getDecidedValue(LayoutDecision.Kind.SIZE);
writePayloadFields(out, symtabOffset, symtabEntriesCount, strtabOffset, strtabSize);
}
private void writePayloadFields(OutputAssembler out, int symtabOffset, int symtabEntriesCount, int strtabOffset, int strtabSize) {
out.write4Byte(symtabOffset);
out.write4Byte(symtabEntriesCount);
out.write4Byte(strtabOffset);
out.write4Byte(strtabSize);
}
private int getPayloadWrittenSize() {
OutputAssembler oa = AssemblyBuffer.createOutputAssembler();
writePayloadFields(oa, 0, 0, 0, 0);
return oa.pos();
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return getWrittenSize(getPayloadWrittenSize());
}
@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);
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;
}
}
public class DySymtabCommand extends LoadCommand {
MachOSymtab symtab;
public DySymtabCommand(String name, MachOSymtab symtab) {
super(name, LoadCommandKind.DYSYMTAB);
this.symtab = symtab;
}
private static final int PAYLOAD_SIZE = 18 * 4;
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
int startPos = out.pos();
out.write4Byte(symtab.firstLocal());
out.write4Byte(symtab.nLocals());
out.write4Byte(symtab.firstExtDef());
out.write4Byte(symtab.nExtDef());
out.write4Byte(symtab.firstUndef());
out.write4Byte(symtab.nUndef());
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
out.write4Byte(0);
assert out.pos() == startPos + PAYLOAD_SIZE;
}
@Override
public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) {
HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this);
return deps;
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return getWrittenSize(PAYLOAD_SIZE);
}
}
public enum VMProt implements ValueEnum {
READ(0x01),
WRITE(0x02),
EXECUTE(0x04);
private final int value;
VMProt(int value) {
this.value = value;
}
@Override
public long value() {
return value;
}
}
public static class SectionInfoStruct {
public static final int DEFAULT_SIZE = 80;
String sectName;
String segName;
long addr;
long size;
int offset;
int align;
int reloff;
int nreloc;
int flags;
int reserved1;
int reserved2;
public SectionInfoStruct(String sectName, String segName, long addr, long size, int offset, int align, int reloff, int nreloc, int flags, int reserved1, int reserved2) {
super();
this.sectName = sectName;
this.segName = segName;
this.addr = addr;
this.size = size;
this.offset = offset;
this.align = align;
this.reloff = reloff;
this.nreloc = nreloc;
this.flags = flags;
this.reserved1 = reserved1;
this.reserved2 = reserved2;
}
public void write(OutputAssembler db) {
db.writeStringPadded(sectName, 16);
db.writeStringPadded(segName, 16);
db.write8Byte(addr);
db.write8Byte(size);
db.write4Byte(offset);
db.write4Byte(align);
db.write4Byte(reloff);
db.write4Byte(nreloc);
db.write4Byte(flags);
db.write4Byte(reserved1);
db.write4Byte(reserved2);
db.align(8);
}
@Override
public String toString() {
return String.format("Section Info, name %s, segment %s", sectName, segName) +
String.format("\n address %#x, size %d (%2$#x), offset %d (%3$#x), align %#x", addr, size, offset, align) +
String.format("\n first relocation entry at %d (%1$#x), number of relocation entries %d", reloff, nreloc) +
String.format("\n flags %#x, reserved %d %d", flags, reserved1, reserved2);
}
}
private int minimumFileSize = 0;
@Override
protected int getMinimumFileSize() {
return minimumFileSize;
}
@Override
public int bake(List<Element> sortedObjectFileElements) {
minimumFileSize = 0;
return super.bake(sortedObjectFileElements);
}
int segmentVaddrGivenFirstSectionVaddr(int sectionVaddr) {
int effectiveMinVaddr = ((sectionVaddr >> getPageSizeShift()) << getPageSizeShift());
assert effectiveMinVaddr <= sectionVaddr;
return effectiveMinVaddr;
}
public class Segment64Command extends LoadCommand implements Segment {
String segname;
EnumSet<VMProt> maxprot = EnumSet.noneOf(VMProt.class);
EnumSet<VMProt> initprot = EnumSet.noneOf(VMProt.class);
int flags;
@Override
public String getName() {
return segname;
}
@Override
public void setName(String name) {
this.segname = name;
}
List<Element> elementsInSegment = new ArrayList<>();
@Override
public boolean isExecutable() {
return initprot.contains(VMProt.EXECUTE);
}
@Override
public boolean isWritable() {
return initprot.contains(VMProt.WRITE);
}
List<SectionInfoStruct> readStructs = new ArrayList<>();
public Segment64Command(String name, String segmentName) {
super(name, LoadCommandKind.SEGMENT_64);
this.segname = segmentName;
}
@Override
protected void writePayload(OutputAssembler db, final Map<Element, LayoutDecisionMap> alreadyDecided) {
db.writeStringPadded(segname, 16);
Map<Element, LayoutDecisionMap> decidedAboutOurElements = new HashMap<>();
for (Element e : elementsInSegment) {
if (e instanceof MachOSection) {
decidedAboutOurElements.put(e, alreadyDecided.get(e));
}
}
List<LayoutDecision> minVaddrDecisions = ObjectFile.minimalDecisionValues(decidedAboutOurElements, LayoutDecision.Kind.VADDR, new IntegerDecisionComparator(true));
int minVaddr = (minVaddrDecisions == null || minVaddrDecisions.size() == 0) ? 0 : (int) minVaddrDecisions.get(0).getValue();
List<LayoutDecision> maxVaddrDecisions = ObjectFile.maximalDecisionValues(decidedAboutOurElements, LayoutDecision.Kind.VADDR, new IntegerDecisionComparator(false));
Collections.sort(maxVaddrDecisions, new SizeTiebreakComparator(decidedAboutOurElements, false));
LayoutDecision maxVaddrDecision = maxVaddrDecisions.get(maxVaddrDecisions.size() - 1);
int maxVaddr = (maxVaddrDecision == null) ? 0 : ((int) maxVaddrDecision.getValue() + maxVaddrDecision.getElement().getMemSize(alreadyDecided));
int vmSize = ObjectFile.nextIntegerMultiple(maxVaddr - minVaddr, getPageSize());
@SuppressWarnings("unused")
Element firstSectionByVaddr = (minVaddrDecisions == null) ? null : minVaddrDecisions.get(0).getElement();
@SuppressWarnings("unused")
Element lastSectionByVaddr = (maxVaddrDecision == null) ? null : maxVaddrDecision.getElement();
List<LayoutDecision> minOffsetDecisions = ObjectFile.minimalDecisionValues(decidedAboutOurElements, LayoutDecision.Kind.OFFSET, new IntegerDecisionComparator(true));
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));
Collections.sort(maxOffsetDecisions, new SizeTiebreakComparator(decidedAboutOurElements, false));
LayoutDecision maxOffsetDecision = maxOffsetDecisions.get(maxOffsetDecisions.size() - 1);
Element firstElementByOffset = (minOffsetDecisions == null) ? null : minOffsetDecisions.get(0).getElement();
@SuppressWarnings("unused")
Element lastElementByOffset = (maxOffsetDecision == null) ? null : maxOffsetDecision.getElement();
int fileOffset = (firstElementByOffset == null) ? 0 : (int) alreadyDecided.get(firstElementByOffset).getDecidedValue(LayoutDecision.Kind.OFFSET);
int maxOffset = (maxOffsetDecision == null) ? 0 : ((int) maxOffsetDecision.getValue() + (int) alreadyDecided.get(maxOffsetDecision.getElement()).getDecidedValue(LayoutDecision.Kind.SIZE));
int fileSize = maxOffset - minOffset;
int effectiveMinVaddr = segmentVaddrGivenFirstSectionVaddr(minVaddr);
assert effectiveMinVaddr >= 0;
int prePadding = minVaddr - effectiveMinVaddr;
int effectiveVmSize = vmSize + prePadding;
int effectiveFileOffset = fileOffset - prePadding;
int effectiveFileSize = fileSize + prePadding;
db.write8Byte(effectiveMinVaddr);
db.write8Byte(effectiveVmSize);
db.write8Byte(effectiveFileOffset);
if (this != getLinkEditSegment()) {
effectiveFileSize = ObjectFile.nextIntegerMultiple(effectiveFileSize, getPageSize());
minimumFileSize = Math.max(minimumFileSize, effectiveFileOffset + effectiveFileSize);
}
db.write8Byte(effectiveFileSize);
db.write4Byte((int) ObjectFile.flagSetAsLong(maxprot));
db.write4Byte((int) ObjectFile.flagSetAsLong(initprot));
int sectionCountPos = db.pos();
db.write4Byte(0);
db.write4Byte(flags);
db.align(8);
int sectionCount = 0;
for (Element el : elementsInSegment) {
if (!(el instanceof Section)) {
continue;
}
++sectionCount;
MachOSection s = (MachOSection) el;
int logAlignment = (int) (Math.log10(s.getAlignment()) / Math.log10(2.0));
MachORelocationElement ourRelocs = null;
if (getLinkEditSegment() != null) {
for (Element e : getLinkEditSegment().elementsInSegment) {
if (e instanceof MachORelocationElement && ((MachORelocationElement) e).relocatesSegment(this)) {
if (ourRelocs == null) {
ourRelocs = (MachORelocationElement) e;
continue;
}
assert false;
}
}
}
assert s.destinationSegmentName != null;
SectionInfoStruct si = new SectionInfoStruct(
s.getName(),
s.destinationSegmentName,
s.getElement().isReferenceable() ? (int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.VADDR) : 0,
(int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.SIZE),
(int) alreadyDecided.get(s).getDecidedValue(LayoutDecision.Kind.OFFSET),
logAlignment,
ourRelocs == null ? 0 : (int) alreadyDecided.get(ourRelocs).getDecidedValue(LayoutDecision.Kind.OFFSET) + ourRelocs.startIndexFor(s) * ourRelocs.encodedEntrySize(),
ourRelocs == null ? 0 : ourRelocs.countFor(s),
(int) ObjectFile.flagSetAsLong(s.flags) | s.type.getValue(),
0,
0);
int startPos = db.pos();
si.write(db);
assert db.pos() - startPos == SectionInfoStruct.DEFAULT_SIZE;
}
db.pushSeek(sectionCountPos);
db.write4Byte(sectionCount);
db.pop();
}
private int sectionsInSegment() {
int count = 0;
for (Element e : elementsInSegment) {
if (e instanceof Section) {
++count;
}
}
return count;
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return 4 + 4 + 16 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + (sectionsInSegment() * SectionInfoStruct.DEFAULT_SIZE);
}
@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);
for (Element s : elementsInSegment) {
deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.SIZE)));
deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.OFFSET)));
if (s.getElement().isReferenceable()) {
deps.add(BuildDependency.createOrGet(ourContent, decisions.get(s).getDecision(LayoutDecision.Kind.VADDR)));
}
}
if ((getName() != null) && (getName().equals("__LINKEDIT"))) {
assert this == loadCommands.linkEditCommand;
} else {
if (getLinkEditSegment() != null) {
for (Element e : getLinkEditSegment().elementsInSegment) {
if (e instanceof MachORelocationElement && ((MachORelocationElement) e).relocatesSegment(this)) {
deps.add(BuildDependency.createOrGet(ourContent, decisions.get(e).getDecision(LayoutDecision.Kind.OFFSET)));
}
}
}
}
return deps;
}
@Override
public void add(int arg0, Element arg1) {
elementsInSegment.add(arg0, arg1);
}
@Override
public boolean add(Element arg0) {
return elementsInSegment.add(arg0);
}
@Override
public boolean addAll(Collection<? extends Element> arg0) {
return elementsInSegment.addAll(arg0);
}
@Override
public boolean addAll(int arg0, Collection<? extends Element> arg1) {
return elementsInSegment.addAll(arg0, arg1);
}
@Override
public void clear() {
elementsInSegment.clear();
}
@Override
public boolean contains(Object arg0) {
return elementsInSegment.contains(arg0);
}
@Override
public boolean containsAll(Collection<?> arg0) {
return elementsInSegment.containsAll(arg0);
}
@Override
public Element get(int arg0) {
return elementsInSegment.get(arg0);
}
@Override
public int indexOf(Object arg0) {
return elementsInSegment.indexOf(arg0);
}
@Override
public boolean isEmpty() {
return elementsInSegment.isEmpty();
}
@Override
public Iterator<Element> iterator() {
return elementsInSegment.iterator();
}
@Override
public int lastIndexOf(Object arg0) {
return elementsInSegment.lastIndexOf(arg0);
}
@Override
public ListIterator<Element> listIterator() {
return elementsInSegment.listIterator();
}
@Override
public ListIterator<Element> listIterator(int arg0) {
return elementsInSegment.listIterator(arg0);
}
@Override
public Element remove(int arg0) {
return elementsInSegment.remove(arg0);
}
@Override
public boolean remove(Object arg0) {
return elementsInSegment.remove(arg0);
}
@Override
public boolean removeAll(Collection<?> arg0) {
return elementsInSegment.removeAll(arg0);
}
@Override
public boolean retainAll(Collection<?> arg0) {
return elementsInSegment.retainAll(arg0);
}
@Override
public Element set(int arg0, Element arg1) {
return elementsInSegment.set(arg0, arg1);
}
@Override
public int size() {
return elementsInSegment.size();
}
@Override
public List<Element> subList(int arg0, int arg1) {
return elementsInSegment.subList(arg0, arg1);
}
@Override
public Object[] toArray() {
return elementsInSegment.toArray();
}
@Override
public <T> T[] toArray(T[] arg0) {
return elementsInSegment.toArray(arg0);
}
}
public class LinkEditSegment64Command extends Segment64Command {
private MachOSymtab symtab;
private MachOStrtab strtab;
public LinkEditSegment64Command() {
super("LinkEditSegment", "__LINKEDIT");
initprot = EnumSet.of(VMProt.READ);
maxprot = EnumSet.of(VMProt.READ, VMProt.WRITE, VMProt.EXECUTE);
}
public MachOSymtab getSymtab() {
return symtab;
}
public MachOStrtab getStrtab() {
return strtab;
}
public MachORelocationElement getRelocations() {
return relocs;
}
}
private EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
public Set<Flag> getFlags() {
return Collections.unmodifiableSet(flags);
}
public void setFlags(EnumSet<Flag> flags) {
this.flags.clear();
this.flags.addAll(flags);
}
protected LinkEditSegment64Command getOrCreateLinkEditSegment() {
if (loadCommands.linkEditCommand != null) {
return loadCommands.linkEditCommand;
} else {
return createLinkEditSegment();
}
}
protected LinkEditSegment64Command createLinkEditSegment() {
return new LinkEditSegment64Command();
}
@Override
protected SymbolTable createSymbolTable() {
assert getSegments().size() == 1;
Segment64Command segment = (Segment64Command) getSegments().iterator().next();
MachOStrtab strtab = new MachOStrtab("MachOStrtab", MachOObjectFile.this, segment);
MachOSymtab symtab = new MachOSymtab("MachOSymtab", this, segment, strtab);
assert segment.contains(strtab);
assert segment.contains(symtab);
SymtabCommand cmd = new SymtabCommand("MachOSymtabCommand", symtab);
assert cmd.symtab == symtab;
return symtab;
}
@Override
public MachOSymtab getSymbolTable() {
Segment segment = null;
final String segmentName = getUnnamedSegmentName();
Set<Segment> segs = getSegments();
for (Segment seg : segs) {
if (seg.getName().equals(segmentName)) {
segment = seg;
break;
}
}
assert segment != null;
for (LoadCommand cmd : loadCommands) {
if (cmd instanceof SymtabCommand) {
MachOSymtab e = ((SymtabCommand) cmd).symtab;
assert segment.contains(e);
return e;
}
}
return null;
}
abstract class LinkEditElement extends Element {
@Override
public ElementImpl getImpl() {
return this;
}
final Segment64Command segment;
LinkEditElement(String name, Segment64Command containingSegment) {
this(name, containingSegment, containingSegment.isEmpty() ? getPageSize() : 1);
}
LinkEditElement(String name, Segment64Command containingSegment, int alignment) {
super(name, alignment);
segment = containingSegment;
containingSegment.add(this);
}
@Override
public boolean isLoadable() {
return true;
}
@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);
}
@Override
public int getOrDecideOffset(Map<Element, LayoutDecisionMap> alreadyDecided, int offsetHint) {
return ObjectFile.defaultGetOrDecideOffset(alreadyDecided, this, offsetHint);
}
}
@SuppressWarnings("unused")
public void addOpaqueLoadCommand(String name, LoadCommandKind k, final byte[] bs) {
new LoadCommand(name, k) {
@Override
protected void writePayload(OutputAssembler out, Map<Element, LayoutDecisionMap> alreadyDecided) {
out.writeBlob(bs);
}
};
}
}