[lld] 09c2c5e - [ELF] Replace error(...) with ErrAlways or Err

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 6 22:04:58 PST 2024


Author: Fangrui Song
Date: 2024-11-06T22:04:52-08:00
New Revision: 09c2c5e1e9f3b3bb17f777f153407430f3cef15e

URL: https://github.com/llvm/llvm-project/commit/09c2c5e1e9f3b3bb17f777f153407430f3cef15e
DIFF: https://github.com/llvm/llvm-project/commit/09c2c5e1e9f3b3bb17f777f153407430f3cef15e.diff

LOG: [ELF] Replace error(...) with ErrAlways or Err

Most are migrated to ErrAlways mechanically.
In the future we should change most to Err.

Added: 
    

Modified: 
    lld/ELF/Arch/AArch64.cpp
    lld/ELF/Arch/AMDGPU.cpp
    lld/ELF/Arch/ARM.cpp
    lld/ELF/Arch/AVR.cpp
    lld/ELF/Arch/Hexagon.cpp
    lld/ELF/Arch/LoongArch.cpp
    lld/ELF/Arch/MSP430.cpp
    lld/ELF/Arch/Mips.cpp
    lld/ELF/Arch/MipsArchTree.cpp
    lld/ELF/Arch/PPC.cpp
    lld/ELF/Arch/PPC64.cpp
    lld/ELF/Arch/RISCV.cpp
    lld/ELF/Arch/SPARCV9.cpp
    lld/ELF/Arch/SystemZ.cpp
    lld/ELF/Arch/X86.cpp
    lld/ELF/Arch/X86_64.cpp
    lld/ELF/CallGraphSort.cpp
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/DriverUtils.cpp
    lld/ELF/InputFiles.cpp
    lld/ELF/InputSection.cpp
    lld/ELF/LTO.cpp
    lld/ELF/LinkerScript.cpp
    lld/ELF/MapFile.cpp
    lld/ELF/OutputSections.cpp
    lld/ELF/Relocations.cpp
    lld/ELF/ScriptLexer.cpp
    lld/ELF/ScriptParser.cpp
    lld/ELF/Symbols.cpp
    lld/ELF/SyntheticSections.cpp
    lld/ELF/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 28e0fce6a6f499..025672d4a3665d 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -213,8 +213,8 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
   case R_AARCH64_NONE:
     return R_NONE;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -1113,7 +1113,8 @@ addTaggedSymbolReferences(InputSectionBase &sec,
 
   const RelsOrRelas<ELFT> rels = sec.relsOrRelas<ELFT>();
   if (rels.areRelocsRel())
-    error("non-RELA relocations are not allowed with memtag globals");
+    ErrAlways(ctx)
+        << "non-RELA relocations are not allowed with memtag globals";
 
   for (const typename ELFT::Rela &rel : rels.relas) {
     Symbol &sym = sec.file->getRelocTargetSym(rel);
@@ -1196,7 +1197,7 @@ void elf::createTaggedSymbols(Ctx &ctx) {
   // relocations, the only other way to get written addends is with
   // --apply-dynamic-relocs.
   if (!taggedSymbolReferenceCount.empty() && ctx.arg.writeAddends)
-    error("--apply-dynamic-relocs cannot be used with MTE globals");
+    ErrAlways(ctx) << "--apply-dynamic-relocs cannot be used with MTE globals";
 
   // Now, `taggedSymbolReferenceCount` should only contain symbols that are
   // defined as tagged exactly the same amount as it's referenced, meaning all

diff  --git a/lld/ELF/Arch/AMDGPU.cpp b/lld/ELF/Arch/AMDGPU.cpp
index b4bb029feb72ab..fb567085de3719 100644
--- a/lld/ELF/Arch/AMDGPU.cpp
+++ b/lld/ELF/Arch/AMDGPU.cpp
@@ -56,7 +56,7 @@ uint32_t AMDGPU::calcEFlagsV3() const {
   for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     if (ret == getEFlags(f))
       continue;
-    error("incompatible e_flags: " + toString(f));
+    ErrAlways(ctx) << "incompatible e_flags: " << f;
     return 0;
   }
   return ret;
@@ -73,7 +73,7 @@ uint32_t AMDGPU::calcEFlagsV4() const {
   // features in the same category are either ANY, ANY and ON, or ANY and OFF).
   for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     if (retMach != (getEFlags(f) & EF_AMDGPU_MACH)) {
-      error("incompatible mach: " + toString(f));
+      ErrAlways(ctx) << "incompatible mach: " << f;
       return 0;
     }
 
@@ -82,7 +82,7 @@ uint32_t AMDGPU::calcEFlagsV4() const {
             (getEFlags(f) & EF_AMDGPU_FEATURE_XNACK_V4)
                 != EF_AMDGPU_FEATURE_XNACK_ANY_V4)) {
       if (retXnack != (getEFlags(f) & EF_AMDGPU_FEATURE_XNACK_V4)) {
-        error("incompatible xnack: " + toString(f));
+        ErrAlways(ctx) << "incompatible xnack: " << f;
         return 0;
       }
     } else {
@@ -95,7 +95,7 @@ uint32_t AMDGPU::calcEFlagsV4() const {
             (getEFlags(f) & EF_AMDGPU_FEATURE_SRAMECC_V4) !=
                 EF_AMDGPU_FEATURE_SRAMECC_ANY_V4)) {
       if (retSramEcc != (getEFlags(f) & EF_AMDGPU_FEATURE_SRAMECC_V4)) {
-        error("incompatible sramecc: " + toString(f));
+        ErrAlways(ctx) << "incompatible sramecc: " << f;
         return 0;
       }
     } else {
@@ -116,7 +116,7 @@ uint32_t AMDGPU::calcEFlagsV6() const {
   // Verify that all input files have compatible generic version.
   for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     if (genericVersion != (getEFlags(f) & EF_AMDGPU_GENERIC_VERSION)) {
-      error("incompatible generic version: " + toString(f));
+      ErrAlways(ctx) << "incompatible generic version: " << f;
       return 0;
     }
   }
@@ -143,7 +143,7 @@ uint32_t AMDGPU::calcEFlags() const {
   case ELFABIVERSION_AMDGPU_HSA_V6:
     return calcEFlagsV6();
   default:
-    error("unknown abi version: " + Twine(abiVersion));
+    ErrAlways(ctx) << "unknown abi version: " << Twine(abiVersion);
     return 0;
   }
 }
@@ -193,8 +193,8 @@ RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
   case R_AMDGPU_GOTPCREL32_HI:
     return R_GOT_PC;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }

diff  --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index 0df644c61babdd..4c50f7397ad920 100644
--- a/lld/ELF/Arch/ARM.cpp
+++ b/lld/ELF/Arch/ARM.cpp
@@ -195,8 +195,8 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
     // not ARMv4 output, we can just ignore it.
     return R_NONE;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -556,8 +556,8 @@ void ARM::encodeAluGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
     rot = (lz + 8) << 7;
   }
   if (check && imm > 0xff)
-    error(getErrorLoc(ctx, loc) + "unencodeable immediate " + Twine(val).str() +
-          " for relocation " + toString(rel.type));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unencodeable immediate "
+             << Twine(val).str() << " for relocation " << rel.type;
   write32(ctx, loc,
           (read32(ctx, loc) & 0xff3ff000) | opcode | rot | (imm & 0xff));
 }
@@ -1216,20 +1216,22 @@ template <class ELFT> void ObjFile<ELFT>::importCmseSymbols() {
     sym->stOther = eSym.st_other;
 
     if (eSym.st_shndx != SHN_ABS) {
-      error("CMSE symbol '" + sym->getName() + "' in import library '" +
-            toString(this) + "' is not absolute");
+      ErrAlways(ctx) << "CMSE symbol '" << sym->getName()
+                     << "' in import library '" << this << "' is not absolute";
       continue;
     }
 
     if (!(eSym.st_value & 1) || (eSym.getType() != STT_FUNC)) {
-      error("CMSE symbol '" + sym->getName() + "' in import library '" +
-            toString(this) + "' is not a Thumb function definition");
+      ErrAlways(ctx) << "CMSE symbol '" << sym->getName()
+                     << "' in import library '" << this
+                     << "' is not a Thumb function definition";
       continue;
     }
 
     if (ctx.symtab->cmseImportLib.count(sym->getName())) {
-      error("CMSE symbol '" + sym->getName() +
-            "' is multiply defined in import library '" + toString(this) + "'");
+      ErrAlways(ctx) << "CMSE symbol '" << sym->getName()
+                     << "' is multiply defined in import library '" << this
+                     << "'";
       continue;
     }
 
@@ -1283,7 +1285,8 @@ void elf::processArmCmseSymbols(Ctx &ctx) {
     // If input object build attributes do not support CMSE, error and disable
     // further scanning for <sym>, __acle_se_<sym> pairs.
     if (!ctx.arg.armCMSESupport) {
-      error("CMSE is only supported by ARMv8-M architecture or later");
+      ErrAlways(ctx)
+          << "CMSE is only supported by ARMv8-M architecture or later";
       ctx.arg.cmseImplib = false;
       break;
     }
@@ -1293,16 +1296,17 @@ void elf::processArmCmseSymbols(Ctx &ctx) {
     StringRef name = acleSeSym->getName().substr(std::strlen(ACLESESYM_PREFIX));
     Symbol *sym = ctx.symtab->find(name);
     if (!sym) {
-      error(toString(acleSeSym->file) + ": cmse special symbol '" +
-            acleSeSym->getName() +
-            "' detected, but no associated entry function definition '" + name +
-            "' with external linkage found");
+      ErrAlways(ctx)
+          << acleSeSym->file << ": cmse special symbol '"
+          << acleSeSym->getName()
+          << "' detected, but no associated entry function definition '" << name
+          << "' with external linkage found";
       continue;
     }
 
     std::string errMsg = checkCmseSymAttributes(acleSeSym, sym);
     if (!errMsg.empty()) {
-      error(errMsg);
+      ErrAlways(ctx) << errMsg;
       continue;
     }
 
@@ -1432,7 +1436,8 @@ void ArmCmseSGSection::finalizeContents() {
   // Check if the start address of '.gnu.sgstubs' correspond to the
   // linker-synthesized veneer with the lowest address.
   if ((getVA() & ~1) != (addr & ~1)) {
-    error("start address of '.gnu.sgstubs' is 
diff erent from previous link");
+    ErrAlways(ctx)
+        << "start address of '.gnu.sgstubs' is 
diff erent from previous link";
     return;
   }
 
@@ -1500,8 +1505,8 @@ template <typename ELFT> void elf::writeARMCmseImportLib(Ctx &ctx) {
   Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr =
       FileOutputBuffer::create(ctx.arg.cmseOutputLib, fileSize, flags);
   if (!bufferOrErr) {
-    error("failed to open " + ctx.arg.cmseOutputLib + ": " +
-          llvm::toString(bufferOrErr.takeError()));
+    ErrAlways(ctx) << "failed to open " << ctx.arg.cmseOutputLib << ": "
+                   << llvm::toString(bufferOrErr.takeError());
     return;
   }
 

diff  --git a/lld/ELF/Arch/AVR.cpp b/lld/ELF/Arch/AVR.cpp
index 64790f1ce83ab3..5538acd90de428 100644
--- a/lld/ELF/Arch/AVR.cpp
+++ b/lld/ELF/Arch/AVR.cpp
@@ -93,8 +93,8 @@ RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
   case R_AVR_13_PCREL:
     return R_PC;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -282,8 +282,8 @@ uint32_t AVR::calcEFlags() const {
   for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     uint32_t objFlags = getEFlags(f);
     if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK))
-      error(toString(f) +
-            ": cannot link object files with incompatible target ISA");
+      ErrAlways(ctx)
+          << f << ": cannot link object files with incompatible target ISA";
     if (!(objFlags & EF_AVR_LINKRELAX_PREPARED))
       hasLinkRelaxFlag = false;
   }

diff  --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 80fcb3b747d1ea..b648b3bdc1da69 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -153,8 +153,8 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
   case R_HEX_TPREL_LO16:
     return R_TPREL;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -198,8 +198,8 @@ static uint32_t findMaskR6(uint32_t insn) {
     if ((0xff000000 & insn) == i.cmpMask)
       return i.relocMask;
 
-  error("unrecognized instruction for 6_X relocation: 0x" +
-        utohexstr(insn));
+  ErrAlways(ctx) << "unrecognized instruction for 6_X relocation: 0x"
+                 << utohexstr(insn);
   return 0;
 }
 
@@ -246,8 +246,8 @@ static uint32_t findMaskR16(uint32_t insn) {
     if ((0xff000000 & insn) == i.cmpMask)
       return i.relocMask;
 
-  error("unrecognized instruction for 16_X type: 0x" +
-        utohexstr(insn));
+  ErrAlways(ctx) << "unrecognized instruction for 16_X type: 0x"
+                 << utohexstr(insn);
   return 0;
 }
 

diff  --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 79ad0d60051592..b357b22f1b25f3 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -250,9 +250,9 @@ uint32_t LoongArch::calcEFlags() const {
 
     if ((flags & EF_LOONGARCH_ABI_MODIFIER_MASK) !=
         (target & EF_LOONGARCH_ABI_MODIFIER_MASK))
-      error(toString(f) +
-            ": cannot link object files with 
diff erent ABI from " +
-            toString(targetFile));
+      ErrAlways(ctx) << f
+                     << ": cannot link object files with 
diff erent ABI from "
+                     << targetFile;
 
     // We cannot process psABI v1.x / object ABI v0 files (containing stack
     // relocations), unlike ld.bfd.
@@ -270,7 +270,7 @@ uint32_t LoongArch::calcEFlags() const {
     // and the few impacted users are advised to simply rebuild world or
     // reinstall a recent system.
     if ((flags & EF_LOONGARCH_OBJABI_MASK) != EF_LOONGARCH_OBJABI_V1)
-      error(toString(f) + ": unsupported object file ABI version");
+      ErrAlways(ctx) << f << ": unsupported object file ABI version";
   }
 
   return target;
@@ -528,8 +528,8 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
   //
   // [1]: https://web.archive.org/web/20230709064026/https://github.com/loongson/LoongArch-Documentation/issues/51
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }

diff  --git a/lld/ELF/Arch/MSP430.cpp b/lld/ELF/Arch/MSP430.cpp
index 5d48518c53d8df..fc94424e6c7a17 100644
--- a/lld/ELF/Arch/MSP430.cpp
+++ b/lld/ELF/Arch/MSP430.cpp
@@ -83,8 +83,7 @@ void MSP430::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     break;
   }
   default:
-    error(getErrorLoc(ctx, loc) + "unrecognized relocation " +
-          toString(rel.type));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unrecognized relocation " << rel.type;
   }
 }
 

diff  --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index d84e85239d2ec2..7f6bebb145a090 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -191,8 +191,8 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
   case R_MIPS_NONE:
     return R_NONE;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -503,8 +503,8 @@ calculateMipsRelChain(Ctx &ctx, uint8_t *loc, RelType type, uint64_t val) {
     return std::make_pair(type2, val);
   if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16))
     return std::make_pair(type3, -val);
