debug/elf: support relocations relative to sections with non-zero addresses

commit 72ec930fa70c20ce69b21bf32a7916c04c2e9c2f added basic support for
relocations, but assumed that the symbol value would be 0, likely because
.debug_info always has address == 0 in the ELF section headers.

CL 195679 added further support for relocations, but explicitly encoded
the original assumption that section addresses would be 0.

This change removes that assumption: all relocations will now be
properly computed based on the target symbol value even when that symbol
is a section with a non-zero address.

Typically, sections that are part of a LOAD program segment have
non-zero addresses. For example, .debug_ranges relocations could be
relative to .text, which usually has an address > 0.

Fixes #40879

Change-Id: Ib0a616bb8b05d6c96d179b03ca33a10946fc5d59
GitHub-Last-Rev: 4200de732641995f3a4958a13a5c78f65b7eae50
GitHub-Pull-Request: golang/go#41038
Reviewed-on: https://go-review.googlesource.com/c/go/+/250559
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go
index 1e863ef..cd5bf8f 100644
--- a/src/debug/elf/file.go
+++ b/src/debug/elf/file.go
@@ -628,23 +628,14 @@
 	}
 }
 
-// relocSymbolTargetOK decides whether we should try to apply a
+// canApplyRelocation reports whether we should try to apply a
 // relocation to a DWARF data section, given a pointer to the symbol
-// targeted by the relocation. Most relocations in DWARF data tend to
-// be section-relative, but some target non-section symbols (for
-// example, low_PC attrs on subprogram or compilation unit DIEs that
-// target function symbols), and we need to include these as well.
-// Return value is a pair (X,Y) where X is a boolean indicating
-// whether the relocation is needed, and Y is the symbol value in the
-// case of a non-section relocation that needs to be applied.
-func relocSymbolTargetOK(sym *Symbol) (bool, uint64) {
-	if ST_TYPE(sym.Info) == STT_SECTION {
-		return true, 0
-	}
-	if sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE {
-		return true, sym.Value
-	}
-	return false, 0
+// targeted by the relocation.
+// Most relocations in DWARF data tend to be section-relative, but
+// some target non-section symbols (for example, low_PC attrs on
+// subprogram or compilation unit DIEs that target function symbols).
+func canApplyRelocation(sym *Symbol) bool {
+	return sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE
 }
 
 func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
@@ -670,8 +661,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -684,13 +674,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := val + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_X86_64_32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -796,8 +786,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -810,13 +799,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := uint64(val) + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_AARCH64_ABS32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -847,8 +836,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -857,7 +845,7 @@
 			if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -888,8 +876,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -898,13 +885,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := val + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_PPC64_ADDR32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -980,8 +967,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -990,13 +976,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := val + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_MIPS_32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -1027,8 +1013,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -1037,13 +1022,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := val + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_RISCV_32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -1074,8 +1059,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -1084,13 +1068,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := val + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_390_32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
@@ -1121,8 +1105,7 @@
 			continue
 		}
 		sym := &symbols[symNo-1]
-		needed, val := relocSymbolTargetOK(sym)
-		if !needed {
+		if !canApplyRelocation(sym) {
 			continue
 		}
 
@@ -1131,13 +1114,13 @@
 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val64 := val + uint64(rela.Addend)
+			val64 := sym.Value + uint64(rela.Addend)
 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
 		case R_SPARC_32, R_SPARC_UA32:
 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
 				continue
 			}
-			val32 := uint32(val) + uint32(rela.Addend)
+			val32 := uint32(sym.Value) + uint32(rela.Addend)
 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
 		}
 	}
diff --git a/src/debug/elf/file_test.go b/src/debug/elf/file_test.go
index 4da580d..24948e6 100644
--- a/src/debug/elf/file_test.go
+++ b/src/debug/elf/file_test.go
@@ -293,6 +293,7 @@
 type relocationTestEntry struct {
 	entryNumber int
 	entry       *dwarf.Entry
+	pcRanges    [][2]uint64
 }
 
 type relocationTest struct {
@@ -319,6 +320,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x6}},
 			},
 		},
 	},
