internal/gocore: support PIE ELF binaries

Go binaries built with `-buildmode=pie` are loaded at random addresses
(ASLR) in memory by Linux.

Before this change, the mappings read from the `NT_NOTE` section of the
ELF binary matches the exe mappings:

    # From NT_NOTE:
    0x400000-0x491000             ----   593920 /tmp/TestVersionsgoroot#003207751013/001/test.exe+0x0
    0x491000-0x54b000             ----   761856 /tmp/TestVersionsgoroot#003207751013/001/test.exe+0x91000
    0x54b000-0x556000             ----    45056 /tmp/TestVersionsgoroot#003207751013/001/test.exe+0x14b000

    # From executable:
    0x400000-0x491000             r-xp   593920 /tmp/TestVersionsgoroot#003207751013/001/test.exe+0x0
    0x491000-0x54b000             r--p   761856 /tmp/TestVersionsgoroot#003207751013/001/test.exe+0x91000
    0x54b000-0x555000             rw-p    40960 /tmp/TestVersionsgoroot#003207751013/001/test.exe+0x14b000
    0x555000-0x579000             rw-p   147456 anon+0x-440

    # From core
    0xc000000000-0xc000400000     rw-p  4194304 /tmp/TestVersionsgoroot#003207751013/001/core+0x32000
    0x7f8b17c00000-0x7f8b19c00000 rw-p 33554432 /tmp/TestVersionsgoroot#003207751013/001/core+0x432000
    0x7f8b29d80000-0x7f8b29d81000 rw-p     4096 /tmp/TestVersionsgoroot#003207751013/001/core+0x2432000
    0x7f8b49d80000-0x7f8b49d81000 rw-p     4096 /tmp/TestVersionsgoroot#003207751013/001/core+0x2433000
    0x7f8b5bc30000-0x7f8b5bc31000 rw-p     4096 /tmp/TestVersionsgoroot#003207751013/001/core+0x2434000
    0x7f8b5e006000-0x7f8b5e007000 rw-p     4096 /tmp/TestVersionsgoroot#003207751013/001/core+0x2435000
    0x7f8b5e466000-0x7f8b5e4c6000 rw-p   393216 /tmp/TestVersionsgoroot#003207751013/001/core+0x2436000
    0x7f8b5e4c6000-0x7f8b5e5c6000 rw-p  1048576 /tmp/TestVersionsgoroot#003207751013/001/core+0x2496000
    0x7f8b5e5c6000-0x7f8b5e5d7000 rw-p    69632 /tmp/TestVersionsgoroot#003207751013/001/core+0x2596000
    0x7f8b5e657000-0x7f8b5e658000 rw-p     4096 /tmp/TestVersionsgoroot#003207751013/001/core+0x25a7000
    0x7f8b5e6d7000-0x7f8b5e737000 rw-p   393216 /tmp/TestVersionsgoroot#003207751013/001/core+0x25a8000
    0x7f8b5e737000-0x7f8b5e73b000 r--p    16384 /tmp/TestVersionsgoroot#003207751013/001/core+0x2608000
    0x7f8b5e73b000-0x7f8b5e73d000 r-xp     8192 /tmp/TestVersionsgoroot#003207751013/001/core+0x260c000
    0x7ffd88d3c000-0x7ffd88d5e000 rw-p   139264 /tmp/TestVersionsgoroot#003207751013/001/core+0x260e000