-  error(getErrorLoc(ctx, loc) + "unsupported relocations combination " +
-        Twine(type));
+  Err(ctx) << getErrorLoc(ctx, loc) << "unsupported relocations combination "
+           << Twine(type);
   return std::make_pair(type & 0xff, val);
 }
 
@@ -562,9 +562,10 @@ static uint64_t fixupCrossModeJump(Ctx &ctx, uint8_t *loc, RelType type,
     llvm_unreachable("unexpected jump/branch relocation");
   }
 
-  error(getErrorLoc(ctx, loc) +
-        "unsupported jump/branch instruction between ISA modes referenced by " +
-        toString(type) + " relocation");
+  ErrAlways(ctx)
+      << getErrorLoc(ctx, loc)
+      << "unsupported jump/branch instruction between ISA modes referenced by "
+      << type << " relocation";
   return val;
 }
 

diff  --git a/lld/ELF/Arch/MipsArchTree.cpp b/lld/ELF/Arch/MipsArchTree.cpp
index 72892c1f8427ae..80af3a3edbd409 100644
--- a/lld/ELF/Arch/MipsArchTree.cpp
+++ b/lld/ELF/Arch/MipsArchTree.cpp
@@ -72,22 +72,24 @@ static void checkFlags(Ctx &ctx, ArrayRef<FileFlags> files) {
 
   for (const FileFlags &f : files) {
     if (ctx.arg.is64 && f.flags & EF_MIPS_MICROMIPS)
-      error(toString(f.file) + ": microMIPS 64-bit is not supported");
+      ErrAlways(ctx) << f.file << ": microMIPS 64-bit is not supported";
 
     uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
     if (abi != abi2)
-      error(toString(f.file) + ": ABI '" + getAbiName(abi2) +
-            "' is incompatible with target ABI '" + getAbiName(abi) + "'");
+      ErrAlways(ctx) << f.file << ": ABI '" << getAbiName(abi2)
+                     << "' is incompatible with target ABI '" << getAbiName(abi)
+                     << "'";
 
     bool nan2 = f.flags & EF_MIPS_NAN2008;
     if (nan != nan2)
-      error(toString(f.file) + ": -mnan=" + getNanName(nan2) +
-            " is incompatible with target -mnan=" + getNanName(nan));
+      ErrAlways(ctx) << f.file << ": -mnan=" << getNanName(nan2)
+                     << " is incompatible with target -mnan="
+                     << getNanName(nan);
 
     bool fp2 = f.flags & EF_MIPS_FP64;
     if (fp != fp2)
-      error(toString(f.file) + ": -mfp" + getFpName(fp2) +
-            " is incompatible with target -mfp" + getFpName(fp));
+      ErrAlways(ctx) << f.file << ": -mfp" << getFpName(fp2)
+                     << " is incompatible with target -mfp" << getFpName(fp);
   }
 }
 
@@ -284,9 +286,9 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
     if (isArchMatched(newFlags, ret))
       continue;
     if (!isArchMatched(ret, newFlags)) {
-      error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
-            getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
-            getFullArchName(newFlags));
+      ErrAlways(ctx) << "incompatible target ISA:\n>>> " << files[0].file
+                     << ": " << getFullArchName(ret) << "\n>>> " << f.file
+                     << ": " << getFullArchName(newFlags);
       return 0;
     }
     ret = newFlags;
@@ -355,9 +357,10 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
   if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
     return newFlag;
   if (compareMipsFpAbi(oldFlag, newFlag) < 0)
-    error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) +
-          "' is incompatible with target floating point ABI '" +
-          getMipsFpAbiName(oldFlag) + "'");
+    ErrAlways(ctx) << fileName << ": floating point ABI '"
+                   << getMipsFpAbiName(newFlag)
+                   << "' is incompatible with target floating point ABI '"
+                   << getMipsFpAbiName(oldFlag) << "'";
   return oldFlag;
 }
 

diff  --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp
index 2cd526020f7d35..8b0af6c4a49d70 100644
--- a/lld/ELF/Arch/PPC.cpp
+++ b/lld/ELF/Arch/PPC.cpp
@@ -269,8 +269,8 @@ RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
   case R_PPC_TPREL16_HI:
     return R_TPREL;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -482,14 +482,14 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
   case R_PPC_TLS: {
     uint32_t insn = read32(ctx, loc);
     if (insn >> 26 != 31)
-      error("unrecognized instruction for IE to LE R_PPC_TLS");
+      ErrAlways(ctx) << "unrecognized instruction for IE to LE R_PPC_TLS";
     // addi rT, rT, x at tls --> addi rT, rT, x at tprel@l
     unsigned secondaryOp = (read32(ctx, loc) & 0x000007fe) >> 1;
     uint32_t dFormOp = getPPCDFormOp(secondaryOp);
     if (dFormOp == 0) { // Expecting a DS-Form instruction.
       dFormOp = getPPCDSFormOp(secondaryOp);
       if (dFormOp == 0)
-        error("unrecognized instruction for IE to LE R_PPC_TLS");
+        ErrAlways(ctx) << "unrecognized instruction for IE to LE R_PPC_TLS";
     }
     write32(ctx, loc, (dFormOp | (insn & 0x03ff0000) | lo(val)));
     break;

diff  --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index d0f59681ccbd3c..624b5f5e19f789 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -240,7 +240,8 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
   if (gepToLep < 7)
     return 1 << gepToLep;
 
-  error("reserved value of 7 in the 3 most-significant-bits of st_other");
+  ErrAlways(ctx)
+      << "reserved value of 7 in the 3 most-significant-bits of st_other";
   return 0;
 }
 
@@ -642,9 +643,9 @@ uint32_t PPC64::calcEFlags() const {
   for (InputFile *f : ctx.objectFiles) {
     uint32_t flag = getEFlags(f);
     if (flag == 1)
-      error(toString(f) + ": ABI version 1 is not supported");
+      ErrAlways(ctx) << f << ": ABI version 1 is not supported";
     else if (flag > 2)
-      error(toString(f) + ": unrecognized e_flags: " + Twine(flag));
+      ErrAlways(ctx) << f << ": unrecognized e_flags: " << Twine(flag);
   }
   return 2;
 }
@@ -660,7 +661,8 @@ void PPC64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     // "addi reg, 2, var at toc".
     uint32_t insn = readFromHalf16(ctx, loc);
     if (getPrimaryOpCode(insn) != LD)
-      error("expected a 'ld' for got-indirect to toc-relative relaxing");
+      ErrAlways(ctx)
+          << "expected a 'ld' for got-indirect to toc-relative relaxing";
     writeFromHalf16(ctx, loc, (insn & 0x03ffffff) | 0x38000000);
     relocateNoSym(loc, R_PPC64_TOC16_LO, val);
     break;
@@ -670,7 +672,8 @@ void PPC64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     // instruction (the primary opcode).
     uint64_t insn = readPrefixedInst(ctx, loc);
     if ((insn & 0xfc000000) != 0xe4000000)
-      error("expected a 'pld' for got-indirect to pc-relative relaxing");
+      ErrAlways(ctx)
+          << "expected a 'pld' for got-indirect to pc-relative relaxing";
     insn &= ~0xff000000fc000000;
 
     // Replace the cleared bits with the values for PADDI (0x600000038000000);
@@ -933,14 +936,14 @@ void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
     if (locAsInt % 4 == 0) {
       uint32_t primaryOp = getPrimaryOpCode(read32(ctx, loc));
       if (primaryOp != 31)
-        error("unrecognized instruction for IE to LE R_PPC64_TLS");
+        ErrAlways(ctx) << "unrecognized instruction for IE to LE R_PPC64_TLS";
       uint32_t secondaryOp = (read32(ctx, loc) & 0x000007fe) >> 1; // bits 21-30
       uint32_t dFormOp = getPPCDFormOp(secondaryOp);
       uint32_t finalReloc;
       if (dFormOp == 0) { // Expecting a DS-Form instruction.
         dFormOp = getPPCDSFormOp(secondaryOp);
         if (dFormOp == 0)
-          error("unrecognized instruction for IE to LE R_PPC64_TLS");
+          ErrAlways(ctx) << "unrecognized instruction for IE to LE R_PPC64_TLS";
         finalReloc = R_PPC64_TPREL16_LO_DS;
       } else
         finalReloc = R_PPC64_TPREL16_LO;
@@ -1098,8 +1101,8 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
   case R_PPC64_TLS:
     return R_TLSIE_HINT;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -1334,8 +1337,9 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     if (ctx.arg.tocOptimize && shouldTocOptimize && ha(val) == 0) {
       uint32_t insn = readFromHalf16(ctx, loc);
       if (isInstructionUpdateForm(insn))
-        error(getErrorLoc(ctx, loc) +
-              "can't toc-optimize an update instruction: 0x" + utohexstr(insn));
+        Err(ctx) << getErrorLoc(ctx, loc)
+                 << "can't toc-optimize an update instruction: 0x"
+                 << utohexstr(insn);
       writeFromHalf16(ctx, loc, (insn & 0xffe00000) | 0x00020000 | lo(val));
     } else {
       write16(ctx, loc, lo(val));
@@ -1353,9 +1357,9 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
       // changed into a nop. The lo part then needs to be updated to use the toc
       // pointer register r2, as the base register.
       if (isInstructionUpdateForm(insn))
-        error(getErrorLoc(ctx, loc) +
-              "Can't toc-optimize an update instruction: 0x" +
-              Twine::utohexstr(insn));
+        Err(ctx) << getErrorLoc(ctx, loc)
+                 << "Can't toc-optimize an update instruction: 0x"
+                 << Twine::utohexstr(insn);
       insn &= 0xffe00000 | mask;
       writeFromHalf16(ctx, loc, insn | 0x00020000 | lo(val));
     } else {
@@ -1726,7 +1730,8 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
   // Check that the adjusted size doesn't overflow what we can represent with 2
   // instructions.
   if (stackFrameSize < ctx.arg.splitStackAdjustSize + INT32_MIN) {
-    error(getErrorLoc(ctx, loc) + "split-stack prologue adjustment overflows");
+    Err(ctx) << getErrorLoc(ctx, loc)
+             << "split-stack prologue adjustment overflows";
     return false;
   }
 

diff  --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 888495962d9762..4a8e8fcefe41ea 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -156,14 +156,14 @@ uint32_t RISCV::calcEFlags() const {
       target |= EF_RISCV_RVC;
 
     if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI))
-      error(
-          toString(f) +
-          ": cannot link object files with 
diff erent floating-point ABI from " +
-          toString(ctx.objectFiles[0]));
+      ErrAlways(ctx) << f
+                     << ": cannot link object files with 
diff erent "
+                        "floating-point ABI from "
+                     << ctx.objectFiles[0];
 
     if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE))