@@ -340,6 +342,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x5}},
 			},
 		},
 	},
@@ -361,6 +364,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x6}},
 			},
 		},
 	},
@@ -382,6 +386,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x24}},
 			},
 		},
 	},
@@ -403,6 +408,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x28}},
 			},
 		},
 	},
@@ -421,9 +427,10 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0x0), Class: dwarf.ClassLinePtr},
 						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
-						{Attr: dwarf.AttrHighpc, Val: int64(48), Class: dwarf.ClassConstant},
+						{Attr: dwarf.AttrHighpc, Val: int64(0x30), Class: dwarf.ClassConstant},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x30}},
 			},
 		},
 	},
@@ -445,6 +452,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x44}},
 			},
 		},
 	},
@@ -466,6 +474,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x24}},
 			},
 		},
 	},
@@ -483,10 +492,11 @@
 						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
-						{Attr: dwarf.AttrHighpc, Val: int64(100), Class: dwarf.ClassConstant},
+						{Attr: dwarf.AttrHighpc, Val: int64(0x64), Class: dwarf.ClassConstant},
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x64}},
 			},
 		},
 	},
@@ -504,10 +514,11 @@
 						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
-						{Attr: dwarf.AttrHighpc, Val: int64(58), Class: dwarf.ClassConstant},
+						{Attr: dwarf.AttrHighpc, Val: int64(0x3a), Class: dwarf.ClassConstant},
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x3a}},
 			},
 		},
 	},
@@ -529,6 +540,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x2c}},
 			},
 		},
 	},
@@ -550,6 +562,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x58}},
 			},
 		},
 	},
@@ -571,6 +584,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x5c}},
 			},
 		},
 	},
@@ -588,10 +602,11 @@
 						{Attr: dwarf.AttrName, Val: "hello.c", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
 						{Attr: dwarf.AttrLowpc, Val: uint64(0x0), Class: dwarf.ClassAddress},
-						{Attr: dwarf.AttrHighpc, Val: int64(100), Class: dwarf.ClassConstant},
+						{Attr: dwarf.AttrHighpc, Val: int64(0x64), Class: dwarf.ClassConstant},
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x64}},
 			},
 		},
 	},
@@ -613,6 +628,7 @@
 						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
 					},
 				},
+				pcRanges: [][2]uint64{{0x0, 0x2c}},
 			},
 		},
 	},
@@ -670,6 +686,56 @@
 			},
 		},
 	},