And things worked fine. But, with PIE binaries, we see the following raw
addresses when loading (e.g.):

    # From NT_NOTE:
    0x563f53d35000-0x563f53dc7000 ----   598016 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0x0
    0x563f53dc7000-0x563f53e10000 ----   299008 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0x92000
    0x563f53e10000-0x563f53ea3000 ----   602112 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0xdb000
    0x563f53ea3000-0x563f53eaf000 ----    49152 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0x16e000
    0x7ff5d0deb000-0x7ff5d0dec000 ----     4096 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2+0x0
    0x7ff5d0dec000-0x7ff5d0e11000 ----   151552 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2+0x1000
    0x7ff5d0e11000-0x7ff5d0e1b000 ----    40960 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2+0x26000
    0x7ff5d0e1b000-0x7ff5d0e1f000 ----    16384 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2+0x30000

    # From executable:
    0x400000-0x492000             r-xp   598016 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0x0
    0x492000-0x4db000             r--p   299008 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0x92000
    0x4db000-0x56f000             rw-p   606208 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0xdb000
    0x56f000-0x579000             rw-p    40960 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/test.exe+0x16f000
    0x579000-0x59d000             rw-p   147456 anon+0x-460

    # From core
    0xc000000000-0xc000400000     rw-p  4194304 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x3000
    0x563f53d35000-0x563f53d36000 r-xp     4096 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x403000
    0x563f53d36000-0x563f53dc7000 r-xp   593920 anon+0x0
    0x563f53dc7000-0x563f53e10000 r--p   299008 anon+0x0
    0x563f53e10000-0x563f53ea3000 r--p   602112 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x404000
    0x563f53ea3000-0x563f53eaf000 rw-p    49152 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x497000
    0x563f53eaf000-0x563f53ed2000 rw-p   143360 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x4a3000
    ...
    0x7ff5d0deb000-0x7ff5d0dec000 r--p     4096 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x26a4000
    0x7ff5d0dec000-0x7ff5d0e11000 r-xp   151552 anon+0x0
    0x7ff5d0e11000-0x7ff5d0e1b000 r--p    40960 anon+0x0
    0x7ff5d0e1b000-0x7ff5d0e1f000 rw-p    16384 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x26a5000
    0x7fff34a26000-0x7fff34a48000 rw-p   139264 /tmp/TestVersionsgoroot-buildmode=pie3327190927/001/core+0x26a9000

This caused two issues:

 1. The mappings from the executable and the core were not unified
    properly done in `addProgMappings -> splicedMemory.Add` due to the
    address ranges being different. Both existeded in the
    `splicedMemory` set. Normally, the core mappings would override the
    executable mappings when they overlap. To correct for this, add the
    load offset (`staticBase`) to the executable addresses.
 2. DWARF data (contained in the executable) references the base
    addresses (for example, `allgs` is at 0x4003c4). Due to #1, a lookup
    *would not crash*, but it would read the location from the
    executable. In a PIE binary, such a lookup into the `.data` section
    would be a relocation, and be present in the binary as all zeroes
    (0x0). If #1 were fixed to offset by `staticBase` without fixing #2
    would result in a read from unmapped memory.

Inspiration was taken from the Delve commit that added PIE support for
multiple platforms/architectures in
https://github.com/go-delve/delve/commit/025d47c6e96e8ab5a2d2c142ae554e760cddabf8.

Also add tests that this works. I verified that if the changes from
non-test files are omitted, the test fails.

Change-Id: Ifa08f71e7ed22320b78bc6015554e997b8ae521d
Reviewed-on: https://go-review.googlesource.com/c/debug/+/618977
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Nicolas Hillegeer <aktau@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
3 files changed
tree: ade34da338c958f75b10e955fb23e78e686d65cd
  1. cmd/
  2. dwtest/
  3. internal/
  4. codereview.cfg
  5. CONTRIBUTING.md
  6. go.mod
  7. go.sum
  8. LICENSE
  9. README.md
README.md

Go Debug

Go Reference

This repository holds utilities and libraries for debugging Go programs.

WARNING! Please expect breaking changes and unstable APIs. Most of them are currently are at an early, experimental stage.

Report Issues / Send Patches

This repository uses Gerrit for code changes. To learn how to submit changes to this repository, see https://golang.org/doc/contribute.html.

The main issue tracker for the debug repository is located at https://github.com/golang/go/issues. Prefix your issue with “x/debug:” in the subject line, so it is easy to find.