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},