-      error(toString(f) +
-            ": cannot link object files with 
diff erent EF_RISCV_RVE");
+      ErrAlways(ctx)
+          << f << ": cannot link object files with 
diff erent EF_RISCV_RVE";
   }
 
   return target;
@@ -325,8 +325,8 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
   case R_RISCV_SUB_ULEB128:
     return R_RISCV_LEB128;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }

diff  --git a/lld/ELF/Arch/SPARCV9.cpp b/lld/ELF/Arch/SPARCV9.cpp
index ef71014e12fa97..d733f2e0a43909 100644
--- a/lld/ELF/Arch/SPARCV9.cpp
+++ b/lld/ELF/Arch/SPARCV9.cpp
@@ -78,8 +78,8 @@ RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
   case R_SPARC_TLS_LE_LOX10:
     return R_TPREL;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }

diff  --git a/lld/ELF/Arch/SystemZ.cpp b/lld/ELF/Arch/SystemZ.cpp
index 106b530c31b28b..3b78ce14aa1224 100644
--- a/lld/ELF/Arch/SystemZ.cpp
+++ b/lld/ELF/Arch/SystemZ.cpp
@@ -170,8 +170,8 @@ RelExpr SystemZ::getRelExpr(RelType type, const Symbol &s,
     return R_GOT_PC;
 
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }

diff  --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp
index a36212a5b1690a..ac69d032e2fbff 100644
--- a/lld/ELF/Arch/X86.cpp
+++ b/lld/ELF/Arch/X86.cpp
@@ -151,8 +151,8 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s,
   case R_386_NONE:
     return R_NONE;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -372,8 +372,9 @@ void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
     //
     // Note: call *x at tlsdesc(%eax) may not immediately follow this instruction.
     if (memcmp(loc - 2, "\x8d\x83", 2)) {
-      error(getErrorLoc(ctx, loc - 2) +
-            "R_386_TLS_GOTDESC must be used in leal x at tlsdesc(%ebx), %eax");
+      ErrAlways(ctx)
+          << getErrorLoc(ctx, loc - 2)
+          << "R_386_TLS_GOTDESC must be used in leal x at tlsdesc(%ebx), %eax";
       return;
     }
     loc[-1] = 0x05;
@@ -405,8 +406,9 @@ void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
   } else if (rel.type == R_386_TLS_GOTDESC) {
     // Convert leal x at tlsdesc(%ebx), %eax to movl x at gotntpoff(%ebx), %eax.
     if (memcmp(loc - 2, "\x8d\x83", 2)) {
-      error(getErrorLoc(ctx, loc - 2) +
-            "R_386_TLS_GOTDESC must be used in leal x at tlsdesc(%ebx), %eax");
+      ErrAlways(ctx)
+          << getErrorLoc(ctx, loc - 2)
+          << "R_386_TLS_GOTDESC must be used in leal x at tlsdesc(%ebx), %eax";
       return;
     }
     loc[-2] = 0x8b;

diff  --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index d32ba638b740c5..c3031b1f9a68a8 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -407,8 +407,8 @@ RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
   case R_X86_64_NONE:
     return R_NONE;
   default:
-    error(getErrorLoc(ctx, loc) + "unknown relocation (" + Twine(type) +
-          ") against symbol " + toString(s));
+    Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << Twine(type)
+             << ") against symbol " << &s;
     return R_NONE;
   }
 }
@@ -582,8 +582,9 @@ void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
     memcpy(inst, "\x48\xc7", 2);
     *regSlot = 0xc0 | reg;
   } else {
-    error(getErrorLoc(ctx, loc - 3) +
-          "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only");
+    ErrAlways(ctx)
+        << getErrorLoc(ctx, loc - 3)
+        << "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only";
   }
 
   // The original code used a PC relative relocation.
@@ -627,8 +628,9 @@ void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
     return;
   }
 
-  error(getErrorLoc(ctx, loc - 3) +
-        "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD");
+  ErrAlways(ctx)
+      << getErrorLoc(ctx, loc - 3)
+      << "expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD";
 }
 
 // A JumpInstrMod at a specific offset indicates that the jump instruction
@@ -1031,7 +1033,7 @@ static void relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) {
 bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
                                               uint8_t stOther) const {
   if (!ctx.arg.is64) {
-    error("target doesn't support split stacks");
+    ErrAlways(ctx) << "target doesn't support split stacks";
     return false;
   }
 

diff  --git a/lld/ELF/CallGraphSort.cpp b/lld/ELF/CallGraphSort.cpp
index 537e065081f3fc..35c59d6edb2ad1 100644
--- a/lld/ELF/CallGraphSort.cpp
+++ b/lld/ELF/CallGraphSort.cpp
@@ -248,7 +248,8 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
     std::error_code ec;
     raw_fd_ostream os(ctx.arg.printSymbolOrder, ec, sys::fs::OF_None);
     if (ec) {
-      error("cannot open " + ctx.arg.printSymbolOrder + ": " + ec.message());
+      ErrAlways(ctx) << "cannot open " << ctx.arg.printSymbolOrder << ": "
+                     << ec.message();
       return orderMap;
     }
 

diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 91555573307b74..56db089ec876b4 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -700,6 +700,11 @@ inline const ELFSyncStream &operator<<(const ELFSyncStream &s, const char *v) {
   return s;
 }
 
+inline const ELFSyncStream &operator<<(const ELFSyncStream &s, Error v) {
+  s.os << llvm::toString(std::move(v));
+  return s;
+}
+
 // Report a log if --verbose is specified.
 ELFSyncStream Log(Ctx &ctx);
 

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 75d7f934a990d6..c1832325b22b25 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -88,7 +88,7 @@ void elf::errorOrWarn(const Twine &msg) {
   if (ctx.arg.noinhibitExec)
     warn(msg);
   else
-    error(msg);
+    ErrAlways(ctx) << msg;
 }
 
 ELFSyncStream elf::Log(Ctx &ctx) { return {ctx, DiagLevel::Log}; }
@@ -235,7 +235,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef emul) {
           .Default({ELFNoneKind, EM_NONE});
 
   if (ret.first == ELFNoneKind)
-    error("unknown emulation: " + emul);
+    ErrAlways(ctx) << "unknown emulation: " << emul;
   if (ret.second == EM_MSP430)
     osabi = ELFOSABI_STANDALONE;
   else if (ret.second == EM_AMDGPU)
@@ -355,7 +355,7 @@ void LinkerDriver::addFile(StringRef path, bool withLOption) {
   }
   case file_magic::elf_shared_object: {
     if (ctx.arg.isStatic) {
-      error("attempted static link of dynamic object " + path);
+      ErrAlways(ctx) << "attempted static link of dynamic object " << path;
       return;
     }
 
@@ -407,106 +407,109 @@ static void checkOptions(Ctx &ctx) {
   // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup
   // table which is a relatively new feature.
   if (ctx.arg.emachine == EM_MIPS && ctx.arg.gnuHash)
-    error("the .gnu.hash section is not compatible with the MIPS target");
+    ErrAlways(ctx)
+        << "the .gnu.hash section is not compatible with the MIPS target";
 
   if (ctx.arg.emachine == EM_ARM) {
     if (!ctx.arg.cmseImplib) {
       if (!ctx.arg.cmseInputLib.empty())
-        error("--in-implib may not be used without --cmse-implib");
+        ErrAlways(ctx) << "--in-implib may not be used without --cmse-implib";
       if (!ctx.arg.cmseOutputLib.empty())
-        error("--out-implib may not be used without --cmse-implib");
+        ErrAlways(ctx) << "--out-implib may not be used without --cmse-implib";
     }
   } else {
     if (ctx.arg.cmseImplib)
-      error("--cmse-implib is only supported on ARM targets");
+      ErrAlways(ctx) << "--cmse-implib is only supported on ARM targets";
     if (!ctx.arg.cmseInputLib.empty())
-      error("--in-implib is only supported on ARM targets");
+      ErrAlways(ctx) << "--in-implib is only supported on ARM targets";
     if (!ctx.arg.cmseOutputLib.empty())
-      error("--out-implib is only supported on ARM targets");
+      ErrAlways(ctx) << "--out-implib is only supported on ARM targets";
   }
 
   if (ctx.arg.fixCortexA53Errata843419 && ctx.arg.emachine != EM_AARCH64)
-    error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
+    ErrAlways(ctx)
+        << "--fix-cortex-a53-843419 is only supported on AArch64 targets";
 
   if (ctx.arg.fixCortexA8 && ctx.arg.emachine != EM_ARM)
-    error("--fix-cortex-a8 is only supported on ARM targets");
+    ErrAlways(ctx) << "--fix-cortex-a8 is only supported on ARM targets";
 
   if (ctx.arg.armBe8 && ctx.arg.emachine != EM_ARM)
-    error("--be8 is only supported on ARM targets");
+    ErrAlways(ctx) << "--be8 is only supported on ARM targets";
 
   if (ctx.arg.fixCortexA8 && !ctx.arg.isLE)
-    error("--fix-cortex-a8 is not supported on big endian targets");
+    ErrAlways(ctx) << "--fix-cortex-a8 is not supported on big endian targets";
 
   if (ctx.arg.tocOptimize && ctx.arg.emachine != EM_PPC64)
-    error("--toc-optimize is only supported on PowerPC64 targets");
+    ErrAlways(ctx) << "--toc-optimize is only supported on PowerPC64 targets";
 
   if (ctx.arg.pcRelOptimize && ctx.arg.emachine != EM_PPC64)
-    error("--pcrel-optimize is only supported on PowerPC64 targets");
+    ErrAlways(ctx) << "--pcrel-optimize is only supported on PowerPC64 targets";
 
   if (ctx.arg.relaxGP && ctx.arg.emachine != EM_RISCV)
-    error("--relax-gp is only supported on RISC-V targets");
+    ErrAlways(ctx) << "--relax-gp is only supported on RISC-V targets";
 
   if (ctx.arg.pie && ctx.arg.shared)
-    error("-shared and -pie may not be used together");
+    ErrAlways(ctx) << "-shared and -pie may not be used together";
 
   if (!ctx.arg.shared && !ctx.arg.filterList.empty())
-    error("-F may not be used without -shared");
+    ErrAlways(ctx) << "-F may not be used without -shared";
 
   if (!ctx.arg.shared && !ctx.arg.auxiliaryList.empty())
-    error("-f may not be used without -shared");
+    ErrAlways(ctx) << "-f may not be used without -shared";
 
   if (ctx.arg.strip == StripPolicy::All && ctx.arg.emitRelocs)
-    error("--strip-all and --emit-relocs may not be used together");
+    ErrAlways(ctx) << "--strip-all and --emit-relocs may not be used together";
 
   if (ctx.arg.zText && ctx.arg.zIfuncNoplt)
-    error("-z text and -z ifunc-noplt may not be used together");
+    ErrAlways(ctx) << "-z text and -z ifunc-noplt may not be used together";
 
   if (ctx.arg.relocatable) {
     if (ctx.arg.shared)
-      error("-r and -shared may not be used together");
+      ErrAlways(ctx) << "-r and -shared may not be used together";
     if (ctx.arg.gdbIndex)
-      error("-r and --gdb-index may not be used together");
+      ErrAlways(ctx) << "-r and --gdb-index may not be used together";
     if (ctx.arg.icf != ICFLevel::None)
-      error("-r and --icf may not be used together");
+      ErrAlways(ctx) << "-r and --icf may not be used together";
     if (ctx.arg.pie)
-      error("-r and -pie may not be used together");
+      ErrAlways(ctx) << "-r and -pie may not be used together";
     if (ctx.arg.exportDynamic)
-      error("-r and --export-dynamic may not be used together");
+      ErrAlways(ctx) << "-r and --export-dynamic may not be used together";
     if (ctx.arg.debugNames)
-      error("-r and --debug-names may not be used together");
+      ErrAlways(ctx) << "-r and --debug-names may not be used together";
     if (!ctx.arg.zSectionHeader)
-      error("-r and -z nosectionheader may not be used together");
+      ErrAlways(ctx) << "-r and -z nosectionheader may not be used together";
   }
 
   if (ctx.arg.executeOnly) {
     if (ctx.arg.emachine != EM_AARCH64)
-      error("--execute-only is only supported on AArch64 targets");
+      ErrAlways(ctx) << "--execute-only is only supported on AArch64 targets";
 
     if (ctx.arg.singleRoRx && !ctx.script->hasSectionsCommand)
-      error("--execute-only and --no-rosegment cannot be used together");
+      ErrAlways(ctx)
+          << "--execute-only and --no-rosegment cannot be used together";
   }
 
   if (ctx.arg.zRetpolineplt && ctx.arg.zForceIbt)
-    error("-z force-ibt may not be used with -z retpolineplt");
+    ErrAlways(ctx) << "-z force-ibt may not be used with -z retpolineplt";
 
   if (ctx.arg.emachine != EM_AARCH64) {
     if (ctx.arg.zPacPlt)
-      error("-z pac-plt only supported on AArch64");
+      ErrAlways(ctx) << "-z pac-plt only supported on AArch64";
     if (ctx.arg.zForceBti)
-      error("-z force-bti only supported on AArch64");
+      ErrAlways(ctx) << "-z force-bti only supported on AArch64";
     if (ctx.arg.zBtiReport != "none")
-      error("-z bti-report only supported on AArch64");
+      ErrAlways(ctx) << "-z bti-report only supported on AArch64";
     if (ctx.arg.zPauthReport != "none")
-      error("-z pauth-report only supported on AArch64");
+      ErrAlways(ctx) << "-z pauth-report only supported on AArch64";
     if (ctx.arg.zGcsReport != "none")
-      error("-z gcs-report only supported on AArch64");
+      ErrAlways(ctx) << "-z gcs-report only supported on AArch64";
     if (ctx.arg.zGcs != GcsPolicy::Implicit)
-      error("-z gcs only supported on AArch64");
+      ErrAlways(ctx) << "-z gcs only supported on AArch64";
   }
 
   if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
       ctx.arg.zCetReport != "none")
-    error("-z cet-report only supported on X86 and X86_64");
+    ErrAlways(ctx) << "-z cet-report only supported on X86 and X86_64";
 }
 
 static const char *getReproduceOption(opt::InputArgList &args) {
@@ -589,8 +592,8 @@ static uint8_t getZStartStopVisibility(opt::InputArgList &args) {
       else if (kv.second == "protected")
         ret = STV_PROTECTED;
       else
-        error("unknown -z start-stop-visibility= value: " +
-              StringRef(kv.second));
+        ErrAlways(ctx) << "unknown -z start-stop-visibility= value: "
+                       << StringRef(kv.second);
     }
   }
   return ret;
@@ -609,7 +612,7 @@ static GcsPolicy getZGcs(opt::InputArgList &args) {
       else if (kv.second == "always")
         ret = GcsPolicy::Always;
       else
-        error("unknown -z gcs= value: " + kv.second);
+        ErrAlways(ctx) << "unknown -z gcs= value: " << kv.second;
     }
   }
   return ret;
