[ARM][AArch64] Pass through endian flags to assembler and linker.

The big-endian arm32 Linux builds are currently failing when the
-mbig-endian flag is used but the binutils default on the system is little
endian. This also holds when -mlittle-endian is used and the binutils
default is big endian.

The patch always passes through -EL or -BE to the assembler and linker,
taking into account the target and the -mbig-endian and -mlittle-endian
flag.

Fixes pr38770

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

llvm-svn: 344597
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index b8b69e7..f850f8e 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -228,6 +228,29 @@
   // The types are (hopefully) good enough.
 }
 
+// On Arm the endianness of the output file is determined by the target and
+// can be overridden by the pseudo-target flags '-mlittle-endian'/'-EL' and
+// '-mbig-endian'/'-EB'. Unlike other targets the flag does not result in a
+// normalized triple so we must handle the flag here.
+static bool isArmBigEndian(const llvm::Triple &Triple,
+                           const ArgList &Args) {
+  bool IsBigEndian = false;
+  switch (Triple.getArch()) {
+  case llvm::Triple::armeb:
+  case llvm::Triple::thumbeb:
+    IsBigEndian = true;
+  case llvm::Triple::arm:
+  case llvm::Triple::thumb:
+    if (Arg *A = Args.getLastArg(options::OPT_mlittle_endian,
+                               options::OPT_mbig_endian))
+      IsBigEndian = !A->getOption().matches(options::OPT_mlittle_endian);
+    break;
+  default:
+    break;
+  }
+  return IsBigEndian;
+}
+
 static const char *getLDMOption(const llvm::Triple &T, const ArgList &Args) {
   switch (T.getArch()) {
   case llvm::Triple::x86:
@@ -240,10 +263,9 @@
     return "aarch64linuxb";
   case llvm::Triple::arm:
   case llvm::Triple::thumb:
-    return "armelf_linux_eabi";
   case llvm::Triple::armeb:
   case llvm::Triple::thumbeb:
-    return "armelfb_linux_eabi";
+    return isArmBigEndian(T, Args) ? "armelfb_linux_eabi" : "armelf_linux_eabi";
   case llvm::Triple::ppc:
     return "elf32ppclinux";
   case llvm::Triple::ppc64:
@@ -337,8 +359,13 @@
   if (Args.hasArg(options::OPT_s))
     CmdArgs.push_back("-s");
 
-  if (Arch == llvm::Triple::armeb || Arch == llvm::Triple::thumbeb)
-    arm::appendEBLinkFlags(Args, CmdArgs, Triple);
+  if (Triple.isARM() || Triple.isThumb() || Triple.isAArch64()) {
+    bool IsBigEndian = isArmBigEndian(Triple, Args);
+    if (IsBigEndian)
+      arm::appendBE8LinkFlag(Args, CmdArgs, Triple);
+    IsBigEndian = IsBigEndian || Arch == llvm::Triple::aarch64_be;
+    CmdArgs.push_back(IsBigEndian ? "-EB" : "-EL");
+  }
 
   // Most Android ARM64 targets should enable the linker fix for erratum
   // 843419. Only non-Cortex-A53 devices are allowed to skip this flag.
@@ -640,6 +667,7 @@
   case llvm::Triple::thumb:
   case llvm::Triple::thumbeb: {
     const llvm::Triple &Triple2 = getToolChain().getTriple();
+    CmdArgs.push_back(isArmBigEndian(Triple2, Args) ? "-EB" : "-EL");
     switch (Triple2.getSubArch()) {
     case llvm::Triple::ARMSubArch_v7:
       CmdArgs.push_back("-mfpu=neon");
@@ -672,6 +700,8 @@
   }
   case llvm::Triple::aarch64:
   case llvm::Triple::aarch64_be: {
+    CmdArgs.push_back(
+        getToolChain().getArch() == llvm::Triple::aarch64_be ? "-EB" : "-EL");
     Args.AddLastArg(CmdArgs, options::OPT_march_EQ);
     normalizeCPUNamesForAssembler(Args, CmdArgs);