diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java index 3c879bec2cb9..8abf9a154967 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/ObjectFile.java @@ -344,6 +344,12 @@ public static int getRelocationSize(RelocationKind kind) { case DIRECT_4: case PC_RELATIVE_4: case SECREL_4: + case AARCH64_R_AARCH64_ADR_PREL_PG_HI21: + case AARCH64_R_AARCH64_LDST64_ABS_LO12_NC: + case AARCH64_R_AARCH64_LDST32_ABS_LO12_NC: + case AARCH64_R_AARCH64_LDST16_ABS_LO12_NC: + case AARCH64_R_AARCH64_LDST8_ABS_LO12_NC: + case AARCH64_R_AARCH64_ADD_ABS_LO12_NC: return 4; case DIRECT_8: case PC_RELATIVE_8: diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachORelocationElement.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachORelocationElement.java index 12328ce998c7..c75e591083fc 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachORelocationElement.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachORelocationElement.java @@ -54,7 +54,17 @@ private static int compareSectionThenOffset(RelocationInfo p, RelocationInfo q) if (!p.getRelocatedSection().equals(q.getRelocatedSection())) { return p.getRelocatedSection().hashCode() - q.getRelocatedSection().hashCode(); } - return Math.toIntExact(p.getOffset() - q.getOffset()); + if (p.getOffset() != q.getOffset()) { + return Math.toIntExact(p.getOffset() - q.getOffset()); + } + + if (Objects.equals(p.getAddend(), q.getAddend())) { + return 0; + } else if (p.getAddend() == null) { + return 1; + } else { + return -1; + } } private Map infos = new TreeMap<>(MachORelocationElement::compareSectionThenOffset); @@ -168,21 +178,21 @@ final class RelocationInfo implements RelocationRecord, RelocationMethod { private final Symbol sym; private final MachOSection targetSection; private final byte log2length; + private final Long addend; // `null` for kinds other than ADDEND /** - * Construct a relocation record. + * Construct a relocation record for ARM64_RELOC_ADDEND. * * @param containingElement the containing relocation element * @param relocatedSection the section being relocated * @param offset the offset, within the relocated section, of the relocation site - * @param requestedLength the length of the relocation site - * @param kind the kind of relocation to perform at the relocation site - * @param symbolName the symbol against which to relocate + * @param addend the addend to be encoded */ - RelocationInfo(MachORelocationElement containingElement, MachOSection relocatedSection, int offset, int requestedLength, RelocationKind kind, String symbolName, boolean asLocalReloc) { - this.containingElement = containingElement; - this.relocatedSection = relocatedSection; - this.sectionOffset = offset; // gets turned into a vaddr on write-out + public static RelocationInfo newAddend(MachORelocationElement containingElement, MachOSection relocatedSection, int offset, int requestedLength, long addend) { + return new RelocationInfo(containingElement, relocatedSection, offset, requestedLength, addend); + } + + private static byte encodeRequestedLength(int requestedLength) { /* * NOTE: the Mach-O spec claims that r_length == 3 means a 4-byte length and not an 8-byte * length. But it doesn't say how to encode an 8-bytes-long relocation site. And the @@ -196,7 +206,43 @@ final class RelocationInfo implements RelocationRecord, RelocationMethod { if (requestedLength != 8 && requestedLength != 4 && requestedLength != 2 && requestedLength != 1) { throw new IllegalArgumentException("Mach-O cannot represent relocation lengths other than {1,2,4,8} bytes"); } - this.log2length = (byte) ((requestedLength == 8) ? 3 : (requestedLength == 4) ? 2 : (requestedLength == 2) ? 1 : 0); + return (byte) ((requestedLength == 8) ? 3 : (requestedLength == 4) ? 2 : (requestedLength == 2) ? 1 : 0); + } + + /** + * Construct a relocation record for ARM64_RELOC_ADDEND. + * + * @param containingElement the containing relocation element + * @param relocatedSection the section being relocated + * @param offset the offset, within the relocated section, of the relocation site + * @param addend the addend to be encoded + */ + private RelocationInfo(MachORelocationElement containingElement, MachOSection relocatedSection, int offset, int requestedLength, long addend) { + this.containingElement = containingElement; + this.relocatedSection = relocatedSection; + this.sectionOffset = offset; // gets turned into a vaddr on write-out + this.log2length = encodeRequestedLength(requestedLength); + this.kind = RelocationKind.UNKNOWN; // Placeholder + this.targetSection = null; + this.sym = null; + this.addend = addend; + } + + /** + * Construct a relocation record. + * + * @param containingElement the containing relocation element + * @param relocatedSection the section being relocated + * @param offset the offset, within the relocated section, of the relocation site + * @param requestedLength the length of the relocation site + * @param kind the kind of relocation to perform at the relocation site + * @param symbolName the symbol against which to relocate + */ + RelocationInfo(MachORelocationElement containingElement, MachOSection relocatedSection, int offset, int requestedLength, RelocationKind kind, String symbolName, boolean asLocalReloc) { + this.containingElement = containingElement; + this.relocatedSection = relocatedSection; + this.sectionOffset = offset; // gets turned into a vaddr on write-out + this.log2length = encodeRequestedLength(requestedLength); this.kind = kind; SymbolTable symtab = relocatedSection.getOwner().getSymbolTable(); // FIXME: also allow section numbers here, for non-extern symbols @@ -205,6 +251,7 @@ final class RelocationInfo implements RelocationRecord, RelocationMethod { // if the symbol is defined in the same file, i.e. locally, we have a target section assert !asLocalReloc || this.sym.isDefined(); this.targetSection = asLocalReloc ? (MachOSection) this.sym.getDefinedSection() : null; + this.addend = null; } public static int getEncodedSize() { @@ -216,12 +263,18 @@ public void write(OutputAssembler oa, @SuppressWarnings("unused") Map= 4) { throw new IllegalArgumentException("length must be in {1,2,4,8} bytes, so log2length must be in [0,3]"); @@ -275,9 +328,13 @@ public MachOSection getRelocatedSection() { return relocatedSection; } + public Long getAddend() { + return addend; + } + private boolean isExtern() { // we record localness by grabbing the target section (see constructor) - return targetSection == null; + return targetSection == null && addend == null; } private boolean isPCRelative() { @@ -312,6 +369,9 @@ private int getMachORelocationType() { throw new IllegalArgumentException("unknown relocation kind: " + kind); } case ARM64: + if (addend != null) { + return ARM64Reloc.ADDEND.getValue(); + } switch (kind) { case DIRECT_1: case DIRECT_2: @@ -344,7 +404,8 @@ public boolean equals(Object obj) { RelocationInfo other = (RelocationInfo) obj; return sectionOffset == other.sectionOffset && log2length == other.log2length && Objects.equals(containingElement, other.containingElement) && Objects.equals(getRelocatedSection(), other.getRelocatedSection()) && kind == other.kind && - Objects.equals(sym, other.sym) && Objects.equals(targetSection, other.targetSection); + Objects.equals(sym, other.sym) && Objects.equals(targetSection, other.targetSection) && + Objects.equals(addend, other.addend); } return false; } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachOUserDefinedSection.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachOUserDefinedSection.java index 8a98f5e8448f..4611d3635b8e 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachOUserDefinedSection.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/macho/MachOUserDefinedSection.java @@ -43,6 +43,7 @@ import com.oracle.objectfile.macho.MachOObjectFile.SectionFlag; import com.oracle.objectfile.macho.MachOObjectFile.SectionType; import com.oracle.objectfile.macho.MachOObjectFile.Segment64Command; +import org.graalvm.compiler.debug.GraalError; /** * @see com.oracle.objectfile.elf.ELFUserDefinedSection @@ -164,42 +165,75 @@ public void markRelocationSite(int offset, ByteBuffer bb, RelocationKind k, Stri AssemblyBuffer sbb = new AssemblyBuffer(bb); sbb.setByteOrder(getOwner().getByteOrder()); sbb.pushSeek(offset); - /* - * NOTE: Mach-O does not support explicit addends, and inline addends are applied even - * during dynamic linking. So if the caller supplies an explicit addend, we turn it into an - * implicit one by updating our content. - */ + int length = ObjectFile.RelocationKind.getRelocationSize(k); - long currentInlineAddendValue = sbb.readTruncatedLong(length); - long desiredInlineAddendValue; - if (explicitAddend != null) { + + if (getOwner().cpuType == MachOCpuType.X86_64) { /* - * This assertion is conservatively disallowing double-addend (could - * "add currentValue to explicitAddend"), because that seems more likely to be a bug - * than a feature. + * NOTE: X86_64 Mach-O does not support explicit addends, and inline addends are applied + * even during dynamic linking. So if the caller supplies an explicit addend, we turn it + * into an implicit one by updating our content. */ - assert currentInlineAddendValue == 0; - desiredInlineAddendValue = explicitAddend; - } else { - desiredInlineAddendValue = currentInlineAddendValue; - } + long currentInlineAddendValue = sbb.readTruncatedLong(length); + long desiredInlineAddendValue; + if (explicitAddend != null) { + /* + * This assertion is conservatively disallowing double-addend (could + * "add currentValue to explicitAddend"), because that seems more likely to be a bug + * than a feature. + */ + assert currentInlineAddendValue == 0; + desiredInlineAddendValue = explicitAddend; + } else { + desiredInlineAddendValue = currentInlineAddendValue; + } - /* - * One more complication: for PC-relative relocation, at least on x86-64, Mach-O linkers - * (both AOT ld and dyld) adjust the calculation to compensate for the fact that it's the - * *next* instruction that the PC-relative reference gets resolved against. Note that ELF - * doesn't do this compensation. Our interface duplicates the ELF behaviour, so we have to - * act against this Mach-O-specific fixup here, by *adding* a little to the addend. The - * amount we add is always the length in bytes of the relocation site (since on x86-64 the - * reference is always the last field in a PC-relative instruction). - */ - if (RelocationKind.isPCRelative(k)) { - desiredInlineAddendValue += length; - } + /* + * One more complication: for PC-relative relocation, at least on x86-64, Mach-O linkers + * (both AOT ld and dyld) adjust the calculation to compensate for the fact that it's + * the *next* instruction that the PC-relative reference gets resolved against. Note + * that ELF doesn't do this compensation. Our interface duplicates the ELF behaviour, so + * we have to act against this Mach-O-specific fixup here, by *adding* a little to the + * addend. The amount we add is always the length in bytes of the relocation site (since + * on x86-64 the reference is always the last field in a PC-relative instruction). + */ + if (RelocationKind.isPCRelative(k)) { + desiredInlineAddendValue += length; + } - // Write the inline addend back to the buffer. - sbb.seek(offset); - sbb.writeTruncatedLong(desiredInlineAddendValue, length); + // Write the inline addend back to the buffer. + sbb.seek(offset); + sbb.writeTruncatedLong(desiredInlineAddendValue, length); + } else { + switch (k) { + case DIRECT_4: + case DIRECT_8: + sbb.writeTruncatedLong(explicitAddend, length); + break; + case AARCH64_R_AARCH64_ADR_PREL_PG_HI21: + case AARCH64_R_AARCH64_LDST64_ABS_LO12_NC: + case AARCH64_R_AARCH64_LDST32_ABS_LO12_NC: + case AARCH64_R_AARCH64_LDST16_ABS_LO12_NC: + case AARCH64_R_AARCH64_LDST8_ABS_LO12_NC: + case AARCH64_R_AARCH64_ADD_ABS_LO12_NC: + if (explicitAddend != 0) { + /* + * These relocations should use an explicit addend reloc record instead of + * an embedded addend, according to the Mach-O ld code at + * https://opensource.apple.com/source/ld64/ld64-274.2/src/ld/parsers/ + * macho_relocatable_file.cpp.auto.html + * + * > xxxx instruction at xxxx has embedded addend. ARM64_RELOC_ADDEND should + * be used instead + */ + RelocationInfo addend = RelocationInfo.newAddend(el, this, offset, length, explicitAddend); + el.add(addend); + } + break; + default: + GraalError.shouldNotReachHere(); + } + } // set section flag to note that we have relocations assert symbolName != null;