[ELF] Set Dot initially to --image-base value when using linker scripts

When parsing linker scripts, LLD previously started with a '.' value of 0,
regardless of the internal default image base for the target, and regardless of
switches such as --image-base. It seems reasonable to use a different image base
value when using linker scripts and --image-base is specified, since otherwise the
switch has no effect. This change does this, as well as removing unnecessary
initialisation of Dot where it is not used.

The default image base should not be used when processing linker
scripts, because this will change the behaviour for existing linker script users,
and potentially result in invalid output being produced, as a subsequent assignment
to Dot could move the location counter backwards. Instead, we maintain the existing
behaviour of starting from 0 if --image-base is not specified.

Reviewers: ruiu

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

llvm-svn: 315293
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index a8d0dd4..83c25fa 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -166,7 +166,7 @@
   uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
   uint16_t EMachine = llvm::ELF::EM_NONE;
   uint64_t ErrorLimit = 20;
-  uint64_t ImageBase;
+  llvm::Optional<uint64_t> ImageBase;
   uint64_t MaxPageSize;
   uint64_t ZStackSize;
   unsigned LTOPartitions;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index c259a17..61bf93d 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -908,13 +908,12 @@
 }
 
 // Parses -image-base option.
-static uint64_t getImageBase(opt::InputArgList &Args) {
-  // Use default if no -image-base option is given.
-  // Because we are using "Target" here, this function
-  // has to be called after the variable is initialized.
+static Optional<uint64_t> getImageBase(opt::InputArgList &Args) {
+  // Because we are using "Config->MaxPageSize" here, this function has to be
+  // called after the variable is initialized.
   auto *Arg = Args.getLastArg(OPT_image_base);
   if (!Arg)
-    return Config->Pic ? 0 : Target->DefaultImageBase;
+    return None;
 
   StringRef S = Arg->getValue();
   uint64_t V;
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 591e6ff..c7e103a 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -365,7 +365,6 @@
   // script parser.
   CurAddressState = State.get();
   CurAddressState->OutSec = Aether;
-  Dot = 0;
 
   for (size_t I = 0; I < Opt.Commands.size(); ++I) {
     // Handle symbol assignments outside of any output section.
@@ -438,7 +437,7 @@
     StartAddr = std::min(StartAddr, KV.second);
 
   auto Expr = [=] {
-    return std::min(StartAddr, Config->ImageBase + elf::getHeaderSize());
+    return std::min(StartAddr, Target->getImageBase() + elf::getHeaderSize());
   };
   Opt.Commands.insert(Opt.Commands.begin(),
                       make<SymbolAssignment>(".", Expr, ""));
@@ -780,9 +779,11 @@
   }
 }
 
+// Assign addresses as instructed by linker script SECTIONS sub-commands.
 void LinkerScript::assignAddresses() {
-  // Assign addresses as instructed by linker script SECTIONS sub-commands.
-  Dot = 0;
+  // By default linker scripts use an initial value of 0 for '.', but prefer
+  // -image-base if set.
+  Dot = Config->ImageBase ? *Config->ImageBase : 0;
   auto State = make_unique<AddressState>(Opt);
   // CurAddressState captures the local AddressState and makes it accessible
   // deliberately. This is needed as there are some cases where we cannot just
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index ff81852..bc76c96 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1166,7 +1166,7 @@
   if (Config->EMachine == EM_MIPS) {
     add({DT_MIPS_RLD_VERSION, 1});
     add({DT_MIPS_FLAGS, RHF_NOTPOT});
-    add({DT_MIPS_BASE_ADDRESS, Config->ImageBase});
+    add({DT_MIPS_BASE_ADDRESS, Target->getImageBase()});
     add({DT_MIPS_SYMTABNO, InX::DynSymTab->getNumSymbols()});
     add({DT_MIPS_LOCAL_GOTNO, InX::MipsGot->getLocalEntriesNum()});
     if (const SymbolBody *B = InX::MipsGot->getFirstGlobalEntry())
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 11986ef..5434e14 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -165,3 +165,10 @@
                                 uint64_t Val) const {
   llvm_unreachable("Should not have claimed to be relaxable");
 }
+
+uint64_t TargetInfo::getImageBase() {
+  // Use -image-base if set. Fall back to the target default if not.
+  if (Config->ImageBase)
+    return *Config->ImageBase;
+  return Config->Pic ? 0 : DefaultImageBase;
+}
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 7720e1b..45773f4 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -64,11 +64,7 @@
   unsigned PageSize = 4096;
   unsigned DefaultMaxPageSize = 4096;
 
-  // On FreeBSD x86_64 the first page cannot be mmaped.
-  // On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
-  // installs that is 65536, so the first 15 pages cannot be used.
-  // Given that, the smallest value that can be used in here is 0x10000.
-  uint64_t DefaultImageBase = 0x10000;
+  uint64_t getImageBase();
 
   // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
   // end of .got
@@ -108,6 +104,13 @@
   virtual void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
   virtual void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
   virtual void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
+
+protected:
+  // On FreeBSD x86_64 the first page cannot be mmaped.
+  // On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
+  // installs that is 65536, so the first 15 pages cannot be used.
+  // Given that, the smallest value that can be used in here is 0x10000.
+  uint64_t DefaultImageBase = 0x10000;
 };
 
 TargetInfo *getAArch64TargetInfo();
diff --git a/lld/test/ELF/linkerscript/image-base.s b/lld/test/ELF/linkerscript/image-base.s
new file mode 100644
index 0000000..34ed4ef
--- /dev/null
+++ b/lld/test/ELF/linkerscript/image-base.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86

+

+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o

+# RUN: echo "SECTIONS { mysym = .; }" > %t.script

+

+# RUN: ld.lld %t.o -o %t-default.elf -T %t.script

+# RUN: llvm-readobj --symbols %t-default.elf | FileCheck %s --check-prefix=DEFAULT

+# DEFAULT: Name: mysym

+# DEFAULT-NEXT: Value: 0x0

+

+# RUN: ld.lld %t.o -o %t-switch.elf -T %t.script --image-base=0x100000

+# RUN: llvm-readobj --symbols %t-switch.elf | FileCheck %s --check-prefix=SWITCH

+# SWITCH: Name: mysym

+# SWITCH-NEXT: Value: 0x100000

+

+.global _start

+_start:

+    nop