@@ -680,7 +683,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       if (!ltoSampleProfile.empty())
         readFile(ctx, ltoSampleProfile);
     } else {
-      error("--reproduce: " + toString(errOrWriter.takeError()));
+      ErrAlways(ctx) << "--reproduce: " << toString(errOrWriter.takeError());
     }
   }
 
@@ -755,7 +758,7 @@ static void setUnresolvedSymbolPolicy(Ctx &ctx, opt::InputArgList &args) {
         diagRegular = true;
         diagShlib = true;
       } else {
-        error("unknown --unresolved-symbols value: " + s);
+        ErrAlways(ctx) << "unknown --unresolved-symbols value: " << s;
       }
       break;
     }
@@ -794,7 +797,7 @@ static Target2Policy getTarget2(opt::InputArgList &args) {
     return Target2Policy::Abs;
   if (s == "got-rel")
     return Target2Policy::GotRel;
-  error("unknown --target2 option: " + s);
+  ErrAlways(ctx) << "unknown --target2 option: " << s;
   return Target2Policy::GotRel;
 }
 
@@ -803,7 +806,7 @@ static bool isOutputFormatBinary(opt::InputArgList &args) {
   if (s == "binary")
     return true;
   if (!s.starts_with("elf"))
-    error("unknown --oformat value: " + s);
+    ErrAlways(ctx) << "unknown --oformat value: " << s;
   return false;
 }
 
@@ -850,8 +853,8 @@ static int getMemtagMode(Ctx &ctx, opt::InputArgList &args) {
   if (memtagModeArg == "none")
     return ELF::NT_MEMTAG_LEVEL_NONE;
 
-  error("unknown --android-memtag-mode value: \"" + memtagModeArg +
-        "\", should be one of {async, sync, none}");
+  ErrAlways(ctx) << "unknown --android-memtag-mode value: \"" << memtagModeArg
+                 << "\", should be one of {async, sync, none}";
   return ELF::NT_MEMTAG_LEVEL_NONE;
 }
 
@@ -883,7 +886,7 @@ static uint64_t parseSectionAddress(StringRef s, opt::InputArgList &args,
   uint64_t va = 0;
   s.consume_front("0x");
   if (!to_integer(s, va, 16))
-    error("invalid argument: " + arg.getAsString(args));
+    ErrAlways(ctx) << "invalid argument: " << arg.getAsString(args);
   return va;
 }
 
@@ -912,7 +915,7 @@ static SortSectionPolicy getSortSection(opt::InputArgList &args) {
   if (s == "name")
     return SortSectionPolicy::Name;
   if (!s.empty())
-    error("unknown --sort-section rule: " + s);
+    ErrAlways(ctx) << "unknown --sort-section rule: " << s;
   return SortSectionPolicy::Default;
 }
 
@@ -923,7 +926,7 @@ static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &args) {
   if (s == "error")
     return OrphanHandlingPolicy::Error;
   if (s != "place")
-    error("unknown --orphan-handling mode: " + s);
+    ErrAlways(ctx) << "unknown --orphan-handling mode: " << s;
   return OrphanHandlingPolicy::Place;
 }
 
@@ -949,7 +952,7 @@ getBuildId(opt::InputArgList &args) {
     return {BuildIdKind::Hexstring, parseHex(s.substr(2))};
 
   if (s != "none")
-    error("unknown --build-id style: " + s);
+    ErrAlways(ctx) << "unknown --build-id style: " << s;
   return {BuildIdKind::None, {}};
 }
 
@@ -963,7 +966,7 @@ static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &args) {
     return {true, true};
 
   if (s != "none")
-    error("unknown --pack-dyn-relocs format: " + s);
+    ErrAlways(ctx) << "unknown --pack-dyn-relocs format: " << s;
   return {false, false};
 }
 
@@ -994,7 +997,7 @@ static void readCallGraph(Ctx &ctx, MemoryBufferRef mb) {
     uint64_t count;
 
     if (fields.size() != 3 || !to_integer(fields[2], count)) {
-      error(mb.getBufferIdentifier() + ": parse error");
+      ErrAlways(ctx) << mb.getBufferIdentifier() << ": parse error";
       return;
     }
 
@@ -1125,12 +1128,14 @@ static void ltoValidateAllVtablesHaveTypeInfos(Ctx &ctx,
   for (auto *arg : args.filtered(OPT_lto_known_safe_vtables)) {
     StringRef knownSafeName = arg->getValue();
     if (!knownSafeName.consume_front("_ZTV"))
-      error("--lto-known-safe-vtables=: expected symbol to start with _ZTV, "
-            "but got " +
-            knownSafeName);
+      ErrAlways(ctx)
+          << "--lto-known-safe-vtables=: expected symbol to start with _ZTV, "
+             "but got "
+          << knownSafeName;
     Expected<GlobPattern> pat = GlobPattern::create(knownSafeName);
     if (!pat)
-      error("--lto-known-safe-vtables=: " + toString(pat.takeError()));
+      ErrAlways(ctx) << "--lto-known-safe-vtables=: "
+                     << toString(pat.takeError());
     vtableSymbolsWithNoRTTI.remove_if(
         [&](StringRef s) { return pat->match(s); });
   }
@@ -1152,7 +1157,7 @@ static CGProfileSortKind getCGProfileSortKind(opt::InputArgList &args) {
   if (s == "cdsort")
     return CGProfileSortKind::Cdsort;
   if (s != "none")
-    error("unknown --call-graph-profile-sort= value: " + s);
+    ErrAlways(ctx) << "unknown --call-graph-profile-sort= value: " << s;
   return CGProfileSortKind::None;
 }
 
@@ -1163,10 +1168,10 @@ static DebugCompressionType getCompressionType(StringRef s, StringRef option) {
                                   .Default(DebugCompressionType::None);
   if (type == DebugCompressionType::None) {
     if (s != "none")
-      error("unknown " + option + " value: " + s);
+      ErrAlways(ctx) << "unknown " << option << " value: " << s;
   } else if (const char *reason = compression::getReasonIfUnsupported(
                  compression::formatFor(type))) {
-    error(option + ": " + reason);
+    ErrAlways(ctx) << option << ": " << reason;
   }
   return type;
 }
@@ -1186,7 +1191,8 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
   StringRef s = arg->getValue();
   std::pair<StringRef, StringRef> ret = s.split(';');
   if (ret.second.empty())
-    error(getAliasSpelling(arg) + " expects 'old;new' format, but got " + s);
+    ErrAlways(ctx) << getAliasSpelling(arg)
+                   << " expects 'old;new' format, but got " << s;
   return ret;
 }
 
@@ -1235,7 +1241,7 @@ static void parseClangOption(Ctx &ctx, StringRef opt, const Twine &msg) {
   const char *argv[] = {ctx.arg.progName.data(), opt.data()};
   if (cl::ParseCommandLineOptions(2, argv, "", &os))
     return;
-  error(msg + ": " + StringRef(err).trim());
+  ErrAlways(ctx) << msg << ": " << StringRef(err).trim();
 }
 
 // Checks the parameter of the bti-report and cet-report options.
@@ -1248,7 +1254,7 @@ static bool remapInputs(Ctx &ctx, StringRef line, const Twine &location) {
   SmallVector<StringRef, 0> fields;
   line.split(fields, '=');
   if (fields.size() != 2 || fields[1].empty()) {
-    error(location + ": parse error, not 'from-glob=to-file'");
+    ErrAlways(ctx) << location << ": parse error, not 'from-glob=to-file'";
     return true;
   }
   if (!hasWildcard(fields[0]))
@@ -1256,7 +1262,8 @@ static bool remapInputs(Ctx &ctx, StringRef line, const Twine &location) {
   else if (Expected<GlobPattern> pat = GlobPattern::create(fields[0]))
     ctx.arg.remapInputsWildcards.emplace_back(std::move(*pat), fields[1]);
   else {
-    error(location + ": " + toString(pat.takeError()) + ": " + fields[0]);
+    ErrAlways(ctx) << location << ": " << toString(pat.takeError()) << ": "
+                   << fields[0];
     return true;
   }
   return false;
@@ -1367,13 +1374,15 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
                    OPT_no_lto_validate_all_vtables_have_type_infos, false);
   ctx.arg.ltoo = args::getInteger(args, OPT_lto_O, 2);
   if (ctx.arg.ltoo > 3)
-    error("invalid optimization level for LTO: " + Twine(ctx.arg.ltoo));
+    ErrAlways(ctx) << "invalid optimization level for LTO: "
+                   << Twine(ctx.arg.ltoo);
   unsigned ltoCgo =
       args::getInteger(args, OPT_lto_CGO, args::getCGOptLevel(ctx.arg.ltoo));
   if (auto level = CodeGenOpt::getLevel(ltoCgo))
     ctx.arg.ltoCgo = *level;
   else
-    error("invalid codegen optimization level for LTO: " + Twine(ltoCgo));
+    ErrAlways(ctx) << "invalid codegen optimization level for LTO: "
+                   << Twine(ltoCgo);
   ctx.arg.ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
   ctx.arg.ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
   ctx.arg.ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
@@ -1403,8 +1412,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   if (auto *arg = args.getLastArg(OPT_opt_remarks_hotness_threshold)) {
     auto resultOrErr = remarks::parseHotnessThresholdOption(arg->getValue());
     if (!resultOrErr)
-      error(arg->getSpelling() + ": invalid argument '" + arg->getValue() +
-            "', only integer or 'auto' is supported");
+      ErrAlways(ctx) << arg->getSpelling() << ": invalid argument '"
+                     << arg->getValue()
+                     << "', only integer or 'auto' is supported";
     else
       ctx.arg.optRemarksHotnessThreshold = *resultOrErr;
   }
@@ -1442,7 +1452,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
       if (llvm::is_contained(saveTempsValues, s))
         ctx.arg.saveTempsArgs.insert(s);
       else
-        error("unknown --save-temps value: " + s);
+        ErrAlways(ctx) << "unknown --save-temps value: " << s;
     }
   }
 
@@ -1478,16 +1488,17 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
       getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace_eq);
   if (ctx.arg.thinLTOEmitIndexFiles && !ctx.arg.thinLTOIndexOnly) {
     if (args.hasArg(OPT_thinlto_object_suffix_replace_eq))
-      error("--thinlto-object-suffix-replace is not supported with "
-            "--thinlto-emit-index-files");
+      ErrAlways(ctx) << "--thinlto-object-suffix-replace is not supported with "
+                        "--thinlto-emit-index-files";
     else if (args.hasArg(OPT_thinlto_prefix_replace_eq))
-      error("--thinlto-prefix-replace is not supported with "
-            "--thinlto-emit-index-files");
+      ErrAlways(ctx) << "--thinlto-prefix-replace is not supported with "
+                        "--thinlto-emit-index-files";
   }
   if (!ctx.arg.thinLTOPrefixReplaceNativeObject.empty() &&
       ctx.arg.thinLTOIndexOnlyArg.empty()) {
-    error("--thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with "
-          "--thinlto-index-only=");
+    ErrAlways(ctx)
+        << "--thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with "
+           "--thinlto-index-only=";
   }
   ctx.arg.thinLTOModulesToCompile =
       args::getStrings(args, OPT_thinlto_single_module_eq);
