[ELF] - Implemented --sort-section cmd line option and SORT_NONE script command.

This fixes Bug 30385 - SORT_NONE not implemented,

`SORT_NONE' disables section sorting by ignoring the command line
section sorting option.

That is why this patch also implements --sort-section option.

Description of sorting rules
available at https://sourceware.org/binutils/docs/ld/Input-Section-Wildcards.html 

Differential revision: https://reviews.llvm.org/D24604

llvm-svn: 281771
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index dfff5e7..dd5b5a8 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -42,6 +42,9 @@
 // For --unresolved-symbols.
 enum class UnresolvedPolicy { NoUndef, ReportError, Warn, Ignore };
 
+// For --sort-section and linkerscript sorting rules.
+enum class SortSectionPolicy { None, IgnoreConfig, Alignment, Name, Priority };
+
 struct SymbolVersion {
   llvm::StringRef Name;
   bool IsExternCpp;
@@ -124,6 +127,7 @@
   bool ZOrigin;
   bool ZRelro;
   DiscardPolicy Discard;
+  SortSectionPolicy SortSection;
   StripPolicy Strip = StripPolicy::None;
   UnresolvedPolicy UnresolvedSymbols;
   BuildIdKind BuildId = BuildIdKind::None;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 9f1d91a..90b473b 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -407,6 +407,17 @@
   return Ret;
 }
 
+static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
+  StringRef S = getString(Args, OPT_sort_section);
+  if (S == "alignment")
+    return SortSectionPolicy::Alignment;
+  if (S == "name")
+    return SortSectionPolicy::Name;
+  if (!S.empty())
+    error("unknown --sort-section rule: " + S);
+  return SortSectionPolicy::None;
+}
+
 // Initializes Config members by the command line options.
 void LinkerDriver::readConfigs(opt::InputArgList &Args) {
   for (auto *Arg : Args.filtered(OPT_L))
@@ -530,6 +541,8 @@
   for (auto *Arg : Args.filtered(OPT_undefined))
     Config->Undefined.push_back(Arg->getValue());
 
+  Config->SortSection = getSortKind(Args);
+
   Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
 
   if (auto *Arg = Args.getLastArg(OPT_dynamic_list))
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index d676d38..8881154 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -131,12 +131,17 @@
 }
 
 static std::function<bool(InputSectionData *, InputSectionData *)>
-getComparator(SortKind K) {
-  if (K == SortByPriority)
-    return comparePriority;
-  if (K == SortByName)
+getComparator(SortSectionPolicy K) {
+  switch (K) {
+  case SortSectionPolicy::Alignment:
+    return compareAlignment;
+  case SortSectionPolicy::Name:
     return compareName;
-  return compareAlignment;
+  case SortSectionPolicy::Priority:
+    return comparePriority;
+  default:
+    llvm_unreachable("unknown sort policy");
+  }
 }
 
 static bool checkConstraint(uint64_t Flags, ConstraintKind Kind) {
@@ -180,10 +185,10 @@
     return;
   }
 
-  if (I->SortInner)
+  if (I->SortInner != SortSectionPolicy::None)
     std::stable_sort(I->Sections.begin(), I->Sections.end(),
                      getComparator(I->SortInner));
-  if (I->SortOuter)
+  if (I->SortOuter != SortSectionPolicy::None)
     std::stable_sort(I->Sections.begin(), I->Sections.end(),
                      getComparator(I->SortOuter));
 
@@ -704,7 +709,7 @@
   void readSectionExcludes(InputSectionDescription *Cmd);
   InputSectionDescription *readInputSectionRules(StringRef FilePattern);
   unsigned readPhdrType();
-  SortKind readSortKind();
+  SortSectionPolicy readSortKind();
   SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
   SymbolAssignment *readProvideOrAssignment(StringRef Tok, bool MakeAbsolute);
   void readSort();
@@ -985,14 +990,39 @@
   return compileGlobPatterns(V);
 }
 
-SortKind ScriptParser::readSortKind() {
+SortSectionPolicy ScriptParser::readSortKind() {
   if (skip("SORT") || skip("SORT_BY_NAME"))
-    return SortByName;
+    return SortSectionPolicy::Name;
   if (skip("SORT_BY_ALIGNMENT"))
-    return SortByAlignment;
+    return SortSectionPolicy::Alignment;
   if (skip("SORT_BY_INIT_PRIORITY"))
-    return SortByPriority;
-  return SortNone;
+    return SortSectionPolicy::Priority;
+  // `SORT_NONE' disables section sorting by ignoring the command line
+  // section sorting option.
+  if (skip("SORT_NONE"))
+    return SortSectionPolicy::IgnoreConfig;
+  return SortSectionPolicy::None;
+}
+
+static void selectSortKind(InputSectionDescription *Cmd) {
+  if (Cmd->SortOuter == SortSectionPolicy::IgnoreConfig) {
+    Cmd->SortOuter = SortSectionPolicy::None;
+    return;
+  }
+
+  if (Cmd->SortOuter != SortSectionPolicy::None) {
+    // If the section sorting command in linker script is nested, the command
+    // line option will be ignored.
+    if (Cmd->SortInner != SortSectionPolicy::None)
+      return;
+    // If the section sorting command in linker script isn't nested, the
+    // command line option will make the section sorting command to be treated
+    // as nested sorting command.
+    Cmd->SortInner = Config->SortSection;
+    return;
+  }
+  // If sorting rule not specified, use command line option.
+  Cmd->SortOuter = Config->SortSection;
 }
 
 // Method reads a list of sequence of excluded files and section globs given in
