Index: ELF/AArch64ErrataFix.cpp =================================================================== --- ELF/AArch64ErrataFix.cpp +++ ELF/AArch64ErrataFix.cpp @@ -421,7 +421,7 @@ // Return address is the next instruction after the one we have just copied. uint64_t S = getLDSTAddr() + 4; uint64_t P = PatchSym->getVA() + 4; - Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P); + Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P, nullptr); } void AArch64Err843419Patcher::init() { Index: ELF/Arch/AArch64.cpp =================================================================== --- ELF/Arch/AArch64.cpp +++ ELF/Arch/AArch64.cpp @@ -44,7 +44,8 @@ uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; bool usesOnlyLowPageBits(RelType Type) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; @@ -173,9 +174,9 @@ uint64_t Got = In.GotPlt->getVA(); uint64_t Plt = In.Plt->getVA(); relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(Got + 16) - getAArch64Page(Plt + 4)); - relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16); - relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16); + getAArch64Page(Got + 16) - getAArch64Page(Plt + 4), nullptr); + relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16, nullptr); + relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16, nullptr); } void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, @@ -190,9 +191,10 @@ memcpy(Buf, Inst, sizeof(Inst)); relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr)); - relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr); - relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr); + getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr), + nullptr); + relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr, nullptr); + relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr, nullptr); } bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, @@ -248,7 +250,8 @@ or32le(L, (Imm & 0xFFF) << 10); } -void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: @@ -405,11 +408,11 @@ break; case R_AARCH64_TLSDESC_ADR_PAGE21: write32le(Loc, 0x90000000); // adrp - relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val); + relocateOne(Loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, Val, nullptr); break; case R_AARCH64_TLSDESC_LD64_LO12: write32le(Loc, 0xf9400000); // ldr - relocateOne(Loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, Val); + relocateOne(Loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, Val, nullptr); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); Index: ELF/Arch/AMDGPU.cpp =================================================================== --- ELF/Arch/AMDGPU.cpp +++ ELF/Arch/AMDGPU.cpp @@ -26,7 +26,8 @@ public: AMDGPU(); uint32_t calcEFlags() const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; }; @@ -57,7 +58,8 @@ return Ret; } -void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: Index: ELF/Arch/ARM.cpp =================================================================== --- ELF/Arch/ARM.cpp +++ ELF/Arch/ARM.cpp @@ -42,7 +42,8 @@ uint64_t BranchAddr, const Symbol &S) const override; uint32_t getThunkSectionSpacing() const override; bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; }; } // namespace @@ -370,7 +371,8 @@ return Distance <= Range; } -void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: Index: ELF/Arch/AVR.cpp =================================================================== --- ELF/Arch/AVR.cpp +++ ELF/Arch/AVR.cpp @@ -46,7 +46,8 @@ AVR(); RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; }; } // namespace @@ -57,7 +58,8 @@ return R_ABS; } -void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_AVR_CALL: { uint16_t Hi = Val >> 17; Index: ELF/Arch/Hexagon.cpp =================================================================== --- ELF/Arch/Hexagon.cpp +++ ELF/Arch/Hexagon.cpp @@ -30,7 +30,8 @@ uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; @@ -179,7 +180,8 @@ static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); } -void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_HEX_NONE: break; @@ -267,8 +269,8 @@ // Offset from PLT0 to the GOT. uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA(); - relocateOne(Buf, R_HEX_B32_PCREL_X, Off); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off); + relocateOne(Buf, R_HEX_B32_PCREL_X, Off, nullptr); + relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off, nullptr); } void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, @@ -282,8 +284,9 @@ }; memcpy(Buf, Inst, sizeof(Inst)); - relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr); - relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr); + relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr, nullptr); + relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr, + nullptr); } TargetInfo *elf::getHexagonTargetInfo() { Index: ELF/Arch/Mips.cpp =================================================================== --- ELF/Arch/Mips.cpp +++ ELF/Arch/Mips.cpp @@ -39,7 +39,8 @@ int32_t Index, unsigned RelOff) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; bool usesOnlyLowPageBits(RelType Type) const override; }; } // namespace @@ -258,12 +259,12 @@ write16(Buf + 18, 0x0f83); // move $28, $3 write16(Buf + 20, 0x472b); // jalrc $25 write16(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt); + relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt, nullptr); } else { write16(Buf + 18, 0x45f9); // jalrc $25 write16(Buf + 20, 0x0f83); // move $28, $3 write16(Buf + 22, 0x0c00); // nop - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt); + relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt, nullptr); } return; } @@ -315,13 +316,15 @@ write16(Buf + 4, 0xff22); // lw $25, 0($2) write16(Buf + 8, 0x0f02); // move $24, $2 write16(Buf + 10, 0x4723); // jrc $25 / jr16 $25 - relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr); + relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr, + nullptr); } else { write16(Buf, 0x7900); // addiupc $2, (GOTPLT) - . write16(Buf + 4, 0xff22); // lw $25, 0($2) write16(Buf + 8, 0x4599); // jrc $25 / jr16 $25 write16(Buf + 10, 0x0f02); // move $24, $2 - relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr); + relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr, + nullptr); } return; } @@ -459,13 +462,126 @@ return std::make_pair(Type & 0xff, Val); } +static bool isBranchReloc(RelType Type) { + switch (Type) { + case R_MIPS_26: + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + return true; + default: + return false; + } +} + +static bool isMicroBranchReloc(RelType Type) { + switch (Type) { + case R_MICROMIPS_26_S1: + case R_MICROMIPS_PC26_S1: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + return true; + default: + return false; + } +} + template -void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +static uint64_t fixupCrossModeJump(uint8_t *Loc, RelType Type, uint64_t Val, + bool IsMicroTgt) { + // Here we need to detect jump/branch from regular MIPS code + // to a microMIPS target and vice versa. In that cases jump + // instructions need to be replaced by their "cross-mode" + // equivalents. + const endianness E = ELFT::TargetEndianness; + bool IsCrossJump = (IsMicroTgt && isBranchReloc(Type)) || + (!IsMicroTgt && isMicroBranchReloc(Type)); + if (!IsCrossJump) + return Val; + + switch (Type) { + case R_MIPS_26: { + uint32_t Inst = read32(Loc) >> 26; + if (Inst == 0x3 || Inst == 0x1d) { // JAL or JALX + writeValue(Loc, 0x1d << 26, 32, 0); + return Val; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t Inst = readShuffle(Loc) >> 26; + if (Inst == 0x3d || Inst == 0x3c) { // JAL32 or JALX32 + Val >>= 1; + writeShuffleValue(Loc, 0x3c << 26, 32, 0); + return Val; + } + break; + } + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + // FIXME (simon): Support valid branch relocations. + break; + default: + llvm_unreachable("unexpected jump/branch relocation"); + } + + error(getErrorLocation(Loc) + + "unsupported jump/branch instruction between ISA modes referenced by " + + toString(Type) + " relocation"); + return Val; +} + +template +void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { const endianness E = ELFT::TargetEndianness; if (ELFT::Is64Bits || Config->MipsN32Abi) std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + if (S) { + // MIPS processors use different instructions to jump / branch between + // between the same (regulat-to-regular or microMIPS-to_microMIPS) or + // mixed (regular-to-microMIPS or microMIPS-to_regular) code. When we + // write a symbols's value we need to keep information about type of + // the symbol: regular or microMIPS to allow a reader of this value + // (dynamic linker, loader, debugger etc) will be able to adjust a jump + // / branch instruction properly. To do so we set the less-significant + // bit for microMIPS symbols. + + // We consider a symbol as a microMIPS one if it has the STO_MIPS_MICROMIPS + // flag or if its value is an address of the corresponding PLT entry. + // The best solution is to create a regular PLT entry for a regular symbol + // and a mciroMIPS PLT entry for a microMIPS symbol. But in that case if + // the same routine is called by both regular and microMIPS code, we should + // create two PLT entries. That solution minimizes cross-mode jumps. Now + // LLD implements more simple solution. If the file uses microMIPS code, + // all PLT entries are microMIPS. Otherwise all entries are regular. That + // works fine for/ case when all code is either regular or microMIPS, but + // increase number/ of cross-mode jumps for mixed code. + bool IsMicroTgt = + (S->StOther & STO_MIPS_MICROMIPS) || (S->isInPlt() && isMicroMips()); + if (IsMicroTgt) { + switch (getRelExpr(Type, *S, Loc)) { + case R_ABS: + case R_MIPS_GOTREL: + case R_PC: + Val |= 1; + break; + default: + break; + } + } + + // Detect cross-mode jump/branch and fix instruction. + Val = fixupCrossModeJump(Loc, Type, Val, IsMicroTgt); + } + // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 || Index: ELF/Arch/PPC.cpp =================================================================== --- ELF/Arch/PPC.cpp +++ ELF/Arch/PPC.cpp @@ -22,7 +22,8 @@ class PPC final : public TargetInfo { public: PPC(); - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; }; @@ -47,7 +48,8 @@ } } -void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_PPC_ADDR16_HA: write16be(Loc, (Val + 0x8000) >> 16); Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -109,7 +109,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; void writeGotHeader(uint8_t *Buf) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; @@ -275,7 +276,7 @@ case R_PPC64_GOT_TLSGD16: case R_PPC64_GOT_TLSGD16_LO: writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + relocateOne(Loc, R_PPC64_TPREL16_HA, Val, nullptr); break; case R_PPC64_TLSGD: write32(Loc, 0x60000000); // nop @@ -284,7 +285,7 @@ // the start of an instruction we need to advance the buffer by an extra // 2 bytes on BE. relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, Val); + R_PPC64_TPREL16_LO, Val, nullptr); break; default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); @@ -328,7 +329,7 @@ case R_PPC64_GOT_DTPREL16_LO_DS: case R_PPC64_GOT_DTPREL16_DS: case R_PPC64_GOT_DTPREL16_HI: - relocateOne(Loc, Type, Val); + relocateOne(Loc, Type, Val, nullptr); break; default: llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); @@ -390,7 +391,7 @@ case R_PPC64_GOT_TPREL16_DS: { uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10 write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13 - relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + relocateOne(Loc, R_PPC64_TPREL16_HA, Val, nullptr); break; } case R_PPC64_TLS: { @@ -400,7 +401,7 @@ uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30 uint32_t DFormOp = getDFormOp(SecondaryOp); write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); - relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val, nullptr); break; } default: @@ -588,7 +589,8 @@ Type == R_PPC64_TOC16_LO; } -void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { // For a TOC-relative relocation, proceed in terms of the corresponding // ADDR16 relocation type. bool IsTocRelType = isTocRelType(Type); @@ -764,14 +766,14 @@ case R_PPC64_GOT_TLSGD16_HA: // This is relaxed from addis rT, r2, sym@got@tlsgd@ha to // addis rT, r2, sym@got@tprel@ha. - relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val); + relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val, nullptr); return; case R_PPC64_GOT_TLSGD16_LO: { // Relax from addi r3, rA, sym@got@tlsgd@l to // ld r3, sym@got@tprel@l(rA) uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16)); writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister); - relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val); + relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val, nullptr); return; } case R_PPC64_TLSGD: Index: ELF/Arch/RISCV.cpp =================================================================== --- ELF/Arch/RISCV.cpp +++ ELF/Arch/RISCV.cpp @@ -25,7 +25,8 @@ uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; }; } // end anonymous namespace @@ -87,8 +88,8 @@ return (V & ((1ULL << (Begin + 1)) - 1)) >> End; } -void RISCV::relocateOne(uint8_t *Loc, const RelType Type, - const uint64_t Val) const { +void RISCV::relocateOne(uint8_t *Loc, const RelType Type, const uint64_t Val, + const Symbol *S) const { switch (Type) { case R_RISCV_32: write32le(Loc, Val); @@ -177,8 +178,8 @@ case R_RISCV_CALL: { checkInt(Loc, Val, 32, Type); if (isInt<32>(Val)) { - relocateOne(Loc, R_RISCV_PCREL_HI20, Val); - relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val); + relocateOne(Loc, R_RISCV_PCREL_HI20, Val, nullptr); + relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val, nullptr); } return; } Index: ELF/Arch/SPARCV9.cpp =================================================================== --- ELF/Arch/SPARCV9.cpp +++ ELF/Arch/SPARCV9.cpp @@ -28,7 +28,8 @@ const uint8_t *Loc) const override; void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; }; } // namespace @@ -73,7 +74,8 @@ } } -void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_SPARC_32: case R_SPARC_UA32: @@ -139,8 +141,8 @@ memcpy(Buf, PltData, sizeof(PltData)); uint64_t Off = getPltEntryOffset(Index); - relocateOne(Buf, R_SPARC_22, Off); - relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize)); + relocateOne(Buf, R_SPARC_22, Off, nullptr); + relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize), nullptr); } TargetInfo *elf::getSPARCV9TargetInfo() { Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -34,7 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; @@ -254,7 +255,8 @@ } } -void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -34,7 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; @@ -291,7 +292,8 @@ } template -void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_X86_64_8: checkUInt(Loc, Val, 8, Type); Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -812,14 +812,15 @@ // know Steel Bank Common Lisp as of 2018 have this bug. warn(Msg); Target->relocateOne(BufLoc, Type, - SignExtend64(Sym.getVA(Addend - Offset))); + SignExtend64(Sym.getVA(Addend - Offset)), &Sym); continue; } if (Sym.isTls() && !Out::TlsPhdr) - Target->relocateOne(BufLoc, Type, 0); + Target->relocateOne(BufLoc, Type, 0, nullptr); else - Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend))); + Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend)), + &Sym); } } @@ -836,7 +837,7 @@ assert(Rel.Expr == R_ABS); uint8_t *BufLoc = Buf + Rel.Offset + Sec->OutSecOff; uint64_t TargetVA = SignExtend64(Rel.Sym->getVA(Rel.Addend), Bits); - Target->relocateOne(BufLoc, Rel.Type, TargetVA); + Target->relocateOne(BufLoc, Rel.Type, TargetVA, Rel.Sym); } } @@ -914,10 +915,10 @@ } write32(BufLoc + 4, 0xe8410018); // ld %r2, 24(%r1) } - Target->relocateOne(BufLoc, Type, TargetVA); + Target->relocateOne(BufLoc, Type, TargetVA, Rel.Sym); break; default: - Target->relocateOne(BufLoc, Type, TargetVA); + Target->relocateOne(BufLoc, Type, TargetVA, Rel.Sym); break; } } Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1252,7 +1252,15 @@ template void DynamicSection::addSym(int32_t Tag, Symbol *Sym) { - Entries.push_back({Tag, [=] { return Sym->getVA(); }}); + if (Config->EMachine == EM_MIPS && (Sym->StOther & STO_MIPS_MICROMIPS)) + // Set the less-significant bit for a microMIPS symbol. + // When loader / dynamic linker reads this tag, it will + // know that the symbol is microMIPS and adjust a jump + // instruction appropriately to handle possible cross-mode + // (regular-to-microMIPS) jump. + Entries.push_back({Tag, [=] { return Sym->getVA() | 1; }}); + else + Entries.push_back({Tag, [=] { return Sym->getVA(); }}); } // Add remaining entries to complete .dynamic contents. @@ -3014,7 +3022,7 @@ assert(Highest); uint64_t S = Highest->getVA(Highest->getSize()); uint64_t P = getVA(); - Target->relocateOne(Buf, R_ARM_PREL31, S - P); + Target->relocateOne(Buf, R_ARM_PREL31, S - P, nullptr); write32le(Buf + 4, 1); } Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -81,7 +81,8 @@ virtual RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const = 0; - virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const = 0; + virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const = 0; virtual ~TargetInfo(); Index: ELF/Thunks.cpp =================================================================== --- ELF/Thunks.cpp +++ ELF/Thunks.cpp @@ -305,7 +305,7 @@ }; uint64_t S = getAArch64ThunkDestVA(Destination); memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S); + Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S, nullptr); } void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) { @@ -330,8 +330,8 @@ uint64_t P = getThunkTargetSym()->getVA(); memcpy(Buf, Data, sizeof(Data)); Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21, - getAArch64Page(S) - getAArch64Page(P)); - Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S); + getAArch64Page(S) - getAArch64Page(P), nullptr); + Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S, nullptr); } void AArch64ADRPThunk::addSymbols(ThunkSection &IS) { @@ -375,7 +375,7 @@ 0x00, 0x00, 0x00, 0xea, // b S }; memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf, R_ARM_JUMP24, Offset); + Target->relocateOne(Buf, R_ARM_JUMP24, Offset, nullptr); } bool ARMThunk::isCompatibleWith(RelType Type) const { @@ -412,7 +412,7 @@ 0x00, 0xf0, 0x00, 0xb0, // b.w S }; memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset); + Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset, nullptr); } bool ThumbThunk::isCompatibleWith(RelType Type) const { @@ -428,8 +428,8 @@ }; uint64_t S = getARMThunkDestVA(Destination); memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S); - Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S); + Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S, nullptr); + Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S, nullptr); } void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) { @@ -446,8 +446,8 @@ }; uint64_t S = getARMThunkDestVA(Destination); memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S); - Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S); + Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S, nullptr); + Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S, nullptr); } void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) { @@ -467,8 +467,8 @@ uint64_t P = getThunkTargetSym()->getVA(); uint64_t Offset = S - P - 16; memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset); - Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset); + Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset, nullptr); + Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset, nullptr); } void ARMV7PILongThunk::addSymbols(ThunkSection &IS) { @@ -488,8 +488,8 @@ uint64_t P = getThunkTargetSym()->getVA() & ~0x1; uint64_t Offset = S - P - 12; memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset); - Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset); + Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset, nullptr); + Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset, nullptr); } void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) { @@ -504,7 +504,8 @@ 0x00, 0x00, 0x00, 0x00, // L1: .word S }; memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf + 4, R_ARM_ABS32, getARMThunkDestVA(Destination)); + Target->relocateOne(Buf + 4, R_ARM_ABS32, getARMThunkDestVA(Destination), + nullptr); } void ARMV5ABSLongThunk::addSymbols(ThunkSection &IS) { @@ -529,7 +530,7 @@ uint64_t S = getARMThunkDestVA(Destination); uint64_t P = getThunkTargetSym()->getVA() & ~0x1; memcpy(Buf, Data, sizeof(Data)); - Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12); + Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12, nullptr); } void ARMV5PILongThunk::addSymbols(ThunkSection &IS) { @@ -551,8 +552,8 @@ write32(Buf + 4, 0x08000000 | (S >> 2)); // j func write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func) write32(Buf + 12, 0x00000000); // nop - Target->relocateOne(Buf, R_MIPS_HI16, S); - Target->relocateOne(Buf + 8, R_MIPS_LO16, S); + Target->relocateOne(Buf, R_MIPS_HI16, S, nullptr); + Target->relocateOne(Buf + 8, R_MIPS_LO16, S, nullptr); } void MipsThunk::addSymbols(ThunkSection &IS) { @@ -573,9 +574,9 @@ write16(Buf + 4, 0xd400); // j func write16(Buf + 8, 0x3339); // addiu $25, $25, %lo(func) write16(Buf + 12, 0x0c00); // nop - Target->relocateOne(Buf, R_MICROMIPS_HI16, S); - Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S); - Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S); + Target->relocateOne(Buf, R_MICROMIPS_HI16, S, nullptr); + Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S, nullptr); + Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S, nullptr); } void MicroMipsThunk::addSymbols(ThunkSection &IS) { @@ -597,9 +598,9 @@ write16(Buf, 0x1320); // lui $25, %hi(func) write16(Buf + 4, 0x3339); // addiu $25, $25, %lo(func) write16(Buf + 8, 0x9400); // bc func - Target->relocateOne(Buf, R_MICROMIPS_HI16, S); - Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S); - Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12); + Target->relocateOne(Buf, R_MICROMIPS_HI16, S, nullptr); + Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S, nullptr); + Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12, nullptr); } void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -2277,8 +2277,15 @@ // 6. the address 0. static uint64_t getEntryAddr() { // Case 1, 2 or 3 - if (Symbol *B = Symtab->find(Config->Entry)) + if (Symbol *B = Symtab->find(Config->Entry)) { + // Set the less-significant bit for a microMIPS symbol. + // When loader reads this entry, it will know that the symbol + // is microMIPS and adjust a jump instruction appropriately + // to handle possible cross-mode (regular-to-microMIPS) jump. + if (Config->EMachine == EM_MIPS && (B->StOther & STO_MIPS_MICROMIPS)) + return B->getVA() | 1; return B->getVA(); + } // Case 4 uint64_t Addr; Index: test/ELF/mips-micro-bad-cross-calls.s =================================================================== --- /dev/null +++ test/ELF/mips-micro-bad-cross-calls.s @@ -0,0 +1,16 @@ +# Check error message for invalid cross-mode branch instructions. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %S/Inputs/mips-dynamic.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t1.o +# RUN: not ld.lld -o %t.exe %t1.o %t2.o 2>&1 | FileCheck %s + +# REQUIRES: mips + +# CHECK: (.text+0x0): unsupported jump/branch instruction between ISA modes referenced by R_MICROMIPS_PC10_S1 relocation + + .text + .set micromips + .global __start +__start: + b16 foo0 Index: test/ELF/mips-micro-cross-calls.s =================================================================== --- /dev/null +++ test/ELF/mips-micro-cross-calls.s @@ -0,0 +1,45 @@ +# Check various cases of microMIPS - regular code cross-calls. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mattr=micromips %s -o %t-eb.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -position-independent -mattr=micromips \ +# RUN: %S/Inputs/mips-micro.s -o %t-eb-pic.o +# RUN: ld.lld -o %t-eb.exe %t-eb.o %t-eb-pic.o +# RUN: llvm-objdump -d -mattr=-micromips %t-eb.exe \ +# RUN: | FileCheck --check-prefix=REG %s +# RUN: llvm-objdump -d -mattr=+micromips %t-eb.exe \ +# RUN: | FileCheck --check-prefix=MICRO %s + +# REQUIRES: mips + +# REG: __start: +# REG-NEXT: 20000: 74 00 80 04 jalx 131088 +# REG-NEXT: 20004: 00 00 00 00 nop +# REG-NEXT: 20008: 74 00 80 08 jalx 131104 <__microLA25Thunk_foo> + +# REG: __LA25Thunk_bar: +# REG-NEXT: 20030: 3c 19 00 02 lui $25, 2 +# REG-NEXT: 20034: 08 00 80 11 j 131140 + +# MICRO: micro: +# MICRO-NEXT: 20010: f0 00 80 00 jalx 65536 +# MICRO-NEXT: 20014: 00 00 00 00 nop +# MICRO-NEXT: 20018: f0 00 80 0c jalx 65560 + +# MICRO: __microLA25Thunk_foo: +# MICRO-NEXT: 20020: 41 b9 00 02 lui $25, 2 +# MICRO-NEXT: 20024: d4 01 00 20 j 131136 + + .text + .set nomicromips + .global __start +__start: + jal micro + jal foo + + .set micromips + .global micro +micro: + jal __start + jal bar Index: test/ELF/mips-micro-plt.s =================================================================== --- test/ELF/mips-micro-plt.s +++ test/ELF/mips-micro-plt.s @@ -87,9 +87,9 @@ # ASM: __start: # ASM-NEXT: 20000: fd 1c 80 18 lw $8, -32744($gp) -# ASM-NEXT: 20004: 11 08 00 10 addi $8, $8, 16 +# ASM-NEXT: 20004: 11 08 00 11 addi $8, $8, 17 # ASM-NEXT: 20008: 41 a8 00 02 lui $8, 2 -# ASM-NEXT: 2000c: 11 08 00 40 addi $8, $8, 64 +# ASM-NEXT: 2000c: 11 08 00 41 addi $8, $8, 65 # # ASM: foo: # ASM-NEXT: 20010: f4 01 00 20 jal 131136 Index: test/ELF/mips-micro-relocs.s =================================================================== --- test/ELF/mips-micro-relocs.s +++ test/ELF/mips-micro-relocs.s @@ -6,7 +6,7 @@ # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ # RUN: -mattr=micromips %s -o %t2eb.o # RUN: ld.lld -o %teb.exe %t1eb.o %t2eb.o -# RUN: llvm-objdump -d -t -mattr=micromips %teb.exe \ +# RUN: llvm-objdump -d -t -s -mattr=micromips %teb.exe \ # RUN: | FileCheck --check-prefixes=EB,SYM %s # RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \ @@ -14,12 +14,12 @@ # RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \ # RUN: -mattr=micromips %s -o %t2el.o # RUN: ld.lld -o %tel.exe %t1el.o %t2el.o -# RUN: llvm-objdump -d -t -mattr=micromips %tel.exe \ +# RUN: llvm-objdump -d -t -s -mattr=micromips %tel.exe \ # RUN: | FileCheck --check-prefixes=EL,SYM %s # EB: __start: # EB-NEXT: 20010: 41 a3 00 01 lui $3, 1 -# EB-NEXT: 20014: 30 63 7f df addiu $3, $3, 32735 +# EB-NEXT: 20014: 30 63 7f ef addiu $3, $3, 32751 # EB-NEXT: 20018: fc 7c 80 18 lw $3, -32744($gp) # EB-NEXT: 2001c: fc 63 80 18 lw $3, -32744($3) # EB-NEXT: 20020: 8f 70 beqz16 $6, -32 @@ -28,9 +28,15 @@ # EB-NEXT: 20028: 00 00 00 00 nop # EB-NEXT: 2002c: 94 00 ff e8 b -44 +# EB: Contents of section .data: +# EB-NEXT: 30000 fffe8011 + +# EB: Contents of section .debug_info +# EB-NEXT: 0000 00020011 + # EL: __start: # EL-NEXT: 20010: a3 41 01 00 lui $3, 1 -# EL-NEXT: 20014: 63 30 df 7f addiu $3, $3, 32735 +# EL-NEXT: 20014: 63 30 ef 7f addiu $3, $3, 32751 # EL-NEXT: 20018: 7c fc 18 80 lw $3, -32744($gp) # EL-NEXT: 2001c: 63 fc 18 80 lw $3, -32744($3) # EL-NEXT: 20020: 70 8f beqz16 $6, -32 @@ -39,7 +45,13 @@ # EL-NEXT: 20028: 00 00 00 00 nop # EL-NEXT: 2002c: 00 94 e8 ff b -44 -# SYM: 00037ff0 .got 00000000 .hidden _gp +# EL: Contents of section .data: +# EL-NEXT: 30000 1180feff + +# EL: Contents of section .debug_info +# EL-NEXT: 0000 11000200 + +# SYM: 00038000 .got 00000000 .hidden _gp # SYM: 00020000 g F .text 00000000 foo # SYM: 00020010 .text 00000000 __start @@ -56,3 +68,9 @@ beqz16 $6, foo # R_MICROMIPS_PC7_S1 b16 foo # R_MICROMIPS_PC10_S1 b foo # R_MICROMIPS_PC16_S1 + + .data + .gpword __start # R_MIPS_GPREL32 + + .section .debug_info + .word __start # R_MIPS_32