@@ -1568,18 +1579,20 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     constexpr StringRef errPrefix = "--shuffle-sections=: ";
     std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
     if (kv.first.empty() || kv.second.empty()) {
-      error(errPrefix + "expected <section_glob>=<seed>, but got '" +
-            arg->getValue() + "'");
+      ErrAlways(ctx) << errPrefix << "expected <section_glob>=<seed>, but got '"
+                     << arg->getValue() << "'";
       continue;
     }
     // Signed so that <section_glob>=-1 is allowed.
     int64_t v;
     if (!to_integer(kv.second, v))
-      error(errPrefix + "expected an integer, but got '" + kv.second + "'");
+      ErrAlways(ctx) << errPrefix << "expected an integer, but got '"
+                     << kv.second << "'";
     else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
       ctx.arg.shuffleSections.emplace_back(std::move(*pat), uint32_t(v));
     else
-      error(errPrefix + toString(pat.takeError()) + ": " + kv.first);
+      ErrAlways(ctx) << errPrefix << toString(pat.takeError()) << ": "
+                     << kv.first;
   }
 
   auto reports = {std::make_pair("bti-report", &ctx.arg.zBtiReport),
@@ -1594,8 +1607,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
         continue;
       arg->claim();
       if (!isValidReportString(option.second)) {
-        error(Twine("-z ") + reportArg.first + "= parameter " + option.second +
-              " is not recognized");
+        ErrAlways(ctx) << Twine("-z ") << reportArg.first << "= parameter "
+                       << option.second << " is not recognized";
         continue;
       }
       *reportArg.second = option.second;
@@ -1606,8 +1619,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     SmallVector<StringRef, 0> fields;
     StringRef(arg->getValue()).split(fields, '=');
     if (fields.size() != 2 || fields[1].empty()) {
-      error(arg->getSpelling() +
-            ": parse error, not 'section-glob=[none|zlib|zstd]'");
+      ErrAlways(ctx) << arg->getSpelling()
+                     << ": parse error, not 'section-glob=[none|zlib|zstd]'";
       continue;
     }
     auto [typeStr, levelStr] = fields[1].split(':');
@@ -1615,14 +1628,15 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     unsigned level = 0;
     if (fields[1].size() != typeStr.size() &&
         !llvm::to_integer(levelStr, level)) {
-      error(arg->getSpelling() +
-            ": expected a non-negative integer compression level, but got '" +
-            levelStr + "'");
+      ErrAlways(ctx)
+          << arg->getSpelling()
+          << ": expected a non-negative integer compression level, but got '"
+          << levelStr << "'";
     }
     if (Expected<GlobPattern> pat = GlobPattern::create(fields[0])) {
       ctx.arg.compressSections.emplace_back(std::move(*pat), type, level);
     } else {
-      error(arg->getSpelling() + ": " + toString(pat.takeError()));
+      ErrAlways(ctx) << arg->getSpelling() << ": " << toString(pat.takeError());
       continue;
     }
   }
@@ -1636,17 +1650,18 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     constexpr StringRef errPrefix = "-z dead-reloc-in-nonalloc=: ";
     std::pair<StringRef, StringRef> kv = option.second.split('=');
     if (kv.first.empty() || kv.second.empty()) {
-      error(errPrefix + "expected <section_glob>=<value>");
+      ErrAlways(ctx) << errPrefix << "expected <section_glob>=<value>";
       continue;
     }
     uint64_t v;
     if (!to_integer(kv.second, v))
-      error(errPrefix + "expected a non-negative integer, but got '" +
-            kv.second + "'");
+      ErrAlways(ctx) << errPrefix
+                     << "expected a non-negative integer, but got '"
+                     << kv.second << "'";
     else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
       ctx.arg.deadRelocInNonAlloc.emplace_back(std::move(*pat), v);
     else
-      error(errPrefix + toString(pat.takeError()) + ": " + kv.first);
+      ErrAlways(ctx) << errPrefix << pat.takeError() << ": " << kv.first;
   }
 
   cl::ResetAllOptionOccurrences();
@@ -1667,8 +1682,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq)) {
     StringRef v(arg->getValue());
     if (!v.ends_with("lto-wrapper") && !v.ends_with("lto-wrapper.exe"))
-      error(arg->getSpelling() + ": unknown plugin option '" + arg->getValue() +
-            "'");
+      ErrAlways(ctx) << arg->getSpelling() << ": unknown plugin option '"
+                     << arg->getValue() << "'";
   }
 
   ctx.arg.passPlugins = args::getStrings(args, OPT_load_pass_plugins);
@@ -1689,7 +1704,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     else if (s == "default")
       ctx.arg.ltoKind = LtoKind::Default;
     else
-      error("unknown LTO mode: " + s);
+      ErrAlways(ctx) << "unknown LTO mode: " << s;
   }
 
   // --threads= takes a positive integer and provides the default value for
@@ -1700,8 +1715,9 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     StringRef v(arg->getValue());
     unsigned threads = 0;
     if (!llvm::to_integer(v, threads, 0) || threads == 0)
-      error(arg->getSpelling() + ": expected a positive integer, but got '" +
-            arg->getValue() + "'");
+      ErrAlways(ctx) << arg->getSpelling()
+                     << ": expected a positive integer, but got '"
+                     << arg->getValue() << "'";
     parallel::strategy = hardware_concurrency(threads);
     ctx.arg.thinLTOJobs = v;
   } else if (parallel::strategy.compute_thread_count() > 16) {
@@ -1713,20 +1729,22 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
   ctx.arg.threadCount = parallel::strategy.compute_thread_count();
 
   if (ctx.arg.ltoPartitions == 0)
-    error("--lto-partitions: number of threads must be > 0");
+    ErrAlways(ctx) << "--lto-partitions: number of threads must be > 0";
   if (!get_threadpool_strategy(ctx.arg.thinLTOJobs))
-    error("--thinlto-jobs: invalid job count: " + ctx.arg.thinLTOJobs);
+    ErrAlways(ctx) << "--thinlto-jobs: invalid job count: "
+                   << ctx.arg.thinLTOJobs;
 
   if (ctx.arg.splitStackAdjustSize < 0)
-    error("--split-stack-adjust-size: size must be >= 0");
+    ErrAlways(ctx) << "--split-stack-adjust-size: size must be >= 0";
 
   // The text segment is traditionally the first segment, whose address equals
   // the base address. However, lld places the R PT_LOAD first. -Ttext-segment
   // is an old-fashioned option that does not play well with lld's layout.
   // Suggest --image-base as a likely alternative.
   if (args.hasArg(OPT_Ttext_segment))
-    error("-Ttext-segment is not supported. Use --image-base if you "
-          "intend to set the base address");
+    ErrAlways(ctx)
+        << "-Ttext-segment is not supported. Use --image-base if you "
+           "intend to set the base address";
 
   // Parse ELF{32,64}{LE,BE} and CPU type.
   if (auto *arg = args.getLastArg(OPT_m)) {
@@ -1748,7 +1766,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     else if (s == "both")
       ctx.arg.sysvHash = ctx.arg.gnuHash = true;
     else
-      error("unknown --hash-style: " + s);
+      ErrAlways(ctx) << "unknown --hash-style: " << s;
   }
 
   if (args.hasArg(OPT_print_map))
@@ -1772,8 +1790,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
 
   if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){
     if (args.hasArg(OPT_call_graph_ordering_file))
-      error("--symbol-ordering-file and --call-graph-order-file "
-            "may not be used together");
+      ErrAlways(ctx) << "--symbol-ordering-file and --call-graph-order-file "
+                        "may not be used together";
     if (std::optional<MemoryBufferRef> buffer =
             readFile(ctx, arg->getValue())) {
       ctx.arg.symbolOrderingFile = getSymbolOrderingFile(ctx, *buffer);
@@ -1805,8 +1823,8 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
     if (Expected<GlobPattern> pat = GlobPattern::create(pattern))
       ctx.arg.warnBackrefsExclude.push_back(std::move(*pat));
     else
-      error(arg->getSpelling() + ": " + toString(pat.takeError()) + ": " +
-            pattern);
+      ErrAlways(ctx) << arg->getSpelling() << ": " << pat.takeError() << ": "
+                     << pattern;
   }
 
   // For -no-pie and -pie, --export-dynamic-symbol specifies defined symbols
@@ -1834,7 +1852,7 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
       if (std::optional<MemoryBufferRef> buffer = readFile(ctx, *path))
         readVersionScript(ctx, *buffer);
     } else {
-      error(Twine("cannot find version script ") + arg->getValue());
+      ErrAlways(ctx) << Twine("cannot find version script ") << arg->getValue();
     }
 }
 
@@ -1917,13 +1935,14 @@ static void setConfigs(Ctx &ctx, opt::InputArgList &args) {
   {
     llvm::TimeTraceScope timeScope("Create output files");
     if (auto e = tryCreateFile(ctx.arg.outputFile))
-      error("cannot open output file " + ctx.arg.outputFile + ": " +
-            e.message());
+      ErrAlways(ctx) << "cannot open output file " << ctx.arg.outputFile << ": "
+                     << e.message();
     if (auto e = tryCreateFile(ctx.arg.mapFile))
-      error("cannot open map file " + ctx.arg.mapFile + ": " + e.message());
+      ErrAlways(ctx) << "cannot open map file " << ctx.arg.mapFile << ": "
+                     << e.message();
     if (auto e = tryCreateFile(ctx.arg.whyExtract))
-      error("cannot open --why-extract= file " + ctx.arg.whyExtract + ": " +
-            e.message());
+      ErrAlways(ctx) << "cannot open --why-extract= file " << ctx.arg.whyExtract
+                     << ": " << e.message();
   }
 }
 
@@ -1932,8 +1951,8 @@ static bool isFormatBinary(StringRef s) {
     return true;
   if (s == "elf" || s == "default")
     return false;
-  error("unknown --format value: " + s +
-        " (supported formats: elf, default, binary)");
+  ErrAlways(ctx) << "unknown --format value: " << s
+                 << " (supported formats: elf, default, binary)";
   return false;
 }
 
@@ -1978,7 +1997,7 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
         }
         break;
       }
-      error(Twine("cannot find linker script ") + arg->getValue());
+      ErrAlways(ctx) << Twine("cannot find linker script ") << arg->getValue();
       break;
     case OPT_as_needed:
       ctx.arg.asNeeded = true;
@@ -2012,33 +2031,33 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
       break;
     case OPT_in_implib:
       if (armCmseImpLib)
-        error("multiple CMSE import libraries not supported");
+        ErrAlways(ctx) << "multiple CMSE import libraries not supported";
       else if (std::optional<MemoryBufferRef> mb =
                    readFile(ctx, arg->getValue()))
         armCmseImpLib = createObjFile(ctx, *mb);
       break;
     case OPT_start_group:
       if (isInGroup)
-        error("nested --start-group");
+        ErrAlways(ctx) << "nested --start-group";
       isInGroup = true;
       break;
     case OPT_end_group:
       if (!isInGroup)
-        error("stray --end-group");
+        ErrAlways(ctx) << "stray --end-group";
       isInGroup = false;
       ++nextGroupId;
       break;
     case OPT_start_lib:
       if (inLib)
-        error("nested --start-lib");
+        ErrAlways(ctx) << "nested --start-lib";
       if (isInGroup)
-        error("may not nest --start-lib in --start-group");
+        ErrAlways(ctx) << "may not nest --start-lib in --start-group";
       inLib = true;
       isInGroup = true;
       break;
     case OPT_end_lib:
       if (!inLib)
-        error("stray --end-lib");
+        ErrAlways(ctx) << "stray --end-lib";
       inLib = false;
       isInGroup = false;
       ++nextGroupId;
@@ -2048,7 +2067,7 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
       break;
     case OPT_pop_state:
       if (stack.empty()) {
-        error("unbalanced --push-state/--pop-state");
+        ErrAlways(ctx) << "unbalanced --push-state/--pop-state";
         break;
       }
       std::tie(ctx.arg.asNeeded, ctx.arg.isStatic, inWholeArchive) =
@@ -2061,7 +2080,7 @@ void LinkerDriver::createFiles(opt::InputArgList &args) {
   if (defaultScript && !hasScript)
     readLinkerScript(ctx, *defaultScript);
   if (files.empty() && !hasInput && errorCount() == 0)
-    error("no input files");
+    ErrAlways(ctx) << "no input files";
 }
 
 // If -m <machine_type> was not given, infer it from object files.
@@ -2084,7 +2103,8 @@ void LinkerDriver::inferMachineType() {
       return;
   }
   if (!inferred)
-    error("target emulation unknown: -m or at least one .o file required");
+    ErrAlways(ctx)
+        << "target emulation unknown: -m or at least one .o file required";
 }
 
 // Parse -z max-page-size=<value>. The default value is defined by