@@ -1031,10 +1061,12 @@
   expect("(");
 
   // Read SORT().
-  if (SortKind K1 = readSortKind()) {
+  SortSectionPolicy K1 = readSortKind();
+  if (K1 != SortSectionPolicy::None) {
     Cmd->SortOuter = K1;
     expect("(");
-    if (SortKind K2 = readSortKind()) {
+    SortSectionPolicy K2 = readSortKind();
+    if (K2 != SortSectionPolicy::None) {
       Cmd->SortInner = K2;
       expect("(");
       Cmd->SectionsVec.push_back({llvm::Regex(), readFilePatterns()});
@@ -1043,9 +1075,11 @@
       Cmd->SectionsVec.push_back({llvm::Regex(), readFilePatterns()});
     }
     expect(")");
+    selectSortKind(Cmd);
     return Cmd;
   }
 
+  selectSortKind(Cmd);
   readSectionExcludes(Cmd);
   return Cmd;
 }
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 159cac3..07abe76 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -10,6 +10,7 @@
 #ifndef LLD_ELF_LINKER_SCRIPT_H
 #define LLD_ELF_LINKER_SCRIPT_H
 
+#include "Config.h"
 #include "Strings.h"
 #include "Writer.h"
 #include "lld/Core/LLVM.h"
@@ -97,16 +98,14 @@
   ConstraintKind Constraint = ConstraintKind::NoConstraint;
 };
 
-enum SortKind { SortNone, SortByPriority, SortByName, SortByAlignment };
-
 struct InputSectionDescription : BaseCommand {
   InputSectionDescription(StringRef FilePattern)
       : BaseCommand(InputSectionKind),
         FileRe(compileGlobPatterns({FilePattern})) {}
   static bool classof(const BaseCommand *C);
   llvm::Regex FileRe;
-  SortKind SortOuter = SortNone;
-  SortKind SortInner = SortNone;
+  SortSectionPolicy SortOuter = SortSectionPolicy::None;
+  SortSectionPolicy SortInner = SortSectionPolicy::None;
   // Pairs of section regex and files excluded.
   std::list<std::pair<llvm::Regex, llvm::Regex>> SectionsVec;
   std::vector<InputSectionData *> Sections;
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 274d2a2..6acd7e3 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -160,6 +160,8 @@
 
 def soname: J<"soname=">, HelpText<"Set DT_SONAME">;
 
+def sort_section: S<"sort-section">, HelpText<"Specifies sections sorting rule when linkerscript is used">;
+
 def start_lib: F<"start-lib">,
   HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
 
diff --git a/lld/test/ELF/linkerscript/sort-nested.s b/lld/test/ELF/linkerscript/sort-nested.s
index 21ddddf..e155a91 100644
--- a/lld/test/ELF/linkerscript/sort-nested.s
+++ b/lld/test/ELF/linkerscript/sort-nested.s
@@ -23,6 +23,16 @@
 # SORTED_NA:   02000000 00000000 00000000 00000000
 # SORTED_NA:   55000000 00000000
 
+## If the section sorting command in linker script isn't nested, the
+## command line option will make the section sorting command to be treated
+## as nested sorting command.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_ALIGNMENT(.aaa.*)) } }" > %t3.script
+# RUN: ld.lld --sort-section name -o %t3 --script %t3.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t3 | FileCheck -check-prefix=SORTED_AN %s
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_NAME(.aaa.*)) } }" > %t4.script
+# RUN: ld.lld --sort-section alignment -o %t4 --script %t4.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t4 | FileCheck -check-prefix=SORTED_NA %s
+
 .global _start
 _start:
  nop
diff --git a/lld/test/ELF/linkerscript/sort.s b/lld/test/ELF/linkerscript/sort.s
index 31df7a3..3886d84 100644
--- a/lld/test/ELF/linkerscript/sort.s
+++ b/lld/test/ELF/linkerscript/sort.s
@@ -66,6 +66,27 @@
 # SORTED_ALIGNMENT-NEXT:  0180 44000000 00000000 01000000 00000000
 # SORTED_ALIGNMENT-NEXT:  0190 55000000 00000000
 
+## SORT_NONE itself does not sort anything.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_NONE(.aaa.*)) } }" > %t6.script
+# RUN: ld.lld -o %t7 --script %t6.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t7 | FileCheck -check-prefix=UNSORTED %s
+
+## Check --sort-section alignment option.
+# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t7.script
+# RUN: ld.lld --sort-section alignment -o %t8 --script %t7.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t8 | FileCheck -check-prefix=SORTED_ALIGNMENT %s
+
+## Check --sort-section name option.
+# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t8.script
+# RUN: ld.lld --sort-section name -o %t9 --script %t8.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t9 | FileCheck -check-prefix=SORTED_B %s
+
+## SORT_NONE disables the --sort-section.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_NONE(.aaa.*)) } }" > %t9.script
+# RUN: ld.lld --sort-section name -o %t10 --script %t9.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t10 | FileCheck -check-prefix=UNSORTED %s
+
+
 .global _start
 _start:
  nop