Repository: go-delve/delve
Pull request #2337 (2021-02-09 15:54:41)

fix spelling error
A single word in docs/cli/README

derekparker: Here is where the typo should be fixed: https://github.com/go-delve/delve/blob/master/pkg/terminal/command.go#L137

Patch:
diff --git a/.deepsource.toml b/.deepsource.toml
index 6889372d..5f2b68ef 100644
--- a/.deepsource.toml
+++ b/.deepsource.toml
@@ -2,7 +2,7 @@ version = 1
 
 test_patterns = ["**/*_test.go"]
 
-exclude_patterns = ["vendor", "_fixtures", "_scripts"]
+exclude_patterns = ["vendor/**", "_fixtures/**", "_scripts/**"]
 
 [[analyzers]]
 name = "go"
diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts
index ec8b3cb5..6672672a 100644
--- a/.teamcity/settings.kts
+++ b/.teamcity/settings.kts
@@ -4,6 +4,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.commitStatusPu
 import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.golang
 import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.pullRequests
 import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.dockerCommand
+import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.exec
 import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.powerShell
 import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
 import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
@@ -34,17 +35,24 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
 version = "2020.2"
 
 val targets = arrayOf(
-	"linux/amd64/1.13",
 	"linux/amd64/1.14",
         "linux/amd64/1.15",
         "linux/amd64/1.16",
         "linux/amd64/tip",
 
-        "linux/386/1.15",
+        "linux/386/1.16",
+
+        "linux/arm64/1.16",
+        "linux/arm64/tip",
 
-        "windows/amd64/1.15",
         "windows/amd64/1.16",
-        "windows/amd64/tip"
+        "windows/amd64/tip",
+
+        "mac/amd64/1.16",
+        "mac/amd64/tip",
+
+        "mac/arm64/1.16",
+        "mac/arm64/tip"
 )
 
 project {
@@ -87,7 +95,7 @@ class AggregatorBuild(tests: Collection<BuildType>) : BuildType({
             vcsRootExtId = "${DslContext.settingsRoot.id}"
             provider = github {
                 authType = token {
-                    token = "credentialsJSON:5dc93081-e0b2-41e2-b8f0-dea3c96e6426"
+                    token = "credentialsJSON:a7e7526c-7195-4790-bbb6-9fb4692f92d0"
                 }
                 filterAuthorRole = PullRequests.GitHubRoleFilter.EVERYBODY
             }
@@ -97,12 +105,16 @@ class AggregatorBuild(tests: Collection<BuildType>) : BuildType({
             publisher = github {
                 githubUrl = "https://api.github.com"
                 authType = personalToken {
-                    token = "credentialsJSON:48af6e38-536d-4acb-ae2d-2fba57b6f3db"
+                    token = "credentialsJSON:a7e7526c-7195-4790-bbb6-9fb4692f92d0"
                 }
             }
             param("github_oauth_user", "")
         }
     }
+
+    failureConditions {
+        executionTimeoutMin = 60
+    }
 })
 
 class OSProject(os: String, tests: List<TestBuild>) : Project({
@@ -151,7 +163,13 @@ class TestBuild(val os: String, val arch: String, version: String, buildId: Abso
     steps {
         when (os) {
             "linux" -> {
-                val dockerArch = if (arch == "386") "i$arch" else arch
+                val dockerArch = when (arch) {
+                    "386" -> "i386"
+                    "arm64" -> "arm64v8"
+                    else -> {
+                        arch
+                    }
+                }
                 dockerCommand {
                     name = "Pull Ubuntu"
                     commandType = other {
@@ -183,10 +201,21 @@ class TestBuild(val os: String, val arch: String, version: String, buildId: Abso
                     param("jetbrains_powershell_scriptArguments", "-version ${"go$version"} -arch $arch")
                 }
             }
+            "mac" -> {
+                exec {
+                    name = "Test"
+                    path = "_scripts/test_mac.sh"
+                    arguments = "${"go$version"} $arch %system.teamcity.build.tempDir%"
+                }
+            }
         }
     }
 
     requirements {
+        when (arch) {
+            "386", "amd64" -> equals("teamcity.agent.jvm.os.arch", if (os == "mac") "x86_64" else "amd64")
+            "arm64" -> equals("teamcity.agent.jvm.os.arch", "aarch64")
+        }
         when (os) {
             "linux" -> {
                 matches("teamcity.agent.jvm.os.family", "Linux")
@@ -194,6 +223,9 @@ class TestBuild(val os: String, val arch: String, version: String, buildId: Abso
             "windows" -> {
                 matches("teamcity.agent.jvm.os.family", "Windows")
             }
+            "mac" -> {
+                matches("teamcity.agent.jvm.os.family", "Mac OS")
+            }
         }
     }
 
@@ -202,7 +234,7 @@ class TestBuild(val os: String, val arch: String, version: String, buildId: Abso
             vcsRootExtId = "${DslContext.settingsRoot.id}"
             provider = github {
                 authType = token {
-                    token = "credentialsJSON:5dc93081-e0b2-41e2-b8f0-dea3c96e6426"
+                    token = "credentialsJSON:a7e7526c-7195-4790-bbb6-9fb4692f92d0"
                 }
                 filterAuthorRole = PullRequests.GitHubRoleFilter.EVERYBODY
             }
diff --git a/Documentation/api/json-rpc/README.md b/Documentation/api/json-rpc/README.md
index c690c480..4c1fb2ea 100644
--- a/Documentation/api/json-rpc/README.md
+++ b/Documentation/api/json-rpc/README.md
@@ -1,11 +1,9 @@
 # JSON-RPC interface
 
-Delve exposes a [JSON-RPC](http://json-rpc.org/) API interface. 
+Delve exposes a [JSON-RPC](https://www.jsonrpc.org/specification_v1) API interface. 
 
 Note that this JSON-RPC interface is served over a streaming socket, *not* over HTTP.
 
-Here is an (incomplete) [list of language implementations](http://json-rpc.org/wiki/implementations).
-
 # API versions
 
 Delve currently supports two versions of its API. By default a headless instance of `dlv` will serve APIv1 for backward compatibility with old clients, however new clients should use APIv2 as new features will only be made available through version 2. To select APIv2 use `--api-version=2` command line argument. 
diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md
index 3cab6f7e..d9c2242a 100644
--- a/Documentation/cli/README.md
+++ b/Documentation/cli/README.md
@@ -206,6 +206,16 @@ Defines <alias> as an alias to <command> or removes an alias.
 ## continue
 Run until breakpoint or program termination.
 
+	continue [<linespec>]
+
+Optional linespec argument allows you to continue until a specific location is reached. The program will halt if a breakpoint is hit before reaching the specified location.
+
+For example:
+
+	continue main.main
+	continue encoding/json.Marshal
+
+
 Aliases: c
 
 ## deferred
@@ -380,7 +390,7 @@ If regex is specified only local variables with a name matching it will be retur
 ## next
 Step over to next source line.
 
-	 next [count]
+	next [count]
 
 Optional [count] argument allows you to skip multiple lines.
 
@@ -421,7 +431,7 @@ Restart process.
 
 For recorded targets the command takes the following forms:
 
-	restart					resets ot the start of the recording
+	restart					resets to the start of the recording
 	restart [checkpoint]			resets the recording to the given checkpoint
 	restart -r [newargv...]	[redirects...]	re-records the target process
 	
diff --git a/Documentation/installation/README.md b/Documentation/installation/README.md
index 8daa6434..44f52b43 100644
--- a/Documentation/installation/README.md
+++ b/Documentation/installation/README.md
@@ -1,10 +1,50 @@
 # Installation
 
-Directions for installing Delve on all supported platforms is provided here.
+Clone the git repository and build:
 
-Please note you *must* have **Go 1.10** or higher installed in order to compile Delve.
+```
+$ git clone https://github.com/go-delve/delve
+$ cd delve
+$ go install github.com/go-delve/delve/cmd/dlv
+```
 
-- [OSX](osx/install.md)
-- [Linux](linux/install.md)
-- [Windows](windows/install.md)
-- [FreeBSD](freebsd/install.md)
\ No newline at end of file
+On Go version 1.16 or later, this command will also work:
+
+```
+$ go install github.com/go-delve/delve/cmd/dlv@latest
+```
+
+See `go help install` for details on where the `dlv` executable is saved. 
+
+If during the install step you receive an error similar to this:
+
+```
+found packages native (proc.go) and your_operating_system_and_architecture_combination_is_not_supported_by_delve (support_sentinel.go) in /home/pi/go/src/github.com/go-delve/delve/pkg/proc/native
+```
+
+It means that your combination of operating system and CPU architecture is not supported, check the output of `go version`.
+
+## macOS considerations
+
+On macOS make sure you also install the command line developer tools:
+
+```
+$ xcode-select --install
+```
+
+If you didn't enable Developer Mode using Xcode you will be asked to authorize the debugger every time you use it. To enable Developer Mode and only have to authorize once per session use:
+
+```
+sudo /usr/sbin/DevToolsSecurity -enable
+```
+
+## Compiling macOS native backend
+
+You do not need the macOS native backend and it [has known problems](https://github.com/go-delve/delve/issues/1112). If you still want to build it:
+
+1. Run `xcode-select --install`
+2. On macOS 10.14 manually install the legacy include headers by running `/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg`
+3. Clone the repo into `$GOPATH/src/github.com/go-delve/delve`
+4. Run `make install` in that directory (on some versions of macOS this requires being root, the first time you run it, to install a new certificate)
+
+The makefile will take care of creating and installing a self-signed certificate automatically.
diff --git a/Documentation/installation/freebsd/install.md b/Documentation/installation/freebsd/install.md
index 46c6e4be..ddb60cc4 100644
--- a/Documentation/installation/freebsd/install.md
+++ b/Documentation/installation/freebsd/install.md
@@ -1,20 +1,1 @@
-# Installation on FreeBSD
-
-Please use the following steps to build and install Delve on FreeBSD.
-
-There are two ways to install on FreeBSD. First is the standard `go get` method:
-
-```
-go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-Alternatively make sure $GOPATH is set (e.g. as `~/.go`) and:
-
-```
-$ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve
-$ cd $GOPATH/src/github.com/go-delve/delve
-$ gmake install
-```
-
+See [general install instructions](../README.md).
\ No newline at end of file
diff --git a/Documentation/installation/linux/install.md b/Documentation/installation/linux/install.md
index ffa27325..0efc16ed 100644
--- a/Documentation/installation/linux/install.md
+++ b/Documentation/installation/linux/install.md
@@ -1,19 +1,1 @@
-# Installation on Linux
-
-Please use the following steps to build and install Delve on Linux.
-
-There are two ways to install on Linux. First is the standard `go get` method:
-
-```
-go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-Alternatively make sure $GOPATH is set (e.g. as `~/.go`) and:
-
-```
-$ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve
-$ cd $GOPATH/src/github.com/go-delve/delve
-$ make install
-```
+See [general install instructions](../README.md).
diff --git a/Documentation/installation/osx/install.md b/Documentation/installation/osx/install.md
index f4f3f88e..0efc16ed 100644
--- a/Documentation/installation/osx/install.md
+++ b/Documentation/installation/osx/install.md
@@ -1,34 +1,1 @@
-# Installation on macOS
-
-Ensure you have a proper compilation toolchain.
-
-This should be as simple as:
-
-`xcode-select --install`
-
-Now you can install delve using `go get`:
-
-```
-$ go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-With this method you will not be able to use delve's native backend, *but you don't need it anyway*: the native backend on macOS [has known problems](https://github.com/go-delve/delve/issues/1112) on recent issues of the OS and is not currently maintained.
-
-If you didn't enable Developer Mode using Xcode you will be asked to authorize the debugger every time you use it. To enable Developer Mode and only have to authorize once per session use:
-
-```
-sudo /usr/sbin/DevToolsSecurity -enable
-```
-
-## Compiling the native backend
-
-Only do this if you have a valid reason to use the native backend.
-
-1. Run `xcode-select --install`
-2. On macOS 10.14 manually install the legacy include headers by running `/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg`
-3. Clone the repo into `$GOPATH/src/github.com/go-delve/delve`
-4. Run `make install` in that directory (on some versions of macOS this requires being root, the first time you run it, to install a new certificate)
-
-The makefile will take care of creating and installing a self-signed certificate automatically.
+See [general install instructions](../README.md).
diff --git a/Documentation/installation/windows/install.md b/Documentation/installation/windows/install.md
index 30baabce..ddb60cc4 100644
--- a/Documentation/installation/windows/install.md
+++ b/Documentation/installation/windows/install.md
@@ -1,11 +1,1 @@
-# Installation on Windows
-
-Please use the standard `go get` command to build and install Delve on Windows.
-
-```
-go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-Also, if not already set, you have to add the %GOPATH%\bin directory to your PATH variable.
+See [general install instructions](../README.md).
\ No newline at end of file
diff --git a/pkg/dwarf/frame/entries.go b/pkg/dwarf/frame/entries.go
index df31b231..4c6f94d0 100644
--- a/pkg/dwarf/frame/entries.go
+++ b/pkg/dwarf/frame/entries.go
@@ -18,6 +18,9 @@ type CommonInformationEntry struct {
 	ReturnAddressRegister uint64
 	InitialInstructions   []byte
 	staticBase            uint64
+
+	// eh_frame pointer encoding
+	ptrEncAddr ptrEnc
 }
 
 // FrameDescriptionEntry represents a Frame Descriptor Entry in the
@@ -46,6 +49,11 @@ func (fde *FrameDescriptionEntry) End() uint64 {
 	return fde.begin + fde.size
 }
 
+// Translate moves the beginning of fde forward by delta.
+func (fde *FrameDescriptionEntry) Translate(delta uint64) {
+	fde.begin += delta
+}
+
 // EstablishFrame set up frame for the given PC.
 func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
 	return executeDwarfProgramUntilPC(fde, pc)
@@ -80,8 +88,64 @@ func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry,
 // Append appends otherFDEs to fdes and returns the result.
 func (fdes FrameDescriptionEntries) Append(otherFDEs FrameDescriptionEntries) FrameDescriptionEntries {
 	r := append(fdes, otherFDEs...)
-	sort.Slice(r, func(i, j int) bool {
+	sort.SliceStable(r, func(i, j int) bool {
 		return r[i].Begin() < r[j].Begin()
 	})
+	// remove duplicates
+	uniqFDEs := fdes[:0]
+	for _, fde := range fdes {
+		if len(uniqFDEs) > 0 {
+			last := uniqFDEs[len(uniqFDEs)-1]
+			if last.Begin() == fde.Begin() && last.End() == fde.End() {
+				continue
+			}
+		}
+		uniqFDEs = append(uniqFDEs, fde)
+	}
 	return r
 }
+
+// ptrEnc represents a pointer encoding value, used during eh_frame decoding
+// to determine how pointers were encoded.
+// Least significant 4 (0xf) bytes encode the size  as well as its
+// signed-ness,  most significant 4 bytes (0xf0) are flags describing how
+// the value should be interpreted (absolute, relative...)
+// See https://www.airs.com/blog/archives/460.
+type ptrEnc uint8
+
+const (
+	ptrEncAbs    ptrEnc = 0x00 // pointer-sized unsigned integer
+	ptrEncOmit   ptrEnc = 0xff // omitted
+	ptrEncUleb   ptrEnc = 0x01 // ULEB128
+	ptrEncUdata2 ptrEnc = 0x02 // 2 bytes
+	ptrEncUdata4 ptrEnc = 0x03 // 4 bytes
+	ptrEncUdata8 ptrEnc = 0x04 // 8 bytes
+	ptrEncSigned ptrEnc = 0x08 // pointer-sized signed integer
+	ptrEncSleb   ptrEnc = 0x09 // SLEB128
+	ptrEncSdata2 ptrEnc = 0x0a // 2 bytes, signed
+	ptrEncSdata4 ptrEnc = 0x0b // 4 bytes, signed
+	ptrEncSdata8 ptrEnc = 0x0c // 8 bytes, signed
+
+	ptrEncPCRel    ptrEnc = 0x10 // value is relative to the memory address where it appears
+	ptrEncTextRel  ptrEnc = 0x20 // value is relative to the address of the text section
+	ptrEncDataRel  ptrEnc = 0x30 // value is relative to the address of the data section
+	ptrEncFuncRel  ptrEnc = 0x40 // value is relative to the start of the function
+	ptrEncAligned  ptrEnc = 0x50 // value should be aligned
+	ptrEncIndirect ptrEnc = 0x80 // value is an address where the real value of the pointer is stored
+)
+
+// Supported returns true if this pointer encoding is supported.
+func (ptrEnc ptrEnc) Supported() bool {
+	if ptrEnc != ptrEncOmit {
+		szenc := ptrEnc & 0x0f
+		if ((szenc > ptrEncUdata8) && (szenc < ptrEncSigned)) || (szenc > ptrEncSdata8) {
+			// These values aren't defined at the moment
+			return false
+		}
+		if ptrEnc&0xf0 != ptrEncPCRel {
+			// Currently only the PC relative flag is supported
+			return false
+		}
+	}
+	return true
+}
diff --git a/pkg/dwarf/frame/parser.go b/pkg/dwarf/frame/parser.go
index 8b0c165b..bcec7e56 100644
--- a/pkg/dwarf/frame/parser.go
+++ b/pkg/dwarf/frame/parser.go
@@ -6,6 +6,8 @@ package frame
 import (
 	"bytes"
 	"encoding/binary"
+	"fmt"
+	"io"
 
 	"github.com/go-delve/delve/pkg/dwarf/util"
 )
@@ -15,82 +17,135 @@ type parsefunc func(*parseContext) parsefunc
 type parseContext struct {
 	staticBase uint64
 
-	buf     *bytes.Buffer
-	entries FrameDescriptionEntries
-	common  *CommonInformationEntry
-	frame   *FrameDescriptionEntry
-	length  uint32
-	ptrSize int
+	buf         *bytes.Buffer
+	totalLen    int
+	entries     FrameDescriptionEntries
+	ciemap      map[int]*CommonInformationEntry
+	common      *CommonInformationEntry
+	frame       *FrameDescriptionEntry
+	length      uint32
+	ptrSize     int
+	ehFrameAddr uint64
+	err         error
 }
 
 // Parse takes in data (a byte slice) and returns FrameDescriptionEntries,
 // which is a slice of FrameDescriptionEntry. Each FrameDescriptionEntry
 // has a pointer to CommonInformationEntry.
-func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int) FrameDescriptionEntries {
+// If ehFrameAddr is not zero the .eh_frame format will be used, a minor variant of DWARF described at https://www.airs.com/blog/archives/460.
+// The value of ehFrameAddr will be used as the address at which eh_frame will be mapped into memory
+func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int, ehFrameAddr uint64) (FrameDescriptionEntries, error) {
 	var (
 		buf  = bytes.NewBuffer(data)
-		pctx = &parseContext{buf: buf, entries: newFrameIndex(), staticBase: staticBase, ptrSize: ptrSize}
+		pctx = &parseContext{buf: buf, totalLen: len(data), entries: newFrameIndex(), staticBase: staticBase, ptrSize: ptrSize, ehFrameAddr: ehFrameAddr, ciemap: map[int]*CommonInformationEntry{}}
 	)
 
 	for fn := parselength; buf.Len() != 0; {
 		fn = fn(pctx)
+		if pctx.err != nil {
+			return nil, pctx.err
+		}
 	}
 
 	for i := range pctx.entries {
 		pctx.entries[i].order = order
 	}
 
-	return pctx.entries
+	return pctx.entries, nil
 }
 
-func cieEntry(data []byte) bool {
-	return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
+func (ctx *parseContext) parsingEHFrame() bool {
+	return ctx.ehFrameAddr > 0
+}
+
+func (ctx *parseContext) cieEntry(cieid uint32) bool {
+	if ctx.parsingEHFrame() {
+		return cieid == 0x00
+	}
+	return cieid == 0xffffffff
+}
+
+func (ctx *parseContext) offset() int {
+	return ctx.totalLen - ctx.buf.Len()
 }
 
 func parselength(ctx *parseContext) parsefunc {
-	binary.Read(ctx.buf, binary.LittleEndian, &ctx.length)
+	start := ctx.offset()
+	binary.Read(ctx.buf, binary.LittleEndian, &ctx.length) //TODO(aarzilli): this does not support 64bit DWARF
 
 	if ctx.length == 0 {
 		// ZERO terminator
 		return parselength
 	}
 
-	var data = ctx.buf.Next(4)
+	var cieid uint32
+	binary.Read(ctx.buf, binary.LittleEndian, &cieid)
 
 	ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
 
-	if cieEntry(data) {
-		ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase}
+	if ctx.cieEntry(cieid) {
+		ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase, CIE_id: cieid}
+		ctx.ciemap[start] = ctx.common
 		return parseCIE
 	}
 
-	ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: ctx.common}
+	if ctx.ehFrameAddr > 0 {
+		cieid = uint32(start - int(cieid) + 4)
+	}
+
+	common := ctx.ciemap[int(cieid)]
+
+	if common == nil {
+		ctx.err = fmt.Errorf("unknown CIE_id %#x at %#x", cieid, start)
+	}
+
+	ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: common}
 	return parseFDE
 }
 
 func parseFDE(ctx *parseContext) parsefunc {
-	var num uint64
+	startOff := ctx.offset()
 	r := ctx.buf.Next(int(ctx.length))
 
 	reader := bytes.NewReader(r)
-	num, _ = util.ReadUintRaw(reader, binary.LittleEndian, ctx.ptrSize)
+	num := ctx.readEncodedPtr(addrSum(ctx.ehFrameAddr+uint64(startOff), reader), reader, ctx.frame.CIE.ptrEncAddr)
 	ctx.frame.begin = num + ctx.staticBase
-	num, _ = util.ReadUintRaw(reader, binary.LittleEndian, ctx.ptrSize)
-	ctx.frame.size = num
+
+	// For the size field in .eh_frame only the size encoding portion of the
+	// address pointer encoding is considered.
+	// See decode_frame_entry_1 in gdb/dwarf2-frame.c.
+	// For .debug_frame ptrEncAddr is always ptrEncAbs and never has flags.
+	sizePtrEnc := ctx.frame.CIE.ptrEncAddr & 0x0f
+	ctx.frame.size = ctx.readEncodedPtr(0, reader, sizePtrEnc)
 
 	// Insert into the tree after setting address range begin
 	// otherwise compares won't work.
 	ctx.entries = append(ctx.entries, ctx.frame)
 
+	if ctx.parsingEHFrame() && len(ctx.frame.CIE.Augmentation) > 0 {
+		// If we are parsing a .eh_frame and we saw an agumentation string then we
+		// need to read the augmentation data, which are encoded as a ULEB128
+		// size followed by 'size' bytes.
+		n, _ := util.DecodeULEB128(reader)
+		reader.Seek(int64(n), io.SeekCurrent)
+	}
+
 	// The rest of this entry consists of the instructions
 	// so we can just grab all of the data from the buffer
 	// cursor to length.
-	ctx.frame.Instructions = r[2*ctx.ptrSize:]
+
+	off, _ := reader.Seek(0, io.SeekCurrent)
+	ctx.frame.Instructions = r[off:]
 	ctx.length = 0
 
 	return parselength
 }
 
+func addrSum(base uint64, buf *bytes.Reader) uint64 {
+	n, _ := buf.Seek(0, io.SeekCurrent)
+	return base + uint64(n)
+}
+
 func parseCIE(ctx *parseContext) parsefunc {
 	data := ctx.buf.Next(int(ctx.length))
 	buf := bytes.NewBuffer(data)
@@ -100,6 +155,15 @@ func parseCIE(ctx *parseContext) parsefunc {
 	// parse augmentation
 	ctx.common.Augmentation, _ = util.ParseString(buf)
 
+	if ctx.parsingEHFrame() {
+		if ctx.common.Augmentation == "eh" {
+			ctx.err = fmt.Errorf("unsupported 'eh' augmentation at %#x", ctx.offset())
+		}
+		if len(ctx.common.Augmentation) > 0 && ctx.common.Augmentation[0] != 'z' {
+			ctx.err = fmt.Errorf("unsupported augmentation at %#x (does not start with 'z')", ctx.offset())
+		}
+	}
+
 	// parse code alignment factor
 	ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
 
@@ -107,7 +171,48 @@ func parseCIE(ctx *parseContext) parsefunc {
 	ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
 
 	// parse return address register
-	ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
+	if ctx.parsingEHFrame() && ctx.common.Version == 1 {
+		b, _ := buf.ReadByte()
+		ctx.common.ReturnAddressRegister = uint64(b)
+	} else {
+		ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
+	}
+
+	ctx.common.ptrEncAddr = ptrEncAbs
+
+	if ctx.parsingEHFrame() && len(ctx.common.Augmentation) > 0 {
+		_, _ = util.DecodeULEB128(buf) // augmentation data length
+		for i := 1; i < len(ctx.common.Augmentation); i++ {
+			switch ctx.common.Augmentation[i] {
+			case 'L':
+				_, _ = buf.ReadByte() // LSDA pointer encoding, we don't support this.
+			case 'R':
+				// Pointer encoding, describes how begin and size fields of FDEs are encoded.
+				b, _ := buf.ReadByte()
+				ctx.common.ptrEncAddr = ptrEnc(b)
+				if !ctx.common.ptrEncAddr.Supported() {
+					ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", ctx.common.ptrEncAddr, ctx.offset())
+					return nil
+				}
+			case 'S':
+				// Signal handler invocation frame, we don't support this but there is no associated data to read.
+			case 'P':
+				// Personality function encoded as a pointer encoding byte followed by
+				// the pointer to the personality function encoded as specified by the
+				// pointer encoding.
+				// We don't support this but have to read it anyway.
+				e, _ := buf.ReadByte()
+				if !ptrEnc(e).Supported() {
+					ctx.err = fmt.Errorf("pointer encoding not supported %#x at %#x", e, ctx.offset())
+					return nil
+				}
+				ctx.readEncodedPtr(0, buf, ptrEnc(e))
+			default:
+				ctx.err = fmt.Errorf("unsupported augmentation character %c at %#x", ctx.common.Augmentation[i], ctx.offset())
+				return nil
+			}
+		}
+	}
 
 	// parse initial instructions
 	// The rest of this entry consists of the instructions
@@ -119,6 +224,48 @@ func parseCIE(ctx *parseContext) parsefunc {
 	return parselength
 }
 
+// readEncodedPtr reads a pointer from buf encoded as specified by ptrEnc.
+// This function is used to read pointers from a .eh_frame section, when
+// used to parse a .debug_frame section ptrEnc will always be ptrEncAbs.
+// The parameter addr is the address that the current byte of 'buf' will be
+// mapped to when the executable file containing the eh_frame section being
+// parse is loaded in memory.
+func (ctx *parseContext) readEncodedPtr(addr uint64, buf util.ByteReaderWithLen, ptrEnc ptrEnc) uint64 {
+	if ptrEnc == ptrEncOmit {
+		return 0
+	}
+
+	var ptr uint64
+
+	switch ptrEnc & 0xf {
+	case ptrEncAbs, ptrEncSigned:
+		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, ctx.ptrSize)
+	case ptrEncUleb:
+		ptr, _ = util.DecodeULEB128(buf)
+	case ptrEncUdata2:
+		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 2)
+	case ptrEncSdata2:
+		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 2)
+		ptr = uint64(int16(ptr))
+	case ptrEncUdata4:
+		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 4)
+	case ptrEncSdata4:
+		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 4)
+		ptr = uint64(int32(ptr))
+	case ptrEncUdata8, ptrEncSdata8:
+		ptr, _ = util.ReadUintRaw(buf, binary.LittleEndian, 8)
+	case ptrEncSleb:
+		n, _ := util.DecodeSLEB128(buf)
+		ptr = uint64(n)
+	}
+
+	if ptrEnc&0xf0 == ptrEncPCRel {
+		ptr += addr
+	}
+
+	return ptr
+}
+
 // DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
 // Trick borrowed from "debug/dwarf".New()
 func DwarfEndian(infoSec []byte) binary.ByteOrder {
diff --git a/pkg/dwarf/op/regs.go b/pkg/dwarf/op/regs.go
index 3e4b6b6b..88dcf23f 100644
--- a/pkg/dwarf/op/regs.go
+++ b/pkg/dwarf/op/regs.go
@@ -14,11 +14,12 @@ type DwarfRegisters struct {
 	ObjBase   int64
 	regs      []*DwarfRegister
 
-	ByteOrder binary.ByteOrder
-	PCRegNum  uint64
-	SPRegNum  uint64
-	BPRegNum  uint64
-	LRRegNum  uint64
+	ByteOrder  binary.ByteOrder
+	PCRegNum   uint64
+	SPRegNum   uint64
+	BPRegNum   uint64
+	LRRegNum   uint64
+	ChangeFunc RegisterChangeFunc
 
 	FloatLoadError   error // error produced when loading floating point registers
 	loadMoreCallback func()
@@ -29,6 +30,8 @@ type DwarfRegister struct {
 	Bytes     []byte
 }
 
+type RegisterChangeFunc func(regNum uint64, reg *DwarfRegister) error
+
 // NewDwarfRegisters returns a new DwarfRegisters object.
 func NewDwarfRegisters(staticBase uint64, regs []*DwarfRegister, byteOrder binary.ByteOrder, pcRegNum, spRegNum, bpRegNum, lrRegNum uint64) *DwarfRegisters {
 	return &DwarfRegisters{
@@ -152,3 +155,12 @@ func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister {
 	}
 	return &DwarfRegister{Uint64Val: v, Bytes: bytes}
 }
+
+// FillBytes fills the Bytes slice of reg using Uint64Val.
+func (reg *DwarfRegister) FillBytes() {
+	if reg.Bytes != nil {
+		return
+	}
+	reg.Bytes = make([]byte, 8)
+	binary.LittleEndian.PutUint64(reg.Bytes, reg.Uint64Val)
+}
diff --git a/pkg/dwarf/regnum/amd64.go b/pkg/dwarf/regnum/amd64.go
new file mode 100644
index 00000000..2adfb5c6
--- /dev/null
+++ b/pkg/dwarf/regnum/amd64.go
@@ -0,0 +1,136 @@
+package regnum
+
+import (
+	"fmt"
+	"strings"
+)
+
+// The mapping between hardware registers and DWARF registers is specified
+// in the System V ABI AMD64 Architecture Processor Supplement page 57,
+// figure 3.36
+// https://www.uclibc.org/docs/psABI-x86_64.pdf
+
+const (
+	AMD64_Rax     = 0
+	AMD64_Rdx     = 1
+	AMD64_Rcx     = 2
+	AMD64_Rbx     = 3
+	AMD64_Rsi     = 4
+	AMD64_Rdi     = 5
+	AMD64_Rbp     = 6
+	AMD64_Rsp     = 7
+	AMD64_R8      = 8
+	AMD64_R9      = 9
+	AMD64_R10     = 10
+	AMD64_R11     = 11
+	AMD64_R12     = 12
+	AMD64_R13     = 13
+	AMD64_R14     = 14
+	AMD64_R15     = 15
+	AMD64_Rip     = 16
+	AMD64_XMM0    = 17 // XMM1 through XMM15 follow
+	AMD64_ST0     = 33 // ST(1) through ST(7) follow
+	AMD64_Rflags  = 49
+	AMD64_Es      = 50
+	AMD64_Cs      = 51
+	AMD64_Ss      = 52
+	AMD64_Ds      = 53
+	AMD64_Fs      = 54
+	AMD64_Gs      = 55
+	AMD64_Fs_base = 58
+	AMD64_Gs_base = 59
+	AMD64_MXCSR   = 64
+	AMD64_CW      = 65
+	AMD64_SW      = 66
+)
+
+var amd64DwarfToName = map[uint64]string{
+	AMD64_Rax:       "Rax",
+	AMD64_Rdx:       "Rdx",
+	AMD64_Rcx:       "Rcx",
+	AMD64_Rbx:       "Rbx",
+	AMD64_Rsi:       "Rsi",
+	AMD64_Rdi:       "Rdi",
+	AMD64_Rbp:       "Rbp",
+	AMD64_Rsp:       "Rsp",
+	AMD64_R8:        "R8",
+	AMD64_R9:        "R9",
+	AMD64_R10:       "R10",
+	AMD64_R11:       "R11",
+	AMD64_R12:       "R12",
+	AMD64_R13:       "R13",
+	AMD64_R14:       "R14",
+	AMD64_R15:       "R15",
+	AMD64_Rip:       "Rip",
+	AMD64_XMM0:      "XMM0",
+	AMD64_XMM0 + 1:  "XMM1",
+	AMD64_XMM0 + 2:  "XMM2",
+	AMD64_XMM0 + 3:  "XMM3",
+	AMD64_XMM0 + 4:  "XMM4",
+	AMD64_XMM0 + 5:  "XMM5",
+	AMD64_XMM0 + 6:  "XMM6",
+	AMD64_XMM0 + 7:  "XMM7",
+	AMD64_XMM0 + 8:  "XMM8",
+	AMD64_XMM0 + 9:  "XMM9",
+	AMD64_XMM0 + 10: "XMM10",
+	AMD64_XMM0 + 11: "XMM11",
+	AMD64_XMM0 + 12: "XMM12",
+	AMD64_XMM0 + 13: "XMM13",
+	AMD64_XMM0 + 14: "XMM14",
+	AMD64_XMM0 + 15: "XMM15",
+	AMD64_ST0:       "ST(0)",
+	AMD64_ST0 + 1:   "ST(1)",
+	AMD64_ST0 + 2:   "ST(2)",
+	AMD64_ST0 + 3:   "ST(3)",
+	AMD64_ST0 + 4:   "ST(4)",
+	AMD64_ST0 + 5:   "ST(5)",
+	AMD64_ST0 + 6:   "ST(6)",
+	AMD64_ST0 + 7:   "ST(7)",
+	AMD64_Rflags:    "Rflags",
+	AMD64_Es:        "Es",
+	AMD64_Cs:        "Cs",
+	AMD64_Ss:        "Ss",
+	AMD64_Ds:        "Ds",
+	AMD64_Fs:        "Fs",
+	AMD64_Gs:        "Gs",
+	AMD64_Fs_base:   "Fs_base",
+	AMD64_Gs_base:   "Gs_base",
+	AMD64_MXCSR:     "MXCSR",
+	AMD64_CW:        "CW",
+	AMD64_SW:        "SW",
+}
+
+var AMD64NameToDwarf = func() map[string]int {
+	r := make(map[string]int)
+	for regNum, regName := range amd64DwarfToName {
+		r[strings.ToLower(regName)] = int(regNum)
+	}
+	r["eflags"] = 49
+	r["st0"] = 33
+	r["st1"] = 34
+	r["st2"] = 35
+	r["st3"] = 36
+	r["st4"] = 37
+	r["st5"] = 38
+	r["st6"] = 39
+	r["st7"] = 40
+	return r
+}()
+
+func AMD64MaxRegNum() uint64 {
+	max := uint64(AMD64_Rip)
+	for i := range amd64DwarfToName {
+		if i > max {
+			max = i
+		}
+	}
+	return max
+}
+
+func AMD64ToName(num uint64) string {
+	name, ok := amd64DwarfToName[num]
+	if ok {
+		return name
+	}
+	return fmt.Sprintf("unknown%d", num)
+}
diff --git a/pkg/dwarf/regnum/arm64.go b/pkg/dwarf/regnum/arm64.go
new file mode 100644
index 00000000..13b15c1e
--- /dev/null
+++ b/pkg/dwarf/regnum/arm64.go
@@ -0,0 +1,35 @@
+package regnum
+
+import (
+	"fmt"
+)
+
+// The mapping between hardware registers and DWARF registers is specified
+// in the DWARF for the ARM® Architecture page 7,
+// Table 1
+// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040b/IHI0040B_aadwarf.pdf
+
+const (
+	ARM64_X0 = 0  // X1 through X30 follow
+	ARM64_BP = 29 // also X29
+	ARM64_LR = 30 // also X30
+	ARM64_SP = 31
+	ARM64_PC = 32
+	ARM64_V0 = 64 // V1 through V31 follow
+
+)
+
+func ARM64ToName(num uint64) string {
+	switch {
+	case num <= 30:
+		return fmt.Sprintf("X%d", num)
+	case num == ARM64_SP:
+		return "SP"
+	case num == ARM64_PC:
+		return "PC"
+	case num >= ARM64_V0 && num <= 95:
+		return fmt.Sprintf("V%d", num-64)
+	default:
+		return fmt.Sprintf("unknown%d", num)
+	}
+}
diff --git a/pkg/dwarf/regnum/i386.go b/pkg/dwarf/regnum/i386.go
new file mode 100644
index 00000000..ab722700
--- /dev/null
+++ b/pkg/dwarf/regnum/i386.go
@@ -0,0 +1,102 @@
+package regnum
+
+import (
+	"fmt"
+	"strings"
+)
+
+// The mapping between hardware registers and DWARF registers is specified
+// in the System V ABI Intel386 Architecture Processor Supplement page 25,
+// table 2.14
+// https://www.uclibc.org/docs/psABI-i386.pdf
+
+const (
+	I386_Eax    = 0
+	I386_Ecx    = 1
+	I386_Edx    = 2
+	I386_Ebx    = 3
+	I386_Esp    = 4
+	I386_Ebp    = 5
+	I386_Esi    = 6
+	I386_Edi    = 7
+	I386_Eip    = 8
+	I386_Eflags = 9
+	I386_ST0    = 11 // ST(1) through ST(7) follow
+	I386_XMM0   = 21 // XMM1 through XMM7 follow
+	I386_Es     = 40
+	I386_Cs     = 41
+	I386_Ss     = 42
+	I386_Ds     = 43
+	I386_Fs     = 44
+	I386_Gs     = 45
+)
+
+var i386DwarfToName = map[int]string{
+	I386_Eax:      "Eax",
+	I386_Ecx:      "Ecx",
+	I386_Edx:      "Edx",
+	I386_Ebx:      "Ebx",
+	I386_Esp:      "Esp",
+	I386_Ebp:      "Ebp",
+	I386_Esi:      "Esi",
+	I386_Edi:      "Edi",
+	I386_Eip:      "Eip",
+	I386_Eflags:   "Eflags",
+	I386_ST0:      "ST(0)",
+	I386_ST0 + 1:  "ST(1)",
+	I386_ST0 + 2:  "ST(2)",
+	I386_ST0 + 3:  "ST(3)",
+	I386_ST0 + 4:  "ST(4)",
+	I386_ST0 + 5:  "ST(5)",
+	I386_ST0 + 6:  "ST(6)",
+	I386_ST0 + 7:  "ST(7)",
+	I386_XMM0:     "XMM0",
+	I386_XMM0 + 1: "XMM1",
+	I386_XMM0 + 2: "XMM2",
+	I386_XMM0 + 3: "XMM3",
+	I386_XMM0 + 4: "XMM4",
+	I386_XMM0 + 5: "XMM5",
+	I386_XMM0 + 6: "XMM6",
+	I386_XMM0 + 7: "XMM7",
+	I386_Es:       "Es",
+	I386_Cs:       "Cs",
+	I386_Ss:       "Ss",
+	I386_Ds:       "Ds",
+	I386_Fs:       "Fs",
+	I386_Gs:       "Gs",
+}
+
+var I386NameToDwarf = func() map[string]int {
+	r := make(map[string]int)
+	for regNum, regName := range i386DwarfToName {
+		r[strings.ToLower(regName)] = regNum
+	}
+	r["eflags"] = 9
+	r["st0"] = 11
+	r["st1"] = 12
+	r["st2"] = 13
+	r["st3"] = 14
+	r["st4"] = 15
+	r["st5"] = 16
+	r["st6"] = 17
+	r["st7"] = 18
+	return r
+}()
+
+func I386MaxRegNum() int {
+	max := int(I386_Eip)
+	for i := range i386DwarfToName {
+		if i > max {
+			max = i
+		}
+	}
+	return max
+}
+
+func I386ToName(num int) string {
+	name, ok := i386DwarfToName[num]
+	if ok {
+		return name
+	}
+	return fmt.Sprintf("unknown%d", num)
+}
diff --git a/pkg/dwarf/util/util.go b/pkg/dwarf/util/util.go
index 8cb06e40..0fcf64f7 100644
--- a/pkg/dwarf/util/util.go
+++ b/pkg/dwarf/util/util.go
@@ -8,12 +8,20 @@ import (
 	"io"
 )
 
+// ByteReaderWithLen is a io.ByteReader with a Len method. This interface is
+// satisified by both bytes.Buffer and bytes.Reader.
+type ByteReaderWithLen interface {
+	io.ByteReader
+	io.Reader
+	Len() int
+}
+
 // The Little Endian Base 128 format is defined in the DWARF v4 standard,
 // section 7.6, page 161 and following.
 
 // DecodeULEB128 decodes an unsigned Little Endian Base 128
 // represented number.
-func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
+func DecodeULEB128(buf ByteReaderWithLen) (uint64, uint32) {
 	var (
 		result uint64
 		shift  uint64
@@ -46,7 +54,7 @@ func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
 
 // DecodeSLEB128 decodes a signed Little Endian Base 128
 // represented number.
-func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
+func DecodeSLEB128(buf ByteReaderWithLen) (int64, uint32) {
 	var (
 		b      byte
 		err    error
diff --git a/pkg/gobuild/gobuild.go b/pkg/gobuild/gobuild.go
index fde64f80..5cbea983 100644
--- a/pkg/gobuild/gobuild.go
+++ b/pkg/gobuild/gobuild.go
@@ -6,6 +6,8 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"runtime"
+	"time"
 
 	"github.com/go-delve/delve/pkg/config"
 	"github.com/go-delve/delve/pkg/goversion"
@@ -14,7 +16,16 @@ import (
 // Remove the file at path and issue a warning to stderr if this fails.
 // This can be used to remove the temporary binary generated for the session.
 func Remove(path string) {
-	err := os.Remove(path)
+	var err error
+	for i := 0; i < 20; i++ {
+		err = os.Remove(path)
+		// Open files can be removed on Unix, but not on Windows, where there also appears
+		// to be a delay in releasing the binary when the process exits. So we try again.
+		if err == nil || runtime.GOOS != "windows" {
+			break
+		}
+		time.Sleep(1 * time.Millisecond)
+	}
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "could not remove %v: %v\n", path, err)
 	}
diff --git a/pkg/goversion/compat.go b/pkg/goversion/compat.go
index 270effd2..f800b38c 100644
--- a/pkg/goversion/compat.go
+++ b/pkg/goversion/compat.go
@@ -6,7 +6,7 @@ import (
 
 var (
 	MinSupportedVersionOfGoMajor = 1
-	MinSupportedVersionOfGoMinor = 13
+	MinSupportedVersionOfGoMinor = 14
 	MaxSupportedVersionOfGoMajor = 1
 	MaxSupportedVersionOfGoMinor = 16
 	goTooOldErr                  = fmt.Errorf("Version of Go is too old for this version of Delve (minimum supported version %d.%d, suppress this error with --check-go-version=false)", MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor)
diff --git a/pkg/locspec/doc.go b/pkg/locspec/doc.go
index 5278ab15..1f75ac03 100644
--- a/pkg/locspec/doc.go
+++ b/pkg/locspec/doc.go
@@ -3,13 +3,13 @@
 //
 // Location spec examples:
 //
-// locStr ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
-// * <filename> can be the full path of a file or just a suffix
-// * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
-// * <function> must be unambiguous
-// * /<regex>/ will return a location for each function matched by regex
-// * +<offset> returns a location for the line that is <offset> lines after the current line
-// * -<offset> returns a location for the line that is <offset> lines before the current line
-// * <line> returns a location for a line in the current file
-// * *<address> returns the location corresponding to the specified address
+//  locStr ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
+//  * <filename> can be the full path of a file or just a suffix
+//  * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
+//    <function> must be unambiguous
+//  * /<regex>/ will return a location for each function matched by regex
+//  * +<offset> returns a location for the line that is <offset> lines after the current line
+//  * -<offset> returns a location for the line that is <offset> lines before the current line
+//  * <line> returns a location for a line in the current file
+//  * *<address> returns the location corresponding to the specified address
 package locspec
diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go
index 21805c5a..6a20bbfe 100644
--- a/pkg/proc/amd64_arch.go
+++ b/pkg/proc/amd64_arch.go
@@ -10,12 +10,7 @@ import (
 
 	"github.com/go-delve/delve/pkg/dwarf/frame"
 	"github.com/go-delve/delve/pkg/dwarf/op"
-)
-
-const (
-	amd64DwarfIPRegNum uint64 = 16
-	amd64DwarfSPRegNum uint64 = 7
-	amd64DwarfBPRegNum uint64 = 6
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 )
 
 var amd64BreakInstruction = []byte{0xCC}
@@ -39,6 +34,10 @@ func AMD64Arch(goos string) *Arch {
 		DwarfRegisterToString:            amd64DwarfRegisterToString,
 		inhibitStepInto:                  func(*BinaryInfo, uint64) bool { return false },
 		asmDecode:                        amd64AsmDecode,
+		PCRegNum:                         regnum.AMD64_Rip,
+		SPRegNum:                         regnum.AMD64_Rsp,
+		BPRegNum:                         regnum.AMD64_Rbp,
+		ContextRegNum:                    regnum.AMD64_Rdx,
 	}
 }
 
@@ -68,24 +67,24 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
 		// here).
 
 		return &frame.FrameContext{
-			RetAddrReg: amd64DwarfIPRegNum,
+			RetAddrReg: regnum.AMD64_Rip,
 			Regs: map[uint64]frame.DWRule{
-				amd64DwarfIPRegNum: {
+				regnum.AMD64_Rip: {
 					Rule:   frame.RuleOffset,
 					Offset: int64(-a.PtrSize()),
 				},
-				amd64DwarfBPRegNum: {
+				regnum.AMD64_Rbp: {
 					Rule:   frame.RuleOffset,
 					Offset: int64(-2 * a.PtrSize()),
 				},
-				amd64DwarfSPRegNum: {
+				regnum.AMD64_Rsp: {
 					Rule:   frame.RuleValOffset,
 					Offset: 0,
 				},
 			},
 			CFA: frame.DWRule{
 				Rule:   frame.RuleCFA,
-				Reg:    amd64DwarfBPRegNum,
+				Reg:    regnum.AMD64_Rbp,
 				Offset: int64(2 * a.PtrSize()),
 			},
 		}
@@ -112,10 +111,10 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
 	// so that we can use it to unwind the stack even when we encounter frames
 	// without descriptor entries.
 	// If there isn't a rule already we emit one.
-	if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined {
-		fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{
+	if fctxt.Regs[regnum.AMD64_Rbp].Rule == frame.RuleUndefined {
+		fctxt.Regs[regnum.AMD64_Rbp] = frame.DWRule{
 			Rule:   frame.RuleFramePointer,
-			Reg:    amd64DwarfBPRegNum,
+			Reg:    regnum.AMD64_Rbp,
 			Offset: 0,
 		}
 	}
@@ -244,110 +243,22 @@ func amd64SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
 // in the System V ABI AMD64 Architecture Processor Supplement page 57,
 // figure 3.36
 // https://www.uclibc.org/docs/psABI-x86_64.pdf
-func amd64RegSize(regnum uint64) int {
+func amd64RegSize(rn uint64) int {
 	// XMM registers
-	if regnum > amd64DwarfIPRegNum && regnum <= 32 {
+	if rn > regnum.AMD64_Rip && rn <= 32 {
 		return 16
 	}
 	// x87 registers
-	if regnum >= 33 && regnum <= 40 {
+	if rn >= 33 && rn <= 40 {
 		return 10
 	}
 	return 8
 }
 
-// The mapping between hardware registers and DWARF registers is specified
-// in the System V ABI AMD64 Architecture Processor Supplement page 57,
-// figure 3.36
-// https://www.uclibc.org/docs/psABI-x86_64.pdf
-
-var amd64DwarfToName = map[int]string{
-	0:  "Rax",
-	1:  "Rdx",
-	2:  "Rcx",
-	3:  "Rbx",
-	4:  "Rsi",
-	5:  "Rdi",
-	6:  "Rbp",
-	7:  "Rsp",
-	8:  "R8",
-	9:  "R9",
-	10: "R10",
-	11: "R11",
-	12: "R12",
-	13: "R13",
-	14: "R14",
-	15: "R15",
-	16: "Rip",
-	17: "XMM0",
-	18: "XMM1",
-	19: "XMM2",
-	20: "XMM3",
-	21: "XMM4",
-	22: "XMM5",
-	23: "XMM6",
-	24: "XMM7",
-	25: "XMM8",
-	26: "XMM9",
-	27: "XMM10",
-	28: "XMM11",
-	29: "XMM12",
-	30: "XMM13",
-	31: "XMM14",
-	32: "XMM15",
-	33: "ST(0)",
-	34: "ST(1)",
-	35: "ST(2)",
-	36: "ST(3)",
-	37: "ST(4)",
-	38: "ST(5)",
-	39: "ST(6)",
-	40: "ST(7)",
-	49: "Rflags",
-	50: "Es",
-	51: "Cs",
-	52: "Ss",
-	53: "Ds",
-	54: "Fs",
-	55: "Gs",
-	58: "Fs_base",
-	59: "Gs_base",
-	64: "MXCSR",
-	65: "CW",
-	66: "SW",
-}
-
-var amd64NameToDwarf = func() map[string]int {
-	r := make(map[string]int)
-	for regNum, regName := range amd64DwarfToName {
-		r[strings.ToLower(regName)] = regNum
-	}
-	r["eflags"] = 49
-	r["st0"] = 33
-	r["st1"] = 34
-	r["st2"] = 35
-	r["st3"] = 36
-	r["st4"] = 37
-	r["st5"] = 38
-	r["st6"] = 39
-	r["st7"] = 40
-	return r
-}()
-
-func maxAmd64DwarfRegister() int {
-	max := int(amd64DwarfIPRegNum)
-	for i := range amd64DwarfToName {
-		if i > max {
-			max = i
-		}
-	}
-	return max
-}
-
 func amd64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
-	dregs := initDwarfRegistersFromSlice(maxAmd64DwarfRegister(), regs, amd64NameToDwarf)
-	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0)
-	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, amd64NameToDwarf))
+	dregs := initDwarfRegistersFromSlice(int(regnum.AMD64MaxRegNum()), regs, regnum.AMD64NameToDwarf)
+	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.AMD64_Rip, regnum.AMD64_Rsp, regnum.AMD64_Rbp, 0)
+	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.AMD64NameToDwarf))
 	return *dr
 }
 
@@ -389,18 +300,19 @@ func loadMoreDwarfRegistersFromSliceFunc(dr *op.DwarfRegisters, regs Registers,
 }
 
 func amd64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
-	dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1)
-	dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc)
-	dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp)
-	dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp)
+	dregs := make([]*op.DwarfRegister, regnum.AMD64_Rip+1)
+	dregs[regnum.AMD64_Rip] = op.DwarfRegisterFromUint64(pc)
+	dregs[regnum.AMD64_Rsp] = op.DwarfRegisterFromUint64(sp)
+	dregs[regnum.AMD64_Rbp] = op.DwarfRegisterFromUint64(bp)
 
-	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, amd64DwarfIPRegNum, amd64DwarfSPRegNum, amd64DwarfBPRegNum, 0)
+	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.AMD64_Rip, regnum.AMD64_Rsp, regnum.AMD64_Rbp, 0)
 }
 
 func amd64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
-	name, ok := amd64DwarfToName[i]
-	if !ok {
-		name = fmt.Sprintf("unknown%d", i)
+	name = regnum.AMD64ToName(uint64(i))
+
+	if reg == nil {
+		return name, false, ""
 	}
 
 	switch n := strings.ToLower(name); n {
diff --git a/pkg/proc/amd64util/xsave.go b/pkg/proc/amd64util/xsave.go
index b90bb3e4..16e621fd 100644
--- a/pkg/proc/amd64util/xsave.go
+++ b/pkg/proc/amd64util/xsave.go
@@ -74,6 +74,7 @@ func (xsave *AMD64Xstate) Decode() []proc.Register {
 const (
 	_XSTATE_MAX_KNOWN_SIZE = 2969
 
+	_XSAVE_XMM_REGION_START        = 160
 	_XSAVE_HEADER_START            = 512
 	_XSAVE_HEADER_LEN              = 64
 	_XSAVE_EXTENDED_REGION_START   = 576
@@ -129,3 +130,61 @@ func AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate) er
 
 	return nil
 }
+
+func (xstate *AMD64Xstate) SetXmmRegister(n int, value []byte) error {
+	if n >= 16 {
+		return fmt.Errorf("setting register XMM%d not supported", n)
+	}
+	if len(value) > 64 {
+		return fmt.Errorf("value of register XMM%d too large (%d bytes)", n, len(value))
+	}
+
+	// Copy least significant 16 bytes to Xsave area
+
+	xmmval := value
+	if len(xmmval) > 16 {
+		xmmval = xmmval[:16]
+	}
+	rest := value[len(xmmval):]
+
+	xmmpos := _XSAVE_XMM_REGION_START + (n * 16)
+	if xmmpos >= len(xstate.Xsave) {
+		return fmt.Errorf("could not set XMM%d: not in XSAVE area", n)
+	}
+
+	copy(xstate.Xsave[xmmpos:], xmmval)
+
+	if len(rest) == 0 {
+		return nil
+	}
+
+	// Copy bytes [16, 32) to Xsave area
+
+	ymmval := rest
+	if len(ymmval) > 16 {
+		ymmval = ymmval[:16]
+	}
+	rest = rest[len(ymmval):]
+
+	ymmpos := _XSAVE_EXTENDED_REGION_START + (n * 16)
+	if ymmpos >= len(xstate.Xsave) {
+		return fmt.Errorf("could not set XMM%d: bytes 16..%d not in XSAVE area", n, 16+len(ymmval))
+	}
+
+	copy(xstate.Xsave[ymmpos:], ymmval)
+
+	if len(rest) == 0 {
+		return nil
+	}
+
+	// Copy bytes [32, 64) to Xsave area
+
+	zmmval := rest
+	zmmpos := _XSAVE_AVX512_ZMM_REGION_START + (n * 32)
+	if zmmpos >= len(xstate.Xsave) {
+		return fmt.Errorf("could not set XMM%d: bytes 32..%d not in XSAVE area", n, 32+len(zmmval))
+	}
+
+	copy(xstate.Xsave[zmmpos:], zmmval)
+	return nil
+}
diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go
index 732b8704..39d2ec23 100644
--- a/pkg/proc/arch.go
+++ b/pkg/proc/arch.go
@@ -17,6 +17,10 @@ type Arch struct {
 	breakInstrMovesPC        bool
 	derefTLS                 bool
 	usesLR                   bool // architecture uses a link register, also called RA on some architectures
+	PCRegNum                 uint64
+	SPRegNum                 uint64
+	BPRegNum                 uint64
+	ContextRegNum            uint64 // register used to pass a closure context when calling a function pointer
 
 	// asmDecode decodes the assembly instruction starting at mem[0:] into asmInst.
 	// It assumes that the Loc and AtPC fields of asmInst have already been filled.
@@ -34,7 +38,9 @@ type Arch struct {
 	// addrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in
 	// PC, SP, and BP registers in the format used by the DWARF expression interpreter.
 	addrAndStackRegsToDwarfRegisters func(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
-	// DwarfRegisterToString returns the name and value representation of the given register.
+	// DwarfRegisterToString returns the name and value representation of the
+	// given register, the register value can be nil in which case only the
+	// register name will be returned.
 	DwarfRegisterToString func(int, *op.DwarfRegister) (string, bool, string)
 	// inhibitStepInto returns whether StepBreakpoint can be set at pc.
 	inhibitStepInto func(bi *BinaryInfo, pc uint64) bool
diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go
index 49b9395a..7952ab04 100644
--- a/pkg/proc/arm64_arch.go
+++ b/pkg/proc/arm64_arch.go
@@ -8,16 +8,10 @@ import (
 
 	"github.com/go-delve/delve/pkg/dwarf/frame"
 	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"golang.org/x/arch/arm64/arm64asm"
 )
 
-const (
-	arm64DwarfIPRegNum uint64 = 32
-	arm64DwarfSPRegNum uint64 = 31
-	arm64DwarfLRRegNum uint64 = 30
-	arm64DwarfBPRegNum uint64 = 29
-)
-
 var arm64BreakInstruction = []byte{0x0, 0x0, 0x20, 0xd4}
 
 // ARM64Arch returns an initialized ARM64
@@ -40,6 +34,8 @@ func ARM64Arch(goos string) *Arch {
 		inhibitStepInto:                  func(*BinaryInfo, uint64) bool { return false },
 		asmDecode:                        arm64AsmDecode,
 		usesLR:                           true,
+		PCRegNum:                         regnum.ARM64_PC,
+		SPRegNum:                         regnum.ARM64_SP,
 	}
 }
 
@@ -69,24 +65,24 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
 		// here).
 
 		return &frame.FrameContext{
-			RetAddrReg: arm64DwarfIPRegNum,
+			RetAddrReg: regnum.ARM64_PC,
 			Regs: map[uint64]frame.DWRule{
-				arm64DwarfIPRegNum: frame.DWRule{
+				regnum.ARM64_PC: frame.DWRule{
 					Rule:   frame.RuleOffset,
 					Offset: int64(-a.PtrSize()),
 				},
-				arm64DwarfBPRegNum: frame.DWRule{
+				regnum.ARM64_BP: frame.DWRule{
 					Rule:   frame.RuleOffset,
 					Offset: int64(-2 * a.PtrSize()),
 				},
-				arm64DwarfSPRegNum: frame.DWRule{
+				regnum.ARM64_SP: frame.DWRule{
 					Rule:   frame.RuleValOffset,
 					Offset: 0,
 				},
 			},
 			CFA: frame.DWRule{
 				Rule:   frame.RuleCFA,
-				Reg:    arm64DwarfBPRegNum,
+				Reg:    regnum.ARM64_BP,
 				Offset: int64(2 * a.PtrSize()),
 			},
 		}
@@ -113,17 +109,17 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
 	// so that we can use it to unwind the stack even when we encounter frames
 	// without descriptor entries.
 	// If there isn't a rule already we emit one.
-	if fctxt.Regs[arm64DwarfBPRegNum].Rule == frame.RuleUndefined {
-		fctxt.Regs[arm64DwarfBPRegNum] = frame.DWRule{
+	if fctxt.Regs[regnum.ARM64_BP].Rule == frame.RuleUndefined {
+		fctxt.Regs[regnum.ARM64_BP] = frame.DWRule{
 			Rule:   frame.RuleFramePointer,
-			Reg:    arm64DwarfBPRegNum,
+			Reg:    regnum.ARM64_BP,
 			Offset: 0,
 		}
 	}
-	if fctxt.Regs[arm64DwarfLRRegNum].Rule == frame.RuleUndefined {
-		fctxt.Regs[arm64DwarfLRRegNum] = frame.DWRule{
+	if fctxt.Regs[regnum.ARM64_LR].Rule == frame.RuleUndefined {
+		fctxt.Regs[regnum.ARM64_LR] = frame.DWRule{
 			Rule:   frame.RuleFramePointer,
-			Reg:    arm64DwarfLRRegNum,
+			Reg:    regnum.ARM64_LR,
 			Offset: 0,
 		}
 	}
@@ -317,8 +313,8 @@ var arm64NameToDwarf = func() map[string]int {
 	for i := 0; i <= 30; i++ {
 		r[fmt.Sprintf("x%d", i)] = i
 	}
-	r["pc"] = int(arm64DwarfIPRegNum)
-	r["lr"] = int(arm64DwarfLRRegNum)
+	r["pc"] = int(regnum.ARM64_PC)
+	r["lr"] = int(regnum.ARM64_LR)
 	r["sp"] = 31
 	for i := 0; i <= 31; i++ {
 		r[fmt.Sprintf("v%d", i)] = i + 64
@@ -327,7 +323,7 @@ var arm64NameToDwarf = func() map[string]int {
 }()
 
 func maxArm64DwarfRegister() int {
-	max := int(arm64DwarfIPRegNum)
+	max := int(regnum.ARM64_PC)
 	for i := range arm64DwarfToHardware {
 		if i > max {
 			max = i
@@ -339,11 +335,11 @@ func maxArm64DwarfRegister() int {
 func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
 	dregs := make([]*op.DwarfRegister, maxArm64DwarfRegister()+1)
 
-	dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
-	dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP())
-	dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP())
+	dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(regs.PC())
+	dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(regs.SP())
+	dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(regs.BP())
 	if lr, err := regs.Get(int(arm64asm.X30)); err != nil {
-		dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr)
+		dregs[regnum.ARM64_LR] = op.DwarfRegisterFromUint64(lr)
 	}
 
 	for dwarfReg, asmReg := range arm64DwarfToHardware {
@@ -353,34 +349,26 @@ func arm64RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfR
 		}
 	}
 
-	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, arm64DwarfIPRegNum, arm64DwarfSPRegNum, arm64DwarfBPRegNum, arm64DwarfLRRegNum)
+	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.ARM64_PC, regnum.ARM64_SP, regnum.ARM64_BP, regnum.ARM64_LR)
 	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, arm64NameToDwarf))
 	return *dr
 }
 
 func arm64AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
-	dregs := make([]*op.DwarfRegister, arm64DwarfIPRegNum+1)
-	dregs[arm64DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc)
-	dregs[arm64DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp)
-	dregs[arm64DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp)
-	dregs[arm64DwarfLRRegNum] = op.DwarfRegisterFromUint64(lr)
+	dregs := make([]*op.DwarfRegister, regnum.ARM64_PC+1)
+	dregs[regnum.ARM64_PC] = op.DwarfRegisterFromUint64(pc)
+	dregs[regnum.ARM64_SP] = op.DwarfRegisterFromUint64(sp)
+	dregs[regnum.ARM64_BP] = op.DwarfRegisterFromUint64(bp)
+	dregs[regnum.ARM64_LR] = op.DwarfRegisterFromUint64(lr)
 
-	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, arm64DwarfIPRegNum, arm64DwarfSPRegNum, arm64DwarfBPRegNum, arm64DwarfLRRegNum)
+	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.ARM64_PC, regnum.ARM64_SP, regnum.ARM64_BP, regnum.ARM64_LR)
 }
 
 func arm64DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
-	// see arm64DwarfToHardware table for explanation
-	switch {
-	case i <= 30:
-		name = fmt.Sprintf("X%d", i)
-	case i == 31:
-		name = "SP"
-	case i == 32:
-		name = "PC"
-	case i >= 64 && i <= 95:
-		name = fmt.Sprintf("V%d", i-64)
-	default:
-		name = fmt.Sprintf("unknown%d", i)
+	name = regnum.ARM64ToName(uint64(i))
+
+	if reg == nil {
+		return name, false, ""
 	}
 
 	if reg.Bytes != nil && name[0] == 'V' {
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index c58ddfeb..8bda0702 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -123,8 +123,8 @@ var (
 		elf.EM_386:     true,
 	}
 
-	supportedWindowsArch = map[PEMachine]bool{
-		IMAGE_FILE_MACHINE_AMD64: true,
+	supportedWindowsArch = map[_PEMachine]bool{
+		_IMAGE_FILE_MACHINE_AMD64: true,
 	}
 
 	supportedDarwinArch = map[macho.Cpu]bool{
@@ -227,19 +227,19 @@ func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error
 	return pc, nil
 }
 
-// CpuArch is a stringer interface representing CPU architectures.
-type CpuArch interface {
+// cpuArch is a stringer interface representing CPU architectures.
+type cpuArch interface {
 	String() string
 }
 
 // ErrUnsupportedArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 type ErrUnsupportedArch struct {
 	os      string
-	cpuArch CpuArch
+	cpuArch cpuArch
 }
 
 func (e *ErrUnsupportedArch) Error() string {
-	var supportArchs []CpuArch
+	var supportArchs []cpuArch
 	switch e.os {
 	case "linux":
 		for linuxArch, _ := range supportedLinuxArch {
@@ -704,6 +704,7 @@ func (bi *BinaryInfo) AddImage(path string, addr uint64) error {
 	if err != nil {
 		bi.Images[len(bi.Images)-1].loadErr = err
 	}
+	bi.macOSDebugFrameBugWorkaround()
 	return err
 }
 
@@ -809,7 +810,7 @@ func (bi *BinaryInfo) LoadImageFromData(dwdata *dwarf.Data, debugFrameBytes, deb
 	image.dwarfTreeCache, _ = simplelru.NewLRU(dwarfTreeCacheSize, nil)
 
 	if debugFrameBytes != nil {
-		bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0, bi.Arch.PtrSize())
+		bi.frameEntries, _ = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0, bi.Arch.PtrSize(), 0)
 	}
 
 	image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize())
@@ -1009,6 +1010,38 @@ func (bi *BinaryInfo) funcToImage(fn *Function) *Image {
 	return fn.cu.image
 }
 
+// parseDebugFrameGeneral parses a debug_frame and a eh_frame section.
+// At least one of the two must be present and parsed correctly, if
+// debug_frame is present it must be parsable correctly.
+func (bi *BinaryInfo) parseDebugFrameGeneral(image *Image, debugFrameBytes []byte, debugFrameName string, debugFrameErr error, ehFrameBytes []byte, ehFrameAddr uint64, ehFrameName string, byteOrder binary.ByteOrder) {
+	if debugFrameBytes == nil && ehFrameBytes == nil {
+		image.setLoadError("could not get %s section: %v", debugFrameName, debugFrameErr)
+		return
+	}
+
+	if debugFrameBytes != nil {
+		fe, err := frame.Parse(debugFrameBytes, byteOrder, image.StaticBase, bi.Arch.PtrSize(), 0)
+		if err != nil {
+			image.setLoadError("could not parse %s section: %v", debugFrameName, err)
+			return
+		}
+		bi.frameEntries = bi.frameEntries.Append(fe)
+	}
+
+	if ehFrameBytes != nil && ehFrameAddr > 0 {
+		fe, err := frame.Parse(ehFrameBytes, byteOrder, image.StaticBase, bi.Arch.PtrSize(), ehFrameAddr)
+		if err != nil {
+			if debugFrameBytes == nil {
+				image.setLoadError("could not parse %s section: %v", ehFrameName, err)
+				return
+			}
+			bi.logger.Warnf("could not parse %s section: %v", ehFrameName, err)
+			return
+		}
+		bi.frameEntries = bi.frameEntries.Append(fe)
+	}
+}
+
 // ELF ///////////////////////////////////////////////////////////////
 
 // ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
@@ -1185,8 +1218,8 @@ func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, w
 	return nil
 }
 
-//  STT_FUNC is a code object, see /usr/include/elf.h for a full definition.
-const STT_FUNC = 2
+//  _STT_FUNC is a code object, see /usr/include/elf.h for a full definition.
+const _STT_FUNC = 2
 
 func (bi *BinaryInfo) loadSymbolName(image *Image, file *elf.File, wg *sync.WaitGroup) {
 	defer wg.Done()
@@ -1196,7 +1229,7 @@ func (bi *BinaryInfo) loadSymbolName(image *Image, file *elf.File, wg *sync.Wait
 	symSecs, _ := file.Symbols()
 	if symSecs != nil {
 		for _, symSec := range symSecs {
-			if symSec.Info == STT_FUNC { // TODO(chainhelen), need to parse others types.
+			if symSec.Info == _STT_FUNC { // TODO(chainhelen), need to parse others types.
 				s := symSec
 				bi.SymNames[symSec.Value+image.StaticBase] = &s
 			}
@@ -1207,13 +1240,16 @@ func (bi *BinaryInfo) loadSymbolName(image *Image, file *elf.File, wg *sync.Wait
 func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, debugInfoBytes []byte, wg *sync.WaitGroup) {
 	defer wg.Done()
 
-	debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
-	if err != nil {
-		image.setLoadError("could not get .debug_frame section: %v", err)
-		return
+	debugFrameData, debugFrameErr := godwarf.GetDebugSectionElf(exe, "frame")
+	ehFrameSection := exe.Section(".eh_frame")
+	var ehFrameData []byte
+	var ehFrameAddr uint64
+	if ehFrameSection != nil {
+		ehFrameAddr = ehFrameSection.Addr
+		ehFrameData, _ = ehFrameSection.Data()
 	}
 
-	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
+	bi.parseDebugFrameGeneral(image, debugFrameData, ".debug_frame", debugFrameErr, ehFrameData, ehFrameAddr, ".eh_frame", frame.DwarfEndian(debugInfoBytes))
 }
 
 func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
@@ -1227,19 +1263,9 @@ func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.
 	// - Otherwise, Go asks the external linker to place the G pointer by
 	//   emitting runtime.tlsg, a TLS symbol, which is relocated to the chosen
 	//   offset in libc's TLS block.
-	symbols, err := exe.Symbols()
-	if err != nil {
-		image.setLoadError("could not parse ELF symbols: %v", err)
-		return
-	}
-	var tlsg *elf.Symbol
-	for _, symbol := range symbols {
-		if symbol.Name == "runtime.tlsg" {
-			s := symbol
-			tlsg = &s
-			break
-		}
-	}
+	// - On ARM64 (but really, any architecture other than i386 and 86x64) the
+	//   offset is calculate using runtime.tls_g and the formula is different.
+
 	var tls *elf.Prog
 	for _, prog := range exe.Progs {
 		if prog.Type == elf.PT_TLS {
@@ -1247,20 +1273,54 @@ func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.
 			break
 		}
 	}
-	if tlsg == nil || tls == nil {
-		bi.gStructOffset = ^uint64(bi.Arch.PtrSize()) + 1 //-ptrSize
-		return
+
+	switch exe.Machine {
+	case elf.EM_X86_64, elf.EM_386:
+		tlsg := getSymbol(image, exe, "runtime.tlsg")
+		if tlsg == nil || tls == nil {
+			bi.gStructOffset = ^uint64(bi.Arch.PtrSize()) + 1 //-ptrSize
+			return
+		}
+
+		// According to https://reviews.llvm.org/D61824, linkers must pad the actual
+		// size of the TLS segment to ensure that (tlsoffset%align) == (vaddr%align).
+		// This formula, copied from the lld code, matches that.
+		// https://github.com/llvm-mirror/lld/blob/9aef969544981d76bea8e4d1961d3a6980980ef9/ELF/InputSection.cpp#L643
+		memsz := tls.Memsz + (-tls.Vaddr-tls.Memsz)&(tls.Align-1)
+
+		// The TLS register points to the end of the TLS block, which is
+		// tls.Memsz long. runtime.tlsg is an offset from the beginning of that block.
+		bi.gStructOffset = ^(memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
+
+	case elf.EM_AARCH64:
+		tlsg := getSymbol(image, exe, "runtime.tls_g")
+		if tlsg == nil || tls == nil {
+			bi.gStructOffset = 2 * uint64(bi.Arch.PtrSize())
+			return
+		}
+
+		bi.gStructOffset = tlsg.Value + uint64(bi.Arch.PtrSize()*2) + ((tls.Vaddr - uint64(bi.Arch.PtrSize()*2)) & (tls.Align - 1))
+
+	default:
+		// we should never get here
+		panic("architecture not supported")
 	}
+}
 
-	// According to https://reviews.llvm.org/D61824, linkers must pad the actual
-	// size of the TLS segment to ensure that (tlsoffset%align) == (vaddr%align).
-	// This formula, copied from the lld code, matches that.
-	// https://github.com/llvm-mirror/lld/blob/9aef969544981d76bea8e4d1961d3a6980980ef9/ELF/InputSection.cpp#L643
-	memsz := tls.Memsz + (-tls.Vaddr-tls.Memsz)&(tls.Align-1)
+func getSymbol(image *Image, exe *elf.File, name string) *elf.Symbol {
+	symbols, err := exe.Symbols()
+	if err != nil {
+		image.setLoadError("could not parse ELF symbols: %v", err)
+		return nil
+	}
 
-	// The TLS register points to the end of the TLS block, which is
-	// tls.Memsz long. runtime.tlsg is an offset from the beginning of that block.
-	bi.gStructOffset = ^(memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
+	for _, symbol := range symbols {
+		if symbol.Name == name {
+			s := symbol
+			return &s
+		}
+	}
+	return nil
 }
 
 // PE ////////////////////////////////////////////////////////////////
@@ -1274,7 +1334,7 @@ func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint
 		return err
 	}
 	image.closer = closer
-	cpuArch := PEMachine(peFile.Machine)
+	cpuArch := _PEMachine(peFile.Machine)
 	if !supportedWindowsArch[cpuArch] {
 		return &ErrUnsupportedArch{os: "windows", cpuArch: cpuArch}
 	}
@@ -1339,12 +1399,7 @@ func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, debugInfoByt
 	defer wg.Done()
 
 	debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
-	if err != nil {
-		image.setLoadError("could not get .debug_frame section: %v", err)
-		return
-	}
-
-	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
+	bi.parseDebugFrameGeneral(image, debugFrameBytes, ".debug_frame", err, nil, 0, "", frame.DwarfEndian(debugInfoBytes))
 }
 
 // Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
@@ -1429,13 +1484,94 @@ func (bi *BinaryInfo) setGStructOffsetMacho() {
 func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, debugInfoBytes []byte, wg *sync.WaitGroup) {
 	defer wg.Done()
 
-	debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
-	if err != nil {
-		image.setLoadError("could not get __debug_frame section: %v", err)
+	debugFrameBytes, debugFrameErr := godwarf.GetDebugSectionMacho(exe, "frame")
+	ehFrameSection := exe.Section("__eh_frame")
+	var ehFrameBytes []byte
+	var ehFrameAddr uint64
+	if ehFrameSection != nil {
+		ehFrameAddr = ehFrameSection.Addr
+		ehFrameBytes, _ = ehFrameSection.Data()
+	}
+
+	bi.parseDebugFrameGeneral(image, debugFrameBytes, "__debug_frame", debugFrameErr, ehFrameBytes, ehFrameAddr, "__eh_frame", frame.DwarfEndian(debugInfoBytes))
+}
+
+// macOSDebugFrameBugWorkaround applies a workaround for:
+//  https://github.com/golang/go/issues/25841
+// It finds the Go function with the lowest entry point and the first
+// debug_frame FDE, calculates the difference between the start of the
+// function and the start of the FDE and sums it to all debug_frame FDEs.
+// A number of additional checks are performed to make sure we don't ruin
+// executables unaffected by this bug.
+func (bi *BinaryInfo) macOSDebugFrameBugWorkaround() {
+	//TODO: log extensively because of bugs in the field
+	if bi.GOOS != "darwin" || bi.Arch.Name != "arm64" {
+		return
+	}
+	if len(bi.Images) > 1 {
+		// Only do this for the first executable, but it might work for plugins as
+		// well if we had a way to distinguish where entries in bi.frameEntries
+		// come from
+		return
+	}
+	exe, ok := bi.Images[0].closer.(*macho.File)
+	if !ok {
+		return
+	}
+	if exe.Flags&macho.FlagPIE == 0 {
+		bi.logger.Infof("debug_frame workaround not needed: not a PIE (%#x)", exe.Flags)
+		return
+	}
+
+	// Find first Go function (first = lowest entry point)
+	var fn *Function
+	for i := range bi.Functions {
+		if bi.Functions[i].cu.isgo && bi.Functions[i].Entry > 0 {
+			fn = &bi.Functions[i]
+			break
+		}
+	}
+	if fn == nil {
+		bi.logger.Warn("debug_frame workaround not applied: could not find a Go function")
+		return
+	}
+
+	if fde, _ := bi.frameEntries.FDEForPC(fn.Entry); fde != nil {
+		// Function is covered, no need to apply workaround
+		bi.logger.Warnf("debug_frame workaround not applied: function %s (at %#x) covered by %#x-%#x", fn.Name, fn.Entry, fde.Begin(), fde.End())
+		return
+	}
+
+	// Find lowest FDE in debug_frame
+	var fde *frame.FrameDescriptionEntry
+	for i := range bi.frameEntries {
+		if bi.frameEntries[i].CIE.CIE_id == ^uint32(0) {
+			fde = bi.frameEntries[i]
+			break
+		}
+	}
+
+	if fde == nil {
+		bi.logger.Warnf("debug_frame workaround not applied because there are no debug_frame entries (%d)", len(bi.frameEntries))
+		return
+	}
+
+	fnsize := fn.End - fn.Entry
+
+	if fde.End()-fde.Begin() != fnsize || fde.Begin() > fn.Entry {
+		bi.logger.Warnf("debug_frame workaround not applied: function %s (at %#x-%#x) has a different size than the first FDE (%#x-%#x) (or the FDE starts after the function)", fn.Name, fn.Entry, fn.End, fde.Begin(), fde.End())
 		return
 	}
 
-	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
+	delta := fn.Entry - fde.Begin()
+
+	bi.logger.Infof("applying debug_frame workaround +%#x: function %s (at %#x-%#x) and FDE %#x-%#x", delta, fn.Name, fn.Entry, fn.End, fde.Begin(), fde.End())
+
+	for i := range bi.frameEntries {
+		if bi.frameEntries[i].CIE.CIE_id == ^uint32(0) {
+			bi.frameEntries[i].Translate(delta)
+		}
+	}
 }
 
 // Do not call this function directly it isn't able to deal correctly with package paths
diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go
index 4855dbb7..c586daae 100644
--- a/pkg/proc/core/core.go
+++ b/pkg/proc/core/core.go
@@ -6,6 +6,7 @@ import (
 	"io"
 
 	"github.com/go-delve/delve/pkg/elfwriter"
+	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/proc"
 )
 
@@ -372,6 +373,12 @@ func (t *thread) SetDX(uint64) error {
 	return ErrChangeRegisterCore
 }
 
+// ChangeRegs will always return an error, you cannot
+// change register values when debugging core files.
+func (t *thread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
+	return ErrChangeRegisterCore
+}
+
 // Breakpoints will return all breakpoints for the process.
 func (p *process) Breakpoints() *proc.BreakpointMap {
 	return &p.breakpoints
diff --git a/pkg/proc/disasm.go b/pkg/proc/disasm.go
index 8d5d50b9..5566bb82 100644
--- a/pkg/proc/disasm.go
+++ b/pkg/proc/disasm.go
@@ -56,7 +56,7 @@ type AssemblyFlavour int
 
 const (
 	// GNUFlavour will display GNU assembly syntax.
-	GNUFlavour = AssemblyFlavour(iota)
+	GNUFlavour AssemblyFlavour = iota
 	// IntelFlavour will display Intel assembly syntax.
 	IntelFlavour
 	// GoFlavour will display Go assembly syntax.
diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go
index e5422a46..026ea39d 100644
--- a/pkg/proc/fncall.go
+++ b/pkg/proc/fncall.go
@@ -421,13 +421,13 @@ func callOP(bi *BinaryInfo, thread Thread, regs Registers, callAddr uint64) erro
 	sp := regs.SP()
 	// push PC on the stack
 	sp -= uint64(bi.Arch.PtrSize())
-	if err := thread.SetSP(sp); err != nil {
+	if err := setSP(thread, sp); err != nil {
 		return err
 	}
 	if err := writePointer(bi, thread.ProcessMemory(), sp, regs.PC()); err != nil {
 		return err
 	}
-	return thread.SetPC(callAddr)
+	return setPC(thread, callAddr)
 }
 
 // funcCallEvalFuncExpr evaluates expr.Fun and returns the function that we're trying to call.
@@ -761,7 +761,7 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
 		if fncall.closureAddr != 0 {
 			// When calling a function pointer we must set the DX register to the
 			// address of the function pointer itself.
-			thread.SetDX(fncall.closureAddr)
+			setClosureReg(thread, fncall.closureAddr)
 		}
 		cfa := regs.SP()
 		oldpc := regs.PC()
@@ -770,8 +770,8 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
 		err := funcCallEvalArgs(callScope, fncall, cfa)
 		if err != nil {
 			// rolling back the call, note: this works because we called regs.Copy() above
-			thread.SetSP(cfa)
-			thread.SetPC(oldpc)
+			setSP(thread, cfa)
+			setPC(thread, oldpc)
 			fncall.err = err
 			fncall.lateCallFailure = true
 			break
@@ -784,10 +784,10 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
 		if err := thread.RestoreRegisters(fncall.savedRegs); err != nil {
 			fncall.err = fmt.Errorf("could not restore registers: %v", err)
 		}
-		if err := thread.SetPC(pc); err != nil {
+		if err := setPC(thread, pc); err != nil {
 			fncall.err = fmt.Errorf("could not restore PC: %v", err)
 		}
-		if err := thread.SetSP(sp); err != nil {
+		if err := setSP(thread, sp); err != nil {
 			fncall.err = fmt.Errorf("could not restore SP: %v", err)
 		}
 		if err := stepInstructionOut(p, thread, debugCallFunctionName, debugCallFunctionName); err != nil {
diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index b997e478..d7c800bf 100644
--- a/pkg/proc/gdbserial/gdbserver.go
+++ b/pkg/proc/gdbserial/gdbserver.go
@@ -80,6 +80,7 @@ import (
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/elfwriter"
 	"github.com/go-delve/delve/pkg/logflags"
 	"github.com/go-delve/delve/pkg/proc"
@@ -1744,7 +1745,7 @@ func (t *gdbThread) SetCurrentBreakpoint(adjustPC bool) error {
 	pc := regs.PC()
 	if bp, ok := t.p.FindBreakpoint(pc); ok {
 		if t.regs.PC() != bp.Addr {
-			if err := t.SetPC(bp.Addr); err != nil {
+			if err := t.setPC(bp.Addr); err != nil {
 				return err
 			}
 		}
@@ -1770,13 +1771,6 @@ func (regs *gdbRegisters) setPC(value uint64) {
 func (regs *gdbRegisters) SP() uint64 {
 	return binary.LittleEndian.Uint64(regs.regs[regnameSP].value)
 }
-func (regs *gdbRegisters) setSP(value uint64) {
-	binary.LittleEndian.PutUint64(regs.regs[regnameSP].value, value)
-}
-
-func (regs *gdbRegisters) setDX(value uint64) {
-	binary.LittleEndian.PutUint64(regs.regs[regnameDX].value, value)
-}
 
 func (regs *gdbRegisters) BP() uint64 {
 	return binary.LittleEndian.Uint64(regs.regs[regnameBP].value)
@@ -2180,7 +2174,7 @@ func (r *gdbRegisters) FloatLoadError() error {
 }
 
 // SetPC will set the value of the PC register to the given value.
-func (t *gdbThread) SetPC(pc uint64) error {
+func (t *gdbThread) setPC(pc uint64) error {
 	_, _ = t.Registers() // Registes must be loaded first
 	t.regs.setPC(pc)
 	if t.p.gcmdok {
@@ -2190,30 +2184,26 @@ func (t *gdbThread) SetPC(pc uint64) error {
 	return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
 }
 
-// SetSP will set the value of the SP register to the given value.
-func (t *gdbThread) SetSP(sp uint64) error {
-	_, _ = t.Registers() // Registes must be loaded first
-	t.regs.setSP(sp)
-	if t.p.gcmdok {
-		return t.p.conn.writeRegisters(t.strID, t.regs.buf)
+// SetReg will change the value of a list of registers
+func (t *gdbThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
+	regName, _, _ := t.p.bi.Arch.DwarfRegisterToString(int(regNum), nil)
+	regName = strings.ToLower(regName)
+	_, _ = t.Registers() // Registers must be loaded first
+	gdbreg, ok := t.regs.regs[regName]
+	if !ok && strings.HasPrefix(regName, "xmm") {
+		// XMMn and YMMn are the same amd64 register (in different sizes), if we
+		// don't find XMMn try YMMn instead.
+		gdbreg, ok = t.regs.regs["y"+regName[1:]]
 	}
-	reg := t.regs.regs[regnameSP]
-	return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
-}
-
-// SetDX will set the value of the DX register to the given value.
-func (t *gdbThread) SetDX(dx uint64) error {
-	if t.p.bi.Arch.Name == "arm64" {
-		return fmt.Errorf("not supported")
+	if !ok {
+		return fmt.Errorf("could not set register %s: not found", regName)
 	}
-
-	_, _ = t.Registers() // Registes must be loaded first
-	t.regs.setDX(dx)
-	if t.p.gcmdok {
-		return t.p.conn.writeRegisters(t.strID, t.regs.buf)
+	reg.FillBytes()
+	if len(reg.Bytes) != len(gdbreg.value) {
+		return fmt.Errorf("could not set register %s: wrong size, expected %d got %d", regName, len(gdbreg.value), len(reg.Bytes))
 	}
-	reg := t.regs.regs[regnameDX]
-	return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
+	copy(gdbreg.value, reg.Bytes)
+	return t.p.conn.writeRegister(t.strID, gdbreg.regnum, gdbreg.value)
 }
 
 func (regs *gdbRegisters) Slice(floatingPoint bool) ([]proc.Register, error) {
diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go
index f22787d1..bf22d359 100644
--- a/pkg/proc/i386_arch.go
+++ b/pkg/proc/i386_arch.go
@@ -3,15 +3,11 @@ package proc
 import (
 	"encoding/binary"
 	"fmt"
-	"github.com/go-delve/delve/pkg/dwarf/frame"
-	"github.com/go-delve/delve/pkg/dwarf/op"
 	"strings"
-)
 
-const (
-	i386DwarfIPRegNum uint64 = 8
-	i386DwarfSPRegNum uint64 = 4
-	i386DwarfBPRegNum uint64 = 5
+	"github.com/go-delve/delve/pkg/dwarf/frame"
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 )
 
 var i386BreakInstruction = []byte{0xCC}
@@ -36,6 +32,8 @@ func I386Arch(goos string) *Arch {
 		DwarfRegisterToString:            i386DwarfRegisterToString,
 		inhibitStepInto:                  i386InhibitStepInto,
 		asmDecode:                        i386AsmDecode,
+		PCRegNum:                         regnum.I386_Eip,
+		SPRegNum:                         regnum.I386_Esp,
 	}
 }
 
@@ -65,24 +63,24 @@ func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryI
 		// here).
 
 		return &frame.FrameContext{
-			RetAddrReg: i386DwarfIPRegNum,
+			RetAddrReg: regnum.I386_Eip,
 			Regs: map[uint64]frame.DWRule{
-				i386DwarfIPRegNum: frame.DWRule{
+				regnum.I386_Eip: frame.DWRule{
 					Rule:   frame.RuleOffset,
 					Offset: int64(-i.PtrSize()),
 				},
-				i386DwarfBPRegNum: frame.DWRule{
+				regnum.I386_Ebp: frame.DWRule{
 					Rule:   frame.RuleOffset,
 					Offset: int64(-2 * i.PtrSize()),
 				},
-				i386DwarfSPRegNum: frame.DWRule{
+				regnum.I386_Esp: frame.DWRule{
 					Rule:   frame.RuleValOffset,
 					Offset: 0,
 				},
 			},
 			CFA: frame.DWRule{
 				Rule:   frame.RuleCFA,
-				Reg:    i386DwarfBPRegNum,
+				Reg:    regnum.I386_Ebp,
 				Offset: int64(2 * i.PtrSize()),
 			},
 		}
@@ -103,10 +101,10 @@ func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryI
 	// so that we can use it to unwind the stack even when we encounter frames
 	// without descriptor entries.
 	// If there isn't i rule already we emit one.
-	if fctxt.Regs[i386DwarfBPRegNum].Rule == frame.RuleUndefined {
-		fctxt.Regs[i386DwarfBPRegNum] = frame.DWRule{
+	if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined {
+		fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{
 			Rule:   frame.RuleFramePointer,
-			Reg:    i386DwarfBPRegNum,
+			Reg:    regnum.I386_Ebp,
 			Offset: 0,
 		}
 	}
@@ -186,94 +184,28 @@ func i386RegSize(regnum uint64) int {
 	return 4
 }
 
-// The mapping between hardware registers and DWARF registers is specified
-// in the System V ABI Intel386 Architecture Processor Supplement page 25,
-// table 2.14
-// https://www.uclibc.org/docs/psABI-i386.pdf
-
-var i386DwarfToName = map[int]string{
-	0:  "Eax",
-	1:  "Ecx",
-	2:  "Edx",
-	3:  "Ebx",
-	4:  "Esp",
-	5:  "Ebp",
-	6:  "Esi",
-	7:  "Edi",
-	8:  "Eip",
-	9:  "Eflags",
-	11: "ST(0)",
-	12: "ST(1)",
-	13: "ST(2)",
-	14: "ST(3)",
-	15: "ST(4)",
-	16: "ST(5)",
-	17: "ST(6)",
-	18: "ST(7)",
-	21: "XMM0",
-	22: "XMM1",
-	23: "XMM2",
-	24: "XMM3",
-	25: "XMM4",
-	26: "XMM5",
-	27: "XMM6",
-	28: "XMM7",
-	40: "Es",
-	41: "Cs",
-	42: "Ss",
-	43: "Ds",
-	44: "Fs",
-	45: "Gs",
-}
-
-var i386NameToDwarf = func() map[string]int {
-	r := make(map[string]int)
-	for regNum, regName := range i386DwarfToName {
-		r[strings.ToLower(regName)] = regNum
-	}
-	r["eflags"] = 9
-	r["st0"] = 11
-	r["st1"] = 12
-	r["st2"] = 13
-	r["st3"] = 14
-	r["st4"] = 15
-	r["st5"] = 16
-	r["st6"] = 17
-	r["st7"] = 18
-	return r
-}()
-
-func maxI386DwarfRegister() int {
-	max := int(i386DwarfIPRegNum)
-	for i := range i386DwarfToName {
-		if i > max {
-			max = i
-		}
-	}
-	return max
-}
-
 func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
-	dregs := initDwarfRegistersFromSlice(maxI386DwarfRegister(), regs, i386NameToDwarf)
-	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0)
-	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, i386NameToDwarf))
+	dregs := initDwarfRegistersFromSlice(regnum.I386MaxRegNum(), regs, regnum.I386NameToDwarf)
+	dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
+	dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.I386NameToDwarf))
 
 	return *dr
 }
 
 func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters {
-	dregs := make([]*op.DwarfRegister, i386DwarfIPRegNum+1)
-	dregs[i386DwarfIPRegNum] = op.DwarfRegisterFromUint64(pc)
-	dregs[i386DwarfSPRegNum] = op.DwarfRegisterFromUint64(sp)
-	dregs[i386DwarfBPRegNum] = op.DwarfRegisterFromUint64(bp)
+	dregs := make([]*op.DwarfRegister, regnum.I386_Eip+1)
+	dregs[regnum.I386_Eip] = op.DwarfRegisterFromUint64(pc)
+	dregs[regnum.I386_Esp] = op.DwarfRegisterFromUint64(sp)
+	dregs[regnum.I386_Ebp] = op.DwarfRegisterFromUint64(bp)
 
-	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, i386DwarfIPRegNum, i386DwarfSPRegNum, i386DwarfBPRegNum, 0)
+	return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0)
 }
 
 func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
-	name, ok := i386DwarfToName[j]
-	if !ok {
-		name = fmt.Sprintf("unknown%d", j)
+	name = regnum.I386ToName(j)
+
+	if reg == nil {
+		return name, false, ""
 	}
 
 	switch n := strings.ToLower(name); n {
diff --git a/pkg/proc/linutil/regs_amd64_arch.go b/pkg/proc/linutil/regs_amd64_arch.go
index 6ea4bbc9..6d6f0cf4 100644
--- a/pkg/proc/linutil/regs_amd64_arch.go
+++ b/pkg/proc/linutil/regs_amd64_arch.go
@@ -1,8 +1,12 @@
 package linutil
 
 import (
+	"fmt"
+
 	"golang.org/x/arch/x86/x86asm"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/amd64util"
 )
@@ -307,3 +311,71 @@ func (r *AMD64Registers) Copy() (proc.Registers, error) {
 	}
 	return &rr, nil
 }
+
+func (r *AMD64Registers) SetReg(regNum uint64, reg *op.DwarfRegister) (bool, error) {
+	var p *uint64
+	switch regNum {
+	case regnum.AMD64_Rax:
+		p = &r.Regs.Rax
+	case regnum.AMD64_Rbx:
+		p = &r.Regs.Rbx
+	case regnum.AMD64_Rcx:
+		p = &r.Regs.Rcx
+	case regnum.AMD64_Rdx:
+		p = &r.Regs.Rdx
+	case regnum.AMD64_Rsi:
+		p = &r.Regs.Rsi
+	case regnum.AMD64_Rdi:
+		p = &r.Regs.Rdi
+	case regnum.AMD64_Rbp:
+		p = &r.Regs.Rbp
+	case regnum.AMD64_Rsp:
+		p = &r.Regs.Rsp
+	case regnum.AMD64_R8:
+		p = &r.Regs.R8
+	case regnum.AMD64_R9:
+		p = &r.Regs.R9
+	case regnum.AMD64_R10:
+		p = &r.Regs.R10
+	case regnum.AMD64_R11:
+		p = &r.Regs.R11
+	case regnum.AMD64_R12:
+		p = &r.Regs.R12
+	case regnum.AMD64_R13:
+		p = &r.Regs.R13
+	case regnum.AMD64_R14:
+		p = &r.Regs.R14
+	case regnum.AMD64_R15:
+		p = &r.Regs.R15
+	case regnum.AMD64_Rip:
+		p = &r.Regs.Rip
+	}
+
+	if p != nil {
+		if reg.Bytes != nil && len(reg.Bytes) != 8 {
+			return false, fmt.Errorf("wrong number of bytes for register %s (%d)", regnum.AMD64ToName(regNum), len(reg.Bytes))
+		}
+		*p = reg.Uint64Val
+		return false, nil
+	}
+
+	if r.loadFpRegs != nil {
+		err := r.loadFpRegs(r)
+		if err != nil {
+			return false, err
+		}
+		r.loadFpRegs = nil
+	}
+
+	if regNum < regnum.AMD64_XMM0 || regNum > regnum.AMD64_XMM0+15 {
+		return false, fmt.Errorf("can not set %s", regnum.AMD64ToName(regNum))
+	}
+
+	reg.FillBytes()
+
+	err := r.Fpregset.SetXmmRegister(int(regNum-regnum.AMD64_XMM0), reg.Bytes)
+	if err != nil {
+		return false, err
+	}
+	return true, nil
+}
diff --git a/pkg/proc/linutil/regs_arm64_arch.go b/pkg/proc/linutil/regs_arm64_arch.go
index a82b0b7d..5141e579 100644
--- a/pkg/proc/linutil/regs_arm64_arch.go
+++ b/pkg/proc/linutil/regs_arm64_arch.go
@@ -9,15 +9,17 @@ import (
 
 // Regs is a wrapper for sys.PtraceRegs.
 type ARM64Registers struct {
-	Regs     *ARM64PtraceRegs //general-purpose registers
-	Fpregs   []proc.Register  //Formatted floating point registers
-	Fpregset []byte           //holding all floating point register values
+	Regs      *ARM64PtraceRegs //general-purpose registers
+	iscgo     bool
+	tpidr_el0 uint64
+	Fpregs    []proc.Register //Formatted floating point registers
+	Fpregset  []byte          //holding all floating point register values
 
 	loadFpRegs func(*ARM64Registers) error
 }
 
-func NewARM64Registers(regs *ARM64PtraceRegs, loadFpRegs func(*ARM64Registers) error) *ARM64Registers {
-	return &ARM64Registers{Regs: regs, loadFpRegs: loadFpRegs}
+func NewARM64Registers(regs *ARM64PtraceRegs, iscgo bool, tpidr_el0 uint64, loadFpRegs func(*ARM64Registers) error) *ARM64Registers {
+	return &ARM64Registers{Regs: regs, iscgo: iscgo, tpidr_el0: tpidr_el0, loadFpRegs: loadFpRegs}
 }
 
 // ARM64PtraceRegs is the struct used by the linux kernel to return the
@@ -102,13 +104,16 @@ func (r *ARM64Registers) BP() uint64 {
 
 // TLS returns the address of the thread local storage memory segment.
 func (r *ARM64Registers) TLS() uint64 {
-	return 0
+	if !r.iscgo {
+		return 0
+	}
+	return r.tpidr_el0
 }
 
 // GAddr returns the address of the G variable if it is known, 0 and false
 // otherwise.
 func (r *ARM64Registers) GAddr() (uint64, bool) {
-	return r.Regs.Regs[28], true
+	return r.Regs.Regs[28], !r.iscgo
 }
 
 // Get returns the value of the n-th register (in arm64asm order).
diff --git a/pkg/proc/mem.go b/pkg/proc/mem.go
index 4a795cbf..70ff9434 100644
--- a/pkg/proc/mem.go
+++ b/pkg/proc/mem.go
@@ -89,27 +89,28 @@ const fakeAddress = 0xbeef0000
 // memory.
 type compositeMemory struct {
 	realmem MemoryReadWriter
+	arch    *Arch
 	regs    op.DwarfRegisters
 	pieces  []op.Piece
 	data    []byte
 }
 
-func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) {
-	cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
-	for _, piece := range pieces {
+func newCompositeMemory(mem MemoryReadWriter, arch *Arch, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) {
+	cmem := &compositeMemory{realmem: mem, arch: arch, regs: regs, pieces: pieces, data: []byte{}}
+	for i := range pieces {
+		piece := &pieces[i]
 		if piece.IsRegister {
 			reg := regs.Bytes(piece.RegNum)
-			sz := piece.Size
-			if sz == 0 && len(pieces) == 1 {
-				sz = len(reg)
+			if piece.Size == 0 && len(pieces) == 1 {
+				piece.Size = len(reg)
 			}
-			if sz > len(reg) {
+			if piece.Size > len(reg) {
 				if regs.FloatLoadError != nil {
-					return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d), also error loading floating point registers: %v", sz, piece.RegNum, len(reg), regs.FloatLoadError)
+					return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d), also error loading floating point registers: %v", piece.Size, piece.RegNum, len(reg), regs.FloatLoadError)
 				}
-				return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", sz, piece.RegNum, len(reg))
+				return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", piece.Size, piece.RegNum, len(reg))
 			}
-			cmem.data = append(cmem.data, reg[:sz]...)
+			cmem.data = append(cmem.data, reg[:piece.Size]...)
 		} else {
 			buf := make([]byte, piece.Size)
 			mem.ReadMemory(buf, uint64(piece.Addr))
@@ -129,8 +130,41 @@ func (mem *compositeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
 }
 
 func (mem *compositeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
-	//TODO(aarzilli): implement
-	return 0, errors.New("can't write composite memory")
+	addr -= fakeAddress
+	if addr >= uint64(len(mem.data)) || addr+uint64(len(data)) > uint64(len(mem.data)) {
+		return 0, errors.New("write out of bounds")
+	}
+	if mem.regs.ChangeFunc == nil {
+		return 0, errors.New("can not write registers")
+	}
+
+	copy(mem.data[addr:], data)
+
+	curAddr := uint64(0)
+	donesz := 0
+	for _, piece := range mem.pieces {
+		if curAddr < (addr+uint64(len(data))) && addr < (curAddr+uint64(piece.Size)) {
+			// changed memory interval overlaps current piece
+			pieceMem := mem.data[curAddr : curAddr+uint64(piece.Size)]
+
+			if piece.IsRegister {
+				err := mem.regs.ChangeFunc(piece.RegNum, op.DwarfRegisterFromBytes(pieceMem))
+				if err != nil {
+					return donesz, err
+				}
+			} else {
+				n, err := mem.realmem.WriteMemory(uint64(piece.Addr), pieceMem)
+				if err != nil {
+					return donesz + n, err
+				}
+
+			}
+			donesz += piece.Size
+		}
+		curAddr += uint64(piece.Size)
+	}
+
+	return len(data), nil
 }
 
 // DereferenceMemory returns a MemoryReadWriter that can read and write the
diff --git a/pkg/proc/native/nonative_darwin.go b/pkg/proc/native/nonative_darwin.go
index 9531239c..95614cc3 100644
--- a/pkg/proc/native/nonative_darwin.go
+++ b/pkg/proc/native/nonative_darwin.go
@@ -6,6 +6,7 @@ import (
 	"errors"
 	"sync"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/proc"
 )
 
@@ -82,17 +83,12 @@ func (dbp *nativeProcess) EntryPoint() (uint64, error) {
 }
 
 // SetPC sets the value of the PC register.
-func (t *nativeThread) SetPC(pc uint64) error {
+func (t *nativeThread) setPC(pc uint64) error {
 	panic(ErrNativeBackendDisabled)
 }
 
-// SetSP sets the value of the SP register.
-func (t *nativeThread) SetSP(sp uint64) error {
-	panic(ErrNativeBackendDisabled)
-}
-
-// SetDX sets the value of the DX register.
-func (t *nativeThread) SetDX(dx uint64) error {
+// SetReg changes the value of the specified register.
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
 	panic(ErrNativeBackendDisabled)
 }
 
diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go
index 00db889a..511b2772 100644
--- a/pkg/proc/native/proc.go
+++ b/pkg/proc/native/proc.go
@@ -41,6 +41,8 @@ type nativeProcess struct {
 	// this process.
 	ctty *os.File
 
+	iscgo bool
+
 	exited, detached bool
 }
 
@@ -281,12 +283,19 @@ func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc
 	if !dbp.childProcess {
 		stopReason = proc.StopAttached
 	}
-	return proc.NewTarget(dbp, dbp.memthread, proc.NewTargetConfig{
+	tgt, err := proc.NewTarget(dbp, dbp.memthread, proc.NewTargetConfig{
 		Path:                path,
 		DebugInfoDirs:       debugInfoDirs,
 		DisableAsyncPreempt: runtime.GOOS == "windows" || runtime.GOOS == "freebsd",
 		StopReason:          stopReason,
 		CanDump:             runtime.GOOS == "linux"})
+	if err != nil {
+		return nil, err
+	}
+	if dbp.bi.Arch.Name == "arm64" {
+		dbp.iscgo = tgt.IsCgo()
+	}
+	return tgt, nil
 }
 
 func (dbp *nativeProcess) handlePtraceFuncs() {
diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go
index 78e92344..3bed3a9a 100644
--- a/pkg/proc/native/proc_linux.go
+++ b/pkg/proc/native/proc_linux.go
@@ -594,7 +594,7 @@ func (dbp *nativeProcess) stop(trapthread *nativeThread) (*nativeThread, error)
 				}
 				if !isHardcodedBreakpoint {
 					// phantom breakpoint hit
-					_ = th.SetPC(pc - uint64(len(dbp.BinInfo().Arch.BreakpointInstruction())))
+					_ = th.setPC(pc - uint64(len(dbp.BinInfo().Arch.BreakpointInstruction())))
 					th.os.setbp = false
 					if trapthread.ThreadID() == th.ThreadID() {
 						// Will switch to a different thread for trapthread because we don't
diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go
index baba658b..dbad2370 100644
--- a/pkg/proc/native/proc_windows.go
+++ b/pkg/proc/native/proc_windows.go
@@ -325,7 +325,7 @@ func (dbp *nativeProcess) waitForDebugEvent(flags waitForDebugEventFlags) (threa
 						}
 					}
 					if !atbp {
-						thread.SetPC(uint64(exception.ExceptionRecord.ExceptionAddress))
+						thread.setPC(uint64(exception.ExceptionRecord.ExceptionAddress))
 					}
 				}
 
diff --git a/pkg/proc/native/register_linux_386.go b/pkg/proc/native/register_linux_386.go
index 72bfd90e..906b34fc 100644
--- a/pkg/proc/native/register_linux_386.go
+++ b/pkg/proc/native/register_linux_386.go
@@ -5,13 +5,15 @@ import (
 
 	sys "golang.org/x/sys/unix"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/amd64util"
 	"github.com/go-delve/delve/pkg/proc/linutil"
 )
 
-// SetPC sets EIP to the value specified by 'pc'.
-func (thread *nativeThread) SetPC(pc uint64) error {
+// setPC sets EIP to the value specified by 'pc'.
+func (thread *nativeThread) setPC(pc uint64) error {
 	ir, err := registers(thread)
 	if err != nil {
 		return err
@@ -22,29 +24,26 @@ func (thread *nativeThread) SetPC(pc uint64) error {
 	return err
 }
 
-// SetSP sets ESP to the value specified by 'sp'
-func (thread *nativeThread) SetSP(sp uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
+	ir, err := registers(thread)
 	if err != nil {
 		return err
 	}
 	r := ir.(*linutil.I386Registers)
-	r.Regs.Esp = int32(sp)
-	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
-	return
-}
-
-func (thread *nativeThread) SetDX(dx uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
-	if err != nil {
-		return err
+	switch regNum {
+	case regnum.I386_Eip:
+		r.Regs.Eip = int32(reg.Uint64Val)
+	case regnum.I386_Esp:
+		r.Regs.Esp = int32(reg.Uint64Val)
+	case regnum.I386_Edx:
+		r.Regs.Edx = int32(reg.Uint64Val)
+	default:
+		//TODO(aarzilli): when the register calling convention is adopted by Go on
+		// i386 this should be implemented.
+		return fmt.Errorf("changing register %d not implemented", regNum)
 	}
-	r := ir.(*linutil.I386Registers)
-	r.Regs.Edx = int32(dx)
 	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
-	return
+	return err
 }
 
 func registers(thread *nativeThread) (proc.Registers, error) {
diff --git a/pkg/proc/native/registers_darwin_amd64.go b/pkg/proc/native/registers_darwin_amd64.go
index 8923441a..e036494c 100644
--- a/pkg/proc/native/registers_darwin_amd64.go
+++ b/pkg/proc/native/registers_darwin_amd64.go
@@ -5,12 +5,13 @@ package native
 // #include "threads_darwin.h"
 import "C"
 import (
-	"errors"
 	"fmt"
 	"unsafe"
 
 	"golang.org/x/arch/x86/x86asm"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
 )
 
@@ -111,7 +112,7 @@ func (r *Regs) GAddr() (uint64, bool) {
 }
 
 // SetPC sets the RIP register to the value specified by `pc`.
-func (thread *nativeThread) SetPC(pc uint64) error {
+func (thread *nativeThread) setPC(pc uint64) error {
 	kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
 	if kret != C.KERN_SUCCESS {
 		return fmt.Errorf("could not set pc")
@@ -119,13 +120,12 @@ func (thread *nativeThread) SetPC(pc uint64) error {
 	return nil
 }
 
-// SetSP sets the RSP register to the value specified by `pc`.
-func (thread *nativeThread) SetSP(sp uint64) error {
-	return errors.New("not implemented")
-}
-
-func (thread *nativeThread) SetDX(dx uint64) error {
-	return errors.New("not implemented")
+// SetReg changes the value of the specified register.
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
+	if regNum != regnum.AMD64_Rip {
+		return fmt.Errorf("changing register %d not implemented", regNum)
+	}
+	return thread.setPC(reg.Uint64Val)
 }
 
 func (r *Regs) Get(n int) (uint64, error) {
diff --git a/pkg/proc/native/registers_freebsd_amd64.go b/pkg/proc/native/registers_freebsd_amd64.go
index 98e09912..7b3ffd9a 100644
--- a/pkg/proc/native/registers_freebsd_amd64.go
+++ b/pkg/proc/native/registers_freebsd_amd64.go
@@ -5,13 +5,15 @@ import (
 
 	sys "golang.org/x/sys/unix"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/amd64util"
 	"github.com/go-delve/delve/pkg/proc/fbsdutil"
 )
 
 // SetPC sets RIP to the value specified by 'pc'.
-func (thread *nativeThread) SetPC(pc uint64) error {
+func (thread *nativeThread) setPC(pc uint64) error {
 	ir, err := registers(thread)
 	if err != nil {
 		return err
@@ -22,27 +24,23 @@ func (thread *nativeThread) SetPC(pc uint64) error {
 	return err
 }
 
-// SetSP sets RSP to the value specified by 'sp'
-func (thread *nativeThread) SetSP(sp uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
+// SetReg changes the value of the specified register.
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) (err error) {
+	ir, err := registers(thread)
 	if err != nil {
 		return err
 	}
 	r := ir.(*fbsdutil.AMD64Registers)
-	r.Regs.Rsp = int64(sp)
-	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) })
-	return
-}
-
-func (thread *nativeThread) SetDX(dx uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
-	if err != nil {
-		return err
+	switch regNum {
+	case regnum.AMD64_Rip:
+		r.Regs.Rip = int64(reg.Uint64Val)
+	case regnum.AMD64_Rsp:
+		r.Regs.Rsp = int64(reg.Uint64Val)
+	case regnum.AMD64_Rdx:
+		r.Regs.Rdx = int64(reg.Uint64Val)
+	default:
+		return fmt.Errorf("changing register %d not implemented", regNum)
 	}
-	r := ir.(*fbsdutil.AMD64Registers)
-	r.Regs.Rdx = int64(dx)
 	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.Reg)(r.Regs)) })
 	return
 }
diff --git a/pkg/proc/native/registers_linux_amd64.go b/pkg/proc/native/registers_linux_amd64.go
index c189de2b..676d8964 100644
--- a/pkg/proc/native/registers_linux_amd64.go
+++ b/pkg/proc/native/registers_linux_amd64.go
@@ -2,16 +2,19 @@ package native
 
 import (
 	"fmt"
+	"syscall"
+	"unsafe"
 
 	sys "golang.org/x/sys/unix"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/amd64util"
 	"github.com/go-delve/delve/pkg/proc/linutil"
 )
 
 // SetPC sets RIP to the value specified by 'pc'.
-func (thread *nativeThread) SetPC(pc uint64) error {
+func (thread *nativeThread) setPC(pc uint64) error {
 	ir, err := registers(thread)
 	if err != nil {
 		return err
@@ -22,29 +25,31 @@ func (thread *nativeThread) SetPC(pc uint64) error {
 	return err
 }
 
-// SetSP sets RSP to the value specified by 'sp'
-func (thread *nativeThread) SetSP(sp uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
+// SetReg changes the value of the specified register.
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
+	ir, err := registers(thread)
 	if err != nil {
 		return err
 	}
 	r := ir.(*linutil.AMD64Registers)
-	r.Regs.Rsp = sp
-	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
-	return
-}
-
-func (thread *nativeThread) SetDX(dx uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
+	fpchanged, err := r.SetReg(regNum, reg)
 	if err != nil {
 		return err
 	}
-	r := ir.(*linutil.AMD64Registers)
-	r.Regs.Rdx = dx
-	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
-	return
+	thread.dbp.execPtraceFunc(func() {
+		err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs))
+		if err != nil {
+			return
+		}
+		if fpchanged && r.Fpregset != nil && r.Fpregset.Xsave != nil {
+			iov := sys.Iovec{Base: &r.Fpregset.Xsave[0], Len: uint64(len(r.Fpregset.Xsave))}
+			_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(thread.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
+			if err == syscall.Errno(0) {
+				err = nil
+			}
+		}
+	})
+	return err
 }
 
 func registers(thread *nativeThread) (proc.Registers, error) {
diff --git a/pkg/proc/native/registers_linux_arm64.go b/pkg/proc/native/registers_linux_arm64.go
index 19ee8aba..cebbd175 100644
--- a/pkg/proc/native/registers_linux_arm64.go
+++ b/pkg/proc/native/registers_linux_arm64.go
@@ -8,6 +8,8 @@ import (
 
 	sys "golang.org/x/sys/unix"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/linutil"
 )
@@ -15,6 +17,7 @@ import (
 const (
 	_AARCH64_GREGS_SIZE  = 34 * 8
 	_AARCH64_FPREGS_SIZE = 32*16 + 8
+	_NT_ARM_TLS          = 0x401 // used in PTRACE_GETREGSET on ARM64 to retrieve the value of TPIDR_EL0, see source/include/uapi/linux/elf.h and source/arch/arm64/kernel/ptrace.c
 )
 
 func ptraceGetGRegs(pid int, regs *linutil.ARM64PtraceRegs) (err error) {
@@ -26,6 +29,15 @@ func ptraceGetGRegs(pid int, regs *linutil.ARM64PtraceRegs) (err error) {
 	return
 }
 
+func ptraceGetTpidr_el0(pid int, tpidr_el0 *uint64) (err error) {
+	iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(tpidr_el0)), Len: uint64(unsafe.Sizeof(*tpidr_el0))}
+	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(pid), uintptr(_NT_ARM_TLS), uintptr(unsafe.Pointer(&iov)), 0, 0)
+	if err == syscall.Errno(0) {
+		err = nil
+	}
+	return
+}
+
 func ptraceSetGRegs(pid int, regs *linutil.ARM64PtraceRegs) (err error) {
 	iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(regs)), Len: _AARCH64_GREGS_SIZE}
 	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(pid), uintptr(elf.NT_PRSTATUS), uintptr(unsafe.Pointer(&iov)), 0, 0)
@@ -54,8 +66,8 @@ func ptraceGetFpRegset(tid int) (fpregset []byte, err error) {
 	return fpregset, err
 }
 
-// SetPC sets PC to the value specified by 'pc'.
-func (thread *nativeThread) SetPC(pc uint64) error {
+// setPC sets PC to the value specified by 'pc'.
+func (thread *nativeThread) setPC(pc uint64) error {
 	ir, err := registers(thread)
 	if err != nil {
 		return err
@@ -66,21 +78,26 @@ func (thread *nativeThread) SetPC(pc uint64) error {
 	return err
 }
 
-// SetSP sets RSP to the value specified by 'sp'
-func (thread *nativeThread) SetSP(sp uint64) (err error) {
-	var ir proc.Registers
-	ir, err = registers(thread)
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
+	ir, err := registers(thread)
 	if err != nil {
 		return err
 	}
 	r := ir.(*linutil.ARM64Registers)
-	r.Regs.Sp = sp
-	thread.dbp.execPtraceFunc(func() { err = ptraceSetGRegs(thread.ID, r.Regs) })
-	return
-}
 
-func (thread *nativeThread) SetDX(dx uint64) (err error) {
-	return fmt.Errorf("not supported")
+	switch regNum {
+	case regnum.ARM64_PC:
+		r.Regs.Pc = reg.Uint64Val
+	case regnum.ARM64_SP:
+		r.Regs.Sp = reg.Uint64Val
+	default:
+		//TODO(aarzilli): when the register calling convention is adopted by Go on
+		// arm64 this should be implemented.
+		return fmt.Errorf("changing register %d not implemented", regNum)
+	}
+
+	thread.dbp.execPtraceFunc(func() { err = ptraceSetGRegs(thread.ID, r.Regs) })
+	return err
 }
 
 func registers(thread *nativeThread) (proc.Registers, error) {
@@ -92,7 +109,14 @@ func registers(thread *nativeThread) (proc.Registers, error) {
 	if err != nil {
 		return nil, err
 	}
-	r := linutil.NewARM64Registers(&regs, func(r *linutil.ARM64Registers) error {
+	var tpidr_el0 uint64
+	if thread.dbp.iscgo {
+		thread.dbp.execPtraceFunc(func() { err = ptraceGetTpidr_el0(thread.ID, &tpidr_el0) })
+		if err != nil {
+			return nil, err
+		}
+	}
+	r := linutil.NewARM64Registers(&regs, thread.dbp.iscgo, tpidr_el0, func(r *linutil.ARM64Registers) error {
 		var floatLoadError error
 		r.Fpregs, r.Fpregset, floatLoadError = thread.fpRegisters()
 		return floatLoadError
diff --git a/pkg/proc/native/registers_windows_amd64.go b/pkg/proc/native/registers_windows_amd64.go
index f3669e40..2f44bc33 100644
--- a/pkg/proc/native/registers_windows_amd64.go
+++ b/pkg/proc/native/registers_windows_amd64.go
@@ -4,12 +4,14 @@ import (
 	"fmt"
 	"unsafe"
 
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"github.com/go-delve/delve/pkg/dwarf/regnum"
 	"github.com/go-delve/delve/pkg/proc"
 	"github.com/go-delve/delve/pkg/proc/winutil"
 )
 
 // SetPC sets the RIP register to the value specified by `pc`.
-func (thread *nativeThread) SetPC(pc uint64) error {
+func (thread *nativeThread) setPC(pc uint64) error {
 	context := winutil.NewCONTEXT()
 	context.ContextFlags = _CONTEXT_ALL
 
@@ -23,8 +25,8 @@ func (thread *nativeThread) SetPC(pc uint64) error {
 	return _SetThreadContext(thread.os.hThread, context)
 }
 
-// SetSP sets the RSP register to the value specified by `sp`.
-func (thread *nativeThread) SetSP(sp uint64) error {
+// SetReg changes the value of the specified register.
+func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
 	context := winutil.NewCONTEXT()
 	context.ContextFlags = _CONTEXT_ALL
 
@@ -33,21 +35,60 @@ func (thread *nativeThread) SetSP(sp uint64) error {
 		return err
 	}
 
-	context.Rsp = sp
-
-	return _SetThreadContext(thread.os.hThread, context)
-}
-
-func (thread *nativeThread) SetDX(dx uint64) error {
-	context := winutil.NewCONTEXT()
-	context.ContextFlags = _CONTEXT_ALL
-
-	err := _GetThreadContext(thread.os.hThread, context)
-	if err != nil {
-		return err
+	var p *uint64
+
+	switch regNum {
+	case regnum.AMD64_Rax:
+		p = &context.Rax
+	case regnum.AMD64_Rbx:
+		p = &context.Rbx
+	case regnum.AMD64_Rcx:
+		p = &context.Rcx
+	case regnum.AMD64_Rdx:
+		p = &context.Rdx
+	case regnum.AMD64_Rsi:
+		p = &context.Rsi
+	case regnum.AMD64_Rdi:
+		p = &context.Rdi
+	case regnum.AMD64_Rbp:
+		p = &context.Rbp
+	case regnum.AMD64_Rsp:
+		p = &context.Rsp
+	case regnum.AMD64_R8:
+		p = &context.R8
+	case regnum.AMD64_R9:
+		p = &context.R9
+	case regnum.AMD64_R10:
+		p = &context.R10
+	case regnum.AMD64_R11:
+		p = &context.R11
+	case regnum.AMD64_R12:
+		p = &context.R12
+	case regnum.AMD64_R13:
+		p = &context.R13
+	case regnum.AMD64_R14:
+		p = &context.R14
+	case regnum.AMD64_R15:
+		p = &context.R15
+	case regnum.AMD64_Rip:
+		p = &context.Rip
 	}
 
-	context.Rdx = dx
+	if p != nil {
+		if reg.Bytes != nil && len(reg.Bytes) != 8 {
+			return fmt.Errorf("wrong number of bytes for register %s (%d)", regnum.AMD64ToName(regNum), len(reg.Bytes))
+		}
+		*p = reg.Uint64Val
+	} else {
+		if regNum < regnum.AMD64_XMM0 || regNum > regnum.AMD64_XMM0+15 {
+			return fmt.Errorf("can not set register %s", regnum.AMD64ToName(regNum))
+		}
+		reg.FillBytes()
+		if len(reg.Bytes) > 16 {
+			return fmt.Errorf("too many bytes when setting register %s", regnum.AMD64ToName(regNum))
+		}
+		copy(context.FltSave.XmmRegisters[(regNum-regnum.AMD64_XMM0)*16:], reg.Bytes)
+	}
 
 	return _SetThreadContext(thread.os.hThread, context)
 }
diff --git a/pkg/proc/native/threads.go b/pkg/proc/native/threads.go
index c15d63bd..286318cd 100644
--- a/pkg/proc/native/threads.go
+++ b/pkg/proc/native/threads.go
@@ -122,7 +122,7 @@ func (t *nativeThread) SetCurrentBreakpoint(adjustPC bool) error {
 
 	if bp, ok := t.dbp.FindBreakpoint(pc, adjustPC); ok {
 		if adjustPC {
-			if err = t.SetPC(bp.Addr); err != nil {
+			if err = t.setPC(bp.Addr); err != nil {
 				return err
 			}
 		}
diff --git a/pkg/proc/pe.go b/pkg/proc/pe.go
index 21a9a27a..44d0eaef 100644
--- a/pkg/proc/pe.go
+++ b/pkg/proc/pe.go
@@ -5,60 +5,60 @@ import (
 )
 
 const (
-	IMAGE_FILE_MACHINE_UNKNOWN   = 0x0
-	IMAGE_FILE_MACHINE_AM33      = 0x1d3
-	IMAGE_FILE_MACHINE_AMD64     = 0x8664
-	IMAGE_FILE_MACHINE_ARM       = 0x1c0
-	IMAGE_FILE_MACHINE_ARMNT     = 0x1c4
-	IMAGE_FILE_MACHINE_ARM64     = 0xaa64
-	IMAGE_FILE_MACHINE_EBC       = 0xebc
-	IMAGE_FILE_MACHINE_I386      = 0x14c
-	IMAGE_FILE_MACHINE_IA64      = 0x200
-	IMAGE_FILE_MACHINE_M32R      = 0x9041
-	IMAGE_FILE_MACHINE_MIPS16    = 0x266
-	IMAGE_FILE_MACHINE_MIPSFPU   = 0x366
-	IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466
-	IMAGE_FILE_MACHINE_POWERPC   = 0x1f0
-	IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1
-	IMAGE_FILE_MACHINE_R4000     = 0x166
-	IMAGE_FILE_MACHINE_SH3       = 0x1a2
-	IMAGE_FILE_MACHINE_SH3DSP    = 0x1a3
-	IMAGE_FILE_MACHINE_SH4       = 0x1a6
-	IMAGE_FILE_MACHINE_SH5       = 0x1a8
-	IMAGE_FILE_MACHINE_THUMB     = 0x1c2
-	IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
+	_IMAGE_FILE_MACHINE_UNKNOWN   = 0x0
+	_IMAGE_FILE_MACHINE_AM33      = 0x1d3
+	_IMAGE_FILE_MACHINE_AMD64     = 0x8664
+	_IMAGE_FILE_MACHINE_ARM       = 0x1c0
+	_IMAGE_FILE_MACHINE_ARMNT     = 0x1c4
+	_IMAGE_FILE_MACHINE_ARM64     = 0xaa64
+	_IMAGE_FILE_MACHINE_EBC       = 0xebc
+	_IMAGE_FILE_MACHINE_I386      = 0x14c
+	_IMAGE_FILE_MACHINE_IA64      = 0x200
+	_IMAGE_FILE_MACHINE_M32R      = 0x9041
+	_IMAGE_FILE_MACHINE_MIPS16    = 0x266
+	_IMAGE_FILE_MACHINE_MIPSFPU   = 0x366
+	_IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466
+	_IMAGE_FILE_MACHINE_POWERPC   = 0x1f0
+	_IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1
+	_IMAGE_FILE_MACHINE_R4000     = 0x166
+	_IMAGE_FILE_MACHINE_SH3       = 0x1a2
+	_IMAGE_FILE_MACHINE_SH3DSP    = 0x1a3
+	_IMAGE_FILE_MACHINE_SH4       = 0x1a6
+	_IMAGE_FILE_MACHINE_SH5       = 0x1a8
+	_IMAGE_FILE_MACHINE_THUMB     = 0x1c2
+	_IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
 )
 
-type PEMachine uint16
+type _PEMachine uint16
 
 // PEMachineString map pe machine to name, See $GOROOT/src/debug/pe/pe.go for detail
-var PEMachineString = map[uint16]string{
-	IMAGE_FILE_MACHINE_UNKNOWN:   "unknown",
-	IMAGE_FILE_MACHINE_AM33:      "am33",
-	IMAGE_FILE_MACHINE_AMD64:     "amd64",
-	IMAGE_FILE_MACHINE_ARM:       "arm",
-	IMAGE_FILE_MACHINE_ARMNT:     "armnt",
-	IMAGE_FILE_MACHINE_ARM64:     "arm64",
-	IMAGE_FILE_MACHINE_EBC:       "ebc",
-	IMAGE_FILE_MACHINE_I386:      "i386",
-	IMAGE_FILE_MACHINE_IA64:      "ia64",
-	IMAGE_FILE_MACHINE_M32R:      "m32r",
-	IMAGE_FILE_MACHINE_MIPS16:    "mips16",
-	IMAGE_FILE_MACHINE_MIPSFPU:   "mipsfpu",
-	IMAGE_FILE_MACHINE_MIPSFPU16: "mipsfpu16",
-	IMAGE_FILE_MACHINE_POWERPC:   "powerpc",
-	IMAGE_FILE_MACHINE_POWERPCFP: "powerpcfp",
-	IMAGE_FILE_MACHINE_R4000:     "r4000",
-	IMAGE_FILE_MACHINE_SH3:       "sh3",
-	IMAGE_FILE_MACHINE_SH3DSP:    "sh3dsp",
-	IMAGE_FILE_MACHINE_SH4:       "sh4",
-	IMAGE_FILE_MACHINE_SH5:       "sh5",
-	IMAGE_FILE_MACHINE_THUMB:     "thumb",
-	IMAGE_FILE_MACHINE_WCEMIPSV2: "wcemipsv2",
+var _PEMachineString = map[_PEMachine]string{
+	_IMAGE_FILE_MACHINE_UNKNOWN:   "unknown",
+	_IMAGE_FILE_MACHINE_AM33:      "am33",
+	_IMAGE_FILE_MACHINE_AMD64:     "amd64",
+	_IMAGE_FILE_MACHINE_ARM:       "arm",
+	_IMAGE_FILE_MACHINE_ARMNT:     "armnt",
+	_IMAGE_FILE_MACHINE_ARM64:     "arm64",
+	_IMAGE_FILE_MACHINE_EBC:       "ebc",
+	_IMAGE_FILE_MACHINE_I386:      "i386",
+	_IMAGE_FILE_MACHINE_IA64:      "ia64",
+	_IMAGE_FILE_MACHINE_M32R:      "m32r",
+	_IMAGE_FILE_MACHINE_MIPS16:    "mips16",
+	_IMAGE_FILE_MACHINE_MIPSFPU:   "mipsfpu",
+	_IMAGE_FILE_MACHINE_MIPSFPU16: "mipsfpu16",
+	_IMAGE_FILE_MACHINE_POWERPC:   "powerpc",
+	_IMAGE_FILE_MACHINE_POWERPCFP: "powerpcfp",
+	_IMAGE_FILE_MACHINE_R4000:     "r4000",
+	_IMAGE_FILE_MACHINE_SH3:       "sh3",
+	_IMAGE_FILE_MACHINE_SH3DSP:    "sh3dsp",
+	_IMAGE_FILE_MACHINE_SH4:       "sh4",
+	_IMAGE_FILE_MACHINE_SH5:       "sh5",
+	_IMAGE_FILE_MACHINE_THUMB:     "thumb",
+	_IMAGE_FILE_MACHINE_WCEMIPSV2: "wcemipsv2",
 }
 
-func (m PEMachine) String() string {
-	str, ok := PEMachineString[uint16(m)]
+func (m _PEMachine) String() string {
+	str, ok := _PEMachineString[m]
 	if ok {
 		return str
 	}
diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go
index aa1b3861..0e05d474 100644
--- a/pkg/proc/stack.go
+++ b/pkg/proc/stack.go
@@ -100,7 +100,9 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
 			return nil, err
 		}
 		so := thread.BinInfo().PCToImage(regs.PC())
-		it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs), 0, nil, -1, nil, 0)
+		dwarfRegs := thread.BinInfo().Arch.RegistersToDwarfRegisters(so.StaticBase, regs)
+		dwarfRegs.ChangeFunc = thread.SetReg
+		it := newStackIterator(thread.BinInfo(), thread.ProcessMemory(), dwarfRegs, 0, nil, -1, nil, 0)
 		return it.stacktrace(depth)
 	}
 	return g.Stacktrace(depth, 0)
@@ -119,9 +121,11 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
 			return nil, err
 		}
 		so := bi.PCToImage(regs.PC())
+		dwarfRegs := bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs)
+		dwarfRegs.ChangeFunc = g.Thread.SetReg
 		return newStackIterator(
 			bi, g.variable.mem,
-			bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs),
+			dwarfRegs,
 			g.stack.hi, stkbar, g.stkbarPos, g, opts), nil
 	}
 	so := g.variable.bi.PCToImage(g.PC)
diff --git a/pkg/proc/target.go b/pkg/proc/target.go
index 1bda30f3..96727a8a 100644
--- a/pkg/proc/target.go
+++ b/pkg/proc/target.go
@@ -61,6 +61,7 @@ type Target struct {
 	// have read and parsed from the targets memory.
 	// This must be cleared whenever the target is resumed.
 	gcache goroutineCache
+	iscgo  *bool
 }
 
 // ErrProcessExited indicates that the process has exited and contains both
@@ -181,6 +182,24 @@ func NewTarget(p Process, currentThread Thread, cfg NewTargetConfig) (*Target, e
 	return t, nil
 }
 
+// IsCgo returns the value of runtime.iscgo
+func (t *Target) IsCgo() bool {
+	if t.iscgo != nil {
+		return *t.iscgo
+	}
+	scope := globalScope(t.BinInfo(), t.BinInfo().Images[0], t.Memory())
+	iscgov, err := scope.findGlobal("runtime", "iscgo")
+	if err == nil {
+		iscgov.loadValue(loadFullValue)
+		if iscgov.Unreadable == nil {
+			t.iscgo = new(bool)
+			*t.iscgo = constant.BoolVal(iscgov.Value)
+			return constant.BoolVal(iscgov.Value)
+		}
+	}
+	return false
+}
+
 // SupportsFunctionCalls returns whether or not the backend supports
 // calling functions during a debug session.
 // Currently only non-recorded processes running on AMD64 support
@@ -303,7 +322,9 @@ func setAsyncPreemptOff(p *Target, v int64) {
 	p.asyncPreemptOff, _ = constant.Int64Val(asyncpreemptoffv.Value)
 
 	err = scope.setValue(asyncpreemptoffv, newConstant(constant.MakeInt64(v), scope.Mem), "")
-	logger.Warnf("could not set asyncpreemptoff %v", err)
+	if err != nil {
+		logger.Warnf("could not set asyncpreemptoff %v", err)
+	}
 }
 
 // createUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go
index ba4aa835..385ce9e2 100644
--- a/pkg/proc/target_exec.go
+++ b/pkg/proc/target_exec.go
@@ -127,7 +127,7 @@ func (dbp *Target) Continue() error {
 				// In linux-arm64, PtraceSingleStep seems cannot step over BRK instruction
 				// (linux-arm64 feature or kernel bug maybe).
 				if !arch.BreakInstrMovesPC() {
-					curthread.SetPC(loc.PC + uint64(arch.BreakpointSize()))
+					setPC(curthread, loc.PC+uint64(arch.BreakpointSize()))
 				}
 				// Single-step current thread until we exit runtime.breakpoint and
 				// runtime.Breakpoint.
@@ -145,7 +145,7 @@ func (dbp *Target) Continue() error {
 					bp := make([]byte, bpsize)
 					_, err = dbp.Memory().ReadMemory(bp, loc.PC)
 					if bytes.Equal(bp, arch.BreakpointInstruction()) {
-						curthread.SetPC(loc.PC + uint64(bpsize))
+						setPC(curthread, loc.PC+uint64(bpsize))
 					}
 				}
 				return conditionErrors(threads)
diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go
index edc36e78..35b33670 100644
--- a/pkg/proc/threads.go
+++ b/pkg/proc/threads.go
@@ -2,6 +2,8 @@ package proc
 
 import (
 	"errors"
+
+	"github.com/go-delve/delve/pkg/dwarf/op"
 )
 
 // Thread represents a thread.
@@ -31,9 +33,10 @@ type Thread interface {
 	// Common returns the CommonThread structure for this thread
 	Common() *CommonThread
 
-	SetPC(uint64) error
-	SetSP(uint64) error
-	SetDX(uint64) error
+	// SetReg changes the value of the specified register. A minimal
+	// implementation of this interface can support just setting the PC
+	// register.
+	SetReg(uint64, *op.DwarfRegister) error
 }
 
 // Location represents the location of a thread.
@@ -83,3 +86,15 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
 		return frames[0], frames[1], nil
 	}
 }
+
+func setPC(thread Thread, newPC uint64) error {
+	return thread.SetReg(thread.BinInfo().Arch.PCRegNum, op.DwarfRegisterFromUint64(newPC))
+}
+
+func setSP(thread Thread, newSP uint64) error {
+	return thread.SetReg(thread.BinInfo().Arch.SPRegNum, op.DwarfRegisterFromUint64(newSP))
+}
+
+func setClosureReg(thread Thread, newClosureReg uint64) error {
+	return thread.SetReg(thread.BinInfo().Arch.ContextRegNum, op.DwarfRegisterFromUint64(newClosureReg))
+}
diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go
index 82dfd81b..3ff8e5f4 100644
--- a/pkg/proc/variables.go
+++ b/pkg/proc/variables.go
@@ -1147,7 +1147,7 @@ func extractVarInfoFromEntry(bi *BinaryInfo, image *Image, regs op.DwarfRegister
 	if pieces != nil {
 		addr = fakeAddress
 		var cmem *compositeMemory
-		cmem, err = newCompositeMemory(mem, regs, pieces)
+		cmem, err = newCompositeMemory(mem, bi.Arch, regs, pieces)
 		if cmem != nil {
 			mem = cmem
 		}
diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go
index 779cb009..41f2ef04 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -134,7 +134,7 @@ See also: "help on", "help cond" and "help clear"`},
 
 For recorded targets the command takes the following forms:
 
-	restart					resets ot the start of the recording
+	restart					resets to the start of the recording
 	restart [checkpoint]			resets the recording to the given checkpoint
 	restart -r [newargv...]	[redirects...]	re-records the target process
 	
@@ -152,12 +152,22 @@ A list of file redirections can be specified after the new argument list to over
 	2>error.txt	redirects the standard error of the target process to error.txt
 `},
 		{aliases: []string{"rebuild"}, group: runCmds, cmdFn: c.rebuild, allowedPrefixes: revPrefix, helpMsg: "Rebuild the target executable and restarts it. It does not work if the executable was not built by delve."},
-		{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: "Run until breakpoint or program termination."},
+		{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: `Run until breakpoint or program termination.
+
+	continue [<linespec>]
+
+Optional linespec argument allows you to continue until a specific location is reached. The program will halt if a breakpoint is hit before reaching the specified location.
+
+For example:
+
+	continue main.main
+	continue encoding/json.Marshal
+`},
 		{aliases: []string{"step", "s"}, group: runCmds, cmdFn: c.step, allowedPrefixes: revPrefix, helpMsg: "Single step through program."},
 		{aliases: []string{"step-instruction", "si"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
 		{aliases: []string{"next", "n"}, group: runCmds, cmdFn: c.next, allowedPrefixes: revPrefix, helpMsg: `Step over to next source line.
 
-	 next [count]
+	next [count]
 
 Optional [count] argument allows you to skip multiple lines.
 `},
@@ -1191,6 +1201,19 @@ func (c *Commands) rebuild(t *Term, ctx callContext, args string) error {
 }
 
 func (c *Commands) cont(t *Term, ctx callContext, args string) error {
+	if args != "" {
+		tmp, err := setBreakpoint(t, ctx, false, args)
+		if err != nil {
+			return err
+		}
+		defer func() {
+			for _, bp := range tmp {
+				if _, err := t.client.ClearBreakpoint(bp.ID); err != nil {
+					fmt.Printf("failed to clear temporary breakpoint: %d", bp.ID)
+				}
+			}
+		}()
+	}
 	if ctx.Prefix == revPrefix {
 		return c.rewind(t, ctx, args)
 	}
@@ -1497,7 +1520,7 @@ func breakpoints(t *Term, ctx callContext, args string) error {
 	return nil
 }
 
-func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) error {
+func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) ([]*api.Breakpoint, error) {
 	args := split2PartsBySpace(argstr)
 
 	requestedBp := &api.Breakpoint{}
@@ -1513,23 +1536,24 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
 			spec = argstr
 		}
 	default:
-		return fmt.Errorf("address required")
+		return nil, fmt.Errorf("address required")
 	}
 
 	requestedBp.Tracepoint = tracepoint
 	locs, err := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
 	if err != nil {
 		if requestedBp.Name == "" {
-			return err
+			return nil, err
 		}
 		requestedBp.Name = ""
 		spec = argstr
 		var err2 error
 		locs, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
 		if err2 != nil {
-			return err
+			return nil, err
 		}
 	}
+	created := []*api.Breakpoint{}
 	for _, loc := range locs {
 		requestedBp.Addr = loc.PC
 		requestedBp.Addrs = loc.PCs
@@ -1539,8 +1563,9 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
 
 		bp, err := t.client.CreateBreakpoint(requestedBp)
 		if err != nil {
-			return err
+			return nil, err
 		}
+		created = append(created, bp)
 
 		fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
 	}
@@ -1548,7 +1573,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
 	var shouldSetReturnBreakpoints bool
 	loc, err := locspec.Parse(spec)
 	if err != nil {
-		return err
+		return nil, err
 	}
 	switch t := loc.(type) {
 	case *locspec.NormalLocationSpec:
@@ -1563,7 +1588,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
 			}
 			addrs, err := t.client.(*rpc2.RPCClient).FunctionReturnLocations(locs[0].Function.Name())
 			if err != nil {
-				return err
+				return nil, err
 			}
 			for j := range addrs {
 				_, err = t.client.CreateBreakpoint(&api.Breakpoint{
@@ -1573,20 +1598,22 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
 					LoadArgs:    &ShortLoadConfig,
 				})
 				if err != nil {
-					return err
+					return nil, err
 				}
 			}
 		}
 	}
-	return nil
+	return created, nil
 }
 
 func breakpoint(t *Term, ctx callContext, args string) error {
-	return setBreakpoint(t, ctx, false, args)
+	_, err := setBreakpoint(t, ctx, false, args)
+	return err
 }
 
 func tracepoint(t *Term, ctx callContext, args string) error {
-	return setBreakpoint(t, ctx, true, args)
+	_, err := setBreakpoint(t, ctx, true, args)
+	return err
 }
 
 func edit(t *Term, ctx callContext, args string) error {
@@ -2640,6 +2667,9 @@ func display(t *Term, ctx callContext, args string) error {
 }
 
 func dump(t *Term, ctx callContext, args string) error {
+	if args == "" {
+		return fmt.Errorf("not enough arguments")
+	}
 	dumpState, err := t.client.CoreDumpStart(args)
 	if err != nil {
 		return err
diff --git a/service/dap/server.go b/service/dap/server.go
index 7206d133..798ea15a 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -87,6 +87,17 @@ var defaultArgs = launchAttachArgs{
 	showGlobalVariables: false,
 }
 
+// DefaultLoadConfig controls how variables are loaded from the target's memory, borrowing the
+// default value from the original vscode-go debug adapter and rpc server.
+// TODO(polina): Support setting config via launch/attach args or only rely on on-demand loading?
+var DefaultLoadConfig = proc.LoadConfig{
+	FollowPointers:     true,
+	MaxVariableRecurse: 1,
+	MaxStringLen:       64,
+	MaxArrayValues:     64,
+	MaxStructFields:    -1,
+}
+
 // NewServer creates a new DAP Server. It takes an opened Listener
 // via config and assumes its ownership. config.disconnectChan has to be set;
 // it will be closed by the server when the client disconnects or requests
@@ -171,6 +182,7 @@ func (s *Server) signalDisconnect() {
 	}
 	if s.binaryToRemove != "" {
 		gobuild.Remove(s.binaryToRemove)
+		s.binaryToRemove = ""
 	}
 }
 
@@ -420,8 +432,19 @@ func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
 const debugBinary string = "./__debug_bin"
 
 func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
-	// TODO(polina): Respond with an error if debug session is in progress?
+	// Validate launch request mode
+	mode, ok := request.Arguments["mode"]
+	if !ok || mode == "" {
+		mode = "debug"
+	}
+	if !isValidLaunchMode(mode) {
+		s.sendErrorResponse(request.Request,
+			FailedToLaunch, "Failed to launch",
+			fmt.Sprintf("Unsupported 'mode' value %q in debug configuration.", mode))
+		return
+	}
 
+	// TODO(polina): Respond with an error if debug session is in progress?
 	program, ok := request.Arguments["program"].(string)
 	if !ok || program == "" {
 		s.sendErrorResponse(request.Request,
@@ -430,11 +453,6 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		return
 	}
 
-	mode, ok := request.Arguments["mode"]
-	if !ok || mode == "" {
-		mode = "debug"
-	}
-
 	if mode == "debug" || mode == "test" {
 		output, ok := request.Arguments["output"].(string)
 		if !ok || output == "" {
@@ -474,14 +492,6 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		s.binaryToRemove = debugname
 	}
 
-	// TODO(polina): support "remote" mode
-	if mode != "exec" && mode != "debug" && mode != "test" {
-		s.sendErrorResponse(request.Request,
-			FailedToLaunch, "Failed to launch",
-			fmt.Sprintf("Unsupported 'mode' value %q in debug configuration.", mode))
-		return
-	}
-
 	s.setLaunchAttachArgs(request)
 
 	var targetArgs []string
@@ -523,13 +533,23 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 	s.send(&dap.LaunchResponse{Response: *newResponse(request.Request)})
 }
 
+// TODO(polina): support "remote" mode
+func isValidLaunchMode(launchMode interface{}) bool {
+	switch launchMode {
+	case "exec", "debug", "test":
+		return true
+	}
+
+	return false
+}
+
 // onDisconnectRequest handles the DisconnectRequest. Per the DAP spec,
 // it disconnects the debuggee and signals that the debug adaptor
 // (in our case this TCP server) can be terminated.
 func (s *Server) onDisconnectRequest(request *dap.DisconnectRequest) {
 	s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)})
 	if s.debugger != nil {
-		_, err := s.debugger.Command(&api.DebuggerCommand{Name: api.Halt})
+		_, err := s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil)
 		if err != nil {
 			s.log.Error(err)
 		}
@@ -599,7 +619,9 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 			response.Body.Breakpoints[i].Line = want.Line
 			response.Body.Breakpoints[i].Message = err.Error()
 		} else {
+			response.Body.Breakpoints[i].Id = got.ID
 			response.Body.Breakpoints[i].Line = got.Line
+			response.Body.Breakpoints[i].Source = dap.Source{Name: request.Arguments.Source.Name, Path: request.Arguments.Source.Path}
 		}
 	}
 	s.send(response)
@@ -823,11 +845,9 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 
 	goid := sf.(stackFrame).goroutineID
 	frame := sf.(stackFrame).frameIndex
-	// TODO(polina): Support setting config via launch/attach args
-	cfg := proc.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1}
 
 	// Retrieve arguments
-	args, err := s.debugger.FunctionArguments(goid, frame, 0, cfg)
+	args, err := s.debugger.FunctionArguments(goid, frame, 0, DefaultLoadConfig)
 	if err != nil {
 		s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error())
 		return
@@ -835,7 +855,7 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 	argScope := &fullyQualifiedVariable{&proc.Variable{Name: "Arguments", Children: slicePtrVarToSliceVar(args)}, "", true}
 
 	// Retrieve local variables
-	locals, err := s.debugger.LocalVariables(goid, frame, 0, cfg)
+	locals, err := s.debugger.LocalVariables(goid, frame, 0, DefaultLoadConfig)
 	if err != nil {
 		s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list locals", err.Error())
 		return
@@ -864,7 +884,7 @@ func (s *Server) onScopesRequest(request *dap.ScopesRequest) {
 			return
 		}
 		currPkgFilter := fmt.Sprintf("^%s\\.", currPkg)
-		globals, err := s.debugger.PackageVariables(currPkgFilter, cfg)
+		globals, err := s.debugger.PackageVariables(currPkgFilter, DefaultLoadConfig)
 		if err != nil {
 			s.sendErrorResponse(request.Request, UnableToListGlobals, "Unable to list globals", err.Error())
 			return
@@ -906,8 +926,6 @@ func (s *Server) onVariablesRequest(request *dap.VariablesRequest) {
 		return
 	}
 	children := make([]dap.Variable, 0)
-	// TODO(polina): check and handle if variable loaded incompletely
-	// https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables
 
 	switch v.Kind {
 	case reflect.Map:
@@ -1043,6 +1061,11 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 		return
 	}
 	typeName := api.PrettyTypeName(v.DwarfType)
+
+	// As per https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables,
+	// some of the types might be fully or partially not loaded based on LoadConfig. For now, clearly
+	// communicate when values are not fully loaded. TODO(polina): look into loading on demand.
+
 	switch v.Kind {
 	case reflect.UnsafePointer:
 		if len(v.Children) == 0 {
@@ -1059,10 +1082,20 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 			value = "void"
 		} else {
 			value = fmt.Sprintf("<%s>(%#x)", typeName, v.Children[0].Addr)
-			variablesReference = maybeCreateVariableHandle(v)
+			if v.Children[0].OnlyAddr { // Not fully loaded
+				value += " (value not loaded)"
+				// Since child is not fully loaded, don't provide a reference to view it.
+			} else {
+				value = fmt.Sprintf("<%s>(%#x)", typeName, v.Children[0].Addr)
+				variablesReference = maybeCreateVariableHandle(v)
+			}
 		}
 	case reflect.Array:
-		value = "<" + typeName + ">"
+		if v.Len > int64(len(v.Children)) { // Not fully loaded
+			value = fmt.Sprintf("<%s> (length: %d, loaded: %d)", typeName, v.Len, len(v.Children))
+		} else {
+			value = fmt.Sprintf("<%s>", typeName)
+		}
 		if len(v.Children) > 0 {
 			variablesReference = maybeCreateVariableHandle(v)
 		}
@@ -1070,7 +1103,11 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 		if v.Base == 0 {
 			value = "nil <" + typeName + ">"
 		} else {
-			value = fmt.Sprintf("<%s> (length: %d, cap: %d)", typeName, v.Len, v.Cap)
+			if v.Len > int64(len(v.Children)) { // Not fully loaded
+				value = fmt.Sprintf("<%s> (length: %d, cap: %d, loaded: %d)", typeName, v.Len, v.Cap, len(v.Children))
+			} else {
+				value = fmt.Sprintf("<%s> (length: %d, cap: %d)", typeName, v.Len, v.Cap)
+			}
 			if len(v.Children) > 0 {
 				variablesReference = maybeCreateVariableHandle(v)
 			}
@@ -1079,7 +1116,11 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 		if v.Base == 0 {
 			value = "nil <" + typeName + ">"
 		} else {
-			value = fmt.Sprintf("<%s> (length: %d)", typeName, v.Len)
+			if v.Len > int64(len(v.Children)/2) { // Not fully loaded
+				value = fmt.Sprintf("<%s> (length: %d, loaded: %d)", typeName, v.Len, len(v.Children)/2)
+			} else {
+				value = fmt.Sprintf("<%s> (length: %d)", typeName, v.Len)
+			}
 			if len(v.Children) > 0 {
 				variablesReference = maybeCreateVariableHandle(v)
 			}
@@ -1087,7 +1128,7 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 	case reflect.String:
 		vvalue := constant.StringVal(v.Value)
 		lenNotLoaded := v.Len - int64(len(vvalue))
-		if lenNotLoaded > 0 {
+		if lenNotLoaded > 0 { // Not fully loaded
 			vvalue += fmt.Sprintf("...+%d more", lenNotLoaded)
 		}
 		value = fmt.Sprintf("%q", vvalue)
@@ -1110,20 +1151,44 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 			value = "nil <" + typeName + ">"
 		} else {
 			value = "<" + typeName + "(" + v.Children[0].TypeString() + ")" + ">"
-			// TODO(polina): should we remove one level of indirection and skip "data"?
-			// Then we will have:
-			// Before:
-			//   i: <interface{}(int)>
-			//      data: 123
-			// After:
-			//   i: <interface{}(int)> 123
-			// Before:
-			//   i: <interface{}(main.MyStruct)>
-			//      data: <main.MyStruct>
-			//         field1: ...
-			// After:
-			//   i: <interface{}(main.MyStruct)>
-			//      field1: ...
+			if v.Children[0].OnlyAddr { // Not fully loaded
+				loadExpr := fmt.Sprintf("*(*%q)(%#x)", typeName, v.Addr)
+				// We might be loading variables from the frame that's not topmost, so use
+				// frame-independent address-based expression.
+				s.log.Debugf("loading %s (type %s) with %s", qualifiedNameOrExpr, typeName, loadExpr)
+				vLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, loadExpr, DefaultLoadConfig)
+				if err != nil {
+					value += fmt.Sprintf(" - FAILED TO LOAD: %s", err)
+				} else {
+					v.Children = vLoaded.Children
+				}
+			}
+			// Provide a reference to the child only if it fully loaded
+			if !v.Children[0].OnlyAddr {
+				// TODO(polina): should we remove one level of indirection and skip "data"?
+				// Then we will have:
+				//   Before:
+				//     i: <interface{}(int)>
+				//        data: 123
+				//   After:
+				//     i: <interface{}(int)> 123
+				//   Before:
+				//     i: <interface{}(main.MyStruct)>
+				//        data: <main.MyStruct>
+				//           field1: ...
+				//   After:
+				//     i: <interface{}(main.MyStruct)>
+				//        field1: ...
+				variablesReference = maybeCreateVariableHandle(v)
+			}
+		}
+	case reflect.Struct:
+		if v.Len > int64(len(v.Children)) { // Not fully loaded
+			value = fmt.Sprintf("<%s> (fields: %d, loaded: %d)", typeName, v.Len, len(v.Children))
+		} else {
+			value = fmt.Sprintf("<%s>", typeName)
+		}
+		if len(v.Children) > 0 {
 			variablesReference = maybeCreateVariableHandle(v)
 		}
 	case reflect.Complex64, reflect.Complex128:
@@ -1140,7 +1205,7 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 			v.Children[1].Kind = reflect.Float64
 		}
 		fallthrough
-	default: // Struct, complex, scalar
+	default: // complex, scalar
 		vvalue := api.VariableValueAsString(v)
 		if vvalue != "" {
 			value = vvalue
@@ -1175,9 +1240,6 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 		goid = sf.(stackFrame).goroutineID
 		frame = sf.(stackFrame).frameIndex
 	}
-	// TODO(polina): Support config settings via launch/attach args vs auto-loading?
-	apiCfg := &api.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1}
-	prcCfg := proc.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1}
 
 	response := &dap.EvaluateResponse{Response: *newResponse(request.Request)}
 	isCall, err := regexp.MatchString(`^\s*call\s+\S+`, request.Arguments.Expression)
@@ -1199,11 +1261,11 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 		}
 		state, err := s.debugger.Command(&api.DebuggerCommand{
 			Name:                 api.Call,
-			ReturnInfoLoadConfig: apiCfg,
+			ReturnInfoLoadConfig: api.LoadConfigFromProc(&DefaultLoadConfig),
 			Expr:                 strings.Replace(request.Arguments.Expression, "call ", "", 1),
 			UnsafeCall:           false,
 			GoroutineID:          goid,
-		})
+		}, nil)
 		if _, isexited := err.(proc.ErrProcessExited); isexited || err == nil && state.Exited {
 			e := &dap.TerminatedEvent{Event: *newEvent("terminated")}
 			s.send(e)
@@ -1222,7 +1284,7 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 			if t.GoroutineID == stateBeforeCall.SelectedGoroutine.ID &&
 				t.Line == stateBeforeCall.SelectedGoroutine.CurrentLoc.Line && t.CallReturn {
 				// The call completed. Get the return values.
-				retVars, err = s.debugger.FindThreadReturnValues(t.ID, prcCfg)
+				retVars, err = s.debugger.FindThreadReturnValues(t.ID, DefaultLoadConfig)
 				if err != nil {
 					s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", err.Error(), showErrorToUser)
 					return
@@ -1262,7 +1324,7 @@ func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
 			}
 		}
 	} else { // {expression}
-		exprVar, err := s.debugger.EvalVariableInScope(goid, frame, 0, request.Arguments.Expression, prcCfg)
+		exprVar, err := s.debugger.EvalVariableInScope(goid, frame, 0, request.Arguments.Expression, DefaultLoadConfig)
 		if err != nil {
 			s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", err.Error(), showErrorToUser)
 			return
@@ -1410,7 +1472,7 @@ func newEvent(event string) *dap.Event {
 }
 
 const BetterBadAccessError = `invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation]
-Unable to propogate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
+Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
 
 func (s *Server) resetHandlesForStop() {
 	s.stackFrameHandles.reset()
@@ -1425,7 +1487,7 @@ func (s *Server) doCommand(command string) {
 		return
 	}
 
-	state, err := s.debugger.Command(&api.DebuggerCommand{Name: command})
+	state, err := s.debugger.Command(&api.DebuggerCommand{Name: command}, nil)
 	if _, isexited := err.(proc.ErrProcessExited); isexited || err == nil && state.Exited {
 		e := &dap.TerminatedEvent{Event: *newEvent("terminated")}
 		s.send(e)
diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go
index 6994f363..e5ca6db5 100644
--- a/service/debugger/debugger.go
+++ b/service/debugger/debugger.go
@@ -413,6 +413,7 @@ func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) {
 // If `kill` is true we will kill the process after
 // detaching.
 func (d *Debugger) Detach(kill bool) error {
+	d.log.Debug("detaching")
 	d.targetMutex.Lock()
 	defer d.targetMutex.Unlock()
 	if ok, _ := d.target.Valid(); !ok {
@@ -894,7 +895,7 @@ func (d *Debugger) isRunning() bool {
 }
 
 // Command handles commands which control the debugger lifecycle
-func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, error) {
+func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struct{}) (*api.DebuggerState, error) {
 	var err error
 
 	if command.Name == api.Halt {
@@ -917,6 +918,12 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
 	d.setRunning(true)
 	defer d.setRunning(false)
 
+	if command.Name != api.SwitchGoroutine && command.Name != api.SwitchThread && command.Name != api.Halt {
+		d.target.ResumeNotify(resumeNotify)
+	} else if resumeNotify != nil {
+		close(resumeNotify)
+	}
+
 	switch command.Name {
 	case api.Continue:
 		d.log.Debug("continuing")
diff --git a/service/rpc1/server.go b/service/rpc1/server.go
index c2ec5a78..da3e3c8d 100644
--- a/service/rpc1/server.go
+++ b/service/rpc1/server.go
@@ -60,7 +60,7 @@ func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error {
 }
 
 func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) {
-	st, err := s.debugger.Command(command)
+	st, err := s.debugger.Command(command, cb.SetupDoneChan())
 	cb.Return(st, err)
 }
 
diff --git a/service/rpc2/server.go b/service/rpc2/server.go
index a27eae0c..65a323d8 100644
--- a/service/rpc2/server.go
+++ b/service/rpc2/server.go
@@ -92,6 +92,7 @@ type RestartOut struct {
 
 // Restart restarts program.
 func (s *RPCServer) Restart(arg RestartIn, cb service.RPCCallback) {
+	close(cb.SetupDoneChan())
 	if s.config.Debugger.AttachPid != 0 {
 		cb.Return(nil, errors.New("cannot restart process Delve did not create"))
 		return
@@ -113,6 +114,7 @@ type StateOut struct {
 
 // State returns the current debugger state.
 func (s *RPCServer) State(arg StateIn, cb service.RPCCallback) {
+	close(cb.SetupDoneChan())
 	var out StateOut
 	st, err := s.debugger.State(arg.NonBlocking)
 	if err != nil {
@@ -129,7 +131,7 @@ type CommandOut struct {
 
 // Command interrupts, continues and steps through the program.
 func (s *RPCServer) Command(command api.DebuggerCommand, cb service.RPCCallback) {
-	st, err := s.debugger.Command(&command)
+	st, err := s.debugger.Command(&command, cb.SetupDoneChan())
 	if err != nil {
 		cb.Return(nil, err)
 		return
@@ -856,6 +858,7 @@ type StopRecordingOut struct {
 }
 
 func (s *RPCServer) StopRecording(arg StopRecordingIn, cb service.RPCCallback) {
+	close(cb.SetupDoneChan())
 	var out StopRecordingOut
 	err := s.debugger.StopRecording()
 	if err != nil {
diff --git a/service/rpccallback.go b/service/rpccallback.go
index 9b047826..224dd6c9 100644
--- a/service/rpccallback.go
+++ b/service/rpccallback.go
@@ -3,4 +3,9 @@ package service
 // RPCCallback is used by RPC methods to return their result asynchronously.
 type RPCCallback interface {
 	Return(out interface{}, err error)
+
+	// SetupDone returns a channel that should be closed to signal that the
+	// asynchornous method has completed setup and the server is ready to
+	// receive other requests.
+	SetupDoneChan() chan struct{}
 }
diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go
index 887540c4..d3eb47dc 100644
--- a/service/rpccommon/server.go
+++ b/service/rpccommon/server.go
@@ -46,12 +46,15 @@ type ServerImpl struct {
 }
 
 type RPCCallback struct {
-	s       *ServerImpl
-	sending *sync.Mutex
-	codec   rpc.ServerCodec
-	req     rpc.Request
+	s         *ServerImpl
+	sending   *sync.Mutex
+	codec     rpc.ServerCodec
+	req       rpc.Request
+	setupDone chan struct{}
 }
 
+var _ service.RPCCallback = &RPCCallback{}
+
 // RPCServer implements the RPC method calls common to all versions of the API.
 type RPCServer struct {
 	s *ServerImpl
@@ -163,8 +166,8 @@ var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
 
 // Is this an exported - upper case - name?
 func isExported(name string) bool {
-	rune, _ := utf8.DecodeRuneInString(name)
-	return unicode.IsUpper(rune)
+	ch, _ := utf8.DecodeRuneInString(name)
+	return unicode.IsUpper(ch)
 }
 
 // Is this type exported or a builtin?
@@ -236,12 +239,10 @@ func suitableMethods(rcvr interface{}, methods map[string]*methodType, log *logr
 				log.Warn("method", mname, "returns", returnType.String(), "not error")
 				continue
 			}
-		} else {
+		} else if mtype.NumOut() != 0 {
 			// Method needs zero outs.
-			if mtype.NumOut() != 0 {
-				log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut())
-				continue
-			}
+			log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut())
+			continue
 		}
 		methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv}
 	}
@@ -328,7 +329,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
 				s.log.Debugf("(async %d) <- %s(%T%s)", req.Seq, req.ServiceMethod, argv.Interface(), argvbytes)
 			}
 			function := mtype.method.Func
-			ctl := &RPCCallback{s, sending, codec, req}
+			ctl := &RPCCallback{s, sending, codec, req, make(chan struct{})}
 			go func() {
 				defer func() {
 					if ierr := recover(); ierr != nil {
@@ -337,6 +338,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
 				}()
 				function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)})
 			}()
+			<-ctl.setupDone
 		}
 	}
 	codec.Close()
@@ -363,6 +365,12 @@ func (s *ServerImpl) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *r
 }
 
 func (cb *RPCCallback) Return(out interface{}, err error) {
+	select {
+	case <-cb.setupDone:
+		// already closed
+	default:
+		close(cb.setupDone)
+	}
 	errmsg := ""
 	if err != nil {
 		errmsg = err.Error()
@@ -375,6 +383,10 @@ func (cb *RPCCallback) Return(out interface{}, err error) {
 	cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg)
 }
 
+func (cb *RPCCallback) SetupDoneChan() chan struct{} {
+	return cb.setupDone
+}
+
 // GetVersion returns the version of delve as well as the API version
 // currently served.
 func (s *RPCServer) GetVersion(args api.GetVersionIn, out *api.GetVersionOut) error {


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 1e94da3b..00000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: Delve CI
-
-on: [push, pull_request]
-
-jobs:
-  build:
-    runs-on: ${{matrix.os}}
-    strategy:
-      matrix:
-        include:
-          - go: 1.15
-            os: macos-latest
-    steps:
-      - uses: actions/checkout@v2
-      - uses: actions/setup-go@v1
-        with:
-          go-version: ${{matrix.go}}
-      - run: go run _scripts/make.go test
diff --git a/Documentation/backend_test_health.md b/Documentation/backend_test_health.md
index bea2522f..c79bd2a8 100644
--- a/Documentation/backend_test_health.md
+++ b/Documentation/backend_test_health.md
@@ -1,19 +1,23 @@
 Tests skipped by each supported backend:
 
-* 386 skipped = 2% (3/147)
+* 386 skipped = 2.7% (4/149)
 	* 1 broken
-	* 2 broken - cgo stacktraces
-* arm64 skipped = 2% (3/147)
+	* 3 broken - cgo stacktraces
+* arm64 skipped = 2.7% (4/149)
 	* 2 broken
+	* 1 broken - cgo stacktraces
 	* 1 broken - global variable symbolication
-* darwin/lldb skipped = 0.68% (1/147)
+* darwin/arm64 skipped = 0.67% (1/149)
+	* 1 broken - cgo stacktraces
+* darwin/lldb skipped = 0.67% (1/149)
 	* 1 upstream issue
-* freebsd skipped = 7.5% (11/147)
+* freebsd skipped = 8.1% (12/149)
 	* 11 broken
-* linux/386/pie skipped = 0.68% (1/147)
+	* 1 not implemented
+* linux/386/pie skipped = 0.67% (1/149)
 	* 1 broken
-* pie skipped = 0.68% (1/147)
+* pie skipped = 0.67% (1/149)
 	* 1 upstream issue - https://github.com/golang/go/issues/29322
-* windows skipped = 1.4% (2/147)
+* windows skipped = 1.3% (2/149)
 	* 1 broken
 	* 1 upstream issue
diff --git a/_fixtures/testvariables.go b/_fixtures/testvariables.go
index d957d29e..41bbf429 100644
--- a/_fixtures/testvariables.go
+++ b/_fixtures/testvariables.go
@@ -56,14 +56,15 @@ func foobar(baz string, bar FooBar) {
 		c128 = complex(float64(2), float64(3))
 		i32  = [2]int32{1, 2}
 		f    = barfoo
-		ms   = Nest{0, &Nest{1, &Nest{2, &Nest{3, &Nest{4, nil}}}}} // Test recursion capping
+		ms   = Nest{0, &Nest{1, &Nest{2, &Nest{3, &Nest{4, nil}}}}} // Test recursion capping on structs
+		ni   = []interface{}([]interface{}{[]interface{}{123}})     // Test recursion capping on interfaces
 		ba   = make([]int, 200, 200)                                // Test array size capping
 		mp   = map[int]interface{}{1: 42, 2: 43}
 	)
 
 	runtime.Breakpoint()
 	barfoo()
-	fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, b1, b2, baz, neg, i8, u8, u16, u32, u64, up, f32, c64, c128, i32, bar, f, ms, ba, p1, mp)
+	fmt.Println(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, b1, b2, baz, neg, i8, u8, u16, u32, u64, up, f32, c64, c128, i32, bar, f, ms, ni, ba, p1, mp)
 }
 
 var p1 = 10
diff --git a/_fixtures/testvariables2.go b/_fixtures/testvariables2.go
index 7efb06aa..a3a6deb7 100644
--- a/_fixtures/testvariables2.go
+++ b/_fixtures/testvariables2.go
@@ -144,6 +144,7 @@ func main() {
 	f1 := 3.0
 	i3 := 3
 	p1 := &i1
+	pp1 := &p1
 	s1 := []string{"one", "two", "three", "four", "five"}
 	s3 := make([]int, 0, 6)
 	a0 := [0]int{}
@@ -356,6 +357,9 @@ func main() {
 		fmt.Println(amb1)
 	}
 
+	longarr := [100]int{}
+	longslice := make([]int, 100, 100)
+
 	runtime.Breakpoint()
-	fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, bytearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5)
+	fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, bytearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5, longarr, longslice)
 }
diff --git a/_scripts/latestver.py b/_scripts/latestver.py
new file mode 100644
index 00000000..d4138136
--- /dev/null
+++ b/_scripts/latestver.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+import json
+import urllib
+import sys
+
+ver = sys.argv[1]
+d = json.loads(urllib.urlopen('https://golang.org/dl/?mode=json&include=all').read())
+for x in d:
+	if x['version'][:len(ver)] == ver:
+		print x['version']
+		exit(0)
diff --git a/_scripts/test_mac.sh b/_scripts/test_mac.sh
new file mode 100644
index 00000000..9ca17d89
--- /dev/null
+++ b/_scripts/test_mac.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+set -x
+set -e
+
+GOVERSION=$1
+ARCH=$2
+TMPDIR=$3
+
+if [ "$GOVERSION" = "gotip" ]; then
+    exit 0
+    bootstrapver=$(curl https://golang.org/VERSION?m=text)
+    curl -sSL "https://storage.googleapis.com/golang/$bootstrapver.darwin-$ARCH.tar.gz" | tar -xz
+    git clone https://go.googlesource.com/go $TMPDIR/go-tip
+    export GOROOT_BOOTSTRAP=$TMPDIR/go
+    export GOROOT=$TMPDIR/go-tip
+    cd $TMPDIR/go-tip/src
+    ./make.bash
+    cd -
+else
+    echo Finding latest patch version for $GOVERSION
+    GOVERSION=$(python _scripts/latestver.py $GOVERSION)
+    echo Go $GOVERSION on $ARCH
+    cd $TMPDIR
+    curl -sSL "https://storage.googleapis.com/golang/$GOVERSION.darwin-$ARCH.tar.gz" | tar -xz
+    cd -
+    export GOROOT="$TMPDIR/go"
+fi
+
+mkdir -p $TMPDIR/gopath
+
+export GOPATH="$TMPDIR/gopath"
+export GOARCH="$ARCH"
+export PATH="$GOROOT/bin:$PATH"
+go version
+
+make test
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 8a9874a6..3d52a509 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -170,13 +170,11 @@ func testOutput(t *testing.T, dlvbin, output string, delveCmds []string) (stdout
 
 	_, err = os.Stat(debugbin)
 	if err == nil {
-		if strings.ToLower(os.Getenv("TRAVIS")) == "true" && runtime.GOOS == "windows" {
-			// Sometimes delve on Travis on Windows can't remove the built binary before
-			// exiting and gets an "Access is denied" error when trying.
-			// Just ignore it.
-			// See: https://travis-ci.com/go-delve/delve/jobs/296325131
-			return
-		}
+		// Sometimes delve on Windows can't remove the built binary before
+		// exiting and gets an "Access is denied" error when trying.
+		// This used to make this test flaky. See: https://travis-ci.com/go-delve/delve/jobs/296325131)
+		// But we have now added a delay to gobuild.Remove. If this flakes again,
+		// adjust it. Leaving temporary files behind can be annoying to users.
 		t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", delveCmds, debugbin, stdout, stderr)
 		return
 	}
diff --git a/pkg/dwarf/frame/entries_test.go b/pkg/dwarf/frame/entries_test.go
index 30506380..593d4516 100644
--- a/pkg/dwarf/frame/entries_test.go
+++ b/pkg/dwarf/frame/entries_test.go
@@ -67,7 +67,7 @@ func BenchmarkFDEForPC(b *testing.B) {
 	if err != nil {
 		b.Fatal(err)
 	}
-	fdes := Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch())
+	fdes, _ := Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch(), 0)
 
 	for i := 0; i < b.N; i++ {
 		// bench worst case, exhaustive search
diff --git a/pkg/dwarf/frame/parser_test.go b/pkg/dwarf/frame/parser_test.go
index ce6c9e50..752ff3b5 100644
--- a/pkg/dwarf/frame/parser_test.go
+++ b/pkg/dwarf/frame/parser_test.go
@@ -14,6 +14,7 @@ func TestParseCIE(t *testing.T) {
 		common: &CommonInformationEntry{Length: 12},
 		length: 12,
 	}
+	ctx.totalLen = ctx.buf.Len()
 	_ = parseCIE(ctx)
 
 	common := ctx.common
@@ -53,6 +54,6 @@ func BenchmarkParse(b *testing.B) {
 
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch())
+		Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch(), 0)
 	}
 }
diff --git a/pkg/proc/dwarf_export_test.go b/pkg/proc/dwarf_export_test.go
index de8f505a..57f723fc 100644
--- a/pkg/proc/dwarf_export_test.go
+++ b/pkg/proc/dwarf_export_test.go
@@ -1,6 +1,21 @@
 package proc
 
+import "github.com/go-delve/delve/pkg/dwarf/op"
+
 // PackageVars returns bi.packageVars (for tests)
 func (bi *BinaryInfo) PackageVars() []packageVar {
 	return bi.packageVars
 }
+
+func NewCompositeMemory(p *Target, pieces []op.Piece) (*compositeMemory, error) {
+	regs, err := p.CurrentThread().Registers()
+	if err != nil {
+		return nil, err
+	}
+
+	arch := p.BinInfo().Arch
+	dwarfregs := arch.RegistersToDwarfRegisters(0, regs)
+	dwarfregs.ChangeFunc = p.CurrentThread().SetReg
+
+	return newCompositeMemory(p.Memory(), arch, dwarfregs, pieces)
+}
diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go
index 0ce6c83e..c7935dac 100644
--- a/pkg/proc/dwarf_expr_test.go
+++ b/pkg/proc/dwarf_expr_test.go
@@ -77,8 +77,17 @@ func (mem *fakeMemory) ReadMemory(data []byte, addr uint64) (int, error) {
 	return len(data), nil
 }
 
-func (mem *fakeMemory) WriteMemory(uint64, []byte) (int, error) {
-	return 0, fmt.Errorf("not implemented")
+func (mem *fakeMemory) WriteMemory(addr uint64, data []byte) (int, error) {
+	if uint64(addr) < mem.base {
+		return 0, fmt.Errorf("write out of bounds %d %#x", len(data), addr)
+	}
+	start := uint64(addr) - mem.base
+	end := uint64(len(data)) + start
+	if end > uint64(len(mem.data)) {
+		panic(fmt.Errorf("write out of bounds %d %#x", len(data), addr))
+	}
+	copy(mem.data[start:end], data)
+	return len(data), nil
 }
 
 func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64) {
@@ -93,13 +102,14 @@ func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64)
 	}
 }
 
-func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, testCases map[string]uint16, fn *proc.Function) *proc.EvalScope {
-	scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi}
+func fakeScope(mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, fn *proc.Function) *proc.EvalScope {
+	return &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi}
+}
+
+func dwarfExprCheck(t *testing.T, scope *proc.EvalScope, testCases map[string]uint16) {
 	for name, value := range testCases {
 		uintExprCheck(t, scope, name, uint64(value))
 	}
-
-	return scope
 }
 
 func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters {
@@ -137,7 +147,7 @@ func TestDwarfExprRegisters(t *testing.T) {
 	regs.Regs.Rax = uint64(testCases["a"])
 	regs.Regs.Rdx = uint64(testCases["c"])
 
-	dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn)
+	dwarfExprCheck(t, fakeScope(mem, dwarfRegisters(bi, &regs), bi, mainfn), testCases)
 }
 
 func TestDwarfExprComposite(t *testing.T) {
@@ -192,7 +202,17 @@ func TestDwarfExprComposite(t *testing.T) {
 	regs.Regs.Rcx = uint64(testCases["pair.k"])
 	regs.Regs.Rbx = uint64(testCases["n"])
 
-	scope := dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn)
+	dwarfRegs := dwarfRegisters(bi, &regs)
+	var changeCalls []string
+	dwarfRegs.ChangeFunc = func(regNum uint64, reg *op.DwarfRegister) error {
+		t.Logf("SetReg(%d, %x)", regNum, reg.Bytes)
+		changeCalls = append(changeCalls, fmt.Sprintf("%d - %x", regNum, reg.Bytes))
+		return nil
+	}
+
+	scope := fakeScope(mem, dwarfRegs, bi, mainfn)
+
+	dwarfExprCheck(t, scope, testCases)
 
 	thevar, err := scope.EvalExpression("s", normalLoadConfig)
 	assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
@@ -203,6 +223,29 @@ func TestDwarfExprComposite(t *testing.T) {
 			t.Errorf("expected value %q got %q", stringVal, v)
 		}
 	}
+
+	// Test writes to composite memory
+
+	assertNoError(scope.SetVariable("n", "47"), t, "SetVariable(n, 47)")
+	assertNoError(scope.SetVariable("pair.k", "12"), t, "SetVariable(pair.k, 12)")
+	assertNoError(scope.SetVariable("pair.v", "13"), t, "SetVariable(pair.v, 13)")
+
+	for i := range changeCalls {
+		t.Logf("%q\n", changeCalls[i])
+	}
+
+	if len(changeCalls) != 2 {
+		t.Errorf("wrong number of calls to SetReg")
+	}
+	if changeCalls[0] != "3 - 2f00000000000000" {
+		t.Errorf("wrong call to SetReg (Rbx)")
+	}
+	if changeCalls[1] != "2 - 0c00" {
+		t.Errorf("wrong call to SetReg (Rcx)")
+	}
+	if mem.data[0x10] != 13 || mem.data[0x11] != 0x00 {
+		t.Errorf("memory was not written %v", mem.data[:2])
+	}
 }
 
 func TestDwarfExprLoclist(t *testing.T) {
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index 4e7f0559..c25f7799 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -2,6 +2,7 @@ package proc_test
 
 import (
 	"bytes"
+	"encoding/binary"
 	"flag"
 	"fmt"
 	"go/ast"
@@ -836,6 +837,8 @@ func TestCGONext(t *testing.T) {
 	}
 	protest.MustHaveCgo(t)
 
+	skipOn(t, "broken - cgo stacktraces", "darwin", "arm64")
+
 	protest.AllowRecording(t)
 	withTestProcess("cgotest", t, func(p *proc.Target, fixture protest.Fixture) {
 		setFunctionBreakpoint(p, t, "main.main")
@@ -942,6 +945,8 @@ func stackMatch(stack []loc, locations []proc.Stackframe, skipRuntime bool) bool
 }
 
 func TestStacktraceGoroutine(t *testing.T) {
+	skipOn(t, "broken - cgo stacktraces", "darwin", "arm64")
+
 	mainStack := []loc{{14, "main.stacktraceme"}, {29, "main.main"}}
 	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
 		mainStack[0].line = 15
@@ -3289,9 +3294,8 @@ func TestCgoStacktrace(t *testing.T) {
 		}
 	}
 
-	if runtime.GOARCH == "386" {
-		t.Skip("cgo stacktraces not supported on i386 for now")
-	}
+	skipOn(t, "broken - cgo stacktraces", "386")
+	skipOn(t, "broken - cgo stacktraces", "arm64")
 	protest.MustHaveCgo(t)
 
 	// Tests that:
@@ -5170,3 +5174,90 @@ func TestDump(t *testing.T) {
 		}
 	})
 }
+
+func TestCompositeMemoryWrite(t *testing.T) {
+	if runtime.GOARCH != "amd64" {
+		t.Skip("only valid on amd64")
+	}
+	skipOn(t, "not implemented", "freebsd")
+	withTestProcess("fputest/", t, func(p *proc.Target, fixture protest.Fixture) {
+		getregs := func() (pc, rax, xmm1 uint64) {
+			regs, err := p.CurrentThread().Registers()
+			assertNoError(err, t, "Registers")
+			fmtregs, err := regs.Slice(true)
+			assertNoError(err, t, "register slice")
+
+			var xmm1buf []byte
+
+			for _, reg := range fmtregs {
+				switch strings.ToLower(reg.Name) {
+				case "rax":
+					rax = reg.Reg.Uint64Val
+				case "xmm1":
+					xmm1buf = reg.Reg.Bytes
+				}
+			}
+
+			xmm1 = binary.LittleEndian.Uint64(xmm1buf[:8])
+
+			return regs.PC(), rax, xmm1
+		}
+
+		getmem := func(mem proc.MemoryReader) uint64 {
+			buf := make([]byte, 8)
+			_, err := mem.ReadMemory(buf, 0xbeef0000)
+			assertNoError(err, t, "ReadMemory")
+			return binary.LittleEndian.Uint64(buf)
+		}
+
+		assertNoError(p.Continue(), t, "Continue()")
+		oldPc, oldRax, oldXmm1 := getregs()
+		t.Logf("PC %#x AX %#x XMM1 %#x", oldPc, oldRax, oldXmm1)
+
+		memRax, err := proc.NewCompositeMemory(p, []op.Piece{{Size: 0, RegNum: 0, IsRegister: true}})
+		assertNoError(err, t, "NewCompositeMemory (rax)")
+		memXmm1, err := proc.NewCompositeMemory(p, []op.Piece{{Size: 0, RegNum: 18, IsRegister: true}})
+		assertNoError(err, t, "NewCompositeMemory (xmm1)")
+
+		if memRax := getmem(memRax); memRax != oldRax {
+			t.Errorf("reading rax memory, expected %#x got %#x", oldRax, memRax)
+		}
+		if memXmm1 := getmem(memXmm1); memXmm1 != oldXmm1 {
+			t.Errorf("reading xmm1 memory, expected %#x got %#x", oldXmm1, memXmm1)
+		}
+
+		_, err = memRax.WriteMemory(0xbeef0000, []byte{0xef, 0xbe, 0x0d, 0xf0, 0xef, 0xbe, 0x0d, 0xf0})
+		assertNoError(err, t, "WriteMemory (rax)")
+		_, err = memXmm1.WriteMemory(0xbeef0000, []byte{0xef, 0xbe, 0x0d, 0xf0, 0xef, 0xbe, 0x0d, 0xf0})
+		assertNoError(err, t, "WriteMemory (xmm1)")
+
+		newPc, newRax, newXmm1 := getregs()
+		t.Logf("PC %#x AX %#x XMM1 %#x", newPc, newRax, newXmm1)
+
+		const tgt = 0xf00dbeeff00dbeef
+		if newRax != tgt {
+			t.Errorf("reading rax register, expected %#x, got %#x", uint64(tgt), newRax)
+		}
+		if newXmm1 != tgt {
+			t.Errorf("reading xmm1 register, expected %#x, got %#x", uint64(tgt), newXmm1)
+		}
+	})
+}
+
+func TestVariablesWithExternalLinking(t *testing.T) {
+	// Tests that macOSDebugFrameBugWorkaround works.
+	// See:
+	//  https://github.com/golang/go/issues/25841
+	//  https://github.com/go-delve/delve/issues/2346
+	withTestProcessArgs("testvariables2", t, ".", []string{}, protest.BuildModeExternalLinker, func(p *proc.Target, fixture protest.Fixture) {
+		assertNoError(p.Continue(), t, "Continue()")
+		str1Var := evalVariable(p, t, "str1")
+		if str1Var.Unreadable != nil {
+			t.Fatalf("variable str1 is unreadable: %v", str1Var.Unreadable)
+		}
+		t.Logf("%#v", str1Var)
+		if constant.StringVal(str1Var.Value) != "01234567890" {
+			t.Fatalf("wrong value for str1: %v", str1Var.Value)
+		}
+	})
+}
diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go
index 5669c98d..ea1f4ef9 100644
--- a/pkg/terminal/command_test.go
+++ b/pkg/terminal/command_test.go
@@ -690,9 +690,9 @@ func TestListCmd(t *testing.T) {
 		term.MustExec("continue")
 		term.MustExec("continue")
 		listIsAt(t, term, "list", 27, 22, 32)
-		listIsAt(t, term, "list 69", 69, 64, 73)
-		listIsAt(t, term, "frame 1 list", 65, 60, 70)
-		listIsAt(t, term, "frame 1 list 69", 69, 64, 73)
+		listIsAt(t, term, "list 69", 69, 64, 74)
+		listIsAt(t, term, "frame 1 list", 66, 61, 71)
+		listIsAt(t, term, "frame 1 list 69", 69, 64, 74)
 		_, err := term.Exec("frame 50 list")
 		if err == nil {
 			t.Fatalf("Expected error requesting 50th frame")
@@ -1140,3 +1140,10 @@ func TestParseNewArgv(t *testing.T) {
 		}
 	}
 }
+
+func TestContinueUntil(t *testing.T) {
+	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
+		listIsAt(t, term, "continue main.main", 16, -1, -1)
+		listIsAt(t, term, "continue main.sayhi", 12, -1, -1)
+	})
+}
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index aecf5520..d7882842 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"flag"
 	"io"
+	"io/ioutil"
 	"net"
 	"os"
 	"os/exec"
@@ -419,8 +420,8 @@ func TestPreSetBreakpoint(t *testing.T) {
 			t.Errorf("got %#v, want len(Breakpoints)=1", sResp)
 		}
 		bkpt0 := sResp.Body.Breakpoints[0]
-		if !bkpt0.Verified || bkpt0.Line != 8 {
-			t.Errorf("got breakpoints[0] = %#v, want Verified=true, Line=8", bkpt0)
+		if !bkpt0.Verified || bkpt0.Line != 8 || bkpt0.Id != 1 || bkpt0.Source.Name != filepath.Base(fixture.Source) || bkpt0.Source.Path != fixture.Source {
+			t.Errorf("got breakpoints[0] = %#v, want Verified=true, Line=8, Id=1, Path=%q", bkpt0, fixture.Source)
 		}
 
 		client.SetExceptionBreakpointsRequest()
@@ -750,7 +751,7 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					client.StackTraceRequest(1, 0, 20)
 					stack := client.ExpectStackTraceResponse(t)
 
-					startLineno := 65
+					startLineno := 66
 					if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) {
 						// Go1.15 on windows inserts a NOP after the call to
 						// runtime.Breakpoint and marks it same line as the
@@ -793,7 +794,7 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 
 					client.VariablesRequest(1001)
 					locals := client.ExpectVariablesResponse(t)
-					expectChildren(t, locals, "Locals", 30)
+					expectChildren(t, locals, "Locals", 31)
 
 					// reflect.Kind == Bool
 					expectVarExact(t, locals, -1, "b1", "b1", "true", noChildren)
@@ -1022,7 +1023,6 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 			// Breakpoints are set within the program
 			fixture.Source, []int{},
 			[]onBreakpoint{{
-				// Stop at line 317
 				execute: func() {
 					client.StackTraceRequest(1, 0, 20)
 					stack := client.ExpectStackTraceResponse(t)
@@ -1035,7 +1035,6 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 				},
 				disconnect: false,
 			}, {
-				// Stop at line 322
 				execute: func() {
 					client.StackTraceRequest(1, 0, 20)
 					stack := client.ExpectStackTraceResponse(t)
@@ -1139,6 +1138,20 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 						expectVarRegex(t, err1, 0, "data", `err1\.\(data\)`, `<\*main\.astruct>\(0x[0-9a-f]+\)`, hasChildren)
 						validateEvaluateName(t, client, err1, 0)
 					}
+					ref = expectVarRegex(t, locals, -1, "ptrinf", "ptrinf", `<\*interface {}>\(0x[0-9a-f]+\)`, hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						ptrinf_val := client.ExpectVariablesResponse(t)
+						expectChildren(t, ptrinf_val, "*ptrinf", 1)
+						ref = expectVarExact(t, ptrinf_val, 0, "", "(*ptrinf)", "<interface {}(**interface {})>", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							ptrinf_val_data := client.ExpectVariablesResponse(t)
+							expectChildren(t, ptrinf_val_data, "(*ptrinf).data", 1)
+							expectVarRegex(t, ptrinf_val_data, 0, "data", `\(\*ptrinf\)\.\(data\)`, `<\*\*interface {}>\(0x[0-9a-f]+\)`, hasChildren)
+							validateEvaluateName(t, client, ptrinf_val_data, 0)
+						}
+					}
 					// reflect.Kind == Map
 					expectVarExact(t, locals, -1, "mnil", "mnil", "nil <map[string]main.astruct>", noChildren)
 					// key - scalar, value - compound
@@ -1217,7 +1230,21 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 						}
 					}
 					expectVarExact(t, locals, -1, "emptymap", "emptymap", "<map[string]string> (length: 0)", noChildren)
-					// reflect.Kind == Ptr - see testvariables
+					// reflect.Kind == Ptr
+					ref = expectVarRegex(t, locals, -1, "pp1", "pp1", `<\*\*int>\(0x[0-9a-f]+\)`, hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						pp1val := client.ExpectVariablesResponse(t)
+						expectChildren(t, pp1val, "*pp1", 1)
+						ref = expectVarRegex(t, pp1val, 0, "", `\(\*pp1\)`, `<\*int>\(0x[0-9a-f]+\)`, hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							pp1valval := client.ExpectVariablesResponse(t)
+							expectChildren(t, pp1valval, "*(*pp1)", 1)
+							expectVarExact(t, pp1valval, 0, "", "(*(*pp1))", "1", noChildren)
+							validateEvaluateName(t, client, pp1valval, 0)
+						}
+					}
 					// reflect.Kind == Slice
 					ref = expectVarExact(t, locals, -1, "zsslice", "zsslice", "<[]struct {}> (length: 3, cap: 3)", hasChildren)
 					if ref > 0 {
@@ -1247,14 +1274,203 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 						expectVarRegex(t, val, 0, "^$", `\(\*unread\)`, "unreadable <.+>", noChildren)
 						validateEvaluateName(t, client, val, 0)
 					}
+				},
+				disconnect: true,
+			}})
+	})
+}
 
-					// Test that variables are not yet loaded completely.
-					ref = expectVarExact(t, locals, -1, "m1", "m1", "<map[string]main.astruct> (length: 66)", hasChildren)
+// TestVariablesLoading exposes test cases where variables might be partiall or
+// fully unloaded.
+func TestVariablesLoading(t *testing.T) {
+	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
+		runDebugSessionWithBPs(t, client, "launch",
+			// Launch
+			func() {
+				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
+			},
+			// Breakpoints are set within the program
+			fixture.Source, []int{},
+			[]onBreakpoint{{
+				execute:    func() {},
+				disconnect: false,
+			}, {
+				execute: func() {
+					// Change default config values to trigger certain unloaded corner cases
+					DefaultLoadConfig.MaxStructFields = 5
+					defer func() { DefaultLoadConfig.MaxStructFields = -1 }()
+
+					client.StackTraceRequest(1, 0, 0)
+					client.ExpectStackTraceResponse(t)
+
+					client.ScopesRequest(1000)
+					client.ExpectScopesResponse(t)
+
+					client.VariablesRequest(1001) // Locals
+					locals := client.ExpectVariablesResponse(t)
+
+					// String partially missing based on LoadConfig.MaxStringLen
+					expectVarExact(t, locals, -1, "longstr", "longstr", "\"very long string 0123456789a0123456789b0123456789c0123456789d012...+73 more\"", noChildren)
+
+					// Array partially missing based on LoadConfig.MaxArrayValues
+					ref := expectVarExact(t, locals, -1, "longarr", "longarr", "<[100]int> (length: 100, loaded: 64)", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						longarr := client.ExpectVariablesResponse(t)
+						expectChildren(t, longarr, "longarr", 64)
+					}
+
+					// Slice partially missing based on LoadConfig.MaxArrayValues
+					ref = expectVarExact(t, locals, -1, "longslice", "longslice", "<[]int> (length: 100, cap: 100, loaded: 64)", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						longarr := client.ExpectVariablesResponse(t)
+						expectChildren(t, longarr, "longslice", 64)
+					}
+
+					// Map partially missing based on LoadConfig.MaxArrayValues
+					ref = expectVarExact(t, locals, -1, "m1", "m1", "<map[string]main.astruct> (length: 66, loaded: 64)", hasChildren)
 					if ref > 0 {
 						client.VariablesRequest(ref)
 						m1 := client.ExpectVariablesResponse(t)
-						expectChildren(t, m1, "m1", 64) // TODO(polina): should be 66.
+						expectChildren(t, m1, "m1", 64)
+					}
+
+					// Struct partially missing based on LoadConfig.MaxStructFields
+					ref = expectVarExact(t, locals, -1, "sd", "sd", "<main.D> (fields: 6, loaded: 5)", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						sd := client.ExpectVariablesResponse(t)
+						expectChildren(t, sd, "sd", 5)
+					}
+
+					// Struct fully missing due to hitting LoadConfig.MaxVariableRecurse (also tests evaluateName)
+					ref = expectVarExact(t, locals, -1, "c1", "c1", "<main.cstruct>", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						c1 := client.ExpectVariablesResponse(t)
+						expectChildren(t, c1, "c1", 2)
+						ref = expectVarExact(t, c1, 1, "sa", "c1.sa", "<[]*main.astruct> (length: 3, cap: 3)", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							c1sa := client.ExpectVariablesResponse(t)
+							expectChildren(t, c1sa, "c1.sa", 3)
+							ref = expectVarRegex(t, c1sa, 0, `\[0\]`, `c1\.sa\[0\]`, `<\*main\.astruct>\(0x[0-9a-f]+\)`, hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								c1sa0 := client.ExpectVariablesResponse(t)
+								expectChildren(t, c1sa0, "c1.sa[0]", 1)
+								// TODO(polina): there should be children here once we support auto loading
+								expectVarExact(t, c1sa0, 0, "", "(*c1.sa[0])", "<main.astruct> (fields: 2, loaded: 0)", noChildren)
+							}
+						}
 					}
+
+					// Struct fully missing due to hitting LoadConfig.MaxVariableRecurse (also tests evaluteName)
+					ref = expectVarExact(t, locals, -1, "aas", "aas", "<[]main.a> (length: 1, cap: 1)", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						aas := client.ExpectVariablesResponse(t)
+						expectChildren(t, aas, "aas", 1)
+						ref = expectVarExact(t, aas, 0, "[0]", "aas[0]", "<main.a>", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							aas0 := client.ExpectVariablesResponse(t)
+							expectChildren(t, aas0, "aas[0]", 1)
+							ref = expectVarExact(t, aas0, 0, "aas", "aas[0].aas", "<[]main.a> (length: 1, cap: 1)", hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								aas0aas := client.ExpectVariablesResponse(t)
+								expectChildren(t, aas0aas, "aas[0].aas", 1)
+								// TODO(polina): there should be a child here once we support auto loading - test for "aas[0].aas[0].aas"
+								expectVarExact(t, aas0aas, 0, "[0]", "aas[0].aas[0]", "<main.a> (fields: 1, loaded: 0)", noChildren)
+							}
+						}
+					}
+
+					// Map fully missing due to hitting LoadConfig.MaxVariableRecurse (also tests evaluateName)
+					ref = expectVarExact(t, locals, -1, "tm", "tm", "<main.truncatedMap>", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						tm := client.ExpectVariablesResponse(t)
+						expectChildren(t, tm, "tm", 1)
+						ref = expectVarExact(t, tm, 0, "v", "tm.v", "<[]map[string]main.astruct> (length: 1, cap: 1)", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							tmV := client.ExpectVariablesResponse(t)
+							expectChildren(t, tmV, "tm.v", 1)
+							// TODO(polina): there should be children here once we support auto loading - test for "tm.v[0]["gutters"]"
+							expectVarExact(t, tmV, 0, "[0]", "tm.v[0]", "<map[string]main.astruct> (length: 66, loaded: 0)", noChildren)
+						}
+					}
+				},
+				disconnect: true,
+			}})
+	})
+	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
+		runDebugSessionWithBPs(t, client, "launch",
+			// Launch
+			func() {
+				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
+			},
+			// Breakpoints are set within the program
+			fixture.Source, []int{},
+			[]onBreakpoint{{
+				execute: func() {
+					DefaultLoadConfig.FollowPointers = false
+					defer func() { DefaultLoadConfig.FollowPointers = true }()
+
+					client.StackTraceRequest(1, 0, 0)
+					client.ExpectStackTraceResponse(t)
+
+					var loadvars = func(frame int) {
+						client.ScopesRequest(frame)
+						scopes := client.ExpectScopesResponse(t)
+						localsRef := 0
+						for _, s := range scopes.Body.Scopes {
+							if s.Name == "Locals" {
+								localsRef = s.VariablesReference
+							}
+						}
+
+						client.VariablesRequest(localsRef)
+						locals := client.ExpectVariablesResponse(t)
+
+						// Interface auto-loaded when hitting LoadConfig.MaxVariableRecurse=1
+
+						ref := expectVarExact(t, locals, -1, "ni", "ni", "<[]interface {}> (length: 1, cap: 1)", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							ni := client.ExpectVariablesResponse(t)
+							ref = expectVarExact(t, ni, 0, "[0]", "ni[0]", "<interface {}([]interface {})>", hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								niI1 := client.ExpectVariablesResponse(t)
+								ref = expectVarExact(t, niI1, 0, "data", "ni[0].(data)", "<[]interface {}> (length: 1, cap: 1)", hasChildren)
+								if ref > 0 {
+									// Auto-loading happens here
+									client.VariablesRequest(ref)
+									niI1Data := client.ExpectVariablesResponse(t)
+									ref = expectVarExact(t, niI1Data, 0, "[0]", "ni[0].(data)[0]", "<interface {}(int)>", hasChildren)
+									if ref > 0 {
+										client.VariablesRequest(ref)
+										niI1DataI2 := client.ExpectVariablesResponse(t)
+										expectVarExact(t, niI1DataI2, 0, "data", "ni[0].(data)[0].(data)", "123", noChildren)
+									}
+								}
+							}
+						}
+
+						// Pointer value not loaded with LoadConfig.FollowPointers=false
+						expectVarRegex(t, locals, -1, "a7", "a7", `<\*main.FooBar>\(0x[0-9a-f]+\) \(value not loaded\)`, noChildren)
+					}
+					loadvars(1000 /*first topmost frame*/)
+					// step into another function
+					client.StepInRequest(1)
+					client.ExpectStepInResponse(t)
+					client.ExpectStoppedEvent(t)
+					handleStop(t, client, 1, "main.barfoo", 24)
+					loadvars(1001 /*second frame here is same as topmost above*/)
 				},
 				disconnect: true,
 			}})
@@ -1371,6 +1587,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					type Breakpoint struct {
 						line      int
+						path      string
 						verified  bool
 						msgPrefix string
 					}
@@ -1382,7 +1599,7 @@ func TestSetBreakpoint(t *testing.T) {
 							return
 						}
 						for i, bp := range got.Body.Breakpoints {
-							if bp.Line != bps[i].line || bp.Verified != bps[i].verified ||
+							if bp.Line != bps[i].line || bp.Verified != bps[i].verified || bp.Source.Path != bps[i].path ||
 								!strings.HasPrefix(bp.Message, bps[i].msgPrefix) {
 								t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i])
 							}
@@ -1391,11 +1608,11 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Set two breakpoints at the next two lines in main
 					client.SetBreakpointsRequest(fixture.Source, []int{17, 18})
-					expectSetBreakpointsResponse([]Breakpoint{{17, true, ""}, {18, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{17, fixture.Source, true, ""}, {18, fixture.Source, true, ""}})
 
 					// Clear 17, reset 18
 					client.SetBreakpointsRequest(fixture.Source, []int{18})
-					expectSetBreakpointsResponse([]Breakpoint{{18, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{18, fixture.Source, true, ""}})
 
 					// Skip 17, continue to 18
 					client.ContinueRequest(1)
@@ -1405,7 +1622,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Set another breakpoint inside the loop in loop(), twice to trigger error
 					client.SetBreakpointsRequest(fixture.Source, []int{8, 8})
-					expectSetBreakpointsResponse([]Breakpoint{{8, true, ""}, {8, false, "Breakpoint exists"}})
+					expectSetBreakpointsResponse([]Breakpoint{{8, fixture.Source, true, ""}, {8, "", false, "Breakpoint exists"}})
 
 					// Continue into the loop
 					client.ContinueRequest(1)
@@ -1418,7 +1635,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Edit the breakpoint to add a condition
 					client.SetConditionalBreakpointsRequest(fixture.Source, []int{8}, map[int]string{8: "i == 3"})
-					expectSetBreakpointsResponse([]Breakpoint{{8, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{8, fixture.Source, true, ""}})
 
 					// Continue until condition is hit
 					client.ContinueRequest(1)
@@ -1431,7 +1648,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Edit the breakpoint to remove a condition
 					client.SetConditionalBreakpointsRequest(fixture.Source, []int{8}, map[int]string{8: ""})
-					expectSetBreakpointsResponse([]Breakpoint{{8, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{8, fixture.Source, true, ""}})
 
 					// Continue for one more loop iteration
 					client.ContinueRequest(1)
@@ -1444,7 +1661,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Set at a line without a statement
 					client.SetBreakpointsRequest(fixture.Source, []int{1000})
-					expectSetBreakpointsResponse([]Breakpoint{{1000, false, "could not find statement"}}) // all cleared, none set
+					expectSetBreakpointsResponse([]Breakpoint{{1000, "", false, "could not find statement"}}) // all cleared, none set
 				},
 				// The program has an infinite loop, so we must kill it by disconnecting.
 				disconnect: true,
@@ -1474,7 +1691,7 @@ func TestEvaluateRequest(t *testing.T) {
 			fixture.Source, []int{}, // Breakpoint set in the program
 			[]onBreakpoint{{ // Stop at first breakpoint
 				execute: func() {
-					handleStop(t, client, 1, "main.foobar", 65)
+					handleStop(t, client, 1, "main.foobar", 66)
 
 					// Variable lookup
 					client.EvaluateRequest("a2", 1000, "this context will be ignored")
@@ -1494,6 +1711,11 @@ func TestEvaluateRequest(t *testing.T) {
 						validateEvaluateName(t, client, a5, 4)
 					}
 
+					// Variable lookup that's not fully loaded
+					client.EvaluateRequest("ba", 1000, "this context will be ignored")
+					got = client.ExpectEvaluateResponse(t)
+					expectEval(t, got, "<[]int> (length: 200, cap: 200, loaded: 64)", hasChildren)
+
 					// All (binary and unary) on basic types except <-, ++ and --
 					client.EvaluateRequest("1+1", 1000, "this context will be ignored")
 					got = client.ExpectEvaluateResponse(t)
@@ -1512,14 +1734,8 @@ func TestEvaluateRequest(t *testing.T) {
 					// Type casts of integer constants into any pointer type and vice versa
 					client.EvaluateRequest("(*int)(2)", 1000, "this context will be ignored")
 					got = client.ExpectEvaluateResponse(t)
-					ref = expectEval(t, got, "<*int>(0x2)", hasChildren)
-					if ref > 0 {
-						client.VariablesRequest(ref)
-						expr := client.ExpectVariablesResponse(t)
-						expectChildren(t, expr, "(*int)(2)", 1)
-						// TODO(polina): should this be printed as (unknown int) instead?
-						expectVarExact(t, expr, 0, "", "(*((*int)(2)))", "<int>", noChildren)
-					}
+					expectEval(t, got, "<*int>(0x2) (value not loaded)", noChildren)
+
 					// Type casts between string, []byte and []rune
 					client.EvaluateRequest("[]byte(\"ABC€\")", 1000, "this context will be ignored")
 					got = client.ExpectEvaluateResponse(t)
@@ -2040,13 +2256,59 @@ func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdReques
 }
 
 func TestLaunchDebugRequest(t *testing.T) {
+	rescueStderr := os.Stderr
+	r, w, _ := os.Pipe()
+	os.Stderr = w
+
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// We reuse the harness that builds, but ignore the built binary,
 		// only relying on the source to be built in response to LaunchRequest.
+		runDebugSession(t, client, "launch", func() {
+			client.LaunchRequestWithArgs(map[string]interface{}{
+				"mode": "debug", "program": fixture.Source, "output": "__mydir"})
+		}, fixture.Source)
+	})
+	// Wait for the test to finish to capture all stderr
+	time.Sleep(100 * time.Millisecond)
+
+	w.Close()
+	err, _ := ioutil.ReadAll(r)
+	t.Log(string(err))
+	os.Stderr = rescueStderr
+
+	rmErrRe, _ := regexp.Compile(`could not remove .*\n`)
+	rmErr := rmErrRe.FindString(string(err))
+	if rmErr != "" {
+		// On Windows, a file in use cannot be removed, resulting in "Access is denied".
+		// When the process exits, Delve releases the binary by calling
+		// BinaryInfo.Close(), but it appears that it is still in use (by Windows?)
+		// shortly after. gobuild.Remove has a delay to address this.
+		// If this test becomes flaky, see if the delay needs adjusting.
+		t.Fatalf("Binary removal failure:\n%s\n", rmErr)
+	}
+}
+
+// TestLaunchRequestDefaults tests defaults for launch attribute that are explicit in other tests.
+func TestLaunchRequestDefaults(t *testing.T) {
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		runDebugSession(t, client, "launch", func() {
+			client.LaunchRequestWithArgs(map[string]interface{}{
+				"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mydir"})
+		}, fixture.Source)
+	})
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		runDebugSession(t, client, "launch", func() {
+			// Use the default output directory.
+			client.LaunchRequestWithArgs(map[string]interface{}{
+				/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mydir"})
+		}, fixture.Source)
+	})
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		runDebugSession(t, client, "launch", func() {
 			// Use the default output directory.
 			client.LaunchRequestWithArgs(map[string]interface{}{
 				"mode": "debug", "program": fixture.Source})
+			// writes to default output dir __debug_bin
 		}, fixture.Source)
 	})
 }
@@ -2088,7 +2350,7 @@ func TestLaunchRequestWithBuildFlags(t *testing.T) {
 			// We reuse the harness that builds, but ignore the built binary,
 			// only relying on the source to be built in response to LaunchRequest.
 			client.LaunchRequestWithArgs(map[string]interface{}{
-				"mode": "debug", "program": fixture.Source,
+				"mode": "debug", "program": fixture.Source, "output": "__mydir",
 				"buildFlags": "-ldflags '-X main.Hello=World'"})
 		}, fixture.Source)
 	})
@@ -2114,7 +2376,8 @@ func TestAttachRequest(t *testing.T) {
 		runDebugSessionWithBPs(t, client, "attach",
 			// Attach
 			func() {
-				client.AttachRequest(map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid, "stopOnEntry": false})
+				client.AttachRequest(map[string]interface{}{
+					/*"mode": "local" by default*/ "processId": cmd.Process.Pid, "stopOnEntry": false})
 			},
 			// Set breakpoints
 			fixture.Source, []int{8},
@@ -2282,18 +2545,20 @@ func TestBadLaunchRequests(t *testing.T) {
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
-		client.LaunchRequestWithArgs(map[string]interface{}{"program": 12345})
+		// Bad "program"
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": 12345})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
-		client.LaunchRequestWithArgs(map[string]interface{}{"program": nil})
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": nil})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
-		client.LaunchRequestWithArgs(map[string]interface{}{})
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug"})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
+		// Bad "mode"
 		client.LaunchRequest("remote", fixture.Path, stopOnEntry)
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: Unsupported 'mode' value \"remote\" in debug configuration.")
@@ -2306,6 +2571,15 @@ func TestBadLaunchRequests(t *testing.T) {
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: Unsupported 'mode' value %!q(float64=12345) in debug configuration.")
 
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": ""}) // empty mode defaults to "debug" (not an error)
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+			"Failed to launch: The program attribute is missing in debug configuration.")
+
+		client.LaunchRequestWithArgs(map[string]interface{}{}) // missing mode defaults to "debug" (not an error)
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+			"Failed to launch: The program attribute is missing in debug configuration.")
+
+		// Bad "args"
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": nil})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: 'args' attribute '<nil>' in debug configuration is not an array.")
@@ -2318,22 +2592,26 @@ func TestBadLaunchRequests(t *testing.T) {
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: value '1' in 'args' attribute in debug configuration is not a string.")
 
+		// Bad "buildFlags"
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": 123})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
 			"Failed to launch: 'buildFlags' attribute '123' in debug configuration is not a string.")
 
 		// Skip detailed message checks for potentially different OS-specific errors.
 		client.LaunchRequest("exec", fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunch(client.ExpectErrorResponse(t))
+		expectFailedToLaunch(client.ExpectErrorResponse(t)) // No such file or directory
 
 		client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunch(client.ExpectErrorResponse(t)) // Build error
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
+
+		client.LaunchRequest("" /*debug by default*/, fixture.Path+"_does_not_exist", stopOnEntry)
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
 
 		client.LaunchRequest("exec", fixture.Source, stopOnEntry)
 		expectFailedToLaunch(client.ExpectErrorResponse(t)) // Not an executable
 
-		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "123"})
-		expectFailedToLaunch(client.ExpectErrorResponse(t)) // Build error
+		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "bad flags"})
+		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t), "Failed to launch: Build error: exit status 1")
 
 		// We failed to launch the program. Make sure shutdown still works.
 		client.DisconnectRequest()
@@ -2372,6 +2650,7 @@ func TestBadAttachRequest(t *testing.T) {
 			}
 		}
 
+		// Bad "mode"
 		client.AttachRequest(map[string]interface{}{"mode": "remote"})
 		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
 			"Failed to attach: Unsupported 'mode' value \"remote\" in debug configuration")
@@ -2384,28 +2663,37 @@ func TestBadAttachRequest(t *testing.T) {
 		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
 			"Failed to attach: Unsupported 'mode' value %!q(float64=123) in debug configuration")
 
-		client.AttachRequest(map[string]interface{}{"mode": ""}) // defaults to "local"
+		client.AttachRequest(map[string]interface{}{"mode": ""}) // empty mode defaults to "local" (not an error)
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+
+		client.AttachRequest(map[string]interface{}{}) // no mode defaults to "local" (not an error)
+		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+			"Failed to attach: The 'processId' attribute is missing in debug configuration")
+
+		// Bad "processId"
+		client.AttachRequest(map[string]interface{}{"mode": "local"})
 		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
-		client.AttachRequest(map[string]interface{}{}) // defaults to "local"
+		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": nil})
 		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
-		client.AttachRequest(map[string]interface{}{"processId": 0}) // defaults to "local"
+		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 0})
 		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
-		client.AttachRequest(map[string]interface{}{"processId": "1"}) // defaults to "local"
+		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": "1"})
 		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
-		client.AttachRequest(map[string]interface{}{"processId": 1}) // defaults to "local"
+		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1})
 		// The exact message varies on different systems, so skip that check
 		expectFailedToAttach(client.ExpectErrorResponse(t)) // could not attach to pid 1
 
 		// This will make debugger.(*Debugger) panic, which we will catch as an internal error.
-		client.AttachRequest(map[string]interface{}{"processId": -1}) // defaults to "local"
+		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": -1})
 		er := client.ExpectErrorResponse(t)
 		if er.RequestSeq != seqCnt {
 			t.Errorf("RequestSeq got %d, want %d", seqCnt, er.RequestSeq)
diff --git a/service/test/integration1_test.go b/service/test/integration1_test.go
index 8b5afdab..bde8f9fd 100644
--- a/service/test/integration1_test.go
+++ b/service/test/integration1_test.go
@@ -716,6 +716,9 @@ func Test1ClientServer_SetVariable(t *testing.T) {
 }
 
 func Test1ClientServer_FullStacktrace(t *testing.T) {
+	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+		t.Skip("cgo doesn't work on darwin/arm64")
+	}
 	withTestClient1("goroutinestackprog", t, func(c *rpc1.RPCClient) {
 		_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1})
 		assertNoError(err, t, "CreateBreakpoint()")
@@ -728,7 +731,7 @@ func Test1ClientServer_FullStacktrace(t *testing.T) {
 		assertNoError(err, t, "GoroutinesInfo()")
 		found := make([]bool, 10)
 		for _, g := range gs {
-			frames, err := c.Stacktrace(g.ID, 10, true)
+			frames, err := c.Stacktrace(g.ID, 40, true)
 			assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
 			t.Logf("goroutine %d", g.ID)
 			for i, frame := range frames {
diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go
index e0468a6b..270b1bd7 100644
--- a/service/test/integration2_test.go
+++ b/service/test/integration2_test.go
@@ -911,6 +911,9 @@ func TestClientServer_SetVariable(t *testing.T) {
 
 func TestClientServer_FullStacktrace(t *testing.T) {
 	protest.AllowRecording(t)
+	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+		t.Skip("cgo doesn't work on darwin/arm64")
+	}
 	withTestClient2("goroutinestackprog", t, func(c service.Client) {
 		_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.stacktraceme", Line: -1})
 		assertNoError(err, t, "CreateBreakpoint()")
@@ -923,7 +926,7 @@ func TestClientServer_FullStacktrace(t *testing.T) {
 		assertNoError(err, t, "GoroutinesInfo()")
 		found := make([]bool, 10)
 		for _, g := range gs {
-			frames, err := c.Stacktrace(g.ID, 10, 0, &normalLoadConfig)
+			frames, err := c.Stacktrace(g.ID, 40, 0, &normalLoadConfig)
 			assertNoError(err, t, fmt.Sprintf("Stacktrace(%d)", g.ID))
 			t.Logf("goroutine %d", g.ID)
 			for i, frame := range frames {
diff --git a/service/test/variables_test.go b/service/test/variables_test.go
index 64ec5dc9..c2087930 100644
--- a/service/test/variables_test.go
+++ b/service/test/variables_test.go
@@ -446,6 +446,7 @@ func TestLocalVariables(t *testing.T) {
 				{"mp", true, "map[int]interface {} [1: 42, 2: 43, ]", "", "map[int]interface {}", nil},
 				{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)…", "", "main.Nest", nil},
 				{"neg", true, "-1", "", "int", nil},
+				{"ni", true, "[]interface {} len: 1, cap: 1, [[]interface {} len: 1, cap: 1, [*(*interface {})…", "", "[]interface {}", nil},
 				{"u16", true, "65535", "", "uint16", nil},
 				{"u32", true, "4294967295", "", "uint32", nil},
 				{"u64", true, "18446744073709551615", "", "uint64", nil},
@@ -1564,6 +1565,10 @@ func TestCgoEval(t *testing.T) {
 		{"v_align_check[90]", false, "align_check {a: 90, b: 90}", "align_check {a: 90, b: 90}", "align_check", nil},
 	}
 
+	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
+		t.Skip("cgo doesn't work on darwin/arm64")
+	}
+
 	protest.AllowRecording(t)
 	withTestProcess("testvariablescgo/", t, func(p *proc.Target, fixture protest.Fixture) {
 		assertNoError(p.Continue(), t, "Continue() returned an error")