@@ -2093,7 +2113,7 @@ static uint64_t getMaxPageSize(Ctx &ctx, opt::InputArgList &args) {
   uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size",
                                        ctx.target->defaultMaxPageSize);
   if (!isPowerOf2_64(val)) {
-    error("max-page-size: value isn't a power of 2");
+    ErrAlways(ctx) << "max-page-size: value isn't a power of 2";
     return ctx.target->defaultMaxPageSize;
   }
   if (ctx.arg.nmagic || ctx.arg.omagic) {
@@ -2110,7 +2130,7 @@ static uint64_t getCommonPageSize(Ctx &ctx, opt::InputArgList &args) {
   uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size",
                                        ctx.target->defaultCommonPageSize);
   if (!isPowerOf2_64(val)) {
-    error("common-page-size: value isn't a power of 2");
+    ErrAlways(ctx) << "common-page-size: value isn't a power of 2";
     return ctx.target->defaultCommonPageSize;
   }
   if (ctx.arg.nmagic || ctx.arg.omagic) {
@@ -2135,7 +2155,7 @@ static std::optional<uint64_t> getImageBase(Ctx &ctx, opt::InputArgList &args) {
   StringRef s = arg->getValue();
   uint64_t v;
   if (!to_integer(s, v)) {
-    error("--image-base: number expected, but got " + s);
+    ErrAlways(ctx) << "--image-base: number expected, but got " << s;
     return 0;
   }
   if ((v % ctx.arg.maxPageSize) != 0)
@@ -2209,7 +2229,7 @@ static void handleUndefined(Ctx &ctx, Symbol *sym, const char *option) {
 static void handleUndefinedGlob(Ctx &ctx, StringRef arg) {
   Expected<GlobPattern> pat = GlobPattern::create(arg);
   if (!pat) {
-    error("--undefined-glob: " + toString(pat.takeError()) + ": " + arg);
+    ErrAlways(ctx) << "--undefined-glob: " << pat.takeError() << ": " << arg;
     return;
   }
 
@@ -2240,8 +2260,8 @@ static void writeArchiveStats(Ctx &ctx) {
   std::error_code ec;
   raw_fd_ostream os = ctx.openAuxiliaryFile(ctx.arg.printArchiveStats, ec);
   if (ec) {
-    error("--print-archive-stats=: cannot open " + ctx.arg.printArchiveStats +
-          ": " + ec.message());
+    ErrAlways(ctx) << "--print-archive-stats=: cannot open "
+                   << ctx.arg.printArchiveStats << ": " << ec.message();
     return;
   }
 
@@ -2270,8 +2290,8 @@ static void writeWhyExtract(Ctx &ctx) {
   std::error_code ec;
   raw_fd_ostream os = ctx.openAuxiliaryFile(ctx.arg.whyExtract, ec);
   if (ec) {
-    error("cannot open --why-extract= file " + ctx.arg.whyExtract + ": " +
-          ec.message());
+    ErrAlways(ctx) << "cannot open --why-extract= file " << ctx.arg.whyExtract
+                   << ": " << ec.message();
     return;
   }
 
@@ -2329,7 +2349,8 @@ static void writeDependencyFile(Ctx &ctx) {
   std::error_code ec;
   raw_fd_ostream os = ctx.openAuxiliaryFile(ctx.arg.dependencyFile, ec);
   if (ec) {
-    error("cannot open " + ctx.arg.dependencyFile + ": " + ec.message());
+    ErrAlways(ctx) << "cannot open " << ctx.arg.dependencyFile << ": "
+                   << ec.message();
     return;
   }
 
@@ -2492,16 +2513,17 @@ static void readSymbolPartitionSection(Ctx &ctx, InputSectionBase *s) {
   // from being used together with various linker features that assume a single
   // set of output sections.
   if (ctx.script->hasSectionsCommand)
-    error(toString(s->file) +
-          ": partitions cannot be used with the SECTIONS command");
+    ErrAlways(ctx) << s->file
+                   << ": partitions cannot be used with the SECTIONS command";
   if (ctx.script->hasPhdrsCommands())
-    error(toString(s->file) +
-          ": partitions cannot be used with the PHDRS command");
+    ErrAlways(ctx) << s->file
+                   << ": partitions cannot be used with the PHDRS command";
   if (!ctx.arg.sectionStartMap.empty())
-    error(toString(s->file) + ": partitions cannot be used with "
-                              "--section-start, -Ttext, -Tdata or -Tbss");
+    ErrAlways(ctx) << s->file
+                   << ": partitions cannot be used with "
+                      "--section-start, -Ttext, -Tdata or -Tbss";
   if (ctx.arg.emachine == EM_MIPS)
-    error(toString(s->file) + ": partitions cannot be used on this target");
+    ErrAlways(ctx) << s->file << ": partitions cannot be used on this target";
 
   // Impose a limit of no more than 254 partitions. This limit comes from the
   // sizes of the Partition fields in InputSectionBase and Symbol, as well as
@@ -2725,7 +2747,7 @@ static void redirectSymbols(Ctx &ctx, ArrayRef<WrappedSymbol> wrapped) {
 
 static void reportMissingFeature(StringRef config, const Twine &report) {
   if (config == "error")
-    error(report);
+    ErrAlways(ctx) << report;
   else if (config == "warning")
     warn(report);
 }

diff  --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index cd7688ee3f80c0..096f033a767ef5 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -62,14 +62,14 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
   else if (s == "never")
     lld::errs().enable_colors(false);
   else if (s != "auto")
-    error("unknown option: --color-diagnostics=" + s);
+    ErrAlways(ctx) << "unknown option: --color-diagnostics=" << s;
 }
 
 static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
   if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
     StringRef s = arg->getValue();
     if (s != "windows" && s != "posix")
-      error("invalid response file quoting: " + s);
+      ErrAlways(ctx) << "invalid response file quoting: " << s;
     if (s == "windows")
       return cl::TokenizeWindowsCommandLine;
     return cl::TokenizeGNUCommandLine;
@@ -122,15 +122,16 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
 
   handleColorDiagnostics(args);
   if (missingCount)
-    error(Twine(args.getArgString(missingIndex)) + ": missing argument");
+    ErrAlways(ctx) << Twine(args.getArgString(missingIndex))
+                   << ": missing argument";
 
   for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) {
     std::string nearest;
     if (findNearest(arg->getAsString(args), nearest) > 1)
-      error("unknown argument '" + arg->getAsString(args) + "'");
+      ErrAlways(ctx) << "unknown argument '" << arg->getAsString(args) << "'";
     else
-      error("unknown argument '" + arg->getAsString(args) +
-            "', did you mean '" + nearest + "'");
+      ErrAlways(ctx) << "unknown argument '" << arg->getAsString(args)
+                     << "', did you mean '" << nearest << "'";
   }
   return args;
 }

diff  --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index f9bf90bdc0a6a2..4c440cebc37f12 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -133,12 +133,13 @@ static void updateARMVFPArgs(Ctx &ctx, const ARMAttributeParser &attributes,
     // Object compatible with all conventions.
     return;
   default:
-    error(toString(f) + ": unknown Tag_ABI_VFP_args value: " + Twine(vfpArgs));
+    ErrAlways(ctx) << f
+                   << ": unknown Tag_ABI_VFP_args value: " << Twine(vfpArgs);
     return;
   }
   // Follow ld.bfd and error if there is a mix of calling conventions.
   if (ctx.arg.armVFPArgs != arg && ctx.arg.armVFPArgs != ARMVFPArgKind::Default)
-    error(toString(f) + ": incompatible Tag_ABI_VFP_args");
+    ErrAlways(ctx) << f << ": incompatible Tag_ABI_VFP_args";
   else
     ctx.arg.armVFPArgs = arg;
 }
@@ -252,7 +253,7 @@ std::optional<MemoryBufferRef> elf::readFile(Ctx &ctx, StringRef path) {
   auto mbOrErr = MemoryBuffer::getFile(path, /*IsText=*/false,
                                        /*RequiresNullTerminator=*/false);
   if (auto ec = mbOrErr.getError()) {
-    error("cannot open " + path + ": " + ec.message());
+    ErrAlways(ctx) << "cannot open " << path << ": " << ec.message();
     return std::nullopt;
   }
 
@@ -281,7 +282,7 @@ static bool isCompatible(Ctx &ctx, InputFile *file) {
   StringRef target =
       !ctx.arg.bfdname.empty() ? ctx.arg.bfdname : ctx.arg.emulation;
   if (!target.empty()) {
-    error(toString(file) + " is incompatible with " + target);
+    ErrAlways(ctx) << file << " is incompatible with " << target;
     return false;
   }
 
@@ -295,7 +296,7 @@ static bool isCompatible(Ctx &ctx, InputFile *file) {
   std::string with;
   if (existing)
     with = " with " + toString(existing);
-  error(toString(file) + " is incompatible" + with);
+  ErrAlways(ctx) << file << " is incompatible" << with;
   return false;
 }
 
@@ -432,9 +433,9 @@ static void addDependentLibrary(Ctx &ctx, StringRef specifier,
   else if (fs::exists(specifier))
     ctx.driver.addFile(specifier, /*withLOption=*/false);
   else
-    error(toString(f) +
-          ": unable to find library from dependent library specifier: " +
-          specifier);
+    ErrAlways(ctx)
+        << f << ": unable to find library from dependent library specifier: "
+        << specifier;
 }
 
 // Record the membership of a section group so that in the garbage collection
@@ -608,10 +609,10 @@ template <class ELFT> void ObjFile<ELFT>::parse(bool ignoreComdats) {
       ArrayRef<char> data = CHECK(
           this->getObj().template getSectionContentsAsArray<char>(sec), this);
       if (!data.empty() && data.back() != '\0') {
-        error(
-            toString(this) +
-            ": corrupted dependent libraries section (unterminated string): " +
-            name);
+        ErrAlways(ctx)
+            << this
+            << ": corrupted dependent libraries section (unterminated string): "
+            << name;
       } else {
         for (const char *d = data.begin(), *e = data.end(); d < e;) {
           StringRef s(d);
@@ -904,9 +905,9 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
       }
 
       if (s->relSecIdx != 0)
-        error(
-            toString(s) +
-            ": multiple relocation sections to one section are not supported");
+        ErrAlways(ctx) << s
+                       << ": multiple relocation sections to one section are "
+                          "not supported";
       s->relSecIdx = i;
 
       // Relocation sections are usually removed from the output, so return
@@ -940,9 +941,10 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats,
     InputSection *isec = cast<InputSection>(this->sections[i]);
     linkSec->dependentSections.push_back(isec);
     if (!isa<InputSection>(linkSec))
-      error("a section " + isec->name +
-            " with SHF_LINK_ORDER should not refer a non-regular section: " +
-            toString(linkSec));
+      ErrAlways(ctx)
+          << "a section " << isec->name
+          << " with SHF_LINK_ORDER should not refer a non-regular section: "
+          << linkSec;
   }
 
   for (ArrayRef<Elf_Word> entries : selectedGroups)
@@ -1040,8 +1042,8 @@ InputSectionBase *ObjFile<ELFT>::getRelocTarget(uint32_t idx, uint32_t info) {
       return target;
   }
 
-  error(toString(this) + Twine(": relocation section (index ") + Twine(idx) +
-        ") has invalid sh_info (" + Twine(info) + ")");
+  ErrAlways(ctx) << this << Twine(": relocation section (index ") << Twine(idx)
+                 << ") has invalid sh_info (" << Twine(info) << ")";
   return nullptr;
 }
 
@@ -1087,8 +1089,8 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
     // for split stack will include a .note.GNU-split-stack section.
     if (name == ".note.GNU-split-stack") {
       if (ctx.arg.relocatable) {
-        error(
-            "cannot mix split-stack and non-split-stack in a relocatable link");
+        ErrAlways(ctx) << "cannot mix split-stack and non-split-stack in a "
+                          "relocatable link";
         return &InputSection::discarded;
       }
       this->splitStack = true;
@@ -1209,8 +1211,9 @@ void ObjFile<ELFT>::initSectionsAndLocalSyms(bool ignoreComdats) {
     if (LLVM_UNLIKELY(secIdx >= sections.size()))
       Fatal(ctx) << this << ": invalid section index: " << Twine(secIdx);
     if (LLVM_UNLIKELY(eSym.getBinding() != STB_LOCAL))
-      error(toString(this) + ": non-local symbol (" + Twine(i) +
-            ") found at index < .symtab's sh_info (" + Twine(end) + ")");
+      ErrAlways(ctx) << this << ": non-local symbol (" << Twine(i)
+                     << ") found at index < .symtab's sh_info (" << Twine(end)
+                     << ")";
 
     InputSectionBase *sec = sections[secIdx];
     uint8_t type = eSym.getType();
@@ -1492,7 +1495,7 @@ template <class ELFT> void SharedFile::parse() {
   }
 
   if (versymSec && numELFSyms == 0) {
-    error("SHT_GNU_versym should be associated with symbol table");
+    ErrAlways(ctx) << "SHT_GNU_versym should be associated with symbol table";
     return;
   }
 
@@ -1571,9 +1574,9 @@ template <class ELFT> void SharedFile::parse() {
       // as of binutils 2.34, GNU ld produces VER_NDX_LOCAL.
       if (ver != VER_NDX_LOCAL && ver != VER_NDX_GLOBAL) {
         if (idx >= verneeds.size()) {
-          error("corrupt input file: version need index " + Twine(idx) +
-                " for symbol " + name + " is out of bounds\n>>> defined in " +
-                toString(this));
+          ErrAlways(ctx) << "corrupt input file: version need index "
+                         << Twine(idx) << " for symbol " << name
+                         << " is out of bounds\n>>> defined in " << this;
           continue;
         }
         StringRef verName = stringTable.data() + verneeds[idx];
@@ -1597,9 +1600,9 @@ template <class ELFT> void SharedFile::parse() {
       // VER_NDX_LOCAL. Workaround this bug.
       if (ctx.arg.emachine == EM_MIPS && name == "_gp_disp")
         continue;
-      error("corrupt input file: version definition index " + Twine(idx) +
-            " for symbol " + name + " is out of bounds\n>>> defined in " +
-            toString(this));
+      ErrAlways(ctx) << "corrupt input file: version definition index "
+                     << Twine(idx) << " for symbol " << name
+                     << " is out of bounds\n>>> defined in " << this;
       continue;
     }
 
@@ -1683,8 +1686,9 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) {
   case Triple::x86_64:
     return EM_X86_64;
   default:
-    error(path + ": could not infer e_machine from bitcode target triple " +
-          t.str());
+    ErrAlways(ctx) << path
+                   << ": could not infer e_machine from bitcode target triple "
+                   << t.str();
     return EM_NONE;
   }
 }

diff  --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index f4a5304d4f251f..92bbc977a20a0b 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -64,7 +64,7 @@ InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags,
   // sections are smaller than 4 GiB, which is not an unreasonable
   // assumption as of 2017.
   if (sectionKind == SectionBase::Merge && content().size() > UINT32_MAX)
-    error(toString(this) + ": section too large");
+    ErrAlways(ctx) << this << ": section too large";
 
   // The ELF spec states that a value of 0 means the section has
   // no alignment constraints.
@@ -256,22 +256,24 @@ template <typename ELFT> void InputSectionBase::parseCompressedHeader() {
 
   // New-style header
   if (content().size() < sizeof(typename ELFT::Chdr)) {
-    error(toString(this) + ": corrupted compressed section");
+    ErrAlways(ctx) << this << ": corrupted compressed section";
     return;
   }
 
   auto *hdr = reinterpret_cast<const typename ELFT::Chdr *>(content().data());
   if (hdr->ch_type == ELFCOMPRESS_ZLIB) {
     if (!compression::zlib::isAvailable())
-      error(toString(this) + " is compressed with ELFCOMPRESS_ZLIB, but lld is "
-                             "not built with zlib support");
+      ErrAlways(ctx) << this
+                     << " is compressed with ELFCOMPRESS_ZLIB, but lld is "
+                        "not built with zlib support";
   } else if (hdr->ch_type == ELFCOMPRESS_ZSTD) {
     if (!compression::zstd::isAvailable())
-      error(toString(this) + " is compressed with ELFCOMPRESS_ZSTD, but lld is "
-                             "not built with zstd support");
+      ErrAlways(ctx) << this
+                     << " is compressed with ELFCOMPRESS_ZSTD, but lld is "
+                        "not built with zstd support";
   } else {
-    error(toString(this) + ": unsupported compression type (" +
-          Twine(hdr->ch_type) + ")");
+    ErrAlways(ctx) << this << ": unsupported compression type ("
+                   << Twine(hdr->ch_type) << ")";
     return;
   }
 
@@ -1157,8 +1159,8 @@ static void switchMorestackCallsToMorestackNonSplit(
   // __morestack_non_split.
   Symbol *moreStackNonSplit = ctx.symtab->find("__morestack_non_split");
   if (!moreStackNonSplit) {
-    error("mixing split-stack objects requires a definition of "
-          "__morestack_non_split");
+    ErrAlways(ctx) << "mixing split-stack objects requires a definition of "
+                      "__morestack_non_split";
     return;
   }
 
@@ -1235,9 +1237,10 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(Ctx &ctx, uint8_t *buf,
                                                        f->stOther))
         continue;
       if (!getFile<ELFT>()->someNoSplitStack)
-        error(lld::toString(this) + ": " + f->getName() +
-              " (with -fsplit-stack) calls " + rel.sym->getName() +
-              " (without -fsplit-stack), but couldn't adjust its prologue");
+        ErrAlways(ctx)
+            << lld::toString(this) << ": " << f->getName()
+            << " (with -fsplit-stack) calls " << rel.sym->getName()
+            << " (without -fsplit-stack), but couldn't adjust its prologue";
     }
   }
 

diff  --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index aef058286cea0d..7214b7f2ae4904 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -78,8 +78,8 @@ static lto::Config createConfig(Ctx &ctx) {
       ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
           MemoryBuffer::getFile(ctx.arg.ltoBasicBlockSections.str());
       if (!MBOrErr) {
-        error("cannot open " + ctx.arg.ltoBasicBlockSections + ":" +
-              MBOrErr.getError().message());
+        ErrAlways(ctx) << "cannot open " << ctx.arg.ltoBasicBlockSections << ":"
+                       << MBOrErr.getError().message();
       } else {
         c.Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
       }

diff  --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index d2088b4c648180..72c876fe716552 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -309,7 +309,8 @@ void LinkerScript::processInsertCommands() {
   SmallVector<OutputDesc *, 0> moves;
   for (const InsertCommand &cmd : insertCommands) {
     if (ctx.arg.enableNonContiguousRegions)
-      error("INSERT cannot be used with --enable-non-contiguous-regions");
+      ErrAlways(ctx)
+          << "INSERT cannot be used with --enable-non-contiguous-regions";
 
     for (StringRef name : cmd.names) {
       // If base is empty, it may have been discarded by
@@ -330,8 +331,8 @@ void LinkerScript::processInsertCommands() {
           return to != nullptr && to->osec.name == cmd.where;
         });
     if (insertPos == sectionCommands.end()) {
-      error("unable to insert " + cmd.names[0] +
-            (cmd.isAfter ? " after " : " before ") + cmd.where);
+      ErrAlways(ctx) << "unable to insert " << cmd.names[0]
+                     << (cmd.isAfter ? " after " : " before ") << cmd.where;
     } else {
       if (cmd.isAfter)
         ++insertPos;
@@ -652,7 +653,7 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd,
 
 void LinkerScript::discard(InputSectionBase &s) {
   if (&s == ctx.in.shStrTab.get())
-    error("discarding " + s.name + " section is not allowed");
+    ErrAlways(ctx) << "discarding " << s.name << " section is not allowed";
 
   s.markDead();
   s.parent = nullptr;
@@ -737,8 +738,8 @@ void LinkerScript::processSectionCommands() {
   // Process OVERWRITE_SECTIONS first so that it can overwrite the main script
   // or orphans.
   if (ctx.arg.enableNonContiguousRegions && !overwriteSections.empty())
-    error("OVERWRITE_SECTIONS cannot be used with "
-          "--enable-non-contiguous-regions");
+    ErrAlways(ctx) << "OVERWRITE_SECTIONS cannot be used with "
+                      "--enable-non-contiguous-regions";
   DenseMap<CachedHashStringRef, OutputDesc *> map;
   size_t i = 0;
   for (OutputDesc *osd : overwriteSections) {
@@ -1052,7 +1053,7 @@ void LinkerScript::diagnoseOrphanHandling() const {
 
     StringRef name = getOutputSectionName(sec);
     if (ctx.arg.orphanHandling == OrphanHandlingPolicy::Error)
-      error(toString(sec) + " is being placed in '" + name + "'");
+      ErrAlways(ctx) << sec << " is being placed in '" << name << "'";
     else
       warn(toString(sec) + " is being placed in '" + name + "'");
   }
@@ -1064,7 +1065,8 @@ void LinkerScript::diagnoseMissingSGSectionAddress() const {
 
   OutputSection *sec = findByName(sectionCommands, ".gnu.sgstubs");
   if (sec && !sec->addrExpr && !ctx.arg.sectionStartMap.count(".gnu.sgstubs"))
-    error("no address assigned to the veneers output section " + sec->name);
+    ErrAlways(ctx) << "no address assigned to the veneers output section "
+                   << sec->name;
 }
 
 // This function searches for a memory region to place the given output
@@ -1092,7 +1094,8 @@ LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) {
   if (!sec->memoryRegionName.empty()) {
     if (MemoryRegion *m = memoryRegions.lookup(sec->memoryRegionName))
       return {m, m};
-    error("memory region '" + sec->memoryRegionName + "' not declared");
+    ErrAlways(ctx) << "memory region '" << sec->memoryRegionName
+                   << "' not declared";
     return {nullptr, nullptr};
   }
 
@@ -1114,7 +1117,8 @@ LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) {
   }
 
   // Otherwise, no suitable region was found.
-  error("no memory region specified for section '" + sec->name + "'");
+  ErrAlways(ctx) << "no memory region specified for section '" << sec->name
+                 << "'";
   return {nullptr, nullptr};
 }
 
@@ -1396,7 +1400,8 @@ void LinkerScript::adjustSectionsAfterSorting() {
         if (MemoryRegion *m = memoryRegions.lookup(sec->lmaRegionName))
           sec->lmaRegion = m;
         else
-          error("memory region '" + sec->lmaRegionName + "' not declared");
+          ErrAlways(ctx) << "memory region '" << sec->lmaRegionName
+                         << "' not declared";
       }
       std::tie(sec->memRegion, hint) = findMemoryRegion(sec, hint);
     }
@@ -1462,7 +1467,7 @@ void LinkerScript::allocateHeaders(SmallVector<PhdrEntry *, 0> &phdrs) {
 
   // Error if we were explicitly asked to allocate headers.
   if (hasExplicitHeaders)
-    error("could not allocate headers");
+    ErrAlways(ctx) << "could not allocate headers";
 
   ctx.out.elfHeader->ptLoad = nullptr;
   ctx.out.programHeaders->ptLoad = nullptr;
@@ -1682,7 +1687,7 @@ ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) {
   if (name == ".") {
     if (state)
       return {state->outSec, false, dot - state->outSec->addr, loc};
-    error(loc + ": unable to get location counter value");
+    ErrAlways(ctx) << loc << ": unable to get location counter value";
     return 0;
   }
 
@@ -1700,7 +1705,7 @@ ExprValue LinkerScript::getSymbolValue(StringRef name, const Twine &loc) {
         return {nullptr, false, 0, loc};
   }
 
-  error(loc + ": symbol not found: " + name);
+  ErrAlways(ctx) << loc << ": symbol not found: " << name;
   return 0;
 }
 
@@ -1722,8 +1727,8 @@ SmallVector<size_t, 0> LinkerScript::getPhdrIndices(OutputSection *cmd) {
     if (std::optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
       ret.push_back(*idx);
     else if (s != "NONE")
-      error(cmd->location + ": program header '" + s +
-            "' is not listed in PHDRS");
+      ErrAlways(ctx) << cmd->location << ": program header '" << s
+                     << "' is not listed in PHDRS";
   }
   return ret;
 }
@@ -1765,9 +1770,9 @@ static void checkMemoryRegion(const MemoryRegion *region,
   uint64_t osecEnd = addr + osec->size;
   uint64_t regionEnd = region->getOrigin() + region->getLength();
   if (osecEnd > regionEnd) {
-    error("section '" + osec->name + "' will not fit in region '" +
-          region->name + "': overflowed by " + Twine(osecEnd - regionEnd) +
-          " bytes");
+    ErrAlways(ctx) << "section '" << osec->name << "' will not fit in region '"
+                   << region->name << "': overflowed by "
+                   << Twine(osecEnd - regionEnd) << " bytes";
   }
 }
 

diff  --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp
index afaf04dc72fe6c..caf386ad2d6278 100644
--- a/lld/ELF/MapFile.cpp
+++ b/lld/ELF/MapFile.cpp
@@ -268,7 +268,7 @@ void elf::writeMapAndCref(Ctx &ctx) {
   StringRef mapFile = ctx.arg.mapFile.empty() ? "-" : ctx.arg.mapFile;
   raw_fd_ostream os = ctx.openAuxiliaryFile(mapFile, ec);
   if (ec) {
-    error("cannot open " + mapFile + ": " + ec.message());
+    ErrAlways(ctx) << "cannot open " << mapFile << ": " << ec.message();
     return;
   }
 

diff  --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 6cae7cf8f8599d..fb4d7130b765bf 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -151,9 +151,10 @@ void OutputSection::commitSection(InputSection *isec) {
   } else {
     // Otherwise, check if new type or flags are compatible with existing ones.
     if ((flags ^ isec->flags) & SHF_TLS)
-      error("incompatible section flags for " + name + "\n>>> " +
-            toString(isec) + ": 0x" + utohexstr(isec->flags) +
-            "\n>>> output section " + name + ": 0x" + utohexstr(flags));
+      ErrAlways(ctx) << "incompatible section flags for " << name << "\n>>> "
+                     << isec << ": 0x" << utohexstr(isec->flags)
+                     << "\n>>> output section " << name << ": 0x"
+                     << utohexstr(flags);
   }
 
   isec->parent = this;

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 6553ff9b06bb1e..88e742585673d6 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1042,8 +1042,9 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
   if (sym.scriptDefined)
       return true;
 
-  error("relocation " + toString(type) + " cannot refer to absolute symbol: " +
-        toString(sym) + getLocation(ctx, *sec, sym, relOff));
+  Err(ctx) << "relocation " << type
+           << " cannot refer to absolute symbol: " << &sym
+           << getLocation(ctx, *sec, sym, relOff);
   return true;
 }
 
@@ -1218,10 +1219,9 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
       // Produce a copy relocation.
       if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
         if (!ctx.arg.zCopyreloc)
-          error("unresolvable relocation " + toString(type) +
-                " against symbol '" + toString(*ss) +
-                "'; recompile with -fPIC or remove '-z nocopyreloc'" +
-                getLocation(ctx, *sec, sym, offset));
+          Err(ctx) << "unresolvable relocation " << type << " against symbol '"
+                   << ss << "'; recompile with -fPIC or remove '-z nocopyreloc'"
+                   << getLocation(ctx, *sec, sym, offset);
         sym.setFlags(NEEDS_COPY);
       }
       sec->addReloc({expr, type, offset, addend, &sym});

diff  --git a/lld/ELF/ScriptLexer.cpp b/lld/ELF/ScriptLexer.cpp
index 7b45e26ee405e0..a89db24e27d039 100644
--- a/lld/ELF/ScriptLexer.cpp
+++ b/lld/ELF/ScriptLexer.cpp
@@ -87,7 +87,7 @@ void ScriptLexer::setError(const Twine &msg) {
   if (prevTok.size())
     s += "\n>>> " + getLine().str() + "\n>>> " +
          std::string(getColumnNumber(), ' ') + "^";
-  error(s);
+  ErrAlways(ctx) << s;
 }
 
 void ScriptLexer::lex() {
@@ -116,7 +116,8 @@ void ScriptLexer::lex() {
       if (e == StringRef::npos) {
         size_t lineno =
             StringRef(curBuf.begin, s.data() - curBuf.begin).count('\n');
-        error(curBuf.filename + ":" + Twine(lineno + 1) + ": unclosed quote");
+        ErrAlways(ctx) << curBuf.filename << ":" << Twine(lineno + 1)
+                       << ": unclosed quote";
         return;
       }
 

diff  --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 3febcfb87da4ac..472605fd294be2 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -975,7 +975,7 @@ static Expr checkAlignment(Expr e, std::string &loc) {
   return [=] {
     uint64_t alignment = std::max((uint64_t)1, e().getValue());
     if (!isPowerOf2_64(alignment)) {
-      error(loc + ": alignment must be power of 2");
+      ErrAlways(ctx) << loc << ": alignment must be power of 2";
       return (uint64_t)1; // Return a dummy value.
     }
     return alignment;
@@ -1081,7 +1081,7 @@ OutputDesc *ScriptParser::readOutputSectionDescription(StringRef outSec) {
   }
 
   if (osec->lmaExpr && !osec->lmaRegionName.empty())
-    error("section can't have both LMA and a load region");
+    ErrAlways(ctx) << "section can't have both LMA and a load region";
 
   osec->phdrs = readOutputSectionPhdrs();
 
@@ -1196,7 +1196,7 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
   Expr e = readExpr();
   if (op != "=") {
     std::string loc = getCurrentLocation();
-    e = [=, s = ctx.script, c = op[0]]() -> ExprValue {
+    e = [=, s = ctx.script, c = op[0], &ctx = ctx]() -> ExprValue {
       ExprValue lhs = s->getSymbolValue(name, loc);
       switch (c) {
       case '*':
@@ -1204,7 +1204,7 @@ SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef name) {
       case '/':
         if (uint64_t rv = e().getValue())
           return lhs.getValue() / rv;
-        error(loc + ": division by zero");
+        ErrAlways(ctx) << loc << ": division by zero";
         return 0;
       case '+':
         return add(*s, lhs, e());
@@ -1248,19 +1248,19 @@ Expr ScriptParser::combine(StringRef op, Expr l, Expr r) {
     return [=] { return l().getValue() * r().getValue(); };
   if (op == "/") {
     std::string loc = getCurrentLocation();
-    return [=]() -> uint64_t {
+    return [=, &ctx = ctx]() -> uint64_t {
       if (uint64_t rv = r().getValue())
         return l().getValue() / rv;
-      error(loc + ": division by zero");
+      ErrAlways(ctx) << loc << ": division by zero";
       return 0;
     };
   }
   if (op == "%") {
     std::string loc = getCurrentLocation();
-    return [=]() -> uint64_t {
+    return [=, &ctx = ctx]() -> uint64_t {
       if (uint64_t rv = r().getValue())
         return l().getValue() % rv;
-      error(loc + ": modulo by zero");
+      ErrAlways(ctx) << loc << ": modulo by zero";
       return 0;
     };
   }
@@ -1327,7 +1327,7 @@ Expr ScriptParser::getPageSize() {
   return [=, &ctx = this->ctx]() -> uint64_t {
     if (ctx.target)
       return ctx.arg.commonPageSize;
-    error(location + ": unable to calculate page size");
+    ErrAlways(ctx) << location << ": unable to calculate page size";
     return 4096; // Return a dummy value.
   };
 }

diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 40f54ea7a2507b..fc3a5115673079 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -248,8 +248,8 @@ void Symbol::parseSymbolVersion(Ctx &ctx) {
   // if the symbol has a local version as it won't be in the dynamic
   // symbol table.
   if (ctx.arg.shared && versionId != VER_NDX_LOCAL)
-    error(toString(file) + ": symbol " + s + " has undefined version " +
-          verstr);
+    ErrAlways(ctx) << file << ": symbol " << s << " has undefined version "
+                   << verstr;
 }
 
 void Symbol::extract(Ctx &ctx) const {

diff  --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 745112164edd6f..6c7abdc7bff547 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -125,15 +125,17 @@ MipsAbiFlagsSection<ELFT>::create(Ctx &ctx) {
     // .MIPS.abiflags instead of merging. To allow for this case (or potential
     // zero padding) we ignore everything after the first Elf_Mips_ABIFlags
     if (size < sizeof(Elf_Mips_ABIFlags)) {
-      error(filename + ": invalid size of .MIPS.abiflags section: got " +
-            Twine(size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
+      ErrAlways(ctx) << filename
+                     << ": invalid size of .MIPS.abiflags section: got "
+                     << Twine(size) << " instead of "
+                     << Twine(sizeof(Elf_Mips_ABIFlags));
       return nullptr;
     }
     auto *s =
         reinterpret_cast<const Elf_Mips_ABIFlags *>(sec->content().data());
     if (s->version != 0) {
-      error(filename + ": unexpected .MIPS.abiflags version " +
-            Twine(s->version));
+      ErrAlways(ctx) << filename << ": unexpected .MIPS.abiflags version "
+                     << Twine(s->version);
       return nullptr;
     }
 
@@ -198,7 +200,7 @@ MipsOptionsSection<ELFT>::create(Ctx &ctx) {
 
     while (!d.empty()) {
       if (d.size() < sizeof(Elf_Mips_Options)) {
-        error(filename + ": invalid size of .MIPS.options section");
+        ErrAlways(ctx) << filename << ": invalid size of .MIPS.options section";
         break;
       }
 
@@ -252,7 +254,7 @@ MipsReginfoSection<ELFT>::create(Ctx &ctx) {
     sec->markDead();
 
     if (sec->content().size() != sizeof(Elf_Mips_RegInfo)) {
-      error(toString(sec->file) + ": invalid size of .reginfo section");
+      ErrAlways(ctx) << sec->file << ": invalid size of .reginfo section";
       return nullptr;
     }
 
@@ -4392,7 +4394,7 @@ static uint8_t getAbiVersion(Ctx &ctx) {
     uint8_t ver = ctx.objectFiles[0]->abiVersion;
     for (InputFile *file : ArrayRef(ctx.objectFiles).slice(1))
       if (file->abiVersion != ver)
-        error("incompatible ABI version: " + toString(file));
+        ErrAlways(ctx) << "incompatible ABI version: " << file;
     return ver;
   }
 

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 12d51b828710d2..1a6ac7bfa15aa2 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -202,8 +202,8 @@ void elf::addReservedSymbols(Ctx &ctx) {
 
   if (Symbol *s = ctx.symtab->find(gotSymName)) {
     if (s->isDefined()) {
-      error(toString(s->file) + " cannot redefine linker defined symbol '" +
-            gotSymName + "'");
+      ErrAlways(ctx) << s->file << " cannot redefine linker defined symbol '"
+                     << gotSymName << "'";
       return;
     }
 
@@ -1413,8 +1413,8 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
         if (isec->flags & SHF_LINK_ORDER) {
           InputSection *link = isec->getLinkOrderDep();
           if (link && !link->getParent())
-            error(toString(isec) + ": sh_link points to discarded section " +
-                  toString(link));
+            ErrAlways(ctx) << isec << ": sh_link points to discarded section "
+                           << link;
           hasLinkOrder = true;
         }
         scriptSections.push_back(&isec);
@@ -1475,8 +1475,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
     // With Thunk Size much smaller than branch range we expect to
     // converge quickly; if we get to 30 something has gone wrong.
     if (changed && pass >= 30) {
-      error(ctx.target->needsThunks ? "thunk creation not converged"
-                                    : "relaxation not converged");
+      Err(ctx) << (ctx.target->needsThunks ? "thunk creation not converged"
+                                           : "relaxation not converged");
       break;
     }
 
@@ -2104,9 +2104,9 @@ template <class ELFT> void Writer<ELFT>::checkExecuteOnly() {
     if (osec->flags & SHF_EXECINSTR)
       for (InputSection *isec : getInputSections(*osec, storage))
         if (!(isec->flags & SHF_EXECINSTR))
-          error("cannot place " + toString(isec) + " into " +
-                toString(osec->name) +
-                ": --execute-only does not support intermingling data and code");
+          ErrAlways(ctx) << "cannot place " << isec << " into " << osec->name
+                         << ": --execute-only does not support intermingling "
+                            "data and code";
 }
 
 // The linker is expected to define SECNAME_start and SECNAME_end
@@ -2236,8 +2236,8 @@ SmallVector<PhdrEntry *, 0> Writer<ELFT>::createPhdrs(Partition &part) {
       if (!relroEnd)
         relRo->add(sec);
       else
-        error("section: " + sec->name + " is not contiguous with other relro" +
-              " sections");
+        ErrAlways(ctx) << "section: " << sec->name
+                       << " is not contiguous with other relro" << " sections";
     } else if (inRelroPhdr) {
       inRelroPhdr = false;
       relroEnd = sec;
@@ -2562,9 +2562,10 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
     if (sec->type == SHT_NOBITS)
       continue;
     if ((sec->offset > fileSize) || (sec->offset + sec->size > fileSize))
-      error("unable to place section " + sec->name + " at file offset " +
-            rangeToString(sec->offset, sec->size) +
-            "; check your linker script for overflows");
+      ErrAlways(ctx) << "unable to place section " << sec->name
+                     << " at file offset "
+                     << rangeToString(sec->offset, sec->size)
+                     << "; check your linker script for overflows";
   }
 }
 
@@ -2785,7 +2786,7 @@ template <class ELFT> void Writer<ELFT>::openFile() {
       << "section sizes:\n";
     for (OutputSection *os : ctx.outputSections)
       s << os->name << ' ' << os->size << "\n";
-    error(msg);
+    ErrAlways(ctx) << msg;
     return;
   }
 
@@ -2799,8 +2800,8 @@ template <class ELFT> void Writer<ELFT>::openFile() {
       FileOutputBuffer::create(ctx.arg.outputFile, fileSize, flags);
 
   if (!bufferOrErr) {
-    error("failed to open " + ctx.arg.outputFile + ": " +
-          llvm::toString(bufferOrErr.takeError()));
+    ErrAlways(ctx) << "failed to open " << ctx.arg.outputFile << ": "
+                   << llvm::toString(bufferOrErr.takeError());
     return;
   }
   buffer = std::move(*bufferOrErr);
@@ -2940,7 +2941,7 @@ template <class ELFT> void Writer<ELFT>::writeBuildId() {
     break;
   case BuildIdKind::Uuid:
     if (auto ec = llvm::getRandomBytes(buildId.get(), hashSize))
-      error("entropy source failure: " + ec.message());
+      ErrAlways(ctx) << "entropy source failure: " << ec.message();
     break;
   default:
     llvm_unreachable("unknown BuildIdKind");


        


More information about the llvm-commits mailing list