[ELF] OVERLAY: support optional start address and LMA

https://reviews.llvm.org/D44780 implemented rudimentary support for
OVERLAY. The start address and `AT(ldaddr)` in `OVERLAY [start] :
[NOCROSSREFS] [AT ( ldaddr )]` are not optional.

In addition, there are two issues:

* When the start address is `.`, subsequent sections don't share the
  address of the first overlay section.
* When the first overlay section is empty and discardable, `p_paddr` is
  incorrectly zero. This is because a discarded section has a zero
  address, causing `prev->getLMA() + prev->size` where `prev` refers to
  the first section to evaluate to zero.

This patch supports optional start address and LMA and fix the issues.
Close #77265

Pull Request: https://github.com/llvm/llvm-project/pull/77272

GitOrigin-RevId: 7c89b20e02ff079ec84fc54880dbc6c063d8c915
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index 55b10f0..4fdb8c7 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -531,13 +531,17 @@
 // linker's sections sanity check failures.
 // https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description
 SmallVector<SectionCommand *, 0> ScriptParser::readOverlay() {
-  // VA and LMA expressions are optional, though for simplicity of
-  // implementation we assume they are not. That is what OVERLAY was designed
-  // for first of all: to allow sections with overlapping VAs at different LMAs.
-  Expr addrExpr = readExpr();
-  expect(":");
-  expect("AT");
-  Expr lmaExpr = readParenExpr();
+  Expr addrExpr;
+  if (consume(":")) {
+    addrExpr = [] { return script->getDot(); };
+  } else {
+    addrExpr = readExpr();
+    expect(":");
+  }
+  // When AT is omitted, LMA should equal VMA. script->getDot() when evaluating
+  // lmaExpr will ensure this, even if the start address is specified.
+  Expr lmaExpr =
+      consume("AT") ? readParenExpr() : [] { return script->getDot(); };
   expect("{");
 
   SmallVector<SectionCommand *, 0> v;
@@ -547,10 +551,15 @@
     // starting from the base load address specified.
     OutputDesc *osd = readOverlaySectionDescription();
     osd->osec.addrExpr = addrExpr;
-    if (prev)
+    if (prev) {
       osd->osec.lmaExpr = [=] { return prev->getLMA() + prev->size; };
-    else
+    } else {
       osd->osec.lmaExpr = lmaExpr;
+      // Use first section address for subsequent sections as initial addrExpr
+      // can be DOT. Ensure the first section, even if empty, is not discarded.
+      osd->osec.usedInExpression = true;
+      addrExpr = [=]() -> ExprValue { return {&osd->osec, false, 0, ""}; };
+    }
     v.push_back(osd);
     prev = &osd->osec;
   }
diff --git a/test/ELF/linkerscript/overlay.test b/test/ELF/linkerscript/overlay.test
index 942e0a2..b939ee4 100644
--- a/test/ELF/linkerscript/overlay.test
+++ b/test/ELF/linkerscript/overlay.test
@@ -16,9 +16,10 @@
 # CHECK-NEXT: .small1    PROGBITS 0000000000001000 002000 000004
 # CHECK-NEXT: .small2    PROGBITS 0000000000001008 002008 000004
 # CHECK-NEXT: .big2      PROGBITS 0000000000001008 003008 000008
+# CHECK-NEXT: .empty3    PROGBITS 0000000000001010 003010 000000
 # CHECK-NEXT: .small3    PROGBITS 0000000000001010 003010 000004
-# CHECK-NEXT: .big3      PROGBITS 0000000000001014 003014 000008
-# CHECK-NEXT: .text      PROGBITS 0000000000001024 003024 000001
+# CHECK-NEXT: .big3      PROGBITS 0000000000001010 004010 000008
+# CHECK-NEXT: .text      PROGBITS 0000000000001018 004018 000001
 
 # CHECK:      Program Headers:
 # CHECK:      Type Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
@@ -26,10 +27,9 @@
 # CHECK-NEXT: LOAD 0x002000 0x0000000000001000 0x0000000000001008 0x000004 0x000004 R   0x1000
 # CHECK-NEXT: LOAD 0x002008 0x0000000000001008 0x0000000000002008 0x000004 0x000004 R   0x1000
 # CHECK-NEXT: LOAD 0x003008 0x0000000000001008 0x000000000000200c 0x000008 0x000008 R   0x1000
-## FIXME Fix p_paddr when the first section in an overlay is empty and discarded.
-# CHECK-NEXT: LOAD 0x003010 0x0000000000001010 0x0000000000000000 0x000004 0x000004 R   0x1000
-# CHECK-NEXT: LOAD 0x003014 0x0000000000001014 0x0000000000000004 0x000008 0x000008 R   0x1000
-# CHECK-NEXT: LOAD 0x003024 0x0000000000001024 0x0000000000000014 0x000001 0x000001 R E 0x1000
+# CHECK-NEXT: LOAD 0x003010 0x0000000000001010 0x0000000000002014 0x000004 0x000004 R   0x1000
+# CHECK-NEXT: LOAD 0x004010 0x0000000000001010 0x0000000000002018 0x000008 0x000008 R   0x1000
+# CHECK-NEXT: LOAD 0x004018 0x0000000000001018 0x0000000000002020 0x000001 0x000001 R E 0x1000
 
 # RUN: not ld.lld a.o -T err1.t 2>&1 | FileCheck %s --check-prefix=ERR1 --match-full-lines --strict-whitespace
 #      ERR1:{{.*}}error: err1.t:3: { expected, but got 0x3000
@@ -57,14 +57,17 @@
 
 #--- a.t
 SECTIONS {
-  OVERLAY 0x1000 : AT( 0x1000 ) {
+## LMA defaults to VMA
+  OVERLAY 0x1000 : {
     .big1 { *(.big1) }
     .small1 { *(.small1) }
   }
-  OVERLAY 0x1008 : AT (0x2008) {
+## .big2 starts at ADDR(.small2)
+  OVERLAY : AT (0x2008) {
     .small2 { *(.small2) }
     .big2 { *(.big2) }
   }
+## .empty3 is not discarded. .small3 and .big3 share its address.
   OVERLAY . : AT (0x2014) {
     .empty3 { *(.empty3) }
     .small3 { *(.small3) }