+	{
+		"testdata/go-relocation-test-gcc930-ranges-no-rela-x86-64",
+		[]relocationTestEntry{
+			{
+				entry: &dwarf.Entry{
+					Offset:   0xb,
+					Tag:      dwarf.TagCompileUnit,
+					Children: true,
+					Field: []dwarf.Field{
+						{Attr: dwarf.AttrProducer, Val: "GNU C17 9.3.0 -mtune=generic -march=x86-64 -g -fno-asynchronous-unwind-tables", Class: dwarf.ClassString},
+						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
+						{Attr: dwarf.AttrName, Val: "multiple-code-sections.c", Class: dwarf.ClassString},
+						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
+						{Attr: dwarf.AttrRanges, Val: int64(0), Class: dwarf.ClassRangeListPtr},
+						{Attr: dwarf.AttrLowpc, Val: uint64(0), Class: dwarf.ClassAddress},
+						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
+					},
+				},
+				pcRanges: [][2]uint64{
+					{0x765, 0x777},
+					{0x7e1, 0x7ec},
+				},
+			},
+		},
+	},
+	{
+		"testdata/go-relocation-test-gcc930-ranges-with-rela-x86-64",
+		[]relocationTestEntry{
+			{
+				entry: &dwarf.Entry{
+					Offset:   0xb,
+					Tag:      dwarf.TagCompileUnit,
+					Children: true,
+					Field: []dwarf.Field{
+						{Attr: dwarf.AttrProducer, Val: "GNU C17 9.3.0 -mtune=generic -march=x86-64 -g -fno-asynchronous-unwind-tables", Class: dwarf.ClassString},
+						{Attr: dwarf.AttrLanguage, Val: int64(12), Class: dwarf.ClassConstant},
+						{Attr: dwarf.AttrName, Val: "multiple-code-sections.c", Class: dwarf.ClassString},
+						{Attr: dwarf.AttrCompDir, Val: "/tmp", Class: dwarf.ClassString},
+						{Attr: dwarf.AttrRanges, Val: int64(0), Class: dwarf.ClassRangeListPtr},
+						{Attr: dwarf.AttrLowpc, Val: uint64(0), Class: dwarf.ClassAddress},
+						{Attr: dwarf.AttrStmtList, Val: int64(0), Class: dwarf.ClassLinePtr},
+					},
+				},
+				pcRanges: [][2]uint64{
+					{0x765, 0x777},
+					{0x7e1, 0x7ec},
+				},
+			},
+		},
+	},
 }
 
 func TestDWARFRelocations(t *testing.T) {
@@ -705,6 +771,13 @@
 				if !reflect.DeepEqual(testEntry.entry, entry) {
 					t.Errorf("entry %d mismatch: got:%#v want:%#v", testEntry.entryNumber, entry, testEntry.entry)
 				}
+				pcRanges, err := dwarf.Ranges(entry)
+				if err != nil {
+					t.Fatal(err)
+				}
+				if !reflect.DeepEqual(testEntry.pcRanges, pcRanges) {
+					t.Errorf("entry %d: PC range mismatch: got:%#v want:%#v", testEntry.entryNumber, pcRanges, testEntry.pcRanges)
+				}
 			}
 		})
 	}
diff --git a/src/debug/elf/testdata/go-relocation-test-gcc930-ranges-no-rela-x86-64 b/src/debug/elf/testdata/go-relocation-test-gcc930-ranges-no-rela-x86-64
new file mode 100644
index 0000000..c013f3e
--- /dev/null
+++ b/src/debug/elf/testdata/go-relocation-test-gcc930-ranges-no-rela-x86-64
Binary files differ
diff --git a/src/debug/elf/testdata/go-relocation-test-gcc930-ranges-with-rela-x86-64 b/src/debug/elf/testdata/go-relocation-test-gcc930-ranges-with-rela-x86-64
new file mode 100644
index 0000000..51e03aa
--- /dev/null
+++ b/src/debug/elf/testdata/go-relocation-test-gcc930-ranges-with-rela-x86-64
Binary files differ
diff --git a/src/debug/elf/testdata/multiple-code-sections.c b/src/debug/elf/testdata/multiple-code-sections.c
new file mode 100644
index 0000000..03b9d53
--- /dev/null
+++ b/src/debug/elf/testdata/multiple-code-sections.c
@@ -0,0 +1,28 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Build with:
+// gcc -g multiple-code-sections.c -Wl,--emit-relocs -Wl,--discard-none -Wl,-zmax-page-size=1 -fno-asynchronous-unwind-tables -o go-relocation-test-gcc930-ranges-with-rela-x86-64
+// gcc -g multiple-code-sections.c -Wl,-zmax-page-size=1 -fno-asynchronous-unwind-tables -o go-relocation-test-gcc930-ranges-no-rela-x86-64
+// Strip with:
+// strip --only-keep-debug \
+//       --remove-section=.eh_frame \
+//       --remove-section=.eh_frame_hdr \
+//       --remove-section=.shstrtab \
+//       --remove-section=.strtab \
+//       --remove-section=.symtab \
+//       --remove-section=.note.gnu.build-id \
+//       --remove-section=.note.ABI-tag \
+//       --remove-section=.dynamic \
+//       --remove-section=.gnu.hash \
+//       --remove-section=.interp \
+//       --remove-section=.rodata
+__attribute__((section(".separate_section"))) // To get GCC to emit a DW_AT_ranges attribute for the CU.
+int func(void) {
+    return 0;
+}
+
+int main(int argc, char *argv[]) {
+    return 0;
+}