Extend GetBase heuristics for PIE kernels. (#660)

We are seeing kernels on Aarch64 with ET_DYN Elf headers.
Extend the current ET_EXEC heuristics to handle ET_DYN as well.

Add tests for these exemplars.

Empirical example PIE:
Header: ET_DYN ProgHeader: &{Off: 0x10000 Vaddr: 0xffff000010080000 Align: 0x10000}
103424: ffff000010080000 0 NOTYPE GLOBAL DEFAULT 1 _text
PERF_RECORD_MMAP -1/0: [0xffff000010080000(0xffffeff7ffff) @ 0xffff000010080000]: x [kernel.kallsyms]_text

Empirical example ASLR:
Header: ET_DYN ProgHeader: &{Off: 0x10800 Vaddr: ffffffc010080800 Align: 0x10000}
98865: ffffffc010080800 0 NOTYPE GLOBAL DEFAULT 2 _stext
PERF_RECORD_MMAP -1/0: [0xffffffdb5d680800(0xb7f800) @ 0xffffffdb5d680800]: x [kernel.kallsyms]_stext

Empirical example remapped ChromeOS:
Header: ET_DYN ProgHeader: &{Off: 0x10800 Vaddr: ffffff8008080800 Align: 0x10000}
149888: ffffff8008080800 0 NOTYPE GLOBAL DEFAULT 2 _stext
mapping start: 0x800 lenght: 0xb7f800 offset: 0
diff --git a/internal/elfexec/elfexec.go b/internal/elfexec/elfexec.go
index 8779ff9..6447092 100644
--- a/internal/elfexec/elfexec.go
+++ b/internal/elfexec/elfexec.go
@@ -269,6 +269,11 @@
 		if loadSegment == nil {
 			return start - offset, nil
 		}
+		// Kernels compiled as PIE can be ET_DYN as well. Use heuristic, similar to
+		// the ET_EXEC case above.
+		if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
+			return base, nil
+		}
 		// The program header, if not nil, indicates the offset in the file where
 		// the executable segment is located (loadSegment.Off), and the base virtual
 		// address where the first byte of the segment is loaded
diff --git a/internal/elfexec/elfexec_test.go b/internal/elfexec/elfexec_test.go
index fe6fc9c..242c5f0 100644
--- a/internal/elfexec/elfexec_test.go
+++ b/internal/elfexec/elfexec_test.go
@@ -44,6 +44,16 @@
 		Vaddr: 0xffffffff80200000,
 		Off:   0x1000,
 	}
+	// Kernel PIE header with vaddr aligned to a 4k boundary
+	kernelPieAlignedHeader := &elf.ProgHeader{
+		Vaddr: 0xffff000010080000,
+		Off:   0x10000,
+	}
+	// Kernel PIE header with vaddr that doesn't fall on a 4k boundary
+	kernelPieUnalignedHeader := &elf.ProgHeader{
+		Vaddr: 0xffffffc010080800,
+		Off:   0x10800,
+	}
 	ppc64KernelHeader := &elf.ProgHeader{
 		Vaddr: 0xc000000000000000,
 	}
@@ -77,6 +87,9 @@
 		{"dyn map", fhDyn, lsOffset, nil, 0x0, 0x300000, 0, 0xFFFFFFFFFFE00000, false},
 		{"dyn nomap", fhDyn, nil, nil, 0x0, 0x0, 0, 0, false},
 		{"dyn map+offset", fhDyn, lsOffset, nil, 0x900000, 0xa00000, 0x200000, 0x500000, false},
+		{"dyn kernel", fhDyn, kernelPieAlignedHeader, uint64p(0xffff000010080000), 0xffff000010080000, 0xffffffffffffffff, 0xffff000010080000, 0, false},
+		{"dyn chromeos aslr kernel", fhDyn, kernelPieUnalignedHeader, uint64p(0xffffffc010080800), 0x800, 0xb7f800, 0, 0x3feff80000, false},
+		{"dyn chromeos aslr kernel unremapped", fhDyn, kernelPieUnalignedHeader, uint64p(0xffffffc010080800), 0xffffffdb5d680800, 0xffffffdb5e200000, 0xffffffdb5d680800, 0x1b4d600000, false},
 		{"rel", fhRel, nil, nil, 0x2000000, 0x3000000, 0, 0x2000000, false},
 		{"rel nomap", fhRel, nil, nil, 0x0, ^uint64(0), 0, 0, false},
 		{"rel offset", fhRel, nil, nil, 0x100000, 0x200000, 0x1, 0, true},