[ELF] Inspect -EL & -EB for OUTPUT_FORMAT(default, big, little)

Choose big if -EB is specified, little if -EL is specified, or default if neither is specified.
The new behavior matches GNU ld.

Fixes: https://github.com/ClangBuiltLinux/linux/issues/1025

Differential Revision: https://reviews.llvm.org/D96214

GitOrigin-RevId: eea34aae2e74e9b6fbdd5b95f479bc7f397bf387
diff --git a/ELF/Config.h b/ELF/Config.h
index 7881dfe..f80c6fb 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -187,6 +187,8 @@
   bool nostdlib;
   bool oFormatBinary;
   bool omagic;
+  bool optEB = false;
+  bool optEL = false;
   bool optimizeBBJumps;
   bool optRemarksWithHotness;
   bool picThunk;
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 09c2c17..fe722eb 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -1125,6 +1125,13 @@
   config->zWxneeded = hasZOption(args, "wxneeded");
   setUnresolvedSymbolPolicy(args);
 
+  if (opt::Arg *arg = args.getLastArg(OPT_eb, OPT_el)) {
+    if (arg->getOption().matches(OPT_eb))
+      config->optEB = true;
+    else
+      config->optEL = true;
+  }
+
   for (opt::Arg *arg : args.filtered(OPT_z)) {
     std::pair<StringRef, StringRef> option =
         StringRef(arg->getValue()).split('=');
diff --git a/ELF/Options.td b/ELF/Options.td
index 65ef3e8..e1eccdb 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -159,6 +159,9 @@
    "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">,
    MetaVarName<"<file>">;
 
+def eb: F<"EB">, HelpText<"Select the big-endian format in OUTPUT_FORMAT">;
+def el: F<"EL">, HelpText<"Select the little-endian format in OUTPUT_FORMAT">;
+
 defm eh_frame_hdr: B<"eh-frame-hdr",
     "Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
     "Do not create .eh_frame_hdr section">;
@@ -687,8 +690,6 @@
 def: F<"warn-execstack">;
 def: F<"warn-once">;
 def: F<"warn-shared-textrel">;
-def: F<"EB">;
-def: F<"EL">;
 def: JoinedOrSeparate<["-"], "G">;
 def: F<"Qy">;
 
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index 3af6383..4b15a71 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -431,13 +431,26 @@
       .Default({ELFNoneKind, EM_NONE});
 }
 
-// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little).
-// Currently we ignore big and little parameters.
+// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(default, big, little). Choose
+// big if -EB is specified, little if -EL is specified, or default if neither is
+// specified.
 void ScriptParser::readOutputFormat() {
   expect("(");
 
+  StringRef s;
   config->bfdname = unquote(next());
-  StringRef s = config->bfdname;
+  if (!consume(")")) {
+    expect(",");
+    s = unquote(next());
+    if (config->optEB)
+      config->bfdname = s;
+    expect(",");
+    s = unquote(next());
+    if (config->optEL)
+      config->bfdname = s;
+    consume(")");
+  }
+  s = config->bfdname;
   if (s.consume_back("-freebsd"))
     config->osabi = ELFOSABI_FREEBSD;
 
@@ -448,14 +461,6 @@
     config->mipsN32Abi = true;
   if (config->emachine == EM_MSP430)
     config->osabi = ELFOSABI_STANDALONE;
-
-  if (consume(")"))
-    return;
-  expect(",");
-  skip();
-  expect(",");
-  skip();
-  expect(")");
 }
 
 void ScriptParser::readPhdrs() {
diff --git a/docs/ld.lld.1 b/docs/ld.lld.1
index 79a684d..cd9052e 100644
--- a/docs/ld.lld.1
+++ b/docs/ld.lld.1
@@ -161,6 +161,10 @@
 (shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object. Implies
 .Cm -Bsymbolic
 but does not set DF_SYMBOLIC
+.It Fl -EB
+Select the big-endian format in the OUTPUT_FORMAT command.
+.It Fl -EL
+Select the little-endian format in the OUTPUT_FORMAT command.
 .It Fl -eh-frame-hdr
 Request creation of
 .Li .eh_frame_hdr
diff --git a/test/ELF/emulation-aarch64.s b/test/ELF/emulation-aarch64.s
index e5ba733..2d26a1e 100644
--- a/test/ELF/emulation-aarch64.s
+++ b/test/ELF/emulation-aarch64.s
@@ -23,6 +23,20 @@
 # RUN: ld.lld %t.script %t.be.o -o %t3.be
 # RUN: llvm-readobj --file-headers %t3.be | FileCheck --check-prefixes=AARCH64,BE %s
 
+## Test OUTPUT_FORMAT(default, big, little).
+# RUN: echo 'OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64")' > %t.script
+# RUN: ld.lld -EL -T %t.script %t.o -o %t4.le
+# RUN: llvm-readobj --file-headers %t4.le | FileCheck --check-prefixes=AARCH64,LE %s
+# RUN: not ld.lld -EB -T %t.script %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR_BE %s
+
+# RUN: not ld.lld -T %t.script %t.be.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR_LE %s
+# RUN: not ld.lld -EL -T %t.script %t.be.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR_LE %s
+# RUN: ld.lld -EB -T %t.script %t.be.o -o %t4.be
+# RUN: llvm-readobj --file-headers %t4.be | FileCheck --check-prefixes=AARCH64,BE %s
+
+# ERR_LE: error: {{.*}}.o is incompatible with elf64-littleaarch64
+# ERR_BE: error: {{.*}}.o is incompatible with elf64-bigaarch64
+
 # AARCH64:      ElfHeader {
 # AARCH64-NEXT:   Ident {
 # AARCH64-NEXT:     Magic: (7F 45 4C 46)
diff --git a/test/ELF/invalid-linkerscript.test b/test/ELF/invalid-linkerscript.test
index e635ae4..c8770bd 100644
--- a/test/ELF/invalid-linkerscript.test
+++ b/test/ELF/invalid-linkerscript.test
@@ -51,7 +51,7 @@
 # RUN: echo "OUTPUT_FORMAT(x y z)" > %t8
 # RUN: not ld.lld %t8 no-such-file 2>&1 | FileCheck -check-prefix=ERR8 %s
 # RUN: not ld.lld -m elf_amd64 %t8 no-such-file 2>&1 | FileCheck -check-prefix=ERR8 %s
-# ERR8: unknown output format name: x
+# ERR8: , expected, but got y
 # ERR8: cannot open no-such-file:
 
 # RUN: echo "OUTPUT_FORMAT(elf64-x86-64 y z)" > %t9