Repository: go-delve/delve
Pull request #1919 (2020-03-10 00:43:51)

*: Remove appveyor, use travis windows support
Travis has windows CI support, so drop Appveyor and just use Travis instead. Takes our CI system load from 3 to 2.

aarzilli: No sure why it's failing on windows on travis-ci

Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index a5727d0f..d7d0cfb3 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,9 +1,28 @@
-env:
-  GOFLAGS: -mod=vendor
+freebsd_task:
+  env:
+    GOFLAGS: -mod=vendor
+  
+  freebsd_instance:
+    image: freebsd-11-2-release-amd64
 
-freebsd_instance:
-  image: freebsd-11-2-release-amd64
-
-test_task:
   install_script: pkg install -y go gcc git
   test_script: make test
+
+linux386_task:
+  container:
+    image: i386/ubuntu:18.04
+  env:
+    matrix:
+      - GOVERSION: 1.12
+      - GOVERSION: 1.13
+      - GOVERSION: 1.14
+  test_script:
+    -  apt-get -y update
+    -  apt-get -y install software-properties-common
+    -  apt-get -y install git
+    -  add-apt-repository ppa:longsleep/golang-backports
+    -  apt-get -y install golang-${GOVERSION}-go
+    -  export PATH=$PATH:/usr/lib/go-${GOVERSION}/bin
+    -  go version
+    -  uname -a
+    -  make test
diff --git a/.travis.yml b/.travis.yml
index 8cfabac3..13b96bfe 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,7 @@ sudo: required
 os:
   - linux
   - osx
+  - windows
 
 arch:
   - amd64
@@ -21,8 +22,26 @@ matrix:
   exclude:
     - os: osx
       arch: arm64
+    - os: windows
+      arch: arm64
+    - os: windows
+      go: 1.13.x
+    - os: windows
+      go: 1.12.x
+    - os: osx
+      go: 1.13.x
+    - os: osx
+      go: 1.12.x
+    - arch: arm64
+      go: 1.13.x
+    - arch: arm64
+      go: 1.12.x
 
 before_install:
   - export GOFLAGS=-mod=vendor
   - if [ $TRAVIS_OS_NAME = "linux" ]; then sudo apt-get -qq update; sudo apt-get install -y dwz; echo "dwz version $(dwz --version)"; fi
+  - if [ $TRAVIS_OS_NAME = "windows" ]; then choco install procdump make; fi
   
+cache:
+  directories:
+    - $HOME/AppData/Local/Temp/chocolatey
diff --git a/Documentation/KnownBugs.md b/Documentation/KnownBugs.md
index f7e56331..9aa24cbb 100644
--- a/Documentation/KnownBugs.md
+++ b/Documentation/KnownBugs.md
@@ -3,3 +3,4 @@
 - Delve does not currently support 32bit systems. This will usually manifest as a compiler error in `proc/disasm.go`. See [Issue #20](https://github.com/go-delve/delve/issues/20).
 - When Delve is compiled with versions of go prior to 1.7.0 it is not possible to set a breakpoint on a function in a remote package using the `Receiver.MethodName` syntax. See [Issue #528](https://github.com/go-delve/delve/issues/528).
 - When running Delve on binaries compiled with a version of go prior to 1.9.0 `locals` will print all local variables, including ones that are out of scope, the shadowed flag will be applied arbitrarily. If there are multiple variables defined with the same name in the current function `print` will not be able to select the correct one for the current line.
+- `reverse step` will not reverse step into functions called by deferred calls.
diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md
index 11314e86..a8dec03d 100644
--- a/Documentation/cli/README.md
+++ b/Documentation/cli/README.md
@@ -14,6 +14,7 @@ Command | Description
 [continue](#continue) | Run until breakpoint or program termination.
 [next](#next) | Step over to next source line.
 [restart](#restart) | Restart process from a checkpoint or event.
+[rev](#rev) | Reverses the execution of the target program for the command specified.
 [rewind](#rewind) | Run backwards until breakpoint or program termination.
 [step](#step) | Single step through program.
 [step-instruction](#step-instruction) | Single step a single cpu instruction.
@@ -83,7 +84,6 @@ Command | Description
 [help](#help) | Prints the help message.
 [libraries](#libraries) | List loaded dynamic libraries
 [list](#list) | Show source code.
-[rev](#rev) | Reverses the execution of the target program for the command specified.
 [source](#source) | Executes a file containing a list of delve commands
 [sources](#sources) | Print list of source files.
 [types](#types) | Print list of types
@@ -301,7 +301,7 @@ Aliases: gr
 ## goroutines
 List program goroutines.
 
-	goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [ -t (stack trace)]
+	goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [-t (stack trace)] [-l (labels)]
 
 Print out info for every goroutine. The flag controls what information is shown along with each goroutine:
 
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index b827b89a..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-version: '{build}'
-os: Windows Server 2012 R2
-clone_folder: c:\gopath\src\github.com\go-delve\delve
-environment:
-  GOPATH: C:\gopath
-install:
-  - ps: |
-      # Install MinGW.
-      if (-Not (Test-Path "C:\mingw64")) {
-              $file = "x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z"
-              $url  = "https://bintray.com/artifact/download/drewwells/generic/"
-              $url += $file
-              Invoke-WebRequest -UserAgent wget -Uri $url -OutFile $file
-              &7z x -oC:\ $file > $null
-      }
-      # Install Procdump
-      if (-Not (Test-Path "C:\procdump")) {
-              mkdir c:\procdump
-              Invoke-WebRequest -UserAgent wget -Uri https://download.sysinternals.com/files/Procdump.zip -OutFile C:\procdump\procdump.zip
-              &7z x -oC:\procdump\ C:\procdump\procdump.zip > $null
-      }
-  - set PATH=c:\procdump;c:\mingw64\bin;%GOPATH%\bin;%PATH%
-  - echo %PATH%
-  - echo %GOPATH%
-  - go version
-  - go env
-cache:
-  - C:\mingw64
-  - C:\procdump
-build_script:
-- mingw32-make install
-test_script:
-- mingw32-make test
diff --git a/pkg/dwarf/frame/parser.go b/pkg/dwarf/frame/parser.go
index f0b1f920..6d12dad2 100644
--- a/pkg/dwarf/frame/parser.go
+++ b/pkg/dwarf/frame/parser.go
@@ -20,15 +20,16 @@ type parseContext struct {
 	common  *CommonInformationEntry
 	frame   *FrameDescriptionEntry
 	length  uint32
+	ptrSize int
 }
 
 // Parse takes in data (a byte slice) and returns a slice of
 // commonInformationEntry structures. Each commonInformationEntry
 // has a slice of frameDescriptionEntry structures.
-func Parse(data []byte, order binary.ByteOrder, staticBase uint64) FrameDescriptionEntries {
+func Parse(data []byte, order binary.ByteOrder, staticBase uint64, ptrSize int) FrameDescriptionEntries {
 	var (
 		buf  = bytes.NewBuffer(data)
-		pctx = &parseContext{buf: buf, entries: NewFrameIndex(), staticBase: staticBase}
+		pctx = &parseContext{buf: buf, entries: NewFrameIndex(), staticBase: staticBase, ptrSize: ptrSize}
 	)
 
 	for fn := parselength; buf.Len() != 0; {
@@ -68,10 +69,14 @@ func parselength(ctx *parseContext) parsefunc {
 }
 
 func parseFDE(ctx *parseContext) parsefunc {
+	var num uint64
 	r := ctx.buf.Next(int(ctx.length))
 
-	ctx.frame.begin = binary.LittleEndian.Uint64(r[:8]) + ctx.staticBase
-	ctx.frame.size = binary.LittleEndian.Uint64(r[8:16])
+	reader := bytes.NewReader(r)
+	num, _ = util.ReadUintRaw(reader, binary.LittleEndian, ctx.ptrSize)
+	ctx.frame.begin = num + ctx.staticBase
+	num, _ = util.ReadUintRaw(reader, binary.LittleEndian, ctx.ptrSize)
+	ctx.frame.size = num
 
 	// Insert into the tree after setting address range begin
 	// otherwise compares won't work.
@@ -80,7 +85,7 @@ func parseFDE(ctx *parseContext) parsefunc {
 	// 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[16:]
+	ctx.frame.Instructions = r[2*ctx.ptrSize:]
 	ctx.length = 0
 
 	return parselength
diff --git a/pkg/dwarf/line/line_parser.go b/pkg/dwarf/line/line_parser.go
index d0b9e901..d0761993 100644
--- a/pkg/dwarf/line/line_parser.go
+++ b/pkg/dwarf/line/line_parser.go
@@ -41,6 +41,7 @@ type DebugLineInfo struct {
 
 	// if normalizeBackslash is true all backslashes (\) will be converted into forward slashes (/)
 	normalizeBackslash bool
+	ptrSize int
 }
 
 type FileEntry struct {
@@ -53,7 +54,7 @@ type FileEntry struct {
 type DebugLines []*DebugLineInfo
 
 // ParseAll parses all debug_line segments found in data
-func ParseAll(data []byte, logfn func(string, ...interface{}), staticBase uint64, normalizeBackslash bool) DebugLines {
+func ParseAll(data []byte, logfn func(string, ...interface{}), staticBase uint64, normalizeBackslash bool, ptrSize int) DebugLines {
 	var (
 		lines = make(DebugLines, 0)
 		buf   = bytes.NewBuffer(data)
@@ -61,7 +62,7 @@ func ParseAll(data []byte, logfn func(string, ...interface{}), staticBase uint64
 
 	// We have to parse multiple file name tables here.
 	for buf.Len() > 0 {
-		lines = append(lines, Parse("", buf, logfn, staticBase, normalizeBackslash))
+		lines = append(lines, Parse("", buf, logfn, staticBase, normalizeBackslash, ptrSize))
 	}
 
 	return lines
@@ -69,10 +70,11 @@ func ParseAll(data []byte, logfn func(string, ...interface{}), staticBase uint64
 
 // Parse parses a single debug_line segment from buf. Compdir is the
 // DW_AT_comp_dir attribute of the associated compile unit.
-func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{}), staticBase uint64, normalizeBackslash bool) *DebugLineInfo {
+func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{}), staticBase uint64, normalizeBackslash bool, ptrSize int) *DebugLineInfo {
 	dbl := new(DebugLineInfo)
 	dbl.Logf = logfn
 	dbl.staticBase = staticBase
+	dbl.ptrSize = ptrSize
 	dbl.Lookup = make(map[string]*FileEntry)
 	dbl.IncludeDirs = append(dbl.IncludeDirs, compdir)
 
diff --git a/pkg/dwarf/line/state_machine.go b/pkg/dwarf/line/state_machine.go
index cb041544..b92f8849 100644
--- a/pkg/dwarf/line/state_machine.go
+++ b/pkg/dwarf/line/state_machine.go
@@ -46,6 +46,7 @@ type StateMachine struct {
 	lastAddress uint64
 	lastFile    string
 	lastLine    int
+	ptrSize     int
 }
 
 type opcodeKind uint8
@@ -102,13 +103,23 @@ var extendedopcodes = map[byte]opcodefn{
 	DW_LINE_define_file:  definefile,
 }
 
-func newStateMachine(dbl *DebugLineInfo, instructions []byte) *StateMachine {
+func newStateMachine(dbl *DebugLineInfo, instructions []byte, ptrSize int) *StateMachine {
 	opcodes := make([]opcodefn, len(standardopcodes)+1)
 	opcodes[0] = execExtendedOpcode
 	for op := range standardopcodes {
 		opcodes[op] = standardopcodes[op]
 	}
-	sm := &StateMachine{dbl: dbl, file: dbl.FileNames[0].Path, line: 1, buf: bytes.NewBuffer(instructions), opcodes: opcodes, isStmt: dbl.Prologue.InitialIsStmt == uint8(1), address: dbl.staticBase, lastAddress: ^uint64(0)}
+	sm := &StateMachine{
+		dbl:         dbl,
+		file:        dbl.FileNames[0].Path,
+		line:        1,
+		buf:         bytes.NewBuffer(instructions),
+		opcodes:     opcodes,
+		isStmt:      dbl.Prologue.InitialIsStmt == uint8(1),
+		address:     dbl.staticBase,
+		lastAddress: ^uint64(0),
+		ptrSize:     ptrSize,
+	}
 	return sm
 }
 
@@ -121,7 +132,7 @@ func (lineInfo *DebugLineInfo) AllPCsForFileLines(f string, m map[int][]uint64)
 
 	var (
 		lastAddr uint64
-		sm       = newStateMachine(lineInfo, lineInfo.Instructions)
+		sm       = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
 	)
 
 	for {
@@ -153,7 +164,7 @@ func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64, excludeFile stri
 	var (
 		pcs      []uint64
 		lastaddr uint64
-		sm       = newStateMachine(lineInfo, lineInfo.Instructions)
+		sm       = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
 	)
 
 	for {
@@ -189,7 +200,7 @@ func (sm *StateMachine) copy() *StateMachine {
 func (lineInfo *DebugLineInfo) stateMachineForEntry(basePC uint64) (sm *StateMachine) {
 	sm = lineInfo.stateMachineCache[basePC]
 	if sm == nil {
-		sm = newStateMachine(lineInfo, lineInfo.Instructions)
+		sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
 		sm.PCToLine(basePC)
 		lineInfo.stateMachineCache[basePC] = sm
 	}
@@ -219,7 +230,7 @@ func (lineInfo *DebugLineInfo) PCToLine(basePC, pc uint64) (string, int) {
 func (lineInfo *DebugLineInfo) stateMachineFor(basePC, pc uint64) *StateMachine {
 	var sm *StateMachine
 	if basePC == 0 {
-		sm = newStateMachine(lineInfo, lineInfo.Instructions)
+		sm = newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
 	} else {
 		// Try to use the last state machine that we used for this function, if
 		// there isn't one or it's already past pc try to clone the cached state
@@ -274,7 +285,7 @@ func (lineInfo *DebugLineInfo) LineToPC(filename string, lineno int) uint64 {
 		return 0
 	}
 
-	sm := newStateMachine(lineInfo, lineInfo.Instructions)
+	sm := newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
 
 	// if no instruction marked is_stmt is found fallback to the first
 	// instruction assigned to the filename:line.
@@ -393,7 +404,7 @@ func (lineInfo *DebugLineInfo) FirstStmtForLine(start, end uint64) (pc uint64, f
 }
 
 func (lineInfo *DebugLineInfo) FirstFile() string {
-	sm := newStateMachine(lineInfo, lineInfo.Instructions)
+	sm := newStateMachine(lineInfo, lineInfo.Instructions, lineInfo.ptrSize)
 	for {
 		if sm.valid {
 			return sm.file
@@ -530,11 +541,10 @@ func endsequence(sm *StateMachine, buf *bytes.Buffer) {
 }
 
 func setaddress(sm *StateMachine, buf *bytes.Buffer) {
-	//TODO: this needs to be changed to support 32bit architectures (addr must be target arch pointer sized) -- also target endianness
-	var addr uint64
-
-	binary.Read(buf, binary.LittleEndian, &addr)
-
+	addr, err := util.ReadUintRaw(buf, binary.LittleEndian, sm.ptrSize)
+	if err != nil {
+		panic(err)
+	}
 	sm.address = addr + sm.dbl.staticBase
 }
 
diff --git a/pkg/dwarf/op/op.go b/pkg/dwarf/op/op.go
index 89af5a68..ac6bd52e 100644
--- a/pkg/dwarf/op/op.go
+++ b/pkg/dwarf/op/op.go
@@ -17,10 +17,11 @@ type Opcode byte
 type stackfn func(Opcode, *context) error
 
 type context struct {
-	buf    *bytes.Buffer
-	stack  []int64
-	pieces []Piece
-	reg    bool
+	buf     *bytes.Buffer
+	stack   []int64
+	pieces  []Piece
+	reg     bool
+	ptrSize int
 
 	DwarfRegisters
 }
@@ -36,11 +37,12 @@ type Piece struct {
 // ExecuteStackProgram executes a DWARF location expression and returns
 // either an address (int64), or a slice of Pieces for location expressions
 // that don't evaluate to an address (such as register and composite expressions).
-func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, []Piece, error) {
+func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int) (int64, []Piece, error) {
 	ctxt := &context{
 		buf:            bytes.NewBuffer(instructions),
 		stack:          make([]int64, 0, 3),
 		DwarfRegisters: regs,
+		ptrSize:        ptrSize,
 	}
 
 	for {
@@ -133,7 +135,12 @@ func callframecfa(opcode Opcode, ctxt *context) error {
 }
 
 func addr(opcode Opcode, ctxt *context) error {
-	ctxt.stack = append(ctxt.stack, int64(binary.LittleEndian.Uint64(ctxt.buf.Next(8))+ctxt.StaticBase))
+	buf := ctxt.buf.Next(ctxt.ptrSize)
+	stack, err := util.ReadUintRaw(bytes.NewReader(buf), binary.LittleEndian, ctxt.ptrSize)
+	if err != nil {
+		return err
+	}
+	ctxt.stack = append(ctxt.stack, int64(stack+ctxt.StaticBase))
 	return nil
 }
 
diff --git a/pkg/dwarf/reader/reader.go b/pkg/dwarf/reader/reader.go
index d59de829..f3118e9f 100644
--- a/pkg/dwarf/reader/reader.go
+++ b/pkg/dwarf/reader/reader.go
@@ -64,7 +64,7 @@ func (reader *Reader) SeekToFunction(pc RelAddr) (*dwarf.Entry, error) {
 }
 
 // Returns the address for the named entry.
-func (reader *Reader) AddrFor(name string, staticBase uint64) (uint64, error) {
+func (reader *Reader) AddrFor(name string, staticBase uint64, ptrSize int) (uint64, error) {
 	entry, err := reader.FindEntryNamed(name, false)
 	if err != nil {
 		return 0, err
@@ -73,7 +73,7 @@ func (reader *Reader) AddrFor(name string, staticBase uint64) (uint64, error) {
 	if !ok {
 		return 0, fmt.Errorf("type assertion failed")
 	}
-	addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions)
+	addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions, ptrSize)
 	if err != nil {
 		return 0, err
 	}
@@ -82,7 +82,7 @@ func (reader *Reader) AddrFor(name string, staticBase uint64) (uint64, error) {
 
 // Returns the address for the named struct member. Expects the reader to be at the parent entry
 // or one of the parents children, thus does not seek to parent by itself.
-func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (uint64, error) {
+func (reader *Reader) AddrForMember(member string, initialInstructions []byte, ptrSize int) (uint64, error) {
 	for {
 		entry, err := reader.NextMemberVariable()
 		if err != nil {
@@ -99,7 +99,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
 		if !ok {
 			continue
 		}
-		addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...))
+		addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...), ptrSize)
 		return uint64(addr), err
 	}
 }
diff --git a/pkg/dwarf/util/util.go b/pkg/dwarf/util/util.go
index 012f5308..20e4e2b9 100644
--- a/pkg/dwarf/util/util.go
+++ b/pkg/dwarf/util/util.go
@@ -2,6 +2,8 @@ package util
 
 import (
 	"bytes"
+	"encoding/binary"
+	"fmt"
 	"io"
 )
 
@@ -124,3 +126,33 @@ func ParseString(data *bytes.Buffer) (string, uint32) {
 
 	return str[:len(str)-1], uint32(len(str))
 }
+
+// ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
+func ReadUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) {
+	switch ptrSize {
+	case 4:
+		var n uint32
+		if err := binary.Read(reader, order, &n); err != nil {
+			return 0, err
+		}
+		return uint64(n), nil
+	case 8:
+		var n uint64
+		if err := binary.Read(reader, order, &n); err != nil {
+			return 0, err
+		}
+		return n, nil
+	}
+	return 0, fmt.Errorf("not supprted ptr size %d", ptrSize)
+}
+
+// WriteUint writes an integer of ptrSize bytes to writer, in the specified byte order.
+func WriteUint(writer io.Writer, order binary.ByteOrder, ptrSize int, data uint64) error {
+	switch ptrSize {
+	case 4:
+		return binary.Write(writer, order, uint32(data))
+	case 8:
+		return binary.Write(writer, order, data)
+	}
+	return fmt.Errorf("not support prt size %d", ptrSize)
+}
diff --git a/pkg/proc/amd64_arch.go b/pkg/proc/amd64_arch.go
index 202dc53b..92731cb4 100644
--- a/pkg/proc/amd64_arch.go
+++ b/pkg/proc/amd64_arch.go
@@ -51,7 +51,7 @@ func (a *AMD64) PtrSize() int {
 	return 8
 }
 
-// MaxInstructionLength returns the maximum lenght of an instruction.
+// MaxInstructionLength returns the maximum length of an instruction.
 func (a *AMD64) MaxInstructionLength() int {
 	return 15
 }
@@ -558,3 +558,9 @@ func formatX87Reg(b []byte) string {
 
 	return fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)
 }
+
+// InhibitStepInto returns whether StepBreakpoint can be set at pc.
+// Always return false on amd64.
+func (a *AMD64) InhibitStepInto(bi *BinaryInfo, pc uint64) bool {
+	return false
+}
diff --git a/pkg/proc/amd64_disasm.go b/pkg/proc/amd64_disasm.go
index 84ac9446..67ba7083 100644
--- a/pkg/proc/amd64_disasm.go
+++ b/pkg/proc/amd64_disasm.go
@@ -5,134 +5,19 @@
 package proc
 
 import (
-	"encoding/binary"
-
 	"golang.org/x/arch/x86/x86asm"
 )
 
 // 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.
 func (a *AMD64) AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error {
-	inst, err := x86asm.Decode(mem, 64)
-	if err != nil {
-		asmInst.Inst = (*amd64ArchInst)(nil)
-		asmInst.Size = 1
-		asmInst.Bytes = mem[:asmInst.Size]
-		return err
-	}
-
-	asmInst.Size = inst.Len
-	asmInst.Bytes = mem[:asmInst.Size]
-	patchPCRelAMD64(asmInst.Loc.PC, &inst)
-	asmInst.Inst = (*amd64ArchInst)(&inst)
-	asmInst.Kind = OtherInstruction
-
-	switch inst.Op {
-	case x86asm.CALL, x86asm.LCALL:
-		asmInst.Kind = CallInstruction
-	case x86asm.RET, x86asm.LRET:
-		asmInst.Kind = RetInstruction
-	}
-
-	asmInst.DestLoc = resolveCallArgAMD64(&inst, asmInst.Loc.PC, asmInst.AtPC, regs, memrw, bi)
-	return nil
+	return x86AsmDecode(asmInst, mem, regs, memrw, bi, 64)
 }
 
 func (a *AMD64) Prologues() []opcodeSeq {
 	return prologuesAMD64
 }
 
-// converts PC relative arguments to absolute addresses
-func patchPCRelAMD64(pc uint64, inst *x86asm.Inst) {
-	for i := range inst.Args {
-		rel, isrel := inst.Args[i].(x86asm.Rel)
-		if isrel {
-			inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
-		}
-	}
-}
-
-type amd64ArchInst x86asm.Inst
-
-func (inst *amd64ArchInst) Text(flavour AssemblyFlavour, pc uint64, symLookup func(uint64) (string, uint64)) string {
-	if inst == nil {
-		return "?"
-	}
-
-	var text string
-
-	switch flavour {
-	case GNUFlavour:
-		text = x86asm.GNUSyntax(x86asm.Inst(*inst), pc, symLookup)
-	case GoFlavour:
-		text = x86asm.GoSyntax(x86asm.Inst(*inst), pc, symLookup)
-	case IntelFlavour:
-		fallthrough
-	default:
-		text = x86asm.IntelSyntax(x86asm.Inst(*inst), pc, symLookup)
-	}
-
-	return text
-
-}
-
-func (inst *amd64ArchInst) OpcodeEquals(op uint64) bool {
-	if inst == nil {
-		return false
-	}
-	return uint64(inst.Op) == op
-}
-
-func resolveCallArgAMD64(inst *x86asm.Inst, instAddr uint64, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
-	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
-		return nil
-	}
-
-	var pc uint64
-	var err error
-
-	switch arg := inst.Args[0].(type) {
-	case x86asm.Imm:
-		pc = uint64(arg)
-	case x86asm.Reg:
-		if !currentGoroutine || regs == nil {
-			return nil
-		}
-		pc, err = regs.Get(int(arg))
-		if err != nil {
-			return nil
-		}
-	case x86asm.Mem:
-		if !currentGoroutine || regs == nil {
-			return nil
-		}
-		if arg.Segment != 0 {
-			return nil
-		}
-		base, err1 := regs.Get(int(arg.Base))
-		index, err2 := regs.Get(int(arg.Index))
-		if err1 != nil || err2 != nil {
-			return nil
-		}
-		addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
-		//TODO: should this always be 64 bits instead of inst.MemBytes?
-		pcbytes := make([]byte, inst.MemBytes)
-		_, err := mem.ReadMemory(pcbytes, addr)
-		if err != nil {
-			return nil
-		}
-		pc = binary.LittleEndian.Uint64(pcbytes)
-	default:
-		return nil
-	}
-
-	file, line, fn := bininfo.PCToLine(pc)
-	if fn == nil {
-		return &Location{PC: pc}
-	}
-	return &Location{PC: pc, File: file, Line: line, Fn: fn}
-}
-
 // Possible stacksplit prologues are inserted by stacksplit in
 // $GOROOT/src/cmd/internal/obj/x86/obj6.go.
 // The stacksplit prologue will always begin with loading curg in CX, this
diff --git a/pkg/proc/arch.go b/pkg/proc/arch.go
index 28d16635..c915575d 100644
--- a/pkg/proc/arch.go
+++ b/pkg/proc/arch.go
@@ -22,8 +22,10 @@ type Arch interface {
 	RegistersToDwarfRegisters(uint64, Registers) op.DwarfRegisters
 	AddrAndStackRegsToDwarfRegisters(uint64, uint64, uint64, uint64, uint64) op.DwarfRegisters
 	DwarfRegisterToString(int, *op.DwarfRegister) (string, bool, string)
+	InhibitStepInto(bi *BinaryInfo, pc uint64) bool
 }
 
+// crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_amd64.s.
 const (
 	crosscall2SPOffsetBad        = 0x8
 	crosscall2SPOffsetWindows    = 0x118
diff --git a/pkg/proc/arm64_arch.go b/pkg/proc/arm64_arch.go
index cdfd4b66..8fe7096c 100644
--- a/pkg/proc/arm64_arch.go
+++ b/pkg/proc/arm64_arch.go
@@ -52,7 +52,7 @@ func (a *ARM64) PtrSize() int {
 	return 8
 }
 
-// MaxInstructionLength returns the maximum lenght of an instruction.
+// MaxInstructionLength returns the maximum length of an instruction.
 func (a *ARM64) MaxInstructionLength() int {
 	return 4
 }
@@ -465,3 +465,9 @@ func (a *ARM64) DwarfRegisterToString(i int, reg *op.DwarfRegister) (name string
 	}
 	return name, false, fmt.Sprintf("%#x", reg.Bytes)
 }
+
+// InhibitStepInto returns whether StepBreakpoint can be set at pc.
+// Always return false on arm64.
+func (a *ARM64) InhibitStepInto(bi *BinaryInfo, pc uint64) bool {
+	return false
+}
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index 47451324..ae3da942 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -27,6 +27,7 @@ import (
 	"github.com/go-delve/delve/pkg/dwarf/loclist"
 	"github.com/go-delve/delve/pkg/dwarf/op"
 	"github.com/go-delve/delve/pkg/dwarf/reader"
+	"github.com/go-delve/delve/pkg/dwarf/util"
 	"github.com/go-delve/delve/pkg/goversion"
 	"github.com/go-delve/delve/pkg/logflags"
 	"github.com/sirupsen/logrus"
@@ -50,6 +51,9 @@ type BinaryInfo struct {
 	// LookupFunc maps function names to a description of the function.
 	LookupFunc map[string]*Function
 
+	// SymNames maps addr to a description *elf.Symbol of this addr.
+	SymNames map[uint64]*elf.Symbol
+
 	// Images is a list of loaded shared libraries (also known as
 	// shared objects on linux or DLLs on windows).
 	Images []*Image
@@ -97,15 +101,6 @@ type BinaryInfo struct {
 	logger *logrus.Entry
 }
 
-// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
-var ErrUnsupportedLinuxArch = errors.New("unsupported architecture - only linux/amd64 and linux/arm64 are supported")
-
-// ErrUnsupportedWindowsArch is returned when attempting to debug a binary compiled for an unsupported architecture.
-var ErrUnsupportedWindowsArch = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
-
-// ErrUnsupportedDarwinArch is returned when attempting to debug a binary compiled for an unsupported architecture.
-var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwin/amd64 is supported")
-
 // ErrCouldNotDetermineRelocation is an error returned when Delve could not determine the base address of a
 // position independant executable.
 var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
@@ -114,6 +109,61 @@ var ErrCouldNotDetermineRelocation = errors.New("could not determine the base ad
 // section or find an external debug info file.
 var ErrNoDebugInfoFound = errors.New("could not open debug info")
 
+// ErrUnsupportedArch is returned when attempting to debug a binary compiled for an unsupported architecture.
+type ErrUnsupportedArch struct {
+	os      string
+	cpuArch CpuArch
+}
+
+type CpuArch interface {
+	String() string
+}
+
+func (e *ErrUnsupportedArch) Error() string {
+	var supportArchs []CpuArch
+	switch e.os {
+	case "linux":
+		for linuxArch, _ := range supportedLinuxArch {
+			supportArchs = append(supportArchs, linuxArch)
+		}
+	case "windows":
+		for windowArch, _ := range supportedWindowsArch {
+			supportArchs = append(supportArchs, windowArch)
+		}
+	case "darwin":
+		for darwinArch, _ := range supportedDarwinArch {
+			supportArchs = append(supportArchs, darwinArch)
+		}
+	}
+
+	errStr := "unsupported architecture of " + e.os + "/" + e.cpuArch.String()
+	errStr += " - only"
+	for _, arch := range supportArchs {
+		errStr += " " + e.os + "/" + arch.String() + " "
+	}
+	if len(supportArchs) == 1 {
+		errStr += "is supported"
+	} else {
+		errStr += "are supported"
+	}
+
+	return errStr
+}
+
+var supportedLinuxArch = map[elf.Machine]bool{
+	elf.EM_X86_64:  true,
+	elf.EM_AARCH64: true,
+	elf.EM_386:     true,
+}
+
+var supportedWindowsArch = map[PEMachine]bool{
+	IMAGE_FILE_MACHINE_AMD64: true,
+}
+
+var supportedDarwinArch = map[macho.Cpu]bool{
+	macho.CpuAmd64: true,
+}
+
 const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
 
 type compileUnit struct {
@@ -260,12 +310,13 @@ func NewBinaryInfo(goos, goarch string) *BinaryInfo {
 
 	// TODO: find better way to determine proc arch (perhaps use executable file info).
 	switch goarch {
+	case "386":
+		r.Arch = I386Arch(goos)
 	case "amd64":
 		r.Arch = AMD64Arch(goos)
 	case "arm64":
 		r.Arch = ARM64Arch(goos)
 	}
-
 	return r
 }
 
@@ -614,7 +665,7 @@ func (bi *BinaryInfo) LoadImageFromData(dwdata *dwarf.Data, debugFrameBytes, deb
 	image.typeCache = make(map[dwarf.Offset]godwarf.Type)
 
 	if debugFrameBytes != nil {
-		bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0)
+		bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0, bi.Arch.PtrSize())
 	}
 
 	image.loclist = loclist.New(debugLocBytes, bi.Arch.PtrSize())
@@ -697,7 +748,7 @@ func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, r
 	if err != nil {
 		return 0, nil, "", err
 	}
-	addr, pieces, err := op.ExecuteStackProgram(regs, instr)
+	addr, pieces, err := op.ExecuteStackProgram(regs, instr, bi.Arch.PtrSize())
 	return addr, pieces, descr, err
 }
 
@@ -827,9 +878,9 @@ func (bi *BinaryInfo) openSeparateDebugInfo(image *Image, exe *elf.File, debugIn
 		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
 	}
 
-	if elfFile.Machine != elf.EM_X86_64 && elfFile.Machine != elf.EM_AARCH64 {
+	if !supportedLinuxArch[elfFile.Machine] {
 		sepFile.Close()
-		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, ErrUnsupportedLinuxArch.Error())
+		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, &ErrUnsupportedArch{os: "linux", cpuArch: elfFile.Machine})
 	}
 
 	return sepFile, elfFile, nil
@@ -875,8 +926,8 @@ func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, w
 	if err != nil {
 		return err
 	}
-	if elfFile.Machine != elf.EM_X86_64 && elfFile.Machine != elf.EM_AARCH64 {
-		return ErrUnsupportedLinuxArch
+	if !supportedLinuxArch[elfFile.Machine] {
+		return &ErrUnsupportedArch{os: "linux", cpuArch: elfFile.Machine}
 	}
 
 	if image.index == 0 {
@@ -924,9 +975,10 @@ func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, w
 	debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc")
 	image.loclist = loclist.New(debugLocBytes, bi.Arch.PtrSize())
 
-	wg.Add(2)
+	wg.Add(3)
 	go bi.parseDebugFrameElf(image, dwarfFile, wg)
 	go bi.loadDebugInfoMaps(image, debugLineBytes, wg, nil)
+	go bi.loadSymbolName(image, elfFile, wg)
 	if image.index == 0 {
 		// determine g struct offset only when loading the executable file
 		wg.Add(1)
@@ -935,6 +987,25 @@ 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
+
+func (bi *BinaryInfo) loadSymbolName(image *Image, file *elf.File, wg *sync.WaitGroup) {
+	defer wg.Done()
+	if bi.SymNames == nil {
+		bi.SymNames = make(map[uint64]*elf.Symbol)
+	}
+	symSecs, _ := file.Symbols()
+	if symSecs != nil {
+		for _, symSec := range symSecs {
+			if symSec.Info == STT_FUNC { // TODO(chainhelen), need to parse others types.
+				s := symSec
+				bi.SymNames[symSec.Value+image.StaticBase] = &s
+			}
+		}
+	}
+}
+
 func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -949,7 +1020,7 @@ func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, wg *sync.W
 		return
 	}
 
-	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), image.StaticBase))
+	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), image.StaticBase, bi.Arch.PtrSize()))
 }
 
 func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
@@ -957,7 +1028,9 @@ func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.
 
 	// This is a bit arcane. Essentially:
 	// - If the program is pure Go, it can do whatever it wants, and puts the G
-	//   pointer at %fs-8.
+	//   pointer at %fs-8 on 64 bit.
+	// - %Gs is the index of private storage in GDT on 32 bit, and puts the G
+	//   pointer at -4(tls).
 	// - 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.
@@ -974,10 +1047,6 @@ func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.
 			break
 		}
 	}
-	if tlsg == nil {
-		bi.gStructOffset = ^uint64(8) + 1 // -8
-		return
-	}
 	var tls *elf.Prog
 	for _, prog := range exe.Progs {
 		if prog.Type == elf.PT_TLS {
@@ -985,8 +1054,8 @@ func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.
 			break
 		}
 	}
-	if tls == nil {
-		bi.gStructOffset = ^uint64(8) + 1 // -8
+	if tlsg == nil || tls == nil {
+		bi.gStructOffset = ^uint64(bi.Arch.PtrSize()) + 1 //-ptrSize
 		return
 	}
 
@@ -1012,8 +1081,9 @@ func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint
 		return err
 	}
 	image.closer = closer
-	if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
-		return ErrUnsupportedWindowsArch
+	cpuArch := PEMachine(peFile.Machine)
+	if !supportedWindowsArch[cpuArch] {
+		return &ErrUnsupportedArch{os: "windows", cpuArch: cpuArch}
 	}
 	image.dwarf, err = peFile.DWARF()
 	if err != nil {
@@ -1078,7 +1148,7 @@ func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, wg *sync.Wai
 		return
 	}
 
-	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase))
+	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
 }
 
 // Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
@@ -1107,8 +1177,8 @@ func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint u
 		return err
 	}
 	image.closer = exe
-	if exe.Cpu != macho.CpuAmd64 {
-		return ErrUnsupportedDarwinArch
+	if !supportedDarwinArch[exe.Cpu] {
+		return &ErrUnsupportedArch{os: "darwin", cpuArch: exe.Cpu}
 	}
 	image.dwarf, err = exe.DWARF()
 	if err != nil {
@@ -1156,7 +1226,7 @@ func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, wg *sy
 		return
 	}
 
-	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase))
+	bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase, bi.Arch.PtrSize()))
 }
 
 // Do not call this function directly it isn't able to deal correctly with package paths
@@ -1327,7 +1397,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
 						logger.Printf(fmt, args)
 					}
 				}
-				cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), logfn, image.StaticBase, bi.GOOS == "windows")
+				cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), logfn, image.StaticBase, bi.GOOS == "windows", bi.Arch.PtrSize())
 			}
 			cu.producer, _ = entry.Val(dwarf.AttrProducer).(string)
 			if cu.isgo && cu.producer != "" {
@@ -1418,7 +1488,7 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
 				var addr uint64
 				if loc, ok := entry.Val(dwarf.AttrLocation).([]byte); ok {
 					if len(loc) == bi.Arch.PtrSize()+1 && op.Opcode(loc[0]) == op.DW_OP_addr {
-						addr = binary.LittleEndian.Uint64(loc[1:])
+						addr, _ = util.ReadUintRaw(bytes.NewReader(loc[1:]), binary.LittleEndian, bi.Arch.PtrSize())
 					}
 				}
 				if !cu.isgo {
@@ -1741,6 +1811,9 @@ func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) {
 		}
 		return "", 0
 	}
+	if sym, ok := bi.SymNames[addr]; ok {
+		return sym.Name, addr
+	}
 	i := sort.Search(len(bi.packageVars), func(i int) bool {
 		return bi.packageVars[i].addr >= addr
 	})
diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go
index 19f67fd7..0be5b1aa 100644
--- a/pkg/proc/breakpoints.go
+++ b/pkg/proc/breakpoints.go
@@ -124,19 +124,13 @@ func (bp *Breakpoint) CheckCondition(thread Thread) BreakpointState {
 	}
 	nextDeferOk := true
 	if bp.Kind&NextDeferBreakpoint != 0 {
+		var err error
 		frames, err := ThreadStacktrace(thread, 2)
 		if err == nil {
-			ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
-			isdeferreturn := false
-			if len(frames) >= 1 {
-				for _, pc := range bp.DeferReturns {
-					if frames[0].Ret == pc {
-						isdeferreturn = true
-						break
-					}
-				}
+			nextDeferOk = isPanicCall(frames)
+			if !nextDeferOk {
+				nextDeferOk, _ = isDeferReturnCall(frames, bp.DeferReturns)
 			}
-			nextDeferOk = ispanic || isdeferreturn
 		}
 	}
 	if bp.IsInternal() {
@@ -155,6 +149,21 @@ func (bp *Breakpoint) CheckCondition(thread Thread) BreakpointState {
 	return bpstate
 }
 
+func isPanicCall(frames []Stackframe) bool {
+	return len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
+}
+
+func isDeferReturnCall(frames []Stackframe, deferReturns []uint64) (bool, uint64) {
+	if len(frames) >= 1 {
+		for _, pc := range deferReturns {
+			if frames[0].Ret == pc {
+				return true, pc
+			}
+		}
+	}
+	return false, 0
+}
+
 // IsInternal returns true if bp is an internal breakpoint.
 // User-set breakpoints can overlap with internal breakpoints, in that case
 // both IsUser and IsInternal will be true.
diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go
index cee1a651..18b8cbe0 100644
--- a/pkg/proc/core/core.go
+++ b/pkg/proc/core/core.go
@@ -155,12 +155,13 @@ type Process struct {
 
 	entryPoint uint64
 
-	bi                *proc.BinaryInfo
-	breakpoints       proc.BreakpointMap
-	currentThread     *Thread
-	selectedGoroutine *proc.G
+	bi            *proc.BinaryInfo
+	breakpoints   proc.BreakpointMap
+	currentThread *Thread
 }
 
+var _ proc.ProcessInternal = &Process{}
+
 // Thread represents a thread in the core file being debugged.
 type Thread struct {
 	th     osThread
@@ -212,17 +213,12 @@ func OpenCore(corePath, exePath string, debugInfoDirs []string) (*proc.Target, e
 		return nil, err
 	}
 
-	if err := p.initialize(exePath, debugInfoDirs); err != nil {
-		return nil, err
-	}
-
-	return proc.NewTarget(p, false), nil
-}
-
-// initialize for core files doesn't do much
-// aside from call the post initialization setup.
-func (p *Process) initialize(path string, debugInfoDirs []string) error {
-	return proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint)
+	return proc.NewTarget(p, proc.NewTargetConfig{
+		Path:                exePath,
+		DebugInfoDirs:       debugInfoDirs,
+		WriteBreakpoint:     p.writeBreakpoint,
+		DisableAsyncPreempt: false,
+		StopReason:          proc.StopAttached})
 }
 
 // BinInfo will return the binary info.
@@ -230,13 +226,6 @@ func (p *Process) BinInfo() *proc.BinaryInfo {
 	return p.bi
 }
 
-// SetSelectedGoroutine will set internally the goroutine that should be
-// the default for any command executed, the goroutine being actively
-// followed.
-func (p *Process) SetSelectedGoroutine(g *proc.G) {
-	p.selectedGoroutine = g
-}
-
 // EntryPoint will return the entry point address for this core file.
 func (p *Process) EntryPoint() (uint64, error) {
 	return p.entryPoint, nil
@@ -254,8 +243,11 @@ func (p *Process) Recorded() (bool, string) { return true, "" }
 // Restart will only return an error for core files, as they are not executing.
 func (p *Process) Restart(string) error { return ErrContinueCore }
 
-// Direction will only return an error as you cannot continue a core process.
-func (p *Process) Direction(proc.Direction) error { return ErrContinueCore }
+// ChangeDirection will only return an error as you cannot continue a core process.
+func (p *Process) ChangeDirection(proc.Direction) error { return ErrContinueCore }
+
+// GetDirection will always return forward.
+func (p *Process) GetDirection() proc.Direction { return proc.Forward }
 
 // When does not apply to core files, it is to support the Mozilla 'rr' backend.
 func (p *Process) When() (string, error) { return "", nil }
@@ -392,8 +384,8 @@ func (p *Process) ClearInternalBreakpoints() error {
 
 // ContinueOnce will always return an error because you
 // cannot control execution of a core file.
-func (p *Process) ContinueOnce() (proc.Thread, error) {
-	return nil, ErrContinueCore
+func (p *Process) ContinueOnce() (proc.Thread, proc.StopReason, error) {
+	return nil, proc.StopUnknown, ErrContinueCore
 }
 
 // StepInstruction will always return an error
@@ -443,40 +435,11 @@ func (p *Process) Pid() int {
 func (p *Process) ResumeNotify(chan<- struct{}) {
 }
 
-// SelectedGoroutine returns the current active and selected
-// goroutine.
-func (p *Process) SelectedGoroutine() *proc.G {
-	return p.selectedGoroutine
-}
-
 // SetBreakpoint will always return an error for core files as you cannot write memory or control execution.
 func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
 	return nil, ErrWriteCore
 }
 
-// SwitchGoroutine will change the selected and active goroutine.
-func (p *Process) SwitchGoroutine(g *proc.G) error {
-	if g == nil {
-		// user specified -1 and selectedGoroutine is nil
-		return nil
-	}
-	if g.Thread != nil {
-		return p.SwitchThread(g.Thread.ThreadID())
-	}
-	p.selectedGoroutine = g
-	return nil
-}
-
-// SwitchThread will change the selected and active thread.
-func (p *Process) SwitchThread(tid int) error {
-	if th, ok := p.Threads[tid]; ok {
-		p.currentThread = th
-		p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
-		return nil
-	}
-	return fmt.Errorf("thread %d does not exist", tid)
-}
-
 // ThreadList will return a list of all threads currently in the process.
 func (p *Process) ThreadList() []proc.Thread {
 	r := make([]proc.Thread, 0, len(p.Threads))
@@ -491,3 +454,8 @@ func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
 	t, ok := p.Threads[threadID]
 	return t, ok
 }
+
+// SetCurrentThread is used internally by proc.Target to change the current thread.
+func (p *Process) SetCurrentThread(th proc.Thread) {
+	p.currentThread = th.(*Thread)
+}
diff --git a/pkg/proc/core/linux_core.go b/pkg/proc/core/linux_core.go
index e52ab0a2..03727246 100644
--- a/pkg/proc/core/linux_core.go
+++ b/pkg/proc/core/linux_core.go
@@ -118,24 +118,29 @@ func readLinuxCore(corePath, exePath string) (*Process, error) {
 		return nil, err
 	}
 	memory := buildMemory(coreFile, exeELF, exe, notes)
-	entryPoint := findEntryPoint(notes)
-	p := &Process{
-		mem:         memory,
-		Threads:     map[int]*Thread{},
-		entryPoint:  entryPoint,
-		breakpoints: proc.NewBreakpointMap(),
-	}
 
+	// TODO support 386
+	var bi *proc.BinaryInfo
 	switch machineType {
 	case EM_X86_64:
-		p.bi = proc.NewBinaryInfo("linux", "amd64")
-		linuxThreadsFromNotes(p, notes, machineType)
+		bi = proc.NewBinaryInfo("linux", "amd64")
 	case EM_AARCH64:
-		p.bi = proc.NewBinaryInfo("linux", "arm64")
-		linuxThreadsFromNotes(p, notes, machineType)
+		bi = proc.NewBinaryInfo("linux", "arm64")
 	default:
 		return nil, fmt.Errorf("unsupported machine type")
 	}
+
+	entryPoint := findEntryPoint(notes, bi.Arch.PtrSize())
+
+	p := &Process{
+		mem:         memory,
+		Threads:     map[int]*Thread{},
+		entryPoint:  entryPoint,
+		bi:          bi,
+		breakpoints: proc.NewBreakpointMap(),
+	}
+
+	linuxThreadsFromNotes(p, notes, machineType)
 	return p, nil
 }
 
@@ -352,10 +357,10 @@ func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*Note) proc.Me
 	return memory
 }
 
-func findEntryPoint(notes []*Note) uint64 {
+func findEntryPoint(notes []*Note, ptrSize int) uint64 {
 	for _, note := range notes {
 		if note.Type == NT_AUXV {
-			return linutil.EntryPointFromAuxvAMD64(note.Desc.([]byte))
+			return linutil.EntryPointFromAuxv(note.Desc.([]byte), ptrSize)
 		}
 	}
 	return 0
diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go
index a64f32e8..9ec3a8f4 100644
--- a/pkg/proc/fncall.go
+++ b/pkg/proc/fncall.go
@@ -542,7 +542,7 @@ func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize i
 			err = fmt.Errorf("could not get argument location of %s: %v", argname, err)
 		} else {
 			var pieces []op.Piece
-			off, pieces, err = op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog)
+			off, pieces, err = op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog, bi.Arch.PtrSize())
 			if err != nil {
 				err = fmt.Errorf("unsupported location expression for argument %s: %v", argname, err)
 			}
diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index b0add2d8..d324d326 100644
--- a/pkg/proc/gdbserial/gdbserver.go
+++ b/pkg/proc/gdbserial/gdbserver.go
@@ -104,9 +104,8 @@ type Process struct {
 	bi   *proc.BinaryInfo
 	conn gdbConn
 
-	threads           map[int]*Thread
-	currentThread     *Thread
-	selectedGoroutine *proc.G
+	threads       map[int]*Thread
+	currentThread *Thread
 
 	exited, detached bool
 	ctrlC            bool // ctrl-c was sent to stop inferior
@@ -125,9 +124,10 @@ type Process struct {
 	waitChan chan *os.ProcessState
 
 	onDetach func() // called after a successful detach
-
 }
 
+var _ proc.ProcessInternal = &Process{}
+
 // Thread represents an operating system thread.
 type Thread struct {
 	ID                int
@@ -198,7 +198,7 @@ func New(process *os.Process) *Process {
 }
 
 // Listen waits for a connection from the stub.
-func (p *Process) Listen(listener net.Listener, path string, pid int, debugInfoDirs []string) error {
+func (p *Process) Listen(listener net.Listener, path string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.Target, error) {
 	acceptChan := make(chan net.Conn)
 
 	go func() {
@@ -210,25 +210,25 @@ func (p *Process) Listen(listener net.Listener, path string, pid int, debugInfoD
 	case conn := <-acceptChan:
 		listener.Close()
 		if conn == nil {
-			return errors.New("could not connect")
+			return nil, errors.New("could not connect")
 		}
-		return p.Connect(conn, path, pid, debugInfoDirs)
+		return p.Connect(conn, path, pid, debugInfoDirs, stopReason)
 	case status := <-p.waitChan:
 		listener.Close()
-		return fmt.Errorf("stub exited while waiting for connection: %v", status)
+		return nil, fmt.Errorf("stub exited while waiting for connection: %v", status)
 	}
 }
 
 // Dial attempts to connect to the stub.
-func (p *Process) Dial(addr string, path string, pid int, debugInfoDirs []string) error {
+func (p *Process) Dial(addr string, path string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.Target, error) {
 	for {
 		conn, err := net.Dial("tcp", addr)
 		if err == nil {
-			return p.Connect(conn, path, pid, debugInfoDirs)
+			return p.Connect(conn, path, pid, debugInfoDirs, stopReason)
 		}
 		select {
 		case status := <-p.waitChan:
-			return fmt.Errorf("stub exited while attempting to connect: %v", status)
+			return nil, fmt.Errorf("stub exited while attempting to connect: %v", status)
 		default:
 		}
 		time.Sleep(time.Second)
@@ -241,13 +241,13 @@ func (p *Process) Dial(addr string, path string, pid int, debugInfoDirs []string
 // program and the PID of the target process, both are optional, however
 // some stubs do not provide ways to determine path and pid automatically
 // and Connect will be unable to function without knowing them.
-func (p *Process) Connect(conn net.Conn, path string, pid int, debugInfoDirs []string) error {
+func (p *Process) Connect(conn net.Conn, path string, pid int, debugInfoDirs []string, stopReason proc.StopReason) (*proc.Target, error) {
 	p.conn.conn = conn
 	p.conn.pid = pid
 	err := p.conn.handshake()
 	if err != nil {
 		conn.Close()
-		return err
+		return nil, err
 	}
 
 	if verbuf, err := p.conn.exec([]byte("$qGDBServerVersion"), "init"); err == nil {
@@ -262,8 +262,9 @@ func (p *Process) Connect(conn net.Conn, path string, pid int, debugInfoDirs []s
 		}
 	}
 
-	if err := p.initialize(path, debugInfoDirs); err != nil {
-		return err
+	tgt, err := p.initialize(path, debugInfoDirs, stopReason)
+	if err != nil {
+		return nil, err
 	}
 
 	// None of the stubs we support returns the value of fs_base or gs_base
@@ -279,7 +280,8 @@ func (p *Process) Connect(conn net.Conn, path string, pid int, debugInfoDirs []s
 			p.loadGInstrAddr = addr
 		}
 	}
-	return nil
+
+	return tgt, nil
 }
 
 // unusedPort returns an unused tcp port
@@ -402,15 +404,13 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
 	p := New(process.Process)
 	p.conn.isDebugserver = isDebugserver
 
+	var tgt *proc.Target
 	if listener != nil {
-		err = p.Listen(listener, cmd[0], 0, debugInfoDirs)
+		tgt, err = p.Listen(listener, cmd[0], 0, debugInfoDirs, proc.StopLaunched)
 	} else {
-		err = p.Dial(port, cmd[0], 0, debugInfoDirs)
-	}
-	if err != nil {
-		return nil, err
+		tgt, err = p.Dial(port, cmd[0], 0, debugInfoDirs, proc.StopLaunched)
 	}
-	return proc.NewTarget(p, runtime.GOOS == "darwin"), nil
+	return tgt, err
 }
 
 // LLDBAttach starts an instance of lldb-server and connects to it, asking
@@ -454,15 +454,13 @@ func LLDBAttach(pid int, path string, debugInfoDirs []string) (*proc.Target, err
 	p := New(process.Process)
 	p.conn.isDebugserver = isDebugserver
 
+	var tgt *proc.Target
 	if listener != nil {
-		err = p.Listen(listener, path, pid, debugInfoDirs)
+		tgt, err = p.Listen(listener, path, pid, debugInfoDirs, proc.StopAttached)
 	} else {
-		err = p.Dial(port, path, pid, debugInfoDirs)
-	}
-	if err != nil {
-		return nil, err
+		tgt, err = p.Dial(port, path, pid, debugInfoDirs, proc.StopAttached)
 	}
-	return proc.NewTarget(p, false), nil
+	return tgt, err
 }
 
 // EntryPoint will return the process entry point address, useful for
@@ -473,7 +471,7 @@ func (p *Process) EntryPoint() (uint64, error) {
 		// If we can't read the auxiliary vector it just means it's not supported
 		// by the OS or by the stub. If we are debugging a PIE and the entry point
 		// is needed proc.LoadBinaryInfo will complain about it.
-		entryPoint = linutil.EntryPointFromAuxvAMD64(auxv)
+		entryPoint = linutil.EntryPointFromAuxv(auxv, p.BinInfo().Arch.PtrSize())
 	}
 	return entryPoint, nil
 }
@@ -481,7 +479,7 @@ func (p *Process) EntryPoint() (uint64, error) {
 // initialize uses qProcessInfo to load the inferior's PID and
 // executable path. This command is not supported by all stubs and not all
 // stubs will report both the PID and executable path.
-func (p *Process) initialize(path string, debugInfoDirs []string) error {
+func (p *Process) initialize(path string, debugInfoDirs []string, stopReason proc.StopReason) (*proc.Target, error) {
 	var err error
 	if path == "" {
 		// If we are attaching to a running process and the user didn't specify
@@ -495,11 +493,11 @@ func (p *Process) initialize(path string, debugInfoDirs []string) error {
 				_, path, err = queryProcessInfo(p, p.Pid())
 				if err != nil {
 					p.conn.conn.Close()
-					return err
+					return nil, err
 				}
 			} else {
 				p.conn.conn.Close()
-				return fmt.Errorf("could not determine executable path: %v", err)
+				return nil, fmt.Errorf("could not determine executable path: %v", err)
 			}
 		}
 	}
@@ -520,7 +518,7 @@ func (p *Process) initialize(path string, debugInfoDirs []string) error {
 	if err != nil {
 		p.conn.conn.Close()
 		p.bi.Close()
-		return err
+		return nil, err
 	}
 	p.clearThreadSignals()
 
@@ -529,14 +527,20 @@ func (p *Process) initialize(path string, debugInfoDirs []string) error {
 		if err != nil && !isProtocolErrorUnsupported(err) {
 			p.conn.conn.Close()
 			p.bi.Close()
-			return err
+			return nil, err
 		}
 	}
-	if err = proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint); err != nil {
+	tgt, err := proc.NewTarget(p, proc.NewTargetConfig{
+		Path:                path,
+		DebugInfoDirs:       debugInfoDirs,
+		WriteBreakpoint:     p.writeBreakpoint,
+		DisableAsyncPreempt: runtime.GOOS == "darwin",
+		StopReason:          stopReason})
+	if err != nil {
 		p.conn.conn.Close()
-		return err
+		return nil, err
 	}
-	return nil
+	return tgt, nil
 }
 
 func queryProcessInfo(p *Process, pid int) (int, string, error) {
@@ -606,9 +610,9 @@ func (p *Process) CurrentThread() proc.Thread {
 	return p.currentThread
 }
 
-// SelectedGoroutine returns the current actuve selected goroutine.
-func (p *Process) SelectedGoroutine() *proc.G {
-	return p.selectedGoroutine
+// SetCurrentThread is used internally by proc.Target to change the current thread.
+func (p *Process) SetCurrentThread(th proc.Thread) {
+	p.currentThread = th.(*Thread)
 }
 
 const (
@@ -628,9 +632,9 @@ const (
 
 // ContinueOnce will continue execution of the process until
 // a breakpoint is hit or signal is received.
-func (p *Process) ContinueOnce() (proc.Thread, error) {
+func (p *Process) ContinueOnce() (proc.Thread, proc.StopReason, error) {
 	if p.exited {
-		return nil, &proc.ErrProcessExited{Pid: p.conn.pid}
+		return nil, proc.StopExited, &proc.ErrProcessExited{Pid: p.conn.pid}
 	}
 
 	if p.conn.direction == proc.Forward {
@@ -638,7 +642,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
 		for _, thread := range p.threads {
 			if thread.CurrentBreakpoint.Breakpoint != nil {
 				if err := thread.stepInstruction(&threadUpdater{p: p}); err != nil {
-					return nil, err
+					return nil, proc.StopUnknown, err
 				}
 			}
 		}
@@ -654,6 +658,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
 	var threadID string
 	var trapthread *Thread
 	var tu = threadUpdater{p: p}
+	var atstart bool
 continueLoop:
 	for {
 		var err error
@@ -664,7 +669,7 @@ continueLoop:
 			if _, exited := err.(proc.ErrProcessExited); exited {
 				p.exited = true
 			}
-			return nil, err
+			return nil, proc.StopExited, err
 		}
 
 		// For stubs that support qThreadStopInfo updateThreadList will
@@ -679,7 +684,7 @@ continueLoop:
 		}
 
 		var shouldStop bool
-		trapthread, shouldStop = p.handleThreadSignals(trapthread)
+		trapthread, atstart, shouldStop = p.handleThreadSignals(trapthread)
 		if shouldStop {
 			break continueLoop
 		}
@@ -687,18 +692,23 @@ continueLoop:
 
 	p.clearThreadRegisters()
 
+	stopReason := proc.StopUnknown
+	if atstart {
+		stopReason = proc.StopLaunched
+	}
+
 	if p.BinInfo().GOOS == "linux" {
 		if err := linutil.ElfUpdateSharedObjects(p); err != nil {
-			return nil, err
+			return nil, stopReason, err
 		}
 	}
 
 	if err := p.setCurrentBreakpoints(); err != nil {
-		return nil, err
+		return nil, stopReason, err
 	}
 
 	if trapthread == nil {
-		return nil, fmt.Errorf("could not find thread %s", threadID)
+		return nil, stopReason, fmt.Errorf("could not find thread %s", threadID)
 	}
 
 	var err error
@@ -720,7 +730,7 @@ continueLoop:
 		// the signals that are reported here can not be propagated back to the target process.
 		trapthread.sig = 0
 	}
-	return trapthread, err
+	return trapthread, stopReason, err
 }
 
 func (p *Process) findThreadByStrID(threadID string) *Thread {
@@ -737,7 +747,7 @@ func (p *Process) findThreadByStrID(threadID string) *Thread {
 // and returns true if we should stop execution in response to one of the
 // signals and return control to the user.
 // Adjusts trapthread to a thread that we actually want to stop at.
-func (p *Process) handleThreadSignals(trapthread *Thread) (trapthreadOut *Thread, shouldStop bool) {
+func (p *Process) handleThreadSignals(trapthread *Thread) (trapthreadOut *Thread, atstart bool, shouldStop bool) {
 	var trapthreadCandidate *Thread
 
 	for _, th := range p.threads {
@@ -773,8 +783,9 @@ func (p *Process) handleThreadSignals(trapthread *Thread) (trapthreadOut *Thread
 		// Signal 0 is returned by rr when it reaches the start of the process
 		// in backward continue mode.
 		case 0:
-			if p.conn.direction == proc.Backward {
+			if p.conn.direction == proc.Backward && th == trapthread {
 				isStopSignal = true
+				atstart = true
 			}
 
 		default:
@@ -805,39 +816,7 @@ func (p *Process) handleThreadSignals(trapthread *Thread) (trapthreadOut *Thread
 		shouldStop = true
 	}
 
-	return trapthread, shouldStop
-}
-
-// SetSelectedGoroutine will set internally the goroutine that should be
-// the default for any command executed, the goroutine being actively
-// followed.
-func (p *Process) SetSelectedGoroutine(g *proc.G) {
-	p.selectedGoroutine = g
-}
-
-// SwitchThread will change the internal selected thread.
-func (p *Process) SwitchThread(tid int) error {
-	if p.exited {
-		return proc.ErrProcessExited{Pid: p.conn.pid}
-	}
-	if th, ok := p.threads[tid]; ok {
-		p.currentThread = th
-		p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
-		return nil
-	}
-	return fmt.Errorf("thread %d does not exist", tid)
-}
-
-// SwitchGoroutine will change the internal selected goroutine.
-func (p *Process) SwitchGoroutine(g *proc.G) error {
-	if g == nil {
-		return nil
-	}
-	if g.Thread != nil {
-		return p.SwitchThread(g.Thread.ThreadID())
-	}
-	p.selectedGoroutine = g
-	return nil
+	return trapthread, atstart, shouldStop
 }
 
 // RequestManualStop will attempt to stop the process
@@ -944,7 +923,6 @@ func (p *Process) Restart(pos string) error {
 	}
 	p.clearThreadSignals()
 	p.clearThreadRegisters()
-	p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 
 	for addr := range p.breakpoints.M {
 		p.conn.setBreakpoint(addr)
@@ -1043,10 +1021,13 @@ func (p *Process) ClearCheckpoint(id int) error {
 	return nil
 }
 
-// Direction sets whether to run the program forwards or in reverse execution.
-func (p *Process) Direction(dir proc.Direction) error {
+// ChangeDirection sets whether to run the program forwards or in reverse execution.
+func (p *Process) ChangeDirection(dir proc.Direction) error {
 	if p.tracedir == "" {
-		return proc.ErrNotRecorded
+		if dir != proc.Forward {
+			return proc.ErrNotRecorded
+		}
+		return nil
 	}
 	if p.conn.conn == nil {
 		return proc.ErrProcessExited{Pid: p.conn.pid}
@@ -1061,6 +1042,11 @@ func (p *Process) Direction(dir proc.Direction) error {
 	return nil
 }
 
+// GetDirection returns the current direction of execution.
+func (p *Process) GetDirection() proc.Direction {
+	return p.conn.direction
+}
+
 // Breakpoints returns the list of breakpoints currently set.
 func (p *Process) Breakpoints() *proc.BreakpointMap {
 	return &p.breakpoints
diff --git a/pkg/proc/gdbserial/rr.go b/pkg/proc/gdbserial/rr.go
index ff5afec9..70db36f5 100644
--- a/pkg/proc/gdbserial/rr.go
+++ b/pkg/proc/gdbserial/rr.go
@@ -90,13 +90,13 @@ func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string)
 			safeRemoveAll(p.tracedir)
 		}
 	}
-	err = p.Dial(init.port, init.exe, 0, debugInfoDirs)
+	tgt, err := p.Dial(init.port, init.exe, 0, debugInfoDirs, proc.StopLaunched)
 	if err != nil {
 		rrcmd.Process.Kill()
 		return nil, err
 	}
 
-	return proc.NewTarget(p, false), nil
+	return tgt, nil
 }
 
 // ErrPerfEventParanoid is the error returned by Reply and Record if
diff --git a/pkg/proc/goroutine_cache.go b/pkg/proc/goroutine_cache.go
index c4613a10..e81f4d2b 100644
--- a/pkg/proc/goroutine_cache.go
+++ b/pkg/proc/goroutine_cache.go
@@ -13,13 +13,13 @@ func (gcache *goroutineCache) init(bi *BinaryInfo) {
 	exeimage := bi.Images[0]
 	rdr := exeimage.DwarfReader()
 
-	gcache.allglenAddr, _ = rdr.AddrFor("runtime.allglen", exeimage.StaticBase)
+	gcache.allglenAddr, _ = rdr.AddrFor("runtime.allglen", exeimage.StaticBase, bi.Arch.PtrSize())
 
 	rdr.Seek(0)
-	gcache.allgentryAddr, err = rdr.AddrFor("runtime.allgs", exeimage.StaticBase)
+	gcache.allgentryAddr, err = rdr.AddrFor("runtime.allgs", exeimage.StaticBase, bi.Arch.PtrSize())
 	if err != nil {
 		// try old name (pre Go 1.6)
-		gcache.allgentryAddr, _ = rdr.AddrFor("runtime.allg", exeimage.StaticBase)
+		gcache.allgentryAddr, _ = rdr.AddrFor("runtime.allg", exeimage.StaticBase, bi.Arch.PtrSize())
 	}
 }
 
@@ -31,6 +31,7 @@ func (gcache *goroutineCache) getRuntimeAllg(bi *BinaryInfo, mem MemoryReadWrite
 	if err != nil {
 		return 0, 0, err
 	}
+	
 	allgptr, err := readUintRaw(mem, uintptr(gcache.allgentryAddr), int64(bi.Arch.PtrSize()))
 	if err != nil {
 		return 0, 0, err
diff --git a/pkg/proc/i386_arch.go b/pkg/proc/i386_arch.go
new file mode 100644
index 00000000..c48342d7
--- /dev/null
+++ b/pkg/proc/i386_arch.go
@@ -0,0 +1,369 @@
+package proc
+
+import (
+	"encoding/binary"
+	"fmt"
+	"github.com/go-delve/delve/pkg/dwarf/frame"
+	"github.com/go-delve/delve/pkg/dwarf/op"
+	"strings"
+)
+
+// I386 represents the Intel386 CPU architecture.
+type I386 struct {
+	gStructOffset uint64
+	goos          string
+
+	// crosscall2fn is the DIE of crosscall2, a function used by the go runtime
+	// to call C functions. This function in go 1.9 (and previous versions) had
+	// a bad frame descriptor which needs to be fixed to generate good stack
+	// traces.
+	crosscall2fn *Function
+
+	// sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for
+	// the signal handler. See comment in FixFrameUnwindContext for a
+	// description of why this is needed.
+	sigreturnfn *Function
+}
+
+const (
+	i386DwarfIPRegNum uint64 = 8
+	i386DwarfSPRegNum uint64 = 4
+	i386DwarfBPRegNum uint64 = 5
+)
+
+var i386BreakInstruction = []byte{0xCC}
+
+// I386Arch returns an initialized I386Arch
+// struct.
+func I386Arch(goos string) *I386 {
+	return &I386{
+		goos: goos,
+	}
+}
+
+// PtrSize returns the size of a pointer
+// on this architecture.
+func (i *I386) PtrSize() int {
+	return 4
+}
+
+// MaxInstructionLength returns the maximum length of an instruction.
+func (i *I386) MaxInstructionLength() int {
+	return 15
+}
+
+// BreakpointInstruction returns the Breakpoint
+// instruction for this architecture.
+func (i *I386) BreakpointInstruction() []byte {
+	return i386BreakInstruction
+}
+
+// BreakInstrMovesPC returns whether the
+// breakpoint instruction will change the value
+// of PC after being executed
+func (i *I386) BreakInstrMovesPC() bool {
+	return true
+}
+
+// BreakpointSize returns the size of the
+// breakpoint instruction on this architecture.
+func (i *I386) BreakpointSize() int {
+	return len(i386BreakInstruction)
+}
+
+// TODO, Not sure, always return false for now. Need to test on windows.
+func (i *I386) DerefTLS() bool {
+	return false
+}
+
+// FixFrameUnwindContext adds default architecture rules to fctxt or returns
+// the default frame unwind context if fctxt is nil.
+func (i *I386) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
+	if i.sigreturnfn == nil {
+		i.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
+	}
+
+	if fctxt == nil || (i.sigreturnfn != nil && pc >= i.sigreturnfn.Entry && pc < i.sigreturnfn.End) {
+		// When there's no frame descriptor entry use BP (the frame pointer) instead
+		// - return register is [bp + i.PtrSize()] (i.e. [cfa-i.PtrSize()])
+		// - cfa is bp + i.PtrSize()*2
+		// - bp is [bp] (i.e. [cfa-i.PtrSize()*2])
+		// - sp is cfa
+
+		// When the signal handler runs it will move the execution to the signal
+		// handling stack (installed using the sigaltstack system call).
+		// This isn't i proper stack switch: the pointer to g in TLS will still
+		// refer to whatever g was executing on that thread before the signal was
+		// received.
+		// Since go did not execute i stack switch the previous value of sp, pc
+		// and bp is not saved inside g.sched, as it normally would.
+		// The only way to recover is to either read sp/pc from the signal context
+		// parameter (the ucontext_t* parameter) or to unconditionally follow the
+		// frame pointer when we get to runtime.sigreturn (which is what we do
+		// here).
+
+		return &frame.FrameContext{
+			RetAddrReg: i386DwarfIPRegNum,
+			Regs: map[uint64]frame.DWRule{
+				i386DwarfIPRegNum: frame.DWRule{
+					Rule:   frame.RuleOffset,
+					Offset: int64(-i.PtrSize()),
+				},
+				i386DwarfBPRegNum: frame.DWRule{
+					Rule:   frame.RuleOffset,
+					Offset: int64(-2 * i.PtrSize()),
+				},
+				i386DwarfSPRegNum: frame.DWRule{
+					Rule:   frame.RuleValOffset,
+					Offset: 0,
+				},
+			},
+			CFA: frame.DWRule{
+				Rule:   frame.RuleCFA,
+				Reg:    i386DwarfBPRegNum,
+				Offset: int64(2 * i.PtrSize()),
+			},
+		}
+	}
+
+	if i.crosscall2fn == nil {
+		i.crosscall2fn = bi.LookupFunc["crosscall2"]
+	}
+
+	// TODO(chainhelen), need to check whether there is a bad frame descriptor like amd64.
+	// crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_386.s.
+	if i.crosscall2fn != nil && pc >= i.crosscall2fn.Entry && pc < i.crosscall2fn.End {
+		rule := fctxt.CFA
+		fctxt.CFA = rule
+	}
+
+	// We assume that EBP is the frame pointer and we want to keep it updated,
+	// 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{
+			Rule:   frame.RuleFramePointer,
+			Reg:    i386DwarfBPRegNum,
+			Offset: 0,
+		}
+	}
+
+	return fctxt
+}
+
+// SwitchStack will use the current frame to determine if it's time to
+// switch between the system stack and the goroutine stack or vice versa.
+// Sets it.atend when the top of the stack is reached.
+func (i *I386) SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool {
+	if it.frame.Current.Fn == nil {
+		return false
+	}
+	switch it.frame.Current.Fn.Name {
+	case "runtime.asmcgocall", "runtime.cgocallback_gofunc": // TODO(chainhelen), need to support cgo stacktraces.
+		return false
+	case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
+		// Look for "top of stack" functions.
+		it.atend = true
+		return true
+
+	case "runtime.mstart":
+		// Calls to runtime.systemstack will switch to the systemstack then:
+		// 1. alter the goroutine stack so that it looks like systemstack_switch
+		//    was called
+		// 2. alter the system stack so that it looks like the bottom-most frame
+		//    belongs to runtime.mstart
+		// If we find a runtime.mstart frame on the system stack of a goroutine
+		// parked on runtime.systemstack_switch we assume runtime.systemstack was
+		// called and continue tracing from the parked position.
+
+		if it.top || !it.systemstack || it.g == nil {
+			return false
+		}
+		if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" {
+			return false
+		}
+
+		it.switchToGoroutineStack()
+		return true
+
+	default:
+		if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.fatalthrow" {
+			// The runtime switches to the system stack in multiple places.
+			// This usually happens through a call to runtime.systemstack but there
+			// are functions that switch to the system stack manually (for example
+			// runtime.morestack).
+			// Since we are only interested in printing the system stack for cgo
+			// calls we switch directly to the goroutine stack if we detect that the
+			// function at the top of the stack is a runtime function.
+			//
+			// The function "runtime.fatalthrow" is deliberately excluded from this
+			// because it can end up in the stack during a cgo call and switching to
+			// the goroutine stack will exclude all the C functions from the stack
+			// trace.
+			it.switchToGoroutineStack()
+			return true
+		}
+
+		return false
+	}
+}
+
+// RegSize returns the size (in bytes) of register regnum.
+// 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
+func (i *I386) RegSize(regnum uint64) int {
+	// XMM registers
+	if regnum >= 21 && regnum <= 36 {
+		return 16
+	}
+	// x87 registers
+	if regnum >= 11 && regnum <= 18 {
+		return 10
+	}
+	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 (i *I386) RegistersToDwarfRegisters(staticBase uint64, regs Registers) op.DwarfRegisters {
+	dregs := make([]*op.DwarfRegister, maxI386DwarfRegister()+1)
+
+	for _, reg := range regs.Slice(true) {
+		if dwarfReg, ok := i386NameToDwarf[strings.ToLower(reg.Name)]; ok {
+			dregs[dwarfReg] = reg.Reg
+		}
+	}
+
+	return op.DwarfRegisters{
+		StaticBase: staticBase,
+		Regs:       dregs,
+		ByteOrder:  binary.LittleEndian,
+		PCRegNum:   i386DwarfIPRegNum,
+		SPRegNum:   i386DwarfSPRegNum,
+		BPRegNum:   i386DwarfBPRegNum,
+	}
+}
+
+// AddrAndStackRegsToDwarfRegisters returns DWARF registers from the passed in
+// PC, SP, and BP registers in the format used by the DWARF expression interpreter.
+func (i *I386) AddrAndStackRegsToDwarfRegisters(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)
+
+	return op.DwarfRegisters{
+		StaticBase: staticBase,
+		Regs:       dregs,
+		ByteOrder:  binary.LittleEndian,
+		PCRegNum:   i386DwarfIPRegNum,
+		SPRegNum:   i386DwarfSPRegNum,
+		BPRegNum:   i386DwarfBPRegNum,
+	}
+}
+
+func (i *I386) DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) {
+	name, ok := i386DwarfToName[j]
+	if !ok {
+		name = fmt.Sprintf("unknown%d", j)
+	}
+
+	switch n := strings.ToLower(name); n {
+	case "eflags":
+		return name, false, eflagsDescription.Describe(reg.Uint64Val, 32)
+
+	case "tw", "fop":
+		return name, true, fmt.Sprintf("%#04x", reg.Uint64Val)
+
+	default:
+		if reg.Bytes != nil && strings.HasPrefix(name, "xmm") {
+			return name, true, formatSSEReg(reg.Bytes)
+		} else if reg.Bytes != nil && strings.HasPrefix(name, "st(") {
+			return name, true, formatX87Reg(reg.Bytes)
+		} else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) <= 8) {
+			return name, false, fmt.Sprintf("%#016x", reg.Uint64Val)
+		} else {
+			return name, false, fmt.Sprintf("%#x", reg.Bytes)
+		}
+	}
+}
+
+// InhibitStepInto returns whether StepBreakpoint can be set at pc.
+// When cgo or pie on 386 linux, compiler will insert more instructions (ex: call __x86.get_pc_thunk.).
+// StepBreakpoint shouldn't be set on __x86.get_pc_thunk and skip it.
+// See comments on stacksplit in $GOROOT/src/cmd/internal/obj/x86/obj6.go for generated instructions details.
+func (i *I386) InhibitStepInto(bi *BinaryInfo, pc uint64) bool {
+	if bi.SymNames != nil && bi.SymNames[pc] != nil &&
+		strings.HasPrefix(bi.SymNames[pc].Name, "__x86.get_pc_thunk.") {
+		return true
+	}
+	return false
+}
diff --git a/pkg/proc/i386_disasm.go b/pkg/proc/i386_disasm.go
new file mode 100644
index 00000000..3f560a2c
--- /dev/null
+++ b/pkg/proc/i386_disasm.go
@@ -0,0 +1,45 @@
+// TODO: disassembler support should be compiled in unconditionally,
+// instead of being decided by the build-target architecture, and be
+// part of the Arch object instead.
+
+package proc
+
+import (
+	"golang.org/x/arch/x86/x86asm"
+)
+
+// 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.
+func (i *I386) AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo) error {
+	return x86AsmDecode(asmInst, mem, regs, memrw, bi, 32)
+}
+
+func (i *I386) Prologues() []opcodeSeq {
+	return prologuesI386
+}
+
+// Possible stacksplit prologues are inserted by stacksplit in
+// $GOROOT/src/cmd/internal/obj/x86/obj6.go.
+// If 386 on linux when pie, the stacksplit prologue beigin with `call __x86.get_pc_thunk.` sometime.
+var prologuesI386 []opcodeSeq
+
+func init() {
+	var i386GetPcIns = opcodeSeq{uint64(x86asm.CALL)}
+	var tinyStacksplit = opcodeSeq{uint64(x86asm.CMP), uint64(x86asm.JBE)}
+	var smallStacksplit = opcodeSeq{uint64(x86asm.LEA), uint64(x86asm.CMP), uint64(x86asm.JBE)}
+	var bigStacksplit = opcodeSeq{uint64(x86asm.MOV), uint64(x86asm.CMP), uint64(x86asm.JE), uint64(x86asm.LEA), uint64(x86asm.SUB), uint64(x86asm.CMP), uint64(x86asm.JBE)}
+	var unixGetG = opcodeSeq{uint64(x86asm.MOV), uint64(x86asm.MOV)}
+
+	prologuesI386 = make([]opcodeSeq, 0, 2*3)
+	for _, getPcIns := range []opcodeSeq{{}, i386GetPcIns} {
+		for _, getG := range []opcodeSeq{unixGetG} { // TODO(chainhelen), need to support other OSs.
+			for _, stacksplit := range []opcodeSeq{tinyStacksplit, smallStacksplit, bigStacksplit} {
+				prologue := make(opcodeSeq, 0, len(getPcIns)+len(getG)+len(stacksplit))
+				prologue = append(prologue, getPcIns...)
+				prologue = append(prologue, getG...)
+				prologue = append(prologue, stacksplit...)
+				prologuesI386 = append(prologuesI386, prologue)
+			}
+		}
+	}
+}
diff --git a/pkg/proc/interface.go b/pkg/proc/interface.go
index 45424b3a..203a1253 100644
--- a/pkg/proc/interface.go
+++ b/pkg/proc/interface.go
@@ -18,18 +18,31 @@ type Process interface {
 	RecordingManipulation
 }
 
-// RecordingManipulation is an interface for manipulating process recordings.
-type RecordingManipulation interface {
-	// Recorded returns true if the current process is a recording and the path
-	// to the trace directory.
-	Recorded() (recorded bool, tracedir string)
+// ProcessInternal holds a set of methods that are not meant to be called by
+// anyone except for an instance of `proc.Target`. These methods are not
+// safe to use by themselves and should never be called directly outside of
+// the `proc` package.
+// This is temporary and in support of an ongoing refactor.
+type ProcessInternal interface {
+	SetCurrentThread(Thread)
 	// Restart restarts the recording from the specified position, or from the
 	// last checkpoint if pos == "".
 	// If pos starts with 'c' it's a checkpoint ID, otherwise it's an event
 	// number.
 	Restart(pos string) error
+	Detach(bool) error
+	ContinueOnce() (trapthread Thread, stopReason StopReason, err error)
+}
+
+// RecordingManipulation is an interface for manipulating process recordings.
+type RecordingManipulation interface {
+	// Recorded returns true if the current process is a recording and the path
+	// to the trace directory.
+	Recorded() (recorded bool, tracedir string)
 	// Direction changes execution direction.
-	Direction(Direction) error
+	ChangeDirection(Direction) error
+	// GetDirection returns the current direction of execution.
+	GetDirection() Direction
 	// When returns current recording position.
 	When() (string, error)
 	// Checkpoint sets a checkpoint at the current position.
@@ -71,7 +84,6 @@ type Info interface {
 	EntryPoint() (uint64, error)
 
 	ThreadInfo
-	GoroutineInfo
 }
 
 // ThreadInfo is an interface for getting information on active threads
@@ -82,22 +94,12 @@ type ThreadInfo interface {
 	CurrentThread() Thread
 }
 
-// GoroutineInfo is an interface for getting information on running goroutines.
-type GoroutineInfo interface {
-	SelectedGoroutine() *G
-	SetSelectedGoroutine(*G)
-}
-
 // ProcessManipulation is an interface for changing the execution state of a process.
 type ProcessManipulation interface {
-	ContinueOnce() (trapthread Thread, err error)
-	SwitchThread(int) error
-	SwitchGoroutine(*G) error
 	RequestManualStop() error
 	// CheckAndClearManualStopRequest returns true the first time it's called
 	// after a call to RequestManualStop.
 	CheckAndClearManualStopRequest() bool
-	Detach(bool) error
 }
 
 // BreakpointManipulation is an interface for managing breakpoints.
diff --git a/pkg/proc/linutil/auxv.go b/pkg/proc/linutil/auxv.go
index 786c3232..a19ea23b 100644
--- a/pkg/proc/linutil/auxv.go
+++ b/pkg/proc/linutil/auxv.go
@@ -6,33 +6,34 @@ import (
 )
 
 const (
-	_AT_NULL_AMD64  = 0
-	_AT_ENTRY_AMD64 = 9
+	_AT_NULL  = 0
+	_AT_ENTRY = 9
 )
 
 // EntryPointFromAuxv searches the elf auxiliary vector for the entry point
 // address.
 // For a description of the auxiliary vector (auxv) format see:
 // System V Application Binary Interface, AMD64 Architecture Processor
-// Supplement, section 3.4.3
-func EntryPointFromAuxvAMD64(auxv []byte) uint64 {
+// Supplement, section 3.4.3.
+// System V Application Binary Interface, Intel386 Architecture Processor
+// Supplement (fourth edition), section 3-28.
+func EntryPointFromAuxv(auxv []byte, ptrSize int) uint64 {
 	rd := bytes.NewBuffer(auxv)
 
 	for {
-		var tag, val uint64
-		err := binary.Read(rd, binary.LittleEndian, &tag)
+		tag, err := readUintRaw(rd, binary.LittleEndian, ptrSize)
 		if err != nil {
 			return 0
 		}
-		err = binary.Read(rd, binary.LittleEndian, &val)
+		val, err := readUintRaw(rd, binary.LittleEndian, ptrSize)
 		if err != nil {
 			return 0
 		}
 
 		switch tag {
-		case _AT_NULL_AMD64:
+		case _AT_NULL:
 			return 0
-		case _AT_ENTRY_AMD64:
+		case _AT_ENTRY:
 			return val
 		}
 	}
diff --git a/pkg/proc/linutil/dynamic.go b/pkg/proc/linutil/dynamic.go
index 1f70ff05..73fd34dc 100644
--- a/pkg/proc/linutil/dynamic.go
+++ b/pkg/proc/linutil/dynamic.go
@@ -5,6 +5,7 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"io"
 
 	"github.com/go-delve/delve/pkg/proc"
 )
@@ -21,6 +22,25 @@ const (
 	_DT_DEBUG = 21 // DT_DEBUG as defined by SysV ABI specification
 )
 
+// readUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
+func readUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) {
+	switch ptrSize {
+	case 4:
+		var n uint32
+		if err := binary.Read(reader, order, &n); err != nil {
+			return 0, err
+		}
+		return uint64(n), nil
+	case 8:
+		var n uint64
+		if err := binary.Read(reader, order, &n); err != nil {
+			return 0, err
+		}
+		return n, nil
+	}
+	return 0, fmt.Errorf("not supprted ptr size %d", ptrSize)
+}
+
 // dynamicSearchDebug searches for the DT_DEBUG entry in the .dynamic section
 func dynamicSearchDebug(p proc.Process) (uint64, error) {
 	bi := p.BinInfo()
@@ -36,10 +56,10 @@ func dynamicSearchDebug(p proc.Process) (uint64, error) {
 
 	for {
 		var tag, val uint64
-		if err := binary.Read(rd, binary.LittleEndian, &tag); err != nil {
+		if tag, err = readUintRaw(rd, binary.LittleEndian, p.BinInfo().Arch.PtrSize()); err != nil {
 			return 0, err
 		}
-		if err := binary.Read(rd, binary.LittleEndian, &val); err != nil {
+		if val, err = readUintRaw(rd, binary.LittleEndian, p.BinInfo().Arch.PtrSize()); err != nil {
 			return 0, err
 		}
 		switch tag {
@@ -51,24 +71,13 @@ func dynamicSearchDebug(p proc.Process) (uint64, error) {
 	}
 }
 
-// hard-coded offsets of the fields of the r_debug and link_map structs, see
-// /usr/include/elf/link.h for a full description of those structs.
-const (
-	_R_DEBUG_MAP_OFFSET   = 8
-	_LINK_MAP_ADDR_OFFSET = 0  // offset of link_map.l_addr field (base address shared object is loaded at)
-	_LINK_MAP_NAME_OFFSET = 8  // offset of link_map.l_name field (absolute file name object was found in)
-	_LINK_MAP_LD          = 16 // offset of link_map.l_ld field (dynamic section of the shared object)
-	_LINK_MAP_NEXT        = 24 // offset of link_map.l_next field
-	_LINK_MAP_PREV        = 32 // offset of link_map.l_prev field
-)
-
 func readPtr(p proc.Process, addr uint64) (uint64, error) {
 	ptrbuf := make([]byte, p.BinInfo().Arch.PtrSize())
 	_, err := p.CurrentThread().ReadMemory(ptrbuf, uintptr(addr))
 	if err != nil {
 		return 0, err
 	}
-	return binary.LittleEndian.Uint64(ptrbuf), nil
+	return readUintRaw(bytes.NewReader(ptrbuf), binary.LittleEndian, p.BinInfo().Arch.PtrSize())
 }
 
 type linkMap struct {
@@ -145,7 +154,11 @@ func ElfUpdateSharedObjects(p proc.Process) error {
 		return nil
 	}
 
-	r_map, err := readPtr(p, debugAddr+_R_DEBUG_MAP_OFFSET)
+	// Offsets of the fields of the r_debug and link_map structs,
+	// see /usr/include/elf/link.h for a full description of those structs.
+	debugMapOffset := uint64(p.BinInfo().Arch.PtrSize())
+
+	r_map, err := readPtr(p, debugAddr+debugMapOffset)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/proc/linutil/regs_i386_arch.go b/pkg/proc/linutil/regs_i386_arch.go
new file mode 100644
index 00000000..6ec04d23
--- /dev/null
+++ b/pkg/proc/linutil/regs_i386_arch.go
@@ -0,0 +1,286 @@
+package linutil
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"github.com/go-delve/delve/pkg/proc"
+	"golang.org/x/arch/x86/x86asm"
+)
+
+// I386Registers implements the proc.Registers interface for the native/linux
+// backend and core/linux backends, on I386.
+type I386Registers struct {
+	Regs     *I386PtraceRegs
+	Fpregs   []proc.Register
+	Fpregset *I386Xstate
+	Tls      uint64
+}
+
+// I386PtraceRegs is the struct used by the linux kernel to return the
+// general purpose registers for I386 CPUs.
+type I386PtraceRegs struct {
+	Ebx      int32
+	Ecx      int32
+	Edx      int32
+	Esi      int32
+	Edi      int32
+	Ebp      int32
+	Eax      int32
+	Xds      int32
+	Xes      int32
+	Xfs      int32
+	Xgs      int32
+	Orig_eax int32
+	Eip      int32
+	Xcs      int32
+	Eflags   int32
+	Esp      int32
+	Xss      int32
+}
+
+// Slice returns the registers as a list of (name, value) pairs.
+func (r *I386Registers) Slice(floatingPoint bool) []proc.Register {
+	var regs = []struct {
+		k string
+		v int32
+	}{
+		{"Ebx", r.Regs.Ebx},
+		{"Ecx", r.Regs.Ecx},
+		{"Edx", r.Regs.Edx},
+		{"Esi", r.Regs.Esi},
+		{"Edi", r.Regs.Edi},
+		{"Ebp", r.Regs.Ebp},
+		{"Eax", r.Regs.Eax},
+		{"Xds", r.Regs.Xds},
+		{"Xes", r.Regs.Xes},
+		{"Xfs", r.Regs.Xfs},
+		{"Xgs", r.Regs.Xgs},
+		{"Orig_eax", r.Regs.Orig_eax},
+		{"Eip", r.Regs.Eip},
+		{"Xcs", r.Regs.Xcs},
+		{"Eflags", r.Regs.Eflags},
+		{"Esp", r.Regs.Esp},
+		{"Xss", r.Regs.Xss},
+	}
+	out := make([]proc.Register, 0, len(regs)+len(r.Fpregs))
+	for _, reg := range regs {
+		out = proc.AppendUint64Register(out, reg.k, uint64(uint32(reg.v)))
+	}
+	if floatingPoint {
+		out = append(out, r.Fpregs...)
+	}
+	return out
+}
+
+// PC returns the value of EIP register.
+func (r *I386Registers) PC() uint64 {
+	return uint64(uint32(r.Regs.Eip))
+}
+
+// SP returns the value of ESP register.
+func (r *I386Registers) SP() uint64 {
+	return uint64(uint32(r.Regs.Esp))
+}
+
+func (r *I386Registers) BP() uint64 {
+	return uint64(uint32(r.Regs.Ebp))
+}
+
+// CX returns the value of ECX register.
+func (r *I386Registers) CX() uint64 {
+	return uint64(uint32(r.Regs.Ecx))
+}
+
+// TLS returns the address of the thread local storage memory segment.
+func (r I386Registers) TLS() uint64 {
+	return r.Tls
+}
+
+// GAddr returns the address of the G variable if it is known, 0 and false
+// otherwise.
+func (r *I386Registers) GAddr() (uint64, bool) {
+	return 0, false
+}
+
+// Get returns the value of the n-th register (in x86asm order).
+func (r *I386Registers) Get(n int) (uint64, error) {
+	reg := x86asm.Reg(n)
+	const (
+		mask8  = 0x000000ff
+		mask16 = 0x0000ffff
+	)
+
+	switch reg {
+	// 8-bit
+	case x86asm.AL:
+		return uint64(r.Regs.Eax) & mask8, nil
+	case x86asm.CL:
+		return uint64(r.Regs.Ecx) & mask8, nil
+	case x86asm.DL:
+		return uint64(r.Regs.Edx) & mask8, nil
+	case x86asm.BL:
+		return uint64(r.Regs.Ebx) & mask8, nil
+	case x86asm.AH:
+		return (uint64(r.Regs.Eax) >> 8) & mask8, nil
+	case x86asm.CH:
+		return (uint64(r.Regs.Ecx) >> 8) & mask8, nil
+	case x86asm.DH:
+		return (uint64(r.Regs.Edx) >> 8) & mask8, nil
+	case x86asm.BH:
+		return (uint64(r.Regs.Ebx) >> 8) & mask8, nil
+	case x86asm.SPB:
+		return uint64(r.Regs.Esp) & mask8, nil
+	case x86asm.BPB:
+		return uint64(r.Regs.Ebp) & mask8, nil
+	case x86asm.SIB:
+		return uint64(r.Regs.Esi) & mask8, nil
+	case x86asm.DIB:
+		return uint64(r.Regs.Edi) & mask8, nil
+
+	// 16-bit
+	case x86asm.AX:
+		return uint64(r.Regs.Eax) & mask16, nil
+	case x86asm.CX:
+		return uint64(r.Regs.Ecx) & mask16, nil
+	case x86asm.DX:
+		return uint64(r.Regs.Edx) & mask16, nil
+	case x86asm.BX:
+		return uint64(r.Regs.Ebx) & mask16, nil
+	case x86asm.SP:
+		return uint64(r.Regs.Esp) & mask16, nil
+	case x86asm.BP:
+		return uint64(r.Regs.Ebp) & mask16, nil
+	case x86asm.SI:
+		return uint64(r.Regs.Esi) & mask16, nil
+	case x86asm.DI:
+		return uint64(r.Regs.Edi) & mask16, nil
+
+	// 32-bit
+	case x86asm.EAX:
+		return uint64(uint32(r.Regs.Eax)), nil
+	case x86asm.ECX:
+		return uint64(uint32(r.Regs.Ecx)), nil
+	case x86asm.EDX:
+		return uint64(uint32(r.Regs.Edx)), nil
+	case x86asm.EBX:
+		return uint64(uint32(r.Regs.Ebx)), nil
+	case x86asm.ESP:
+		return uint64(uint32(r.Regs.Esp)), nil
+	case x86asm.EBP:
+		return uint64(uint32(r.Regs.Ebp)), nil
+	case x86asm.ESI:
+		return uint64(uint32(r.Regs.Esi)), nil
+	case x86asm.EDI:
+		return uint64(uint32(r.Regs.Edi)), nil
+	}
+	return 0, proc.ErrUnknownRegister
+}
+
+// Copy returns a copy of these registers that is guarenteed not to change.
+func (r *I386Registers) Copy() proc.Registers {
+	var rr I386Registers
+	rr.Regs = &I386PtraceRegs{}
+	rr.Fpregset = &I386Xstate{}
+	*(rr.Regs) = *(r.Regs)
+	if r.Fpregset != nil {
+		*(rr.Fpregset) = *(r.Fpregset)
+	}
+	if r.Fpregs != nil {
+		rr.Fpregs = make([]proc.Register, len(r.Fpregs))
+		copy(rr.Fpregs, r.Fpregs)
+	}
+	return &rr
+}
+
+// I386PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
+type I386PtraceFpRegs struct {
+	Cwd      uint16
+	Swd      uint16
+	Ftw      uint16
+	Fop      uint16
+	Rip      uint64
+	Rdp      uint64
+	Mxcsr    uint32
+	MxcrMask uint32
+	StSpace  [32]uint32
+	XmmSpace [256]byte
+	Padding  [24]uint32
+}
+
+// I386Xstate represents amd64 XSAVE area. See Section 13.1 (and
+// following) of Intel® 64 and IA-32 Architectures Software Developer’s
+// Manual, Volume 1: Basic Architecture.
+type I386Xstate struct {
+	I386PtraceFpRegs
+	Xsave    []byte // raw xsave area
+	AvxState bool   // contains AVX state
+	YmmSpace [256]byte
+}
+
+// Decode decodes an XSAVE area to a list of name/value pairs of registers.
+func (xsave *I386Xstate) Decode() (regs []proc.Register) {
+	// x87 registers
+	regs = proc.AppendUint64Register(regs, "CW", uint64(xsave.Cwd))
+	regs = proc.AppendUint64Register(regs, "SW", uint64(xsave.Swd))
+	regs = proc.AppendUint64Register(regs, "TW", uint64(xsave.Ftw))
+	regs = proc.AppendUint64Register(regs, "FOP", uint64(xsave.Fop))
+	regs = proc.AppendUint64Register(regs, "FIP", uint64(xsave.Rip))
+	regs = proc.AppendUint64Register(regs, "FDP", uint64(xsave.Rdp))
+
+	for i := 0; i < len(xsave.StSpace); i += 4 {
+		var buf bytes.Buffer
+		binary.Write(&buf, binary.LittleEndian, uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
+		binary.Write(&buf, binary.LittleEndian, uint16(xsave.StSpace[i+2]))
+		regs = proc.AppendBytesRegister(regs, fmt.Sprintf("ST(%d)", i/4), buf.Bytes())
+	}
+
+	// SSE registers
+	regs = proc.AppendUint64Register(regs, "MXCSR", uint64(xsave.Mxcsr))
+	regs = proc.AppendUint64Register(regs, "MXCSR_MASK", uint64(xsave.MxcrMask))
+
+	for i := 0; i < len(xsave.XmmSpace); i += 16 {
+		regs = proc.AppendBytesRegister(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
+		if xsave.AvxState {
+			regs = proc.AppendBytesRegister(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
+		}
+	}
+
+	return
+}
+
+// LinuxX86XstateRead reads a byte array containing an XSAVE area into regset.
+// If readLegacy is true regset.PtraceFpRegs will be filled with the
+// contents of the legacy region of the XSAVE area.
+// See Section 13.1 (and following) of Intel® 64 and IA-32 Architectures
+// Software Developer’s Manual, Volume 1: Basic Architecture.
+func I386XstateRead(xstateargs []byte, readLegacy bool, regset *I386Xstate) error {
+	if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= len(xstateargs) {
+		return nil
+	}
+	if readLegacy {
+		rdr := bytes.NewReader(xstateargs[:_XSAVE_HEADER_START])
+		if err := binary.Read(rdr, binary.LittleEndian, &regset.I386PtraceFpRegs); err != nil {
+			return err
+		}
+	}
+	xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
+	xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
+	xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
+
+	if xcomp_bv&(1<<63) != 0 {
+		// compact format not supported
+		return nil
+	}
+
+	if xstate_bv&(1<<2) == 0 {
+		// AVX state not present
+		return nil
+	}
+
+	avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:]
+	regset.AvxState = true
+	copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
+
+	return nil
+}
diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go
index 5bff11b6..9299843d 100644
--- a/pkg/proc/native/proc.go
+++ b/pkg/proc/native/proc.go
@@ -1,7 +1,6 @@
 package native
 
 import (
-	"fmt"
 	"go/ast"
 	"runtime"
 	"sync"
@@ -26,10 +25,6 @@ type Process struct {
 	// Active thread
 	currentThread *Thread
 
-	// Goroutine that will be used by default to set breakpoint, eval variables, etc...
-	// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
-	selectedGoroutine *proc.G
-
 	os                  *OSProcessDetails
 	firstStart          bool
 	stopMu              sync.Mutex
@@ -42,6 +37,8 @@ type Process struct {
 	exited, detached bool
 }
 
+var _ proc.ProcessInternal = &Process{}
+
 // New returns an initialized Process struct. Before returning,
 // it will also launch a goroutine in order to handle ptrace(2)
 // functions. For more information, see the documentation on
@@ -73,9 +70,17 @@ func (dbp *Process) Recorded() (bool, string) { return false, "" }
 // recorded traces.
 func (dbp *Process) Restart(string) error { return proc.ErrNotRecorded }
 
-// Direction will always return an error in the native proc backend, only for
+// ChangeDirection will always return an error in the native proc backend, only for
 // recorded traces.
-func (dbp *Process) Direction(proc.Direction) error { return proc.ErrNotRecorded }
+func (dbp *Process) ChangeDirection(dir proc.Direction) error {
+	if dir != proc.Forward {
+		return proc.ErrNotRecorded
+	}
+	return nil
+}
+
+// GetDirection will always return Forward.
+func (p *Process) GetDirection() proc.Direction { return proc.Forward }
 
 // When will always return an empty string and nil, not supported on native proc backend.
 func (dbp *Process) When() (string, error) { return "", nil }
@@ -151,12 +156,6 @@ func (dbp *Process) Pid() int {
 	return dbp.pid
 }
 
-// SelectedGoroutine returns the current selected,
-// active goroutine.
-func (dbp *Process) SelectedGoroutine() *proc.G {
-	return dbp.selectedGoroutine
-}
-
 // ThreadList returns a list of threads in the process.
 func (dbp *Process) ThreadList() []proc.Thread {
 	r := make([]proc.Thread, 0, len(dbp.threads))
@@ -177,6 +176,11 @@ func (dbp *Process) CurrentThread() proc.Thread {
 	return dbp.currentThread
 }
 
+// SetCurrentThread is used internally by proc.Target to change the current thread.
+func (p *Process) SetCurrentThread(th proc.Thread) {
+	p.currentThread = th.(*Thread)
+}
+
 // Breakpoints returns a list of breakpoints currently set.
 func (dbp *Process) Breakpoints() *proc.BreakpointMap {
 	return &dbp.breakpoints
@@ -237,13 +241,13 @@ func (dbp *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
 
 // ContinueOnce will continue the target until it stops.
 // This could be the result of a breakpoint or signal.
-func (dbp *Process) ContinueOnce() (proc.Thread, error) {
+func (dbp *Process) ContinueOnce() (proc.Thread, proc.StopReason, error) {
 	if dbp.exited {
-		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
+		return nil, proc.StopExited, &proc.ErrProcessExited{Pid: dbp.Pid()}
 	}
 
 	if err := dbp.resume(); err != nil {
-		return nil, err
+		return nil, proc.StopUnknown, err
 	}
 
 	for _, th := range dbp.threads {
@@ -257,42 +261,12 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
 
 	trapthread, err := dbp.trapWait(-1)
 	if err != nil {
-		return nil, err
+		return nil, proc.StopUnknown, err
 	}
 	if err := dbp.stop(trapthread); err != nil {
-		return nil, err
+		return nil, proc.StopUnknown, err
 	}
-	return trapthread, err
-}
-
-// SwitchThread changes from current thread to the thread specified by `tid`.
-func (dbp *Process) SwitchThread(tid int) error {
-	if dbp.exited {
-		return &proc.ErrProcessExited{Pid: dbp.Pid()}
-	}
-	if th, ok := dbp.threads[tid]; ok {
-		dbp.currentThread = th
-		dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
-		return nil
-	}
-	return fmt.Errorf("thread %d does not exist", tid)
-}
-
-// SwitchGoroutine changes from current thread to the thread
-// running the specified goroutine.
-func (dbp *Process) SwitchGoroutine(g *proc.G) error {
-	if dbp.exited {
-		return &proc.ErrProcessExited{Pid: dbp.Pid()}
-	}
-	if g == nil {
-		// user specified -1 and selectedGoroutine is nil
-		return nil
-	}
-	if g.Thread != nil {
-		return dbp.SwitchThread(g.Thread.ThreadID())
-	}
-	dbp.selectedGoroutine = g
-	return nil
+	return trapthread, proc.StopUnknown, err
 }
 
 // FindBreakpoint finds the breakpoint for the given pc.
@@ -312,21 +286,23 @@ func (dbp *Process) FindBreakpoint(pc uint64, adjustPC bool) (*proc.Breakpoint,
 
 // initialize will ensure that all relevant information is loaded
 // so the process is ready to be debugged.
-func (dbp *Process) initialize(path string, debugInfoDirs []string) error {
+func (dbp *Process) initialize(path string, debugInfoDirs []string) (*proc.Target, error) {
 	if err := initialize(dbp); err != nil {
-		return err
+		return nil, err
 	}
 	if err := dbp.updateThreadList(); err != nil {
-		return err
+		return nil, err
 	}
-	return proc.PostInitializationSetup(dbp, path, debugInfoDirs, dbp.writeBreakpoint)
-}
-
-// SetSelectedGoroutine will set internally the goroutine that should be
-// the default for any command executed, the goroutine being actively
-// followed.
-func (dbp *Process) SetSelectedGoroutine(g *proc.G) {
-	dbp.selectedGoroutine = g
+	stopReason := proc.StopLaunched
+	if !dbp.childProcess {
+		stopReason = proc.StopAttached
+	}
+	return proc.NewTarget(dbp, proc.NewTargetConfig{
+		Path:                path,
+		DebugInfoDirs:       debugInfoDirs,
+		WriteBreakpoint:     dbp.writeBreakpoint,
+		DisableAsyncPreempt: runtime.GOOS == "windows",
+		StopReason:          stopReason})
 }
 
 // ClearInternalBreakpoints will clear all non-user set breakpoints. These
diff --git a/pkg/proc/native/proc_darwin.go b/pkg/proc/native/proc_darwin.go
index 41a1eaca..bbc8e1af 100644
--- a/pkg/proc/native/proc_darwin.go
+++ b/pkg/proc/native/proc_darwin.go
@@ -117,16 +117,14 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target,
 	}
 
 	dbp.os.initialized = true
-	err = dbp.initialize(argv0Go, []string{})
-	if err != nil {
-		return nil, err
-	}
+	dbp.currentThread = trapthread
 
-	if err := dbp.SwitchThread(trapthread.ID); err != nil {
+	tgt, err := dbp.initialize(argv0Go, []string{})
+	if err != nil {
 		return nil, err
 	}
 
-	return proc.NewTarget(dbp, false), err
+	return tgt, err
 }
 
 // Attach to an existing process with the given PID.
@@ -153,12 +151,12 @@ func Attach(pid int, _ []string) (*proc.Target, error) {
 		return nil, err
 	}
 
-	err = dbp.initialize("", []string{})
+	tgt, err := dbp.initialize("", []string{})
 	if err != nil {
 		dbp.Detach(false)
 		return nil, err
 	}
-	return proc.NewTarget(dbp, false), nil
+	return tgt, nil
 }
 
 // Kill kills the process.
@@ -269,7 +267,7 @@ func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
 	dbp.threads[port] = thread
 	thread.os.threadAct = C.thread_act_t(port)
 	if dbp.currentThread == nil {
-		dbp.SwitchThread(thread.ID)
+		dbp.currentThread = thread
 	}
 	return thread, nil
 }
diff --git a/pkg/proc/native/proc_freebsd.go b/pkg/proc/native/proc_freebsd.go
index f24d6004..b3ae6e85 100644
--- a/pkg/proc/native/proc_freebsd.go
+++ b/pkg/proc/native/proc_freebsd.go
@@ -84,10 +84,11 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
 	if err != nil {
 		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
 	}
-	if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
+	tgt, err := dbp.initialize(cmd[0], debugInfoDirs)
+	if err != nil {
 		return nil, err
 	}
-	return proc.NewTarget(dbp, false), nil
+	return tgt, nil
 }
 
 // Attach to an existing process with the given PID. Once attached, if
@@ -106,12 +107,12 @@ func Attach(pid int, debugInfoDirs []string) (*proc.Target, error) {
 		return nil, err
 	}
 
-	err = dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
+	tgt, err := dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
 	if err != nil {
 		dbp.Detach(false)
 		return nil, err
 	}
-	return proc.NewTarget(dbp, false), nil
+	return tgt, nil
 }
 
 func initialize(dbp *Process) error {
@@ -165,7 +166,7 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
 	}
 
 	if dbp.currentThread == nil {
-		dbp.SwitchThread(tid)
+		dbp.currentThread = dbp.threads[tid]
 	}
 
 	return dbp.threads[tid], nil
diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go
index 75dece1e..61368b96 100644
--- a/pkg/proc/native/proc_linux.go
+++ b/pkg/proc/native/proc_linux.go
@@ -90,10 +90,11 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
 	if err != nil {
 		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
 	}
-	if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
+	tgt, err := dbp.initialize(cmd[0], debugInfoDirs)
+	if err != nil {
 		return nil, err
 	}
-	return proc.NewTarget(dbp, false), nil
+	return tgt, nil
 }
 
 // Attach to an existing process with the given PID. Once attached, if
@@ -112,7 +113,7 @@ func Attach(pid int, debugInfoDirs []string) (*proc.Target, error) {
 		return nil, err
 	}
 
-	err = dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
+	tgt, err := dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
 	if err != nil {
 		dbp.Detach(false)
 		return nil, err
@@ -124,7 +125,7 @@ func Attach(pid int, debugInfoDirs []string) (*proc.Target, error) {
 	if err != nil {
 		return nil, err
 	}
-	return proc.NewTarget(dbp, false), nil
+	return tgt, nil
 }
 
 func initialize(dbp *Process) error {
@@ -222,7 +223,7 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
 		os:  new(OSSpecificDetails),
 	}
 	if dbp.currentThread == nil {
-		dbp.SwitchThread(tid)
+		dbp.currentThread = dbp.threads[tid]
 	}
 	return dbp.threads[tid], nil
 }
@@ -545,7 +546,7 @@ func (dbp *Process) EntryPoint() (uint64, error) {
 		return 0, fmt.Errorf("could not read auxiliary vector: %v", err)
 	}
 
-	return linutil.EntryPointFromAuxvAMD64(auxvbuf), nil
+	return linutil.EntryPointFromAuxv(auxvbuf, dbp.bi.Arch.PtrSize()), nil
 }
 
 func killProcess(pid int) error {
diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go
index 63a2b969..4e3c56ca 100644
--- a/pkg/proc/native/proc_windows.go
+++ b/pkg/proc/native/proc_windows.go
@@ -78,11 +78,12 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target,
 	dbp.pid = p.Pid
 	dbp.childProcess = true
 
-	if err = dbp.initialize(argv0Go, []string{}); err != nil {
+	tgt, err := dbp.initialize(argv0Go, []string{})
+	if err != nil {
 		dbp.Detach(true)
 		return nil, err
 	}
-	return proc.NewTarget(dbp, true), nil
+	return tgt, nil
 }
 
 func initialize(dbp *Process) error {
@@ -167,11 +168,12 @@ func Attach(pid int, _ []string) (*proc.Target, error) {
 	if err != nil {
 		return nil, err
 	}
-	if err = dbp.initialize(exepath, []string{}); err != nil {
+	tgt, err := dbp.initialize(exepath, []string{})
+	if err != nil {
 		dbp.Detach(true)
 		return nil, err
 	}
-	return proc.NewTarget(dbp, true), nil
+	return tgt, nil
 }
 
 // kill kills the process.
@@ -223,7 +225,7 @@ func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, susp
 	thread.os.hThread = hThread
 	dbp.threads[threadID] = thread
 	if dbp.currentThread == nil {
-		dbp.SwitchThread(thread.ID)
+		dbp.currentThread = dbp.threads[threadID]
 	}
 	if suspendNewThreads {
 		_, err := _SuspendThread(thread.os.hThread)
diff --git a/pkg/proc/native/ptrace_linux.go b/pkg/proc/native/ptrace_linux.go
index 849516d8..ab4f5372 100644
--- a/pkg/proc/native/ptrace_linux.go
+++ b/pkg/proc/native/ptrace_linux.go
@@ -49,31 +49,3 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
 	}
 	return val, nil
 }
-
-// ProcessVmRead calls process_vm_readv
-func ProcessVmRead(tid int, addr uintptr, data []byte) (int, error) {
-	len_iov := uint64(len(data))
-	local_iov := sys.Iovec{Base: &data[0], Len: len_iov}
-	remote_iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(addr)), Len: len_iov}
-	p_local := uintptr(unsafe.Pointer(&local_iov))
-	p_remote := uintptr(unsafe.Pointer(&remote_iov))
-	n, _, err := syscall.Syscall6(sys.SYS_PROCESS_VM_READV, uintptr(tid), p_local, 1, p_remote, 1, 0)
-	if err != syscall.Errno(0) {
-		return 0, err
-	}
-	return int(n), nil
-}
-
-// ProcessVmWrite calls process_vm_writev
-func ProcessVmWrite(tid int, addr uintptr, data []byte) (int, error) {
-	len_iov := uint64(len(data))
-	local_iov := sys.Iovec{Base: &data[0], Len: len_iov}
-	remote_iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(addr)), Len: len_iov}
-	p_local := uintptr(unsafe.Pointer(&local_iov))
-	p_remote := uintptr(unsafe.Pointer(&remote_iov))
-	n, _, err := syscall.Syscall6(sys.SYS_PROCESS_VM_WRITEV, uintptr(tid), p_local, 1, p_remote, 1, 0)
-	if err != syscall.Errno(0) {
-		return 0, err
-	}
-	return int(n), nil
-}
diff --git a/pkg/proc/native/ptrace_linux_386.go b/pkg/proc/native/ptrace_linux_386.go
new file mode 100644
index 00000000..269f95e2
--- /dev/null
+++ b/pkg/proc/native/ptrace_linux_386.go
@@ -0,0 +1,93 @@
+package native
+
+import "C"
+
+import (
+	"fmt"
+	"syscall"
+	"unsafe"
+
+	sys "golang.org/x/sys/unix"
+
+	"github.com/go-delve/delve/pkg/proc/linutil"
+)
+
+// PtraceGetRegset returns floating point registers of the specified thread
+// using PTRACE.
+// See i386_linux_fetch_inferior_registers in gdb/i386-linux-nat.c.html
+// and i386_supply_xsave in gdb/i386-tdep.c.html
+// and Section 13.1 (and following) of Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture
+func PtraceGetRegset(tid int) (regset linutil.I386Xstate, err error) {
+	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(&regset.I386PtraceFpRegs)), 0, 0)
+	if err == syscall.Errno(0) || err == syscall.ENODEV {
+		// ignore ENODEV, it just means this CPU doesn't have X87 registers (??)
+		err = nil
+	}
+
+	var xstateargs [_X86_XSTATE_MAX_SIZE]byte
+	iov := sys.Iovec{Base: &xstateargs[0], Len: _X86_XSTATE_MAX_SIZE}
+	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(tid), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
+	if err != syscall.Errno(0) {
+		if err == syscall.ENODEV || err == syscall.EIO {
+			// ignore ENODEV, it just means this CPU or kernel doesn't support XSTATE, see https://github.com/go-delve/delve/issues/1022
+			// also ignore EIO, it means that we are running on an old kernel (pre 2.6.34) and PTRACE_GETREGSET is not implemented
+			err = nil
+		}
+		return
+	} else {
+		err = nil
+	}
+
+	regset.Xsave = xstateargs[:iov.Len]
+	err = linutil.I386XstateRead(regset.Xsave, false, &regset)
+	return
+}
+
+// PtraceGetTls return the addr of tls by PTRACE_GET_THREAD_AREA for specify thread.
+// See http://man7.org/linux/man-pages/man2/ptrace.2.html for detail about PTRACE_GET_THREAD_AREA.
+// struct user_desc at https://golang.org/src/runtime/sys_linux_386.s
+// type UserDesc struct {
+// 	 EntryNumber uint32
+//	 BaseAddr    uint32
+//	 Limit       uint32
+//	 Flag        uint32
+// }
+func PtraceGetTls(gs int32, tid int) (uint32, error) {
+	ud := [4]uint32{}
+
+	// Gs usually is 0x33
+	_, _, err := syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GET_THREAD_AREA, uintptr(tid), uintptr(gs>>3), uintptr(unsafe.Pointer(&ud)), 0, 0)
+	if err == syscall.ENODEV || err == syscall.EIO {
+		return 0, fmt.Errorf("%s", err)
+	}
+
+	return uint32(ud[1]), nil
+}
+
+// ProcessVmRead calls process_vm_readv
+func ProcessVmRead(tid int, addr uintptr, data []byte) (int, error) {
+	len_iov := uint32(len(data))
+	local_iov := sys.Iovec{Base: &data[0], Len: len_iov}
+	remote_iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(addr)), Len: len_iov}
+	p_local := uintptr(unsafe.Pointer(&local_iov))
+	p_remote := uintptr(unsafe.Pointer(&remote_iov))
+	n, _, err := syscall.Syscall6(sys.SYS_PROCESS_VM_READV, uintptr(tid), p_local, 1, p_remote, 1, 0)
+	if err != syscall.Errno(0) {
+		return 0, err
+	}
+	return int(n), nil
+}
+
+// ProcessVmWrite calls process_vm_writev
+func ProcessVmWrite(tid int, addr uintptr, data []byte) (int, error) {
+	len_iov := uint32(len(data))
+	local_iov := sys.Iovec{Base: &data[0], Len: len_iov}
+	remote_iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(addr)), Len: len_iov}
+	p_local := uintptr(unsafe.Pointer(&local_iov))
+	p_remote := uintptr(unsafe.Pointer(&remote_iov))
+	n, _, err := syscall.Syscall6(sys.SYS_PROCESS_VM_WRITEV, uintptr(tid), p_local, 1, p_remote, 1, 0)
+	if err != syscall.Errno(0) {
+		return 0, err
+	}
+	return int(n), nil
+}
diff --git a/pkg/proc/native/ptrace_linux_64bit.go b/pkg/proc/native/ptrace_linux_64bit.go
new file mode 100644
index 00000000..9206f046
--- /dev/null
+++ b/pkg/proc/native/ptrace_linux_64bit.go
@@ -0,0 +1,38 @@
+// +build linux,amd64 linux,arm64
+
+package native
+
+import (
+	"syscall"
+	"unsafe"
+
+	sys "golang.org/x/sys/unix"
+)
+
+// ProcessVmRead calls process_vm_readv
+func ProcessVmRead(tid int, addr uintptr, data []byte) (int, error) {
+	len_iov := uint64(len(data))
+	local_iov := sys.Iovec{Base: &data[0], Len: len_iov}
+	remote_iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(addr)), Len: len_iov}
+	p_local := uintptr(unsafe.Pointer(&local_iov))
+	p_remote := uintptr(unsafe.Pointer(&remote_iov))
+	n, _, err := syscall.Syscall6(sys.SYS_PROCESS_VM_READV, uintptr(tid), p_local, 1, p_remote, 1, 0)
+	if err != syscall.Errno(0) {
+		return 0, err
+	}
+	return int(n), nil
+}
+
+// ProcessVmWrite calls process_vm_writev
+func ProcessVmWrite(tid int, addr uintptr, data []byte) (int, error) {
+	len_iov := uint64(len(data))
+	local_iov := sys.Iovec{Base: &data[0], Len: len_iov}
+	remote_iov := sys.Iovec{Base: (*byte)(unsafe.Pointer(addr)), Len: len_iov}
+	p_local := uintptr(unsafe.Pointer(&local_iov))
+	p_remote := uintptr(unsafe.Pointer(&remote_iov))
+	n, _, err := syscall.Syscall6(sys.SYS_PROCESS_VM_WRITEV, uintptr(tid), p_local, 1, p_remote, 1, 0)
+	if err != syscall.Errno(0) {
+		return 0, err
+	}
+	return int(n), nil
+}
diff --git a/pkg/proc/native/ptrace_linux_amd64.go b/pkg/proc/native/ptrace_linux_amd64.go
index 089410fa..5532c76f 100644
--- a/pkg/proc/native/ptrace_linux_amd64.go
+++ b/pkg/proc/native/ptrace_linux_amd64.go
@@ -38,4 +38,4 @@ func PtraceGetRegset(tid int) (regset linutil.AMD64Xstate, err error) {
 	regset.Xsave = xstateargs[:iov.Len]
 	err = linutil.AMD64XstateRead(regset.Xsave, false, &regset)
 	return
-}
+}
\ No newline at end of file
diff --git a/pkg/proc/native/register_linux_386.go b/pkg/proc/native/register_linux_386.go
new file mode 100644
index 00000000..9eb7d162
--- /dev/null
+++ b/pkg/proc/native/register_linux_386.go
@@ -0,0 +1,91 @@
+package native
+
+import (
+	"fmt"
+
+	sys "golang.org/x/sys/unix"
+
+	"github.com/go-delve/delve/pkg/proc"
+	"github.com/go-delve/delve/pkg/proc/linutil"
+)
+
+// SetPC sets EIP to the value specified by 'pc'.
+func (thread *Thread) SetPC(pc uint64) error {
+	ir, err := registers(thread, false)
+	if err != nil {
+		return err
+	}
+	r := ir.(*linutil.I386Registers)
+	r.Regs.Eip = int32(pc)
+	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
+	return err
+}
+
+// SetSP sets ESP to the value specified by 'sp'
+func (thread *Thread) SetSP(sp uint64) (err error) {
+	var ir proc.Registers
+	ir, err = registers(thread, false)
+	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 *Thread) SetDX(dx uint64) (err error) {
+	var ir proc.Registers
+	ir, err = registers(thread, false)
+	if err != nil {
+		return err
+	}
+	r := ir.(*linutil.I386Registers)
+	r.Regs.Edx = int32(dx)
+	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
+	return
+}
+
+func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
+	var (
+		regs linutil.I386PtraceRegs
+		err  error
+	)
+	thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, (*sys.PtraceRegs)(&regs)) })
+	if err != nil {
+		return nil, err
+	}
+	r := &linutil.I386Registers{&regs, nil, nil, 0}
+	if floatingPoint {
+		var fpregset linutil.I386Xstate
+		r.Fpregs, fpregset, err = thread.fpRegisters()
+		r.Fpregset = &fpregset
+		if err != nil {
+			return nil, err
+		}
+	}
+	thread.dbp.execPtraceFunc(func() {
+		tls, _ := PtraceGetTls(regs.Xgs, thread.ThreadID())
+		r.Tls = uint64(tls)
+	})
+	return r, nil
+}
+
+const (
+	_X86_XSTATE_MAX_SIZE = 2688
+	_NT_X86_XSTATE       = 0x202
+
+	_XSAVE_HEADER_START          = 512
+	_XSAVE_HEADER_LEN            = 64
+	_XSAVE_EXTENDED_REGION_START = 576
+	_XSAVE_SSE_REGION_LEN        = 416
+)
+
+func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs linutil.I386Xstate, err error) {
+	thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
+	regs = fpregs.Decode()
+	if err != nil {
+		err = fmt.Errorf("could not get floating point registers: %v", err.Error())
+	}
+	return
+}
diff --git a/pkg/proc/native/support_sentinel.go b/pkg/proc/native/support_sentinel.go
index 826a021f..8dcdb5a8 100644
--- a/pkg/proc/native/support_sentinel.go
+++ b/pkg/proc/native/support_sentinel.go
@@ -1,5 +1,5 @@
 // This file is used to detect build on unsupported GOOS/GOARCH combinations.
 
-//+build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64 darwin,!amd64 windows,!amd64 freebsd,!amd64
+//+build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64,!386 darwin,!amd64 windows,!amd64 freebsd,!amd64
 
 package your_operating_system_and_architecture_combination_is_not_supported_by_delve
diff --git a/pkg/proc/native/threads_linux_386.go b/pkg/proc/native/threads_linux_386.go
new file mode 100644
index 00000000..02e2972f
--- /dev/null
+++ b/pkg/proc/native/threads_linux_386.go
@@ -0,0 +1,10 @@
+package native
+
+import (
+	"fmt"
+	"github.com/go-delve/delve/pkg/proc"
+)
+
+func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
+	return fmt.Errorf("restore regs not supported on i386")
+}
diff --git a/pkg/proc/pe.go b/pkg/proc/pe.go
new file mode 100644
index 00000000..21a9a27a
--- /dev/null
+++ b/pkg/proc/pe.go
@@ -0,0 +1,66 @@
+package proc
+
+import (
+	"fmt"
+)
+
+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
+)
+
+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",
+}
+
+func (m PEMachine) String() string {
+	str, ok := PEMachineString[uint16(m)]
+	if ok {
+		return str
+	}
+	return fmt.Sprintf("unkown image file machine code %d\n", uint16(m))
+}
diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go
index 5121872c..72bbe6eb 100644
--- a/pkg/proc/proc.go
+++ b/pkg/proc/proc.go
@@ -8,7 +8,6 @@ import (
 	"go/constant"
 	"go/token"
 	"os"
-	"path/filepath"
 	"strconv"
 	"strings"
 
@@ -55,33 +54,6 @@ func (pe ProcessDetachedError) Error() string {
 	return "detached from the process"
 }
 
-// PostInitializationSetup handles all of the initialization procedures
-// that must happen after Delve creates or attaches to a process.
-func PostInitializationSetup(p Process, path string, debugInfoDirs []string, writeBreakpoint WriteBreakpointFn) error {
-	entryPoint, err := p.EntryPoint()
-	if err != nil {
-		return err
-	}
-
-	err = p.BinInfo().LoadBinaryInfo(path, entryPoint, debugInfoDirs)
-	if err != nil {
-		return err
-	}
-	for _, image := range p.BinInfo().Images {
-		if image.loadErr != nil {
-			return image.loadErr
-		}
-	}
-
-	g, _ := GetG(p.CurrentThread())
-	p.SetSelectedGoroutine(g)
-
-	createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
-	createFatalThrowBreakpoint(p, writeBreakpoint)
-
-	return nil
-}
-
 // FindFileLocation returns the PC for a given file:line.
 // Assumes that `file` is normalized to lower case and '/' on Windows.
 func FindFileLocation(p Process, fileName string, lineno int) ([]uint64, error) {
@@ -175,19 +147,25 @@ func Continue(dbp *Target) error {
 		// Make sure we clear internal breakpoints if we simultaneously receive a
 		// manual stop request and hit a breakpoint.
 		if dbp.CheckAndClearManualStopRequest() {
+			dbp.StopReason = StopManual
 			dbp.ClearInternalBreakpoints()
 		}
 	}()
 	for {
 		if dbp.CheckAndClearManualStopRequest() {
+			dbp.StopReason = StopManual
 			dbp.ClearInternalBreakpoints()
 			return nil
 		}
 		dbp.ClearAllGCache()
-		trapthread, err := dbp.ContinueOnce()
+		trapthread, stopReason, err := dbp.proc.ContinueOnce()
+		dbp.StopReason = stopReason
 		if err != nil {
 			return err
 		}
+		if dbp.StopReason == StopLaunched {
+			dbp.ClearInternalBreakpoints()
+		}
 
 		threads := dbp.ThreadList()
 
@@ -232,6 +210,7 @@ func Continue(dbp *Target) error {
 				if err := stepInstructionOut(dbp, curthread, "runtime.breakpoint", "runtime.Breakpoint"); err != nil {
 					return err
 				}
+				dbp.StopReason = StopHardcodedBreakpoint
 				return conditionErrors(threads)
 			case g == nil || dbp.fncallForG[g.ID] == nil:
 				// a hardcoded breakpoint somewhere else in the code (probably cgo), or manual stop in cgo
@@ -252,26 +231,26 @@ func Continue(dbp *Target) error {
 				if err := conditionErrors(threads); err != nil {
 					return err
 				}
-				regs, err := curthread.Registers(false)
-				if err != nil {
-					return err
-				}
-				pc := regs.PC()
-				text, err := disassemble(curthread, regs, dbp.Breakpoints(), dbp.BinInfo(), pc, pc+uint64(dbp.BinInfo().Arch.MaxInstructionLength()), true)
-				if err != nil {
-					return err
-				}
-				// here we either set a breakpoint into the destination of the CALL
-				// instruction or we determined that the called function is hidden,
-				// either way we need to resume execution
-				if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
-					return err
+				if dbp.GetDirection() == Forward {
+					text, err := disassembleCurrentInstruction(dbp, curthread)
+					// here we either set a breakpoint into the destination of the CALL
+					// instruction or we determined that the called function is hidden,
+					// either way we need to resume execution
+					if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
+						return err
+					}
+				} else {
+					if err := dbp.ClearInternalBreakpoints(); err != nil {
+						return err
+					}
+					return StepInstruction(dbp)
 				}
 			default:
 				curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(curthread)
 				if err := dbp.ClearInternalBreakpoints(); err != nil {
 					return err
 				}
+				dbp.StopReason = StopNextFinished
 				return conditionErrors(threads)
 			}
 		case curbp.Active:
@@ -288,6 +267,7 @@ func Continue(dbp *Target) error {
 			if curbp.Name == UnrecoveredPanic {
 				dbp.ClearInternalBreakpoints()
 			}
+			dbp.StopReason = StopBreakpoint
 			return conditionErrors(threads)
 		default:
 			// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
@@ -295,6 +275,7 @@ func Continue(dbp *Target) error {
 		if callInjectionDone {
 			// a call injection was finished, don't let a breakpoint with a failed
 			// condition or a step breakpoint shadow this.
+			dbp.StopReason = StopCallReturned
 			return conditionErrors(threads)
 		}
 	}
@@ -318,7 +299,7 @@ func conditionErrors(threads []Thread) error {
 // 	- a thread with onTriggeredInternalBreakpoint() == true
 // 	- a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
 // 	- trapthread
-func pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
+func pickCurrentThread(dbp *Target, trapthread Thread, threads []Thread) error {
 	for _, th := range threads {
 		if bp := th.Breakpoint(); bp.Active && bp.Internal {
 			return dbp.SwitchThread(th.ThreadID())
@@ -335,11 +316,20 @@ func pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
 	return dbp.SwitchThread(trapthread.ThreadID())
 }
 
+func disassembleCurrentInstruction(p Process, thread Thread) ([]AsmInstruction, error) {
+	regs, err := thread.Registers(false)
+	if err != nil {
+		return nil, err
+	}
+	pc := regs.PC()
+	return disassemble(thread, regs, p.Breakpoints(), p.BinInfo(), pc, pc+uint64(p.BinInfo().Arch.MaxInstructionLength()), true)
+}
+
 // stepInstructionOut repeatedly calls StepInstruction until the current
 // function is neither fnname1 or fnname2.
 // This function is used to step out of runtime.Breakpoint as well as
 // runtime.debugCallV1.
-func stepInstructionOut(dbp Process, curthread Thread, fnname1, fnname2 string) error {
+func stepInstructionOut(dbp *Target, curthread Thread, fnname1, fnname2 string) error {
 	for {
 		if err := curthread.StepInstruction(); err != nil {
 			return err
@@ -375,6 +365,11 @@ func Step(dbp *Target) (err error) {
 		}
 	}
 
+	if bp := dbp.CurrentThread().Breakpoint().Breakpoint; bp != nil && bp.Kind == StepBreakpoint && dbp.GetDirection() == Backward {
+		dbp.ClearInternalBreakpoints()
+		return StepInstruction(dbp)
+	}
+
 	return Continue(dbp)
 }
 
@@ -422,6 +417,7 @@ func andFrameoffCondition(cond ast.Expr, frameoff int64) ast.Expr {
 // StepOut will continue until the current goroutine exits the
 // function currently being executed or a deferred function is executed
 func StepOut(dbp *Target) error {
+	backward := dbp.GetDirection() == Backward
 	if _, err := dbp.Valid(); err != nil {
 		return err
 	}
@@ -456,29 +452,20 @@ func StepOut(dbp *Target) error {
 	sameGCond := SameGoroutineCondition(selg)
 	retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
 
-	var deferpc uint64
-	if filepath.Ext(topframe.Current.File) == ".go" {
-		if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
-			deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
-			deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
-			if err != nil {
-				return err
-			}
+	if backward {
+		if err := stepOutReverse(dbp, topframe, retframe, sameGCond); err != nil {
+			return err
 		}
+
+		success = true
+		return Continue(dbp)
 	}
 
-	if deferpc != 0 && deferpc != topframe.Current.PC {
-		bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
+	var deferpc uint64
+	if !backward {
+		deferpc, err = setDeferBreakpoint(dbp, nil, topframe, sameGCond, false)
 		if err != nil {
-			if _, ok := err.(BreakpointExistsError); !ok {
-				return err
-			}
-		}
-		if bp != nil {
-			// For StepOut we do not want to step into the deferred function
-			// when it's called by runtime.deferreturn so we do not populate
-			// DeferReturns.
-			bp.DeferReturns = []uint64{}
+			return err
 		}
 	}
 
@@ -487,11 +474,9 @@ func StepOut(dbp *Target) error {
 	}
 
 	if topframe.Ret != 0 {
-		bp, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond)
+		bp, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond))
 		if err != nil {
-			if _, isexists := err.(BreakpointExistsError); !isexists {
-				return err
-			}
+			return err
 		}
 		if bp != nil {
 			configureReturnBreakpoint(dbp.BinInfo(), bp, &topframe, retFrameCond)
@@ -538,11 +523,124 @@ func StepInstruction(dbp *Target) (err error) {
 		return err
 	}
 	if tg, _ := GetG(thread); tg != nil {
-		dbp.SetSelectedGoroutine(tg)
+		dbp.selectedGoroutine = tg
 	}
 	return nil
 }
 
+func allowDuplicateBreakpoint(bp *Breakpoint, err error) (*Breakpoint, error) {
+	if err != nil {
+		if _, isexists := err.(BreakpointExistsError); isexists {
+			return bp, nil
+		}
+	}
+	return bp, err
+}
+
+// setDeferBreakpoint is a helper function used by next and StepOut to set a
+// breakpoint on the first deferred function.
+func setDeferBreakpoint(p Process, text []AsmInstruction, topframe Stackframe, sameGCond ast.Expr, stepInto bool) (uint64, error) {
+	// Set breakpoint on the most recently deferred function (if any)
+	var deferpc uint64
+	if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
+		deferfn := p.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
+		var err error
+		deferpc, err = FirstPCAfterPrologue(p, deferfn, false)
+		if err != nil {
+			return 0, err
+		}
+	}
+	if deferpc != 0 && deferpc != topframe.Current.PC {
+		bp, err := allowDuplicateBreakpoint(p.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond))
+		if err != nil {
+			return 0, err
+		}
+		if bp != nil && stepInto {
+			// If DeferReturns is set then the breakpoint will also be triggered when
+			// called from runtime.deferreturn. We only do this for the step command,
+			// not for next or stepout.
+			bp.DeferReturns = FindDeferReturnCalls(text)
+		}
+	}
+
+	return deferpc, nil
+}
+
+// findCallInstrForRet returns the PC address of the CALL instruction
+// immediately preceding the instruction at ret.
+func findCallInstrForRet(p Process, mem MemoryReadWriter, ret uint64, fn *Function) (uint64, error) {
+	text, err := disassemble(mem, nil, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End, false)
+	if err != nil {
+		return 0, err
+	}
+	var prevInstr AsmInstruction
+	for _, instr := range text {
+		if instr.Loc.PC == ret {
+			return prevInstr.Loc.PC, nil
+		}
+		prevInstr = instr
+	}
+	return 0, fmt.Errorf("could not find CALL instruction for address %#x in %s", ret, fn.Name)
+}
+
+// stepOutReverse sets a breakpoint on the CALL instruction that created the current frame, this is either:
+// - the CALL instruction immediately preceding the return address of the
+//   current frame
+// - the return address of the current frame if the current frame was
+//   created by a runtime.deferreturn run
+// - the return address of the runtime.gopanic frame if the current frame
+//   was created by a panic
+// This function is used to implement reversed StepOut
+func stepOutReverse(p *Target, topframe, retframe Stackframe, sameGCond ast.Expr) error {
+	curthread := p.CurrentThread()
+	selg := p.SelectedGoroutine()
+
+	if selg != nil && selg.Thread != nil {
+		curthread = selg.Thread
+	}
+
+	callerText, err := disassemble(curthread, nil, p.Breakpoints(), p.BinInfo(), retframe.Current.Fn.Entry, retframe.Current.Fn.End, false)
+	if err != nil {
+		return err
+	}
+	deferReturns := FindDeferReturnCalls(callerText)
+
+	var frames []Stackframe
+	if selg == nil {
+		if !curthread.Blocked() {
+			frames, err = ThreadStacktrace(curthread, 3)
+		}
+	} else {
+		frames, err = selg.Stacktrace(3, 0)
+	}
+	if err != nil {
+		return err
+	}
+
+	var callpc uint64
+
+	if isPanicCall(frames) {
+		if len(frames) < 4 || frames[3].Current.Fn == nil {
+			return &ErrNoSourceForPC{frames[2].Current.PC}
+		}
+		callpc, err = findCallInstrForRet(p, curthread, frames[2].Ret, frames[3].Current.Fn)
+		if err != nil {
+			return err
+		}
+	} else if ok, pc := isDeferReturnCall(frames, deferReturns); ok {
+		callpc = pc
+	} else {
+		callpc, err = findCallInstrForRet(p, curthread, topframe.Ret, retframe.Current.Fn)
+		if err != nil {
+			return err
+		}
+	}
+
+	_, err = allowDuplicateBreakpoint(p.SetBreakpoint(callpc, NextBreakpoint, sameGCond))
+
+	return err
+}
+
 // GoroutinesInfo searches for goroutines starting at index 'start', and
 // returns an array of up to 'count' (or all found elements, if 'count' is 0)
 // G structures representing the information Delve care about from the internal
diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go
index f99d0e2d..0efb92f7 100644
--- a/pkg/proc/stack.go
+++ b/pkg/proc/stack.go
@@ -112,21 +112,22 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
 		return nil, err
 	}
 
+	bi := g.variable.bi
 	if g.Thread != nil {
 		regs, err := g.Thread.Registers(true)
 		if err != nil {
 			return nil, err
 		}
-		so := g.variable.bi.PCToImage(regs.PC())
+		so := bi.PCToImage(regs.PC())
 		return newStackIterator(
-			g.variable.bi, g.Thread,
-			g.variable.bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs),
+			bi, g.Thread,
+			bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs),
 			g.stackhi, stkbar, g.stkbarPos, g, opts), nil
 	}
 	so := g.variable.bi.PCToImage(g.PC)
 	return newStackIterator(
-		g.variable.bi, g.variable.mem,
-		g.variable.bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
+		bi, g.variable.mem,
+		bi.Arch.AddrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
 		g.stackhi, stkbar, g.stkbarPos, g, opts), nil
 }
 
@@ -438,11 +439,7 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
 	// implicit.
 	// See also the comment in dwarf2_frame_default_init in
 	// $GDB_SOURCE/dwarf2-frame.c
-	if _, ok := it.bi.Arch.(*ARM64); ok {
-		callFrameRegs.AddReg(uint64(arm64DwarfSPRegNum), cfareg)
-	} else {
-		callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
-	}
+	callFrameRegs.AddReg(callFrameRegs.SPRegNum, cfareg)
 
 	for i, regRule := range framectx.Regs {
 		reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
@@ -488,13 +485,13 @@ func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, c
 	case frame.RuleRegister:
 		return it.regs.Reg(rule.Reg), nil
 	case frame.RuleExpression:
-		v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
+		v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression, it.bi.Arch.PtrSize())
 		if err != nil {
 			return nil, err
 		}
 		return it.readRegisterAt(regnum, uint64(v))
 	case frame.RuleValExpression:
-		v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
+		v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression, it.bi.Arch.PtrSize())
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/proc/target.go b/pkg/proc/target.go
index 6a4bc43b..f56ee242 100644
--- a/pkg/proc/target.go
+++ b/pkg/proc/target.go
@@ -1,9 +1,24 @@
 package proc
 
+import (
+	"fmt"
+)
+
 // Target represents the process being debugged.
 type Target struct {
 	Process
 
+	proc ProcessInternal
+
+	// StopReason describes the reason why the target process is stopped.
+	// A process could be stopped for multiple simultaneous reasons, in which
+	// case only one will be reported.
+	StopReason StopReason
+
+	// Goroutine that will be used by default to set breakpoint, eval variables, etc...
+	// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
+	selectedGoroutine *G
+
 	// fncallForG stores a mapping of current active function calls.
 	fncallForG map[int]*callInjection
 
@@ -16,19 +31,69 @@ type Target struct {
 	gcache goroutineCache
 }
 
+// StopReason describes the reason why the target process is stopped.
+// A process could be stopped for multiple simultaneous reasons, in which
+// case only one will be reported.
+type StopReason uint8
+
+const (
+	StopUnknown             StopReason = iota
+	StopLaunched                       // The process was just launched
+	StopAttached                       // The debugger stopped the process after attaching
+	StopExited                         // The target proces terminated
+	StopBreakpoint                     // The target process hit one or more software breakpoints
+	StopHardcodedBreakpoint            // The target process hit a hardcoded breakpoint (for example runtime.Breakpoint())
+	StopManual                         // A manual stop was requested
+	StopNextFinished                   // The next/step/stepout command terminated
+	StopCallReturned                   // An injected call commpleted
+)
+
+// NewTargetConfig contains the configuration for a new Target object,
+type NewTargetConfig struct {
+	Path                string            // path of the main executable
+	DebugInfoDirs       []string          // Directories to search for split debug info
+	WriteBreakpoint     WriteBreakpointFn // Function to write a breakpoint to the target process
+	DisableAsyncPreempt bool              // Go 1.14 asynchronous preemption should be disabled
+	StopReason          StopReason        // Initial stop reason
+}
+
 // NewTarget returns an initialized Target object.
-func NewTarget(p Process, disableAsyncPreempt bool) *Target {
+func NewTarget(p Process, cfg NewTargetConfig) (*Target, error) {
+	entryPoint, err := p.EntryPoint()
+	if err != nil {
+		return nil, err
+	}
+
+	err = p.BinInfo().LoadBinaryInfo(cfg.Path, entryPoint, cfg.DebugInfoDirs)
+	if err != nil {
+		return nil, err
+	}
+	for _, image := range p.BinInfo().Images {
+		if image.loadErr != nil {
+			return nil, image.loadErr
+		}
+	}
+
 	t := &Target{
 		Process:    p,
+		proc:       p.(ProcessInternal),
 		fncallForG: make(map[int]*callInjection),
+		StopReason: cfg.StopReason,
 	}
+
+	g, _ := GetG(p.CurrentThread())
+	t.selectedGoroutine = g
+
+	createUnrecoveredPanicBreakpoint(p, cfg.WriteBreakpoint)
+	createFatalThrowBreakpoint(p, cfg.WriteBreakpoint)
+
 	t.gcache.init(p.BinInfo())
 
-	if disableAsyncPreempt {
+	if cfg.DisableAsyncPreempt {
 		setAsyncPreemptOff(t, 1)
 	}
 
-	return t
+	return t, nil
 }
 
 // SupportsFunctionCalls returns whether or not the backend supports
@@ -47,6 +112,9 @@ func (t *Target) SupportsFunctionCalls() bool {
 // This should be called anytime the target process executes instructions.
 func (t *Target) ClearAllGCache() {
 	t.gcache.Clear()
+	for _, thread := range t.ThreadList() {
+		thread.Common().g = nil
+	}
 }
 
 // Restart will start the process over from the location specified by the "from" locspec.
@@ -54,7 +122,50 @@ func (t *Target) ClearAllGCache() {
 // Restarting of a normal process happens at a higher level (debugger.Restart).
 func (t *Target) Restart(from string) error {
 	t.ClearAllGCache()
-	return t.Process.Restart(from)
+	err := t.proc.Restart(from)
+	if err != nil {
+		return err
+	}
+	t.selectedGoroutine, _ = GetG(t.CurrentThread())
+	if from != "" {
+		t.StopReason = StopManual
+	} else {
+		t.StopReason = StopLaunched
+	}
+	return nil
+}
+
+// SelectedGoroutine returns the currently selected goroutine.
+func (t *Target) SelectedGoroutine() *G {
+	return t.selectedGoroutine
+}
+
+// SwitchGoroutine will change the selected and active goroutine.
+func (p *Target) SwitchGoroutine(g *G) error {
+	if ok, err := p.Valid(); !ok {
+		return err
+	}
+	if g == nil {
+		return nil
+	}
+	if g.Thread != nil {
+		return p.SwitchThread(g.Thread.ThreadID())
+	}
+	p.selectedGoroutine = g
+	return nil
+}
+
+// SwitchThread will change the selected and active thread.
+func (p *Target) SwitchThread(tid int) error {
+	if ok, err := p.Valid(); !ok {
+		return err
+	}
+	if th, ok := p.FindThread(tid); ok {
+		p.proc.SetCurrentThread(th)
+		p.selectedGoroutine, _ = GetG(p.CurrentThread())
+		return nil
+	}
+	return fmt.Errorf("thread %d does not exist", tid)
 }
 
 // Detach will detach the target from the underylying process.
@@ -65,5 +176,6 @@ func (t *Target) Detach(kill bool) error {
 	if !kill && t.asyncPreemptChanged {
 		setAsyncPreemptOff(t, t.asyncPreemptOff)
 	}
-	return t.Process.Detach(kill)
+	t.StopReason = StopUnknown
+	return t.proc.Detach(kill)
 }
diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go
index 514a1fa2..613e6b59 100644
--- a/pkg/proc/threads.go
+++ b/pkg/proc/threads.go
@@ -1,7 +1,6 @@
 package proc
 
 import (
-	"encoding/binary"
 	"errors"
 	"fmt"
 	"go/ast"
@@ -70,6 +69,7 @@ func (tbe ErrThreadBlocked) Error() string {
 // implementations of the Thread interface.
 type CommonThread struct {
 	returnValues []*Variable
+	g            *G // cached g for this thread
 }
 
 // ReturnValues reads the return values from the function executing on
@@ -142,7 +142,8 @@ func (err *ErrNoSourceForPC) Error() string {
 // for an inlined function call. Everything works the same as normal except
 // when removing instructions belonging to inlined calls we also remove all
 // instructions belonging to the current inlined call.
-func next(dbp Process, stepInto, inlinedStepOut bool) error {
+func next(dbp *Target, stepInto, inlinedStepOut bool) error {
+	backward := dbp.GetDirection() == Backward
 	selg := dbp.SelectedGoroutine()
 	curthread := dbp.CurrentThread()
 	topframe, retframe, err := topframe(selg, curthread)
@@ -154,6 +155,10 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 		return &ErrNoSourceForPC{topframe.Current.PC}
 	}
 
+	if backward && retframe.Current.Fn == nil {
+		return &ErrNoSourceForPC{retframe.Current.PC}
+	}
+
 	// sanity check
 	if inlinedStepOut && !topframe.Inlined {
 		panic("next called with inlinedStepOut but topframe was not inlined")
@@ -178,12 +183,36 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 		}
 	}
 
+	sameGCond := SameGoroutineCondition(selg)
+
+	var firstPCAfterPrologue uint64
+
+	if backward {
+		firstPCAfterPrologue, err = FirstPCAfterPrologue(dbp, topframe.Current.Fn, false)
+		if err != nil {
+			return err
+		}
+		if firstPCAfterPrologue == topframe.Current.PC {
+			// We don't want to step into the prologue so we just execute a reverse step out instead
+			if err := stepOutReverse(dbp, topframe, retframe, sameGCond); err != nil {
+				return err
+			}
+
+			success = true
+			return nil
+		}
+
+		topframe.Ret, err = findCallInstrForRet(dbp, thread, topframe.Ret, retframe.Current.Fn)
+		if err != nil {
+			return err
+		}
+	}
+
 	text, err := disassemble(thread, regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.Current.Fn.Entry, topframe.Current.Fn.End, false)
 	if err != nil && stepInto {
 		return err
 	}
 
-	sameGCond := SameGoroutineCondition(selg)
 	retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
 	sameFrameCond := andFrameoffCondition(sameGCond, topframe.FrameOffset())
 	var sameOrRetFrameCond ast.Expr
@@ -203,50 +232,17 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 		}
 	}
 
-	if stepInto {
-		for _, instr := range text {
-			if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
-				continue
-			}
-
-			if instr.DestLoc != nil && instr.DestLoc.Fn != nil {
-				if err := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, sameGCond); err != nil {
-					return err
-				}
-			} else {
-				// Non-absolute call instruction, set a StepBreakpoint here
-				if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, sameGCond); err != nil {
-					if _, ok := err.(BreakpointExistsError); !ok {
-						return err
-					}
-				}
-			}
+	if stepInto && !backward {
+		err := setStepIntoBreakpoints(dbp, text, topframe, sameGCond)
+		if err != nil {
+			return err
 		}
 	}
 
-	if !csource {
-		deferreturns := FindDeferReturnCalls(text)
-
-		// Set breakpoint on the most recently deferred function (if any)
-		var deferpc uint64
-		if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
-			deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
-			var err error
-			deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
-			if err != nil {
-				return err
-			}
-		}
-		if deferpc != 0 && deferpc != topframe.Current.PC {
-			bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
-			if err != nil {
-				if _, ok := err.(BreakpointExistsError); !ok {
-					return err
-				}
-			}
-			if bp != nil && stepInto {
-				bp.DeferReturns = deferreturns
-			}
+	if !backward {
+		_, err = setDeferBreakpoint(dbp, text, topframe, sameGCond, stepInto)
+		if err != nil {
+			return err
 		}
 	}
 
@@ -256,6 +252,20 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 		return err
 	}
 
+	if backward {
+		// Ensure that pcs contains firstPCAfterPrologue when reverse stepping.
+		found := false
+		for _, pc := range pcs {
+			if pc == firstPCAfterPrologue {
+				found = true
+				break
+			}
+		}
+		if !found {
+			pcs = append(pcs, firstPCAfterPrologue)
+		}
+	}
+
 	if !stepInto {
 		// Removing any PC range belonging to an inlined call
 		frame := topframe
@@ -286,14 +296,19 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 	}
 
 	for _, pc := range pcs {
-		if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, sameFrameCond); err != nil {
-			if _, ok := err.(BreakpointExistsError); !ok {
-				dbp.ClearInternalBreakpoints()
-				return err
-			}
+		if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(pc, NextBreakpoint, sameFrameCond)); err != nil {
+			dbp.ClearInternalBreakpoints()
+			return err
 		}
+	}
 
+	if stepInto && backward {
+		err := setStepIntoBreakpointsReverse(dbp, text, topframe, sameGCond)
+		if err != nil {
+			return err
+		}
 	}
+
 	if !topframe.Inlined {
 		// Add a breakpoint on the return address for the current frame.
 		// For inlined functions there is no need to do this, the set of PCs
@@ -325,6 +340,46 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 	return nil
 }
 
+func setStepIntoBreakpoints(dbp Process, text []AsmInstruction, topframe Stackframe, sameGCond ast.Expr) error {
+	for _, instr := range text {
+		if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
+			continue
+		}
+
+		if instr.DestLoc != nil {
+			if err := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, sameGCond); err != nil {
+				return err
+			}
+		} else {
+			// Non-absolute call instruction, set a StepBreakpoint here
+			if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, sameGCond)); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func setStepIntoBreakpointsReverse(dbp Process, text []AsmInstruction, topframe Stackframe, sameGCond ast.Expr) error {
+	// Set a breakpoint after every CALL instruction
+	for i, instr := range text {
+		if instr.Loc.File != topframe.Current.File || !instr.IsCall() || instr.DestLoc == nil || instr.DestLoc.Fn == nil {
+			continue
+		}
+
+		if fn := instr.DestLoc.Fn; strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) {
+			continue
+		}
+
+		if nextIdx := i + 1; nextIdx < len(text) {
+			if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(text[nextIdx].Loc.PC, StepBreakpoint, sameGCond)); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
 func FindDeferReturnCalls(text []AsmInstruction) []uint64 {
 	const deferreturn = "runtime.deferreturn"
 	deferreturns := []uint64{}
@@ -399,6 +454,11 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er
 
 	pc := instr.DestLoc.PC
 
+	// Skip InhibitStepInto functions for different arch.
+	if dbp.BinInfo().Arch.InhibitStepInto(dbp.BinInfo(), pc) {
+		return nil
+	}
+
 	// We want to skip the function prologue but we should only do it if the
 	// destination address of the CALL instruction is the entry point of the
 	// function.
@@ -410,10 +470,8 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er
 	}
 
 	// Set a breakpoint after the function's prologue
-	if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
-		if _, ok := err.(BreakpointExistsError); !ok {
-			return err
-		}
+	if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(pc, NextBreakpoint, cond)); err != nil {
+		return err
 	}
 
 	return nil
@@ -427,12 +485,11 @@ func getGVariable(thread Thread) (*Variable, error) {
 
 	gaddr, hasgaddr := regs.GAddr()
 	if !hasgaddr {
-		gaddrbs := make([]byte, thread.Arch().PtrSize())
-		_, err := thread.ReadMemory(gaddrbs, uintptr(regs.TLS()+thread.BinInfo().GStructOffset()))
+		var err error
+		gaddr, err = readUintRaw(thread, uintptr(regs.TLS()+thread.BinInfo().GStructOffset()), int64(thread.BinInfo().Arch.PtrSize()))
 		if err != nil {
 			return nil, err
 		}
-		gaddr = binary.LittleEndian.Uint64(gaddrbs)
 	}
 
 	return newGVariable(thread, uintptr(gaddr), thread.Arch().DerefTLS())
@@ -478,6 +535,9 @@ func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) {
 // In order to get around all this craziness, we read the address of the G structure for
 // the current thread from the thread local storage area.
 func GetG(thread Thread) (*G, error) {
+	if thread.Common().g != nil {
+		return thread.Common().g, nil
+	}
 	if loc, _ := thread.Location(); loc != nil && loc.Fn != nil && loc.Fn.Name == "runtime.clone" {
 		// When threads are executing runtime.clone the value of TLS is unreliable.
 		return nil, nil
@@ -520,6 +580,7 @@ func GetG(thread Thread) (*G, error) {
 	if loc, err := thread.Location(); err == nil {
 		g.CurrentLoc = *loc
 	}
+	thread.Common().g = g
 	return g, nil
 }
 
diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go
index 96a559fd..2a06aa3a 100644
--- a/pkg/proc/variables.go
+++ b/pkg/proc/variables.go
@@ -1488,13 +1488,12 @@ func (v *Variable) readFunctionPtr() {
 
 // funcvalAddr reads the address of the funcval contained in a function variable.
 func (v *Variable) funcvalAddr() uint64 {
-	val := make([]byte, v.bi.Arch.PtrSize())
-	_, err := v.mem.ReadMemory(val, v.Addr)
+	val, err := readUintRaw(v.mem, v.Addr, int64(v.bi.Arch.PtrSize()))
 	if err != nil {
 		v.Unreadable = err
 		return 0
 	}
-	return binary.LittleEndian.Uint64(val)
+	return val
 }
 
 func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
diff --git a/pkg/proc/x86_disasm.go b/pkg/proc/x86_disasm.go
new file mode 100644
index 00000000..a4c1ef38
--- /dev/null
+++ b/pkg/proc/x86_disasm.go
@@ -0,0 +1,120 @@
+package proc
+
+import (
+	"golang.org/x/arch/x86/x86asm"
+)
+
+type x86Inst x86asm.Inst
+
+// 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.
+func x86AsmDecode(asmInst *AsmInstruction, mem []byte, regs Registers, memrw MemoryReadWriter, bi *BinaryInfo, bit int) error {
+	inst, err := x86asm.Decode(mem, bit)
+	if err != nil {
+		asmInst.Inst = (*x86Inst)(nil)
+		asmInst.Size = 1
+		asmInst.Bytes = mem[:asmInst.Size]
+		return err
+	}
+
+	asmInst.Size = inst.Len
+	asmInst.Bytes = mem[:asmInst.Size]
+	patchPCRelX86(asmInst.Loc.PC, &inst)
+	asmInst.Inst = (*x86Inst)(&inst)
+	asmInst.Kind = OtherInstruction
+
+	switch inst.Op {
+	case x86asm.CALL, x86asm.LCALL:
+		asmInst.Kind = CallInstruction
+	case x86asm.RET, x86asm.LRET:
+		asmInst.Kind = RetInstruction
+	}
+
+	asmInst.DestLoc = resolveCallArgX86(&inst, asmInst.Loc.PC, asmInst.AtPC, regs, memrw, bi)
+	return nil
+}
+
+// converts PC relative arguments to absolute addresses
+func patchPCRelX86(pc uint64, inst *x86asm.Inst) {
+	for i := range inst.Args {
+		rel, isrel := inst.Args[i].(x86asm.Rel)
+		if isrel {
+			inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
+		}
+	}
+}
+
+func (inst *x86Inst) Text(flavour AssemblyFlavour, pc uint64, symLookup func(uint64) (string, uint64)) string {
+	if inst == nil {
+		return "?"
+	}
+
+	var text string
+
+	switch flavour {
+	case GNUFlavour:
+		text = x86asm.GNUSyntax(x86asm.Inst(*inst), pc, symLookup)
+	case GoFlavour:
+		text = x86asm.GoSyntax(x86asm.Inst(*inst), pc, symLookup)
+	case IntelFlavour:
+		fallthrough
+	default:
+		text = x86asm.IntelSyntax(x86asm.Inst(*inst), pc, symLookup)
+	}
+
+	return text
+}
+
+func (inst *x86Inst) OpcodeEquals(op uint64) bool {
+	if inst == nil {
+		return false
+	}
+	return uint64(inst.Op) == op
+}
+
+func resolveCallArgX86(inst *x86asm.Inst, instAddr uint64, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
+	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
+		return nil
+	}
+
+	var pc uint64
+	var err error
+
+	switch arg := inst.Args[0].(type) {
+	case x86asm.Imm:
+		pc = uint64(arg)
+	case x86asm.Reg:
+		if !currentGoroutine || regs == nil {
+			return nil
+		}
+		pc, err = regs.Get(int(arg))
+		if err != nil {
+			return nil
+		}
+	case x86asm.Mem:
+		if !currentGoroutine || regs == nil {
+			return nil
+		}
+		if arg.Segment != 0 {
+			return nil
+		}
+		base, err1 := regs.Get(int(arg.Base))
+		index, err2 := regs.Get(int(arg.Index))
+		if err1 != nil || err2 != nil {
+			return nil
+		}
+		addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
+		pc, err = readUintRaw(mem, addr, int64(inst.MemBytes))
+		if err != nil {
+			return nil
+		}
+	default:
+		return nil
+	}
+
+	file, line, fn := bininfo.PCToLine(pc)
+	if fn == nil {
+		return &Location{PC: pc}
+	}
+	return &Location{PC: pc, File: file, Line: line, Fn: fn}
+}
diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go
index f3c5a7b7..b34174f4 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -142,16 +142,16 @@ For live targets the command takes the following forms:
 If newargv is omitted the process is restarted (or re-recorded) with the same argument vector.
 If -noargs is specified instead, the argument vector is cleared.
 `},
-		{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, helpMsg: "Run until breakpoint or program termination."},
-		{aliases: []string{"step", "s"}, group: runCmds, cmdFn: c.step, helpMsg: "Single step through program."},
+		{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: "Run until breakpoint or program termination."},
+		{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, helpMsg: `Step over to next source line.
+		{aliases: []string{"next", "n"}, group: runCmds, cmdFn: c.next, allowedPrefixes: revPrefix, helpMsg: `Step over to next source line.
 
 	 next [count]
 
 Optional [count] argument allows you to skip multiple lines.
 `},
-		{aliases: []string{"stepout", "so"}, group: runCmds, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
+		{aliases: []string{"stepout", "so"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
 		{aliases: []string{"call"}, group: runCmds, cmdFn: c.call, helpMsg: `Resumes process, injecting a function call (EXPERIMENTAL!!!)
 	
 	call [-unsafe] <function call expression>
@@ -182,7 +182,7 @@ Current limitations:
 If called with the linespec argument it will delete all the breakpoints matching the linespec. If linespec is omitted all breakpoints are deleted.`},
 		{aliases: []string{"goroutines", "grs"}, group: goroutineCmds, cmdFn: goroutines, helpMsg: `List program goroutines.
 
-	goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [ -t (stack trace)]
+	goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [-t (stack trace)] [-l (labels)]
 
 Print out info for every goroutine. The flag controls what information is shown along with each goroutine:
 
@@ -390,7 +390,7 @@ For example:
 		c.cmds = append(c.cmds, command{
 			aliases: []string{"rewind", "rw"},
 			group:   runCmds,
-			cmdFn:   rewind,
+			cmdFn:   c.rewind,
 			helpMsg: "Run backwards until breakpoint or program termination.",
 		})
 		c.cmds = append(c.cmds, command{
@@ -416,6 +416,7 @@ The "note" is arbitrary text that can be used to identify the checkpoint, if it
 		})
 		c.cmds = append(c.cmds, command{
 			aliases: []string{"rev"},
+			group: runCmds,
 			cmdFn:   c.revCmd,
 			helpMsg: `Reverses the execution of the target program for the command specified.
 Currently, only the rev step-instruction command is supported.`,
@@ -1056,6 +1057,9 @@ func printcontextNoState(t *Term) {
 }
 
 func (c *Commands) cont(t *Term, ctx callContext, args string) error {
+	if ctx.Prefix == revPrefix {
+		return c.rewind(t, ctx, args)
+	}
 	c.frame = 0
 	stateChan := t.client.Continue()
 	var state *api.DebuggerState
@@ -1079,7 +1083,7 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string, sho
 	}
 	for {
 		fmt.Printf("\tbreakpoint hit during %s, continuing...\n", op)
-		stateChan := t.client.Continue()
+		stateChan := t.client.DirectionCongruentContinue()
 		var state *api.DebuggerState
 		for state = range stateChan {
 			if state.Err != nil {
@@ -1117,7 +1121,11 @@ func (c *Commands) step(t *Term, ctx callContext, args string) error {
 		return err
 	}
 	c.frame = 0
-	state, err := exitedToError(t.client.Step())
+	stepfn := t.client.Step
+	if ctx.Prefix == revPrefix {
+		stepfn = t.client.ReverseStep
+	}
+	state, err := exitedToError(stepfn())
 	if err != nil {
 		printcontextNoState(t)
 		return err
@@ -1172,6 +1180,12 @@ func (c *Commands) next(t *Term, ctx callContext, args string) error {
 	if c.frame != 0 {
 		return notOnFrameZeroErr
 	}
+
+	nextfn := t.client.Next
+	if ctx.Prefix == revPrefix {
+		nextfn = t.client.ReverseNext
+	}
+
 	var count int64
 	var err error
 	if count, err = parseOptionalCount(args); err != nil {
@@ -1180,7 +1194,7 @@ func (c *Commands) next(t *Term, ctx callContext, args string) error {
 		return errors.New("Invalid next count")
 	}
 	for ; count > 0; count-- {
-		state, err := exitedToError(t.client.Next())
+		state, err := exitedToError(nextfn())
 		if err != nil {
 			printcontextNoState(t)
 			return err
@@ -1204,7 +1218,13 @@ func (c *Commands) stepout(t *Term, ctx callContext, args string) error {
 	if c.frame != 0 {
 		return notOnFrameZeroErr
 	}
-	state, err := exitedToError(t.client.StepOut())
+
+	stepoutfn := t.client.StepOut
+	if ctx.Prefix == revPrefix {
+		stepoutfn = t.client.ReverseStepOut
+	}
+
+	state, err := exitedToError(stepoutfn())
 	if err != nil {
 		printcontextNoState(t)
 		return err
@@ -2304,7 +2324,8 @@ func (c *Commands) executeFile(t *Term, name string) error {
 	return scanner.Err()
 }
 
-func rewind(t *Term, ctx callContext, args string) error {
+func (c *Commands) rewind(t *Term, ctx callContext, args string) error {
+	c.frame = 0
 	stateChan := t.client.Rewind()
 	var state *api.DebuggerState
 	for state = range stateChan {
diff --git a/service/api/types.go b/service/api/types.go
index d8905348..7f3864de 100644
--- a/service/api/types.go
+++ b/service/api/types.go
@@ -366,16 +366,24 @@ const (
 	Continue = "continue"
 	// Rewind resumes process execution backwards (target must be a recording).
 	Rewind = "rewind"
+	// DirecitonCongruentContinue resumes process execution, if a reverse next, step or stepout operation is in progress it will resume execution backward.
+	DirectionCongruentContinue = "directionCongruentContinue"
 	// Step continues to next source line, entering function calls.
 	Step = "step"
+	// ReverseStep continues backward to the previous line of source code, entering function calls.
+	ReverseStep = "reverseStep"
 	// StepOut continues to the return address of the current function
 	StepOut = "stepOut"
+	// ReverseStepOut continues backward to the calle rof the current function.
+	ReverseStepOut = "reverseStepOut"
 	// StepInstruction continues for exactly 1 cpu instruction.
 	StepInstruction = "stepInstruction"
 	// ReverseStepInstruction reverses execution for exactly 1 cpu instruction.
 	ReverseStepInstruction = "reverseStepInstruction"
 	// Next continues to the next source line, not entering function calls.
 	Next = "next"
+	// ReverseNext continues backward to the previous line of source code, not entering function calls.
+	ReverseNext = "reverseNext"
 	// SwitchThread switches the debugger's current thread context.
 	SwitchThread = "switchThread"
 	// SwitchGoroutine switches the debugger's current thread context to the thread running the specified goroutine
diff --git a/service/client.go b/service/client.go
index fc7c71fd..f8117ebf 100644
--- a/service/client.go
+++ b/service/client.go
@@ -32,12 +32,20 @@ type Client interface {
 	Continue() <-chan *api.DebuggerState
 	// Rewind resumes process execution backwards.
 	Rewind() <-chan *api.DebuggerState
+	// DirecitonCongruentContinue resumes process execution, if a reverse next, step or stepout operation is in progress it will resume execution backward.
+	DirectionCongruentContinue() <-chan *api.DebuggerState
 	// Next continues to the next source line, not entering function calls.
 	Next() (*api.DebuggerState, error)
+	// ReverseNext continues backward to the previous line of source code, not entering function calls.
+	ReverseNext() (*api.DebuggerState, error)
 	// Step continues to the next source line, entering function calls.
 	Step() (*api.DebuggerState, error)
-	// StepOut continues to the return address of the current function
+	// ReverseStep continues backward to the previous line of source code, entering function calls.
+	ReverseStep() (*api.DebuggerState, error)
+	// StepOut continues to the return address of the current function.
 	StepOut() (*api.DebuggerState, error)
+	// ReverseStepOut continues backward to the calle rof the current function.
+	ReverseStepOut() (*api.DebuggerState, error)
 	// Call resumes process execution while making a function call.
 	Call(goroutineID int, expr string, unsafe bool) (*api.DebuggerState, error)
 
diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go
index 6f448476..9f1b9f05 100644
--- a/service/dap/error_ids.go
+++ b/service/dap/error_ids.go
@@ -11,6 +11,7 @@ const (
 	// TODO(polina): confirm if the extension expects specific ids
 	// for specific cases, and we must match the existing adaptor
 	// or if these codes can evolve.
-	FailedToContinue = 3000
+	FailedToContinue       = 3000
+	UnableToDisplayThreads = 2003
 	// Add more codes as we support more requests
 )
diff --git a/service/dap/server.go b/service/dap/server.go
index c9931ae9..78646d26 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -244,7 +244,7 @@ func (s *Server) handleRequest(request dap.Message) {
 	case *dap.SourceRequest:
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.ThreadsRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		s.onThreadsRequest(request)
 	case *dap.TerminateThreadsRequest:
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.EvaluateRequest:
@@ -453,6 +453,49 @@ func (s *Server) onContinueRequest(request *dap.ContinueRequest) {
 	s.doContinue()
 }
 
+func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
+	if s.debugger == nil {
+		s.sendErrorResponse(request.Request, UnableToDisplayThreads, "Unable to display threads", "debugger is nil")
+		return
+	}
+	gs, _, err := s.debugger.Goroutines(0, 0)
+	if err != nil {
+		switch err.(type) {
+		case *proc.ErrProcessExited:
+			// If the program exits very quickly, the initial threads request will complete after it has exited.
+			// A TerminatedEvent has already been sent. Ignore the err returned in this case.
+			s.send(&dap.ThreadsResponse{Response: *newResponse(request.Request)})
+		default:
+			s.sendErrorResponse(request.Request, UnableToDisplayThreads, "Unable to display threads", err.Error())
+		}
+		return
+	}
+
+	threads := make([]dap.Thread, len(gs))
+	if len(threads) == 0 {
+		// Depending on the debug session stage, goroutines information
+		// might not be available. However, the DAP spec states that
+		// "even if a debug adapter does not support multiple threads,
+		// it must implement the threads request and return a single
+		// (dummy) thread".
+		threads = []dap.Thread{{Id: 1, Name: "Dummy"}}
+	} else {
+		for i, g := range gs {
+			threads[i].Id = g.ID
+			if loc := g.UserCurrentLoc; loc.Function != nil {
+				threads[i].Name = loc.Function.Name()
+			} else {
+				threads[i].Name = fmt.Sprintf("%s@%d", loc.File, loc.Line)
+			}
+		}
+	}
+	response := &dap.ThreadsResponse{
+		Response: *newResponse(request.Request),
+		Body:     dap.ThreadsResponseBody{Threads: threads},
+	}
+	s.send(response)
+}
+
 func (s *Server) sendErrorResponse(request dap.Request, id int, summary string, details string) {
 	er := &dap.ErrorResponse{}
 	er.Type = "response"
diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go
index 7b54a1ee..07e62cc8 100644
--- a/service/debugger/debugger.go
+++ b/service/debugger/debugger.go
@@ -127,7 +127,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
 		d.log.Infof("launching process with args: %v", d.processArgs)
 		p, err := d.Launch(d.processArgs, d.config.WorkingDir)
 		if err != nil {
-			if err != proc.ErrNotExecutable && err != proc.ErrUnsupportedLinuxArch && err != proc.ErrUnsupportedWindowsArch && err != proc.ErrUnsupportedDarwinArch {
+			if _, ok := err.(*proc.ErrUnsupportedArch); !ok {
 				err = go11DecodeErrorCheck(err)
 				err = fmt.Errorf("could not launch process: %s", err)
 			}
@@ -695,9 +695,18 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
 	switch command.Name {
 	case api.Continue:
 		d.log.Debug("continuing")
+		if err := d.target.ChangeDirection(proc.Forward); err != nil {
+			return nil, err
+		}
+		err = proc.Continue(d.target)
+	case api.DirectionCongruentContinue:
+		d.log.Debug("continuing (direction congruent)")
 		err = proc.Continue(d.target)
 	case api.Call:
 		d.log.Debugf("function call %s", command.Expr)
+		if err := d.target.ChangeDirection(proc.Forward); err != nil {
+			return nil, err
+		}
 		if command.ReturnInfoLoadConfig == nil {
 			return nil, errors.New("can not call function with nil ReturnInfoLoadConfig")
 		}
@@ -711,33 +720,57 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
 		err = proc.EvalExpressionWithCalls(d.target, g, command.Expr, *api.LoadConfigToProc(command.ReturnInfoLoadConfig), !command.UnsafeCall)
 	case api.Rewind:
 		d.log.Debug("rewinding")
-		if err := d.target.Direction(proc.Backward); err != nil {
+		if err := d.target.ChangeDirection(proc.Backward); err != nil {
 			return nil, err
 		}
-		defer func() {
-			d.target.Direction(proc.Forward)
-		}()
 		err = proc.Continue(d.target)
 	case api.Next:
 		d.log.Debug("nexting")
+		if err := d.target.ChangeDirection(proc.Forward); err != nil {
+			return nil, err
+		}
+		err = proc.Next(d.target)
+	case api.ReverseNext:
+		d.log.Debug("reverse nexting")
+		if err := d.target.ChangeDirection(proc.Backward); err != nil {
+			return nil, err
+		}
 		err = proc.Next(d.target)
 	case api.Step:
 		d.log.Debug("stepping")
+		if err := d.target.ChangeDirection(proc.Forward); err != nil {
+			return nil, err
+		}
+		err = proc.Step(d.target)
+	case api.ReverseStep:
+		d.log.Debug("reverse stepping")
+		if err := d.target.ChangeDirection(proc.Backward); err != nil {
+			return nil, err
+		}
 		err = proc.Step(d.target)
 	case api.StepInstruction:
 		d.log.Debug("single stepping")
+		if err := d.target.ChangeDirection(proc.Forward); err != nil {
+			return nil, err
+		}
 		err = proc.StepInstruction(d.target)
 	case api.ReverseStepInstruction:
 		d.log.Debug("reverse single stepping")
-		if err := d.target.Direction(proc.Backward); err != nil {
+		if err := d.target.ChangeDirection(proc.Backward); err != nil {
 			return nil, err
 		}
-		defer func() {
-			d.target.Direction(proc.Forward)
-		}()
 		err = proc.StepInstruction(d.target)
 	case api.StepOut:
 		d.log.Debug("step out")
+		if err := d.target.ChangeDirection(proc.Forward); err != nil {
+			return nil, err
+		}
+		err = proc.StepOut(d.target)
+	case api.ReverseStepOut:
+		d.log.Debug("reverse step out")
+		if err := d.target.ChangeDirection(proc.Backward); err != nil {
+			return nil, err
+		}
 		err = proc.StepOut(d.target)
 	case api.SwitchThread:
 		d.log.Debugf("switching to thread %d", command.ThreadID)
diff --git a/service/rpc2/client.go b/service/rpc2/client.go
index 2d9577b7..ea58fec5 100644
--- a/service/rpc2/client.go
+++ b/service/rpc2/client.go
@@ -92,6 +92,10 @@ func (c *RPCClient) Rewind() <-chan *api.DebuggerState {
 	return c.continueDir(api.Rewind)
 }
 
+func (c *RPCClient) DirectionCongruentContinue() <-chan *api.DebuggerState {
+	return c.continueDir(api.DirectionCongruentContinue)
+}
+
 func (c *RPCClient) continueDir(cmd string) <-chan *api.DebuggerState {
 	ch := make(chan *api.DebuggerState)
 	go func() {
@@ -136,18 +140,36 @@ func (c *RPCClient) Next() (*api.DebuggerState, error) {
 	return &out.State, err
 }
 
+func (c *RPCClient) ReverseNext() (*api.DebuggerState, error) {
+	var out CommandOut
+	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseNext, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
+	return &out.State, err
+}
+
 func (c *RPCClient) Step() (*api.DebuggerState, error) {
 	var out CommandOut
 	err := c.call("Command", api.DebuggerCommand{Name: api.Step, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
 	return &out.State, err
 }
 
+func (c *RPCClient) ReverseStep() (*api.DebuggerState, error) {
+	var out CommandOut
+	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStep, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
+	return &out.State, err
+}
+
 func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
 	var out CommandOut
 	err := c.call("Command", api.DebuggerCommand{Name: api.StepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
 	return &out.State, err
 }
 
+func (c *RPCClient) ReverseStepOut() (*api.DebuggerState, error) {
+	var out CommandOut
+	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
+	return &out.State, err
+}
+
 func (c *RPCClient) Call(goroutineID int, expr string, unsafe bool) (*api.DebuggerState, error) {
 	var out CommandOut
 	err := c.call("Command", api.DebuggerCommand{Name: api.Call, ReturnInfoLoadConfig: c.retValLoadCfg, Expr: expr, UnsafeCall: unsafe, GoroutineID: goroutineID}, &out)


Test patch:
diff --git a/_fixtures/cgostacktest/hello.c b/_fixtures/cgostacktest/hello.c
index 7e179be1..7226ffed 100644
--- a/_fixtures/cgostacktest/hello.c
+++ b/_fixtures/cgostacktest/hello.c
@@ -4,6 +4,8 @@
 
 #ifdef __amd64__
 #define BREAKPOINT asm("int3;")
+#elif __i386__
+#define BREAKPOINT asm("int3;")
 #elif __aarch64__
 #define BREAKPOINT asm("brk 0;")
 #endif
diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 5d77390b..51f2ffa4 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -171,12 +171,14 @@ func testOutput(t *testing.T, dlvbin, output string, delveCmds []string) (stdout
 
 	_, err = os.Stat(debugbin)
 	if err == nil {
-		if strings.ToLower(os.Getenv("APPVEYOR")) != "true" {
-			// Sometimes delve on Appveyor can't remove the built binary before
+		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.
-			// See: https://ci.appveyor.com/project/go-delve/delve/build/1527
-			t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", delveCmds, debugbin, stdout, stderr)
+			// Just ignore it.
+			// See: https://travis-ci.com/go-delve/delve/jobs/296325131
+			return
 		}
+		t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", delveCmds, debugbin, stdout, stderr)
 		return
 	}
 	if !os.IsNotExist(err) {
@@ -276,6 +278,9 @@ func slurpFile(t *testing.T, filename string) []byte {
 // TestGeneratedDoc tests that the autogenerated documentation has been
 // updated.
 func TestGeneratedDoc(t *testing.T) {
+	if strings.ToLower(os.Getenv("TRAVIS")) == "true" && runtime.GOOS == "windows" {
+		t.Skip("skipping test on Windows in CI")
+	}
 	// Checks gen-cli-docs.go
 	var generatedBuf bytes.Buffer
 	commands := terminal.DebugCommands(nil)
@@ -445,7 +450,7 @@ func TestTypecheckRPC(t *testing.T) {
 			continue
 		}
 
-		if fndecl.Name.Name == "Continue" || fndecl.Name.Name == "Rewind" {
+		if fndecl.Name.Name == "Continue" || fndecl.Name.Name == "Rewind" || fndecl.Name.Name == "DirectionCongruentContinue" {
 			// using continueDir
 			continue
 		}
diff --git a/pkg/dwarf/frame/entries_test.go b/pkg/dwarf/frame/entries_test.go
index a608120a..cd919af4 100644
--- a/pkg/dwarf/frame/entries_test.go
+++ b/pkg/dwarf/frame/entries_test.go
@@ -5,8 +5,13 @@ import (
 	"io/ioutil"
 	"os"
 	"testing"
+	"unsafe"
 )
 
+func ptrSizeByRuntimeArch() int {
+	return int(unsafe.Sizeof(uintptr(0)))
+}
+
 func TestFDEForPC(t *testing.T) {
 	frames := NewFrameIndex()
 	frames = append(frames,
@@ -62,7 +67,7 @@ func BenchmarkFDEForPC(b *testing.B) {
 	if err != nil {
 		b.Fatal(err)
 	}
-	fdes := Parse(data, binary.BigEndian, 0)
+	fdes := Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch())
 
 	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 d9d37673..d03ea01c 100644
--- a/pkg/dwarf/frame/parser_test.go
+++ b/pkg/dwarf/frame/parser_test.go
@@ -12,8 +12,8 @@ import (
 
 func TestParseCIE(t *testing.T) {
 	ctx := &parseContext{
-		buf : bytes.NewBuffer([]byte{3,0,1,124,16,12,7,8,5,16,2,0,36,0,0,0,0,0,0,0,0,16,64,0,0,0,0,0}),
-		common: &CommonInformationEntry{Length:12},
+		buf:    bytes.NewBuffer([]byte{3, 0, 1, 124, 16, 12, 7, 8, 5, 16, 2, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 16, 64, 0, 0, 0, 0, 0}),
+		common: &CommonInformationEntry{Length: 12},
 		length: 12,
 	}
 	_ = parseCIE(ctx)
@@ -35,8 +35,8 @@ func TestParseCIE(t *testing.T) {
 	if common.ReturnAddressRegister != 16 {
 		t.Fatalf("Expected ReturnAddressRegister 16, but get %d", common.ReturnAddressRegister)
 	}
-	initialInstructions := []byte{12,7,8,5,16,2,0}
-	if !bytes.Equal(common.InitialInstructions, initialInstructions){
+	initialInstructions := []byte{12, 7, 8, 5, 16, 2, 0}
+	if !bytes.Equal(common.InitialInstructions, initialInstructions) {
 		t.Fatalf("Expected InitialInstructions %v, but get %v", initialInstructions, common.InitialInstructions)
 	}
 }
@@ -56,6 +56,6 @@ func BenchmarkParse(b *testing.B) {
 
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		Parse(data, binary.BigEndian, 0)
+		Parse(data, binary.BigEndian, 0, ptrSizeByRuntimeArch())
 	}
 }
diff --git a/pkg/dwarf/line/line_parser_test.go b/pkg/dwarf/line/line_parser_test.go
index 08a8a6eb..ac3c2405 100644
--- a/pkg/dwarf/line/line_parser_test.go
+++ b/pkg/dwarf/line/line_parser_test.go
@@ -13,6 +13,7 @@ import (
 	"strings"
 	"testing"
 	"time"
+	"unsafe"
 
 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 	"github.com/pkg/profile"
@@ -65,10 +66,13 @@ const (
 	opcodeBaseGo111 uint8  = 11
 )
 
+func ptrSizeByRuntimeArch() int {
+	return int(unsafe.Sizeof(uintptr(0)))
+}
+
 func testDebugLinePrologueParser(p string, t *testing.T) {
 	data := grabDebugLineSection(p, t)
-	debugLines := ParseAll(data, nil, 0, true)
-
+	debugLines := ParseAll(data, nil, 0, true, ptrSizeByRuntimeArch())
 	mainFileFound := false
 
 	for _, dbl := range debugLines {
@@ -173,7 +177,7 @@ func BenchmarkLineParser(b *testing.B) {
 
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		_ = ParseAll(data, nil, 0, true)
+		_ = ParseAll(data, nil, 0, true, ptrSizeByRuntimeArch())
 	}
 }
 
@@ -188,7 +192,7 @@ func loadBenchmarkData(tb testing.TB) DebugLines {
 		tb.Fatal("Could not read test data", err)
 	}
 
-	return ParseAll(data, nil, 0, true)
+	return ParseAll(data, nil, 0, true, ptrSizeByRuntimeArch())
 }
 
 func BenchmarkStateMachine(b *testing.B) {
@@ -196,7 +200,7 @@ func BenchmarkStateMachine(b *testing.B) {
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		sm := newStateMachine(lineInfos[0], lineInfos[0].Instructions)
+		sm := newStateMachine(lineInfos[0], lineInfos[0].Instructions, ptrSizeByRuntimeArch())
 
 		for {
 			if err := sm.next(); err != nil {
@@ -223,7 +227,7 @@ func setupTestPCToLine(t testing.TB, lineInfos DebugLines) ([]pctolineEntry, []u
 	entries := []pctolineEntry{}
 	basePCs := []uint64{}
 
-	sm := newStateMachine(lineInfos[0], lineInfos[0].Instructions)
+	sm := newStateMachine(lineInfos[0], lineInfos[0].Instructions, ptrSizeByRuntimeArch())
 	for {
 		if err := sm.next(); err != nil {
 			break
@@ -310,7 +314,7 @@ func TestDebugLineC(t *testing.T) {
 		t.Fatal("Could not read test data", err)
 	}
 
-	parsed := ParseAll(data, nil, 0, true)
+	parsed := ParseAll(data, nil, 0, true, ptrSizeByRuntimeArch())
 
 	if len(parsed) == 0 {
 		t.Fatal("Parser result is empty")
diff --git a/pkg/dwarf/line/state_machine_test.go b/pkg/dwarf/line/state_machine_test.go
index 3a36d183..5088c4f4 100644
--- a/pkg/dwarf/line/state_machine_test.go
+++ b/pkg/dwarf/line/state_machine_test.go
@@ -43,7 +43,6 @@ func TestGrafana(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		t.Skip("filepath.Join ruins this test on windows")
 	}
-
 	debugBytes, err := slurpGzip("_testdata/debug.grafana.debug.gz")
 	if err != nil {
 		t.Fatal(err)
@@ -80,8 +79,8 @@ func TestGrafana(t *testing.T) {
 		}
 		cuname, _ := e.Val(dwarf.AttrName).(string)
 
-		lineInfo := Parse(e.Val(dwarf.AttrCompDir).(string), debugLineBuffer, t.Logf, 0, false)
-		sm := newStateMachine(lineInfo, lineInfo.Instructions)
+		lineInfo := Parse(e.Val(dwarf.AttrCompDir).(string), debugLineBuffer, t.Logf, 0, false, 8)
+		sm := newStateMachine(lineInfo, lineInfo.Instructions, 8)
 
 		lnrdr, err := data.LineReader(e)
 		if err != nil {
@@ -137,13 +136,13 @@ func TestMultipleSequences(t *testing.T) {
 	const thefile = "thefile.go"
 
 	instr := bytes.NewBuffer(nil)
+	ptrSize := ptrSizeByRuntimeArch()
 
 	write_DW_LNE_set_address := func(addr uint64) {
 		instr.WriteByte(0)
 		util.EncodeULEB128(instr, 9) // 1 + ptr_size
 		instr.WriteByte(DW_LINE_set_address)
-		binary.Write(instr, binary.LittleEndian, addr)
-
+		util.WriteUint(instr, binary.LittleEndian, ptrSize, addr)
 	}
 
 	write_DW_LNS_copy := func() {
@@ -215,6 +214,7 @@ func TestMultipleSequences(t *testing.T) {
 		IncludeDirs:  []string{},
 		FileNames:    []*FileEntry{&FileEntry{Path: thefile}},
 		Instructions: instr.Bytes(),
+		ptrSize:      ptrSize,
 	}
 
 	// Test that PCToLine is correct for all three sequences
@@ -235,7 +235,7 @@ func TestMultipleSequences(t *testing.T) {
 		{0x600002, 12},
 		{0x600004, 13},
 	} {
-		sm := newStateMachine(lines, lines.Instructions)
+		sm := newStateMachine(lines, lines.Instructions, lines.ptrSize)
 		file, curline, ok := sm.PCToLine(testCase.pc)
 		if !ok {
 			t.Fatalf("Could not find %#x", testCase.pc)
diff --git a/pkg/dwarf/op/op_test.go b/pkg/dwarf/op/op_test.go
index c46c8c7a..0a346261 100644
--- a/pkg/dwarf/op/op_test.go
+++ b/pkg/dwarf/op/op_test.go
@@ -1,13 +1,20 @@
 package op
 
-import "testing"
+import (
+	"testing"
+	"unsafe"
+)
+
+func ptrSizeByRuntimeArch() int {
+	return int(unsafe.Sizeof(uintptr(0)))
+}
 
 func TestExecuteStackProgram(t *testing.T) {
 	var (
 		instructions = []byte{byte(DW_OP_consts), 0x1c, byte(DW_OP_consts), 0x1c, byte(DW_OP_plus)}
 		expected     = int64(56)
 	)
-	actual, _, err := ExecuteStackProgram(DwarfRegisters{}, instructions)
+	actual, _, err := ExecuteStackProgram(DwarfRegisters{}, instructions, ptrSizeByRuntimeArch())
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go
index 3d8d429b..6a236e26 100644
--- a/pkg/proc/dwarf_expr_test.go
+++ b/pkg/proc/dwarf_expr_test.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	"go/constant"
 	"testing"
+	"unsafe"
 
 	"github.com/go-delve/delve/pkg/dwarf/dwarfbuilder"
 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
@@ -19,7 +20,20 @@ import (
 	"github.com/go-delve/delve/pkg/proc/linutil"
 )
 
-const defaultCFA = 0xc420051d00
+func ptrSizeByRuntimeArch() int {
+	return int(unsafe.Sizeof(uintptr(0)))
+}
+
+func fakeCFA() uint64 {
+	ptrSize := ptrSizeByRuntimeArch()
+	if ptrSize == 8 {
+		return 0xc420051d00
+	}
+	if ptrSize == 4 {
+		return 0xc4251d00
+	}
+	panic(fmt.Errorf("not support ptr size %d", ptrSize))
+}
 
 func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) (*proc.BinaryInfo, *dwarf.Data) {
 	abbrev, aranges, frame, info, line, pubnames, ranges, str, loc, err := dwb.Build()
@@ -92,8 +106,8 @@ func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfR
 	a := proc.AMD64Arch("linux")
 	so := bi.PCToImage(regs.PC())
 	dwarfRegs := a.RegistersToDwarfRegisters(so.StaticBase, regs)
-	dwarfRegs.CFA = defaultCFA
-	dwarfRegs.FrameBase = defaultCFA
+	dwarfRegs.CFA = int64(fakeCFA())
+	dwarfRegs.FrameBase = int64(fakeCFA())
 	return dwarfRegs
 }
 
@@ -118,8 +132,7 @@ func TestDwarfExprRegisters(t *testing.T) {
 	bi, _ := fakeBinaryInfo(t, dwb)
 
 	mainfn := bi.LookupFunc["main.main"]
-
-	mem := newFakeMemory(defaultCFA, uint64(0), uint64(testCases["b"]))
+	mem := newFakeMemory(fakeCFA(), uint64(0), uint64(testCases["b"]))
 	regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
 	regs.Regs.Rax = uint64(testCases["a"])
 	regs.Regs.Rdx = uint64(testCases["c"])
@@ -171,11 +184,11 @@ func TestDwarfExprComposite(t *testing.T) {
 
 	mainfn := bi.LookupFunc["main.main"]
 
-	mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal))
+	mem := newFakeMemory(fakeCFA(), uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal))
 	var regs linutil.AMD64Registers
 	regs.Regs = &linutil.AMD64PtraceRegs{}
 	regs.Regs.Rax = uint64(len(stringVal))
-	regs.Regs.Rdx = defaultCFA + 18
+	regs.Regs.Rdx = fakeCFA() + 18
 	regs.Regs.Rcx = uint64(testCases["pair.k"])
 	regs.Regs.Rbx = uint64(testCases["n"])
 
@@ -211,7 +224,7 @@ func TestDwarfExprLoclist(t *testing.T) {
 
 	mainfn := bi.LookupFunc["main.main"]
 
-	mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
+	mem := newFakeMemory(fakeCFA(), uint16(before), uint16(after))
 	const PC = 0x40100
 	regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{Rip: PC}}
 
@@ -248,7 +261,7 @@ func TestIssue1419(t *testing.T) {
 
 	mainfn := bi.LookupFunc["main.main"]
 
-	mem := newFakeMemory(defaultCFA)
+	mem := newFakeMemory(fakeCFA())
 
 	scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: op.DwarfRegisters{}, Mem: mem, BinInfo: bi}
 
diff --git a/pkg/proc/fbsdutil/regs_test.go b/pkg/proc/fbsdutil/regs_test.go
index 30773ffa..19aa7641 100644
--- a/pkg/proc/fbsdutil/regs_test.go
+++ b/pkg/proc/fbsdutil/regs_test.go
@@ -46,7 +46,7 @@ func TestAMD64Get(t *testing.T) {
 		t.Fatal(err)
 	}
 	if eax != 0xDEADBEEF {
-		t.Fatalf("expected %#v, got %#v\n", 0xdeadbeef, eax)
+		t.Fatalf("expected %#v, got %#v\n", uint64(0xdeadbeef), eax)
 	}
 
 	// Test RAX, full 64 bits of register
diff --git a/pkg/proc/gdbserial/rr_test.go b/pkg/proc/gdbserial/rr_test.go
index 9e00df1a..7e2be946 100644
--- a/pkg/proc/gdbserial/rr_test.go
+++ b/pkg/proc/gdbserial/rr_test.go
@@ -146,7 +146,7 @@ func TestReverseBreakpointCounts(t *testing.T) {
 		}
 
 		p.ClearBreakpoint(endbp.Addr)
-		assertNoError(p.Direction(proc.Backward), t, "Switching to backward direction")
+		assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction")
 		bp := setFileBreakpoint(p, t, fixture, 12)
 		startbp := setFileBreakpoint(p, t, fixture, 20)
 
@@ -285,7 +285,7 @@ func TestIssue1376(t *testing.T) {
 		assertNoError(proc.Continue(p), t, "Continue (forward)")
 		_, err := p.ClearBreakpoint(bp.Addr)
 		assertNoError(err, t, "ClearBreakpoint")
-		assertNoError(p.Direction(proc.Backward), t, "Switching to backward direction")
+		assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction")
 		assertNoError(proc.Continue(p), t, "Continue (backward)")
 	})
 }
diff --git a/pkg/proc/linutil/regs_test.go b/pkg/proc/linutil/regs_test.go
index 7d6fb879..a9ed0fbf 100644
--- a/pkg/proc/linutil/regs_test.go
+++ b/pkg/proc/linutil/regs_test.go
@@ -46,7 +46,7 @@ func TestAMD64Get(t *testing.T) {
 		t.Fatal(err)
 	}
 	if eax != 0xDEADBEEF {
-		t.Fatalf("expected %#v, got %#v\n", 0xdeadbeef, eax)
+		t.Fatalf("expected %#v, got %#v\n", uint64(0xdeadbeef), eax)
 	}
 
 	// Test RAX, full 64 bits of register
diff --git a/pkg/proc/proc_general_test.go b/pkg/proc/proc_general_test.go
index 8af69eac..2e35469d 100644
--- a/pkg/proc/proc_general_test.go
+++ b/pkg/proc/proc_general_test.go
@@ -2,13 +2,25 @@ package proc
 
 import (
 	"testing"
+	"unsafe"
 )
 
+func ptrSizeByRuntimeArch() int {
+	return int(unsafe.Sizeof(uintptr(0)))
+}
+
 func TestIssue554(t *testing.T) {
 	// unsigned integer overflow in proc.(*memCache).contains was
 	// causing it to always return true for address 0xffffffffffffffff
 	mem := memCache{true, 0x20, make([]byte, 100), nil}
-	if mem.contains(0xffffffffffffffff, 40) {
+	var addr uint64
+	switch ptrSizeByRuntimeArch() {
+	case 4:
+		addr = 0xffffffff
+	case 8:
+		addr = 0xffffffffffffffff
+	}
+	if mem.contains(uintptr(addr), 40) {
 		t.Fatalf("should be false")
 	}
 }
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index dd15e580..73b990c2 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -385,6 +385,10 @@ const (
 	contNext
 	contStep
 	contStepout
+	contReverseNext
+	contReverseStep
+	contReverseStepout
+	contContinueToBreakpoint
 )
 
 type seqTest struct {
@@ -459,6 +463,35 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te
 					_, err := p.ClearBreakpoint(bp.Addr)
 					assertNoError(err, t, "ClearBreakpoint() returned an error")
 				}
+			case contReverseNext:
+				if traceTestseq2 {
+					t.Log("reverse-next")
+				}
+				assertNoError(p.ChangeDirection(proc.Backward), t, "direction switch")
+				assertNoError(proc.Next(p), t, "reverse Next() returned an error")
+				assertNoError(p.ChangeDirection(proc.Forward), t, "direction switch")
+			case contReverseStep:
+				if traceTestseq2 {
+					t.Log("reverse-step")
+				}
+				assertNoError(p.ChangeDirection(proc.Backward), t, "direction switch")
+				assertNoError(proc.Step(p), t, "reverse Step() returned an error")
+				assertNoError(p.ChangeDirection(proc.Forward), t, "direction switch")
+			case contReverseStepout:
+				if traceTestseq2 {
+					t.Log("reverse-stepout")
+				}
+				assertNoError(p.ChangeDirection(proc.Backward), t, "direction switch")
+				assertNoError(proc.StepOut(p), t, "reverse StepOut() returned an error")
+				assertNoError(p.ChangeDirection(proc.Forward), t, "direction switch")
+			case contContinueToBreakpoint:
+				bp := setFileBreakpoint(p, t, fixture.Source, tc.pos.(int))
+				if traceTestseq2 {
+					t.Log("continue")
+				}
+				assertNoError(proc.Continue(p), t, "Continue() returned an error")
+				_, err := p.ClearBreakpoint(bp.Addr)
+				assertNoError(err, t, "ClearBreakpoint() returned an error")
 			}
 
 			f, ln = currentLineNumber(p, t)
@@ -467,7 +500,7 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te
 
 			if traceTestseq2 {
 				t.Logf("at %#x %s:%d", pc, f, ln)
-				fmt.Printf("at %#x %s:%d", pc, f, ln)
+				fmt.Printf("at %#x %s:%d\n", pc, f, ln)
 			}
 			switch pos := tc.pos.(type) {
 			case int:
@@ -2073,15 +2106,17 @@ func TestUnsupportedArch(t *testing.T) {
 		t.Skip("test not valid for this backend")
 	}
 
-	switch err {
-	case proc.ErrUnsupportedLinuxArch, proc.ErrUnsupportedWindowsArch, proc.ErrUnsupportedDarwinArch:
-		// all good
-	case nil:
+	if err == nil {
 		p.Detach(true)
 		t.Fatal("Launch is expected to fail, but succeeded")
-	default:
-		t.Fatal(err)
 	}
+
+	if _, ok := err.(*proc.ErrUnsupportedArch); ok {
+		// all good
+		return
+	}
+
+	t.Fatal(err)
 }
 
 func TestIssue573(t *testing.T) {
@@ -3261,6 +3296,10 @@ func TestCgoStacktrace(t *testing.T) {
 		}
 	}
 
+	if runtime.GOARCH == "386" {
+		t.Skip("cgo stacktraces not supported on i386 for now")
+	}
+
 	// Tests that:
 	// a) we correctly identify the goroutine while we are executing cgo code
 	// b) that we can stitch together the system stack (where cgo code
@@ -3360,6 +3399,10 @@ func TestCgoSources(t *testing.T) {
 		}
 	}
 
+	if runtime.GOARCH == "386" {
+		t.Skip("cgo stacktraces not supported on i386 for now")
+	}
+
 	withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) {
 		sources := p.BinInfo().Sources
 		for _, needle := range []string{"main.go", "hello.c"} {
@@ -3429,6 +3472,10 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) {
 }
 
 func TestIssue1034(t *testing.T) {
+	if runtime.GOARCH == "386" {
+		t.Skip("cgo stacktraces not supported on i386 for now")
+	}
+
 	// The external linker on macOS produces an abbrev for DW_TAG_subprogram
 	// without the "has children" flag, we should support this.
 	withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) {
@@ -3446,6 +3493,10 @@ func TestIssue1034(t *testing.T) {
 }
 
 func TestIssue1008(t *testing.T) {
+	if runtime.GOARCH == "386" {
+		t.Skip("cgo stacktraces not supported on i386 for now")
+	}
+
 	// The external linker on macOS inserts "end of sequence" extended opcodes
 	// in debug_line. which we should support correctly.
 	withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) {
@@ -3565,6 +3616,14 @@ func TestDisassembleGlobalVars(t *testing.T) {
 	if runtime.GOARCH == "arm64" {
 		t.Skip("On ARM64 symLookup can't look up variables due to how they are loaded, see issue #1778")
 	}
+	// On 386 linux when pie, the genered code use __x86.get_pc_thunk to ensure position-independent.
+	// Locate global variable by
+	//    `CALL __x86.get_pc_thunk.ax(SB) 0xb0f7f
+	//     LEAL 0xc0a19(AX), AX`
+	// dynamically.
+	if runtime.GOARCH == "386" && runtime.GOOS == "linux" && buildMode == "pie" {
+		t.Skip("On 386 linux when pie, symLookup can't look up global variables")
+	}
 	withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
 		mainfn := p.BinInfo().LookupFunc["main.main"]
 		regs, _ := p.CurrentThread().Registers(false)
@@ -4115,8 +4174,8 @@ func TestReadDeferArgs(t *testing.T) {
 }
 
 func TestIssue1374(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("arm64 does not support FunctionCall for now")
+	if runtime.GOARCH == "arm64" || runtime.GOARCH == "386" {
+		t.Skip(fmt.Errorf("%s does not support FunctionCall for now", runtime.GOARCH))
 	}
 	// Continue did not work when stopped at a breakpoint immediately after calling CallFunction.
 	protest.MustSupportFunctionCalls(t, testBackend)
@@ -4337,9 +4396,10 @@ func TestCallConcurrent(t *testing.T) {
 	if runtime.GOOS == "freebsd" {
 		t.Skip("test is not valid on FreeBSD")
 	}
-	if runtime.GOARCH == "arm64" {
-		t.Skip("arm64 does not support FunctionCall for now")
+	if runtime.GOARCH == "arm64" || runtime.GOARCH == "386" {
+		t.Skip(fmt.Sprintf("%s does not support FunctionCall for now", runtime.GOARCH))
 	}
+
 	protest.MustSupportFunctionCalls(t, testBackend)
 	withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
 		bp := setFileBreakpoint(p, t, fixture.Source, 24)
@@ -4573,3 +4633,118 @@ func BenchmarkConditionalBreakpoints(b *testing.B) {
 		}
 	})
 }
+
+func TestBackwardNextGeneral(t *testing.T) {
+	if testBackend != "rr" {
+		t.Skip("Reverse stepping test needs rr")
+	}
+	testseq2(t, "testnextprog", "main.helloworld", []seqTest{
+		{contContinue, 13},
+		{contNext, 14},
+		{contReverseNext, 13},
+		{contReverseNext, 34},
+		{contReverseNext, 28},
+		{contReverseNext, 27},
+		{contReverseNext, 26},
+		{contReverseNext, 24},
+		{contReverseNext, 23},
+		{contReverseNext, 31},
+		{contReverseNext, 26},
+		{contReverseNext, 24},
+		{contReverseNext, 23},
+		{contReverseNext, 31},
+		{contReverseNext, 26},
+		{contReverseNext, 24},
+		{contReverseNext, 23},
+		{contReverseNext, 20},
+		{contReverseNext, 19},
+		{contReverseNext, 17},
+		{contReverseNext, 39},
+		{contReverseNext, 38},
+		{contReverseNext, 37},
+	})
+}
+
+func TestBackwardStepOutGeneral(t *testing.T) {
+	if testBackend != "rr" {
+		t.Skip("Reverse stepping test needs rr")
+	}
+	testseq2(t, "testnextprog", "main.helloworld", []seqTest{
+		{contContinue, 13},
+		{contNext, 14},
+		{contReverseStepout, 34},
+		{contReverseStepout, 39},
+	})
+}
+
+func TestBackwardStepGeneral(t *testing.T) {
+	if testBackend != "rr" {
+		t.Skip("Reverse stepping test needs rr")
+	}
+	testseq2(t, "testnextprog", "main.helloworld", []seqTest{
+		{contContinue, 13},
+		{contNext, 14},
+		{contReverseStep, 13},
+		{contReverseStep, 34},
+		{contReverseStep, 28},
+		{contReverseNext, 27}, // skip fmt.Printf
+		{contReverseStep, 26},
+		{contReverseStep, 24},
+		{contReverseStep, 23},
+		{contReverseStep, 11},
+		{contReverseNext, 10}, // skip time.Sleep
+		{contReverseStep, 9},
+
+		{contReverseStep, 31},
+		{contReverseStep, 26},
+		{contReverseStep, 24},
+		{contReverseStep, 23},
+		{contReverseStep, 11},
+		{contReverseNext, 10}, // skip time.Sleep
+		{contReverseStep, 9},
+
+		{contReverseStep, 31},
+		{contReverseStep, 26},
+		{contReverseStep, 24},
+		{contReverseStep, 23},
+		{contReverseStep, 20},
+		{contReverseStep, 19},
+		{contReverseStep, 17},
+		{contReverseStep, 39},
+		{contReverseStep, 38},
+		{contReverseStep, 37},
+	})
+}
+
+func TestBackwardNextDeferPanic(t *testing.T) {
+	if testBackend != "rr" {
+		t.Skip("Reverse stepping test needs rr")
+	}
+	testseq2(t, "defercall", "", []seqTest{
+		{contContinue, 12},
+		{contReverseNext, 11},
+		{contReverseNext, 10},
+		{contReverseNext, 9},
+		{contReverseNext, 27},
+
+		{contContinueToBreakpoint, 12}, // skip first call to sampleFunction
+		{contContinueToBreakpoint, 6},  // go to call to sampleFunction through deferreturn
+		{contReverseNext, 13},
+		{contReverseNext, 12},
+		{contReverseNext, 11},
+		{contReverseNext, 10},
+		{contReverseNext, 9},
+		{contReverseNext, 27},
+
+		{contContinueToBreakpoint, 18}, // go to panic call
+		{contNext, 6},                  // panic so the deferred call happens
+		{contReverseNext, 18},
+		{contReverseNext, 17},
+		{contReverseNext, 16},
+		{contReverseNext, 15},
+		{contReverseNext, 23},
+		{contReverseNext, 22},
+		{contReverseNext, 21},
+		{contReverseNext, 28},
+	})
+}
diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go
index 384e2c13..d422ccb4 100644
--- a/pkg/terminal/command_test.go
+++ b/pkg/terminal/command_test.go
@@ -978,8 +978,8 @@ func findStarFile(name string) string {
 }
 
 func TestIssue1598(t *testing.T) {
-	if runtime.GOARCH == "arm64" {
-		t.Skip("arm64 does not support FunctionCall for now")
+	if runtime.GOARCH == "arm64" || runtime.GOARCH == "386" {
+		t.Skip(fmt.Errorf("%s does not support FunctionCall for now", runtime.GOARCH))
 	}
 	test.MustSupportFunctionCalls(t, testBackend)
 	withTestTerminal("issue1598", t, func(term *FakeTerminal) {
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index 7d607bc7..1a97e428 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -4,7 +4,6 @@ package daptest
 
 import (
 	"bufio"
-	"encoding/json"
 	"fmt"
 	"log"
 	"net"
@@ -34,6 +33,7 @@ func NewClient(addr string) *Client {
 		log.Fatal("dialing:", err)
 	}
 	c := &Client{conn: conn, reader: bufio.NewReader(conn)}
+	c.seq = 1 // match VS Code numbering
 	return c
 }
 
@@ -43,8 +43,6 @@ func (c *Client) Close() {
 }
 
 func (c *Client) send(request dap.Message) {
-	jsonmsg, _ := json.Marshal(request)
-	fmt.Println("[client -> server]", string(jsonmsg))
 	dap.WriteProtocolMessage(c.conn, request)
 }
 
@@ -120,6 +118,16 @@ func (c *Client) ExpectConfigurationDoneResponse(t *testing.T) *dap.Configuratio
 	return c.expectReadProtocolMessage(t).(*dap.ConfigurationDoneResponse)
 }
 
+func (c *Client) ExpectThreadsResponse(t *testing.T) *dap.ThreadsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.ThreadsResponse)
+}
+
+func (c *Client) ExpectStackTraceResponse(t *testing.T) *dap.StackTraceResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.StackTraceResponse)
+}
+
 // InitializeRequest sends an 'initialize' request.
 func (c *Client) InitializeRequest() {
 	request := &dap.InitializeRequest{Request: *c.newRequest("initialize")}
@@ -199,6 +207,18 @@ func (c *Client) ContinueRequest(thread int) {
 	c.send(request)
 }
 
+// ThreadsRequest sends a 'threads' request.
+func (c *Client) ThreadsRequest() {
+	request := &dap.ThreadsRequest{Request: *c.newRequest("threads")}
+	c.send(request)
+}
+
+// StackTraceRequest sends a 'stackTrace' request.
+func (c *Client) StackTraceRequest() {
+	request := &dap.StackTraceRequest{Request: *c.newRequest("stackTrace")}
+	c.send(request)
+}
+
 // UnknownRequest triggers dap.DecodeProtocolMessageFieldError.
 func (c *Client) UnknownRequest() {
 	request := c.newRequest("unknown")
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index 5e1ceeef..a462ed95 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -6,6 +6,8 @@ import (
 	"net"
 	"os"
 	"path/filepath"
+	"reflect"
+	"strings"
 	"sync"
 	"testing"
 	"time"
@@ -66,66 +68,190 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F
 	test(client, fixture)
 }
 
+// TestStopOnEntry emulates the message exchange that can be observed with
+// VS Code for the most basic debug session with "stopOnEntry" enabled:
+// - User selects "Start Debugging":  1 >> initialize
+//                                 :  1 << initialize
+//                                 :  2 >> launch
+//                                 :    << initialized event
+//                                 :  2 << launch
+//                                 :  3 >> setBreakpoints (empty)
+//                                 :  3 << setBreakpoints
+//                                 :  4 >> setExceptionBreakpoints (empty)
+//                                 :  4 << setExceptionBreakpoints
+//                                 :  5 >> configurationDone
+// - Program stops upon launching  :    << stopped event
+//                                 :  5 << configurationDone
+//                                 :  6 >> threads
+//                                 :  6 << threads (Dummy)
+//                                 :  7 >> threads
+//                                 :  7 << threads (Dummy)
+//                                 :  8 >> stackTrace
+//                                 :  8 << stackTrace (Unable to produce stack trace)
+//                                 :  9 >> stackTrace
+//                                 :  9 << stackTrace (Unable to produce stack trace)
+// - User selects "Continue"       : 10 >> continue
+//                                 : 10 << continue
+// - Program runs to completion    :    << terminated event
+//                                 : 11 >> disconnect
+//                                 : 11 << disconnect
+// This test exhaustively tests Seq and RequestSeq on all messages from the
+// server. Other tests do not necessarily need to repeat all these checks.
 func TestStopOnEntry(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
-		// This test exhaustively tests Seq and RequestSeq on all messages from the
-		// server. Other tests shouldn't necessarily repeat these checks.
+		// 1 >> initialize, << initialize
 		client.InitializeRequest()
 		initResp := client.ExpectInitializeResponse(t)
-		if initResp.Seq != 0 || initResp.RequestSeq != 0 {
-			t.Errorf("got %#v, want Seq=0, RequestSeq=0", initResp)
+		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
 		}
 
+		// 2 >> launch, << initialized, << launch
 		client.LaunchRequest("exec", fixture.Path, stopOnEntry)
-		initEv := client.ExpectInitializedEvent(t)
-		if initEv.Seq != 0 {
-			t.Errorf("got %#v, want Seq=0", initEv)
+		initEvent := client.ExpectInitializedEvent(t)
+		if initEvent.Seq != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0", initEvent)
 		}
-
 		launchResp := client.ExpectLaunchResponse(t)
-		if launchResp.Seq != 0 || launchResp.RequestSeq != 1 {
-			t.Errorf("got %#v, want Seq=0, RequestSeq=1", launchResp)
+		if launchResp.Seq != 0 || launchResp.RequestSeq != 2 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", launchResp)
+		}
+
+		// 3 >> setBreakpoints, << setBreakpoints
+		client.SetBreakpointsRequest(fixture.Source, nil)
+		sbpResp := client.ExpectSetBreakpointsResponse(t)
+		if sbpResp.Seq != 0 || sbpResp.RequestSeq != 3 || len(sbpResp.Body.Breakpoints) != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0", sbpResp)
 		}
 
+		// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
 		client.SetExceptionBreakpointsRequest()
-		sResp := client.ExpectSetExceptionBreakpointsResponse(t)
-		if sResp.Seq != 0 || sResp.RequestSeq != 2 {
-			t.Errorf("got %#v, want Seq=0, RequestSeq=2", sResp)
+		sebpResp := client.ExpectSetExceptionBreakpointsResponse(t)
+		if sebpResp.Seq != 0 || sebpResp.RequestSeq != 4 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=4", sebpResp)
 		}
 
+		// 5 >> configurationDone, << stopped, << configurationDone
 		client.ConfigurationDoneRequest()
 		stopEvent := client.ExpectStoppedEvent(t)
 		if stopEvent.Seq != 0 ||
 			stopEvent.Body.Reason != "breakpoint" ||
 			stopEvent.Body.ThreadId != 1 ||
 			!stopEvent.Body.AllThreadsStopped {
-			t.Errorf("got %#v, want Seq=0, Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", stopEvent)
+			t.Errorf("\ngot %#v\nwant Seq=0, Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", stopEvent)
 		}
-
 		cdResp := client.ExpectConfigurationDoneResponse(t)
-		if cdResp.Seq != 0 || cdResp.RequestSeq != 3 {
-			t.Errorf("got %#v, want Seq=0, RequestSeq=3", cdResp)
+		if cdResp.Seq != 0 || cdResp.RequestSeq != 5 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp)
+		}
+
+		// 6 >> threads, << threads
+		client.ThreadsRequest()
+		tResp := client.ExpectThreadsResponse(t)
+		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 1 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1", tResp)
+		}
+		if tResp.Body.Threads[0].Id != 1 || tResp.Body.Threads[0].Name != "Dummy" {
+			t.Errorf("\ngot %#v\nwant Id=1, Name=\"Dummy\"", tResp)
 		}
 
+		// 7 >> threads, << threads
+		client.ThreadsRequest()
+		tResp = client.ExpectThreadsResponse(t)
+		if tResp.Seq != 0 || tResp.RequestSeq != 7 || len(tResp.Body.Threads) != 1 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7 len(Threads)=1", tResp)
+		}
+
+		// 8 >> stackTrace, << stackTrace
+		client.StackTraceRequest()
+		stResp := client.ExpectErrorResponse(t)
+		if stResp.Seq != 0 || stResp.RequestSeq != 8 || stResp.Message != "Unsupported command" {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Message=\"Unsupported command\"", stResp)
+		}
+
+		// 9 >> stackTrace, << stackTrace
+		client.StackTraceRequest()
+		stResp = client.ExpectErrorResponse(t)
+		if stResp.Seq != 0 || stResp.RequestSeq != 9 || stResp.Message != "Unsupported command" {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Message=\"Unsupported command\"", stResp)
+		}
+
+		// 10 >> continue, << continue, << terminated
 		client.ContinueRequest(1)
 		contResp := client.ExpectContinueResponse(t)
-		if contResp.Seq != 0 || contResp.RequestSeq != 4 {
-			t.Errorf("got %#v, want Seq=0, RequestSeq=4", contResp)
+		if contResp.Seq != 0 || contResp.RequestSeq != 10 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10", contResp)
+		}
+		termEvent := client.ExpectTerminatedEvent(t)
+		if termEvent.Seq != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0", termEvent)
 		}
 
-		termEv := client.ExpectTerminatedEvent(t)
-		if termEv.Seq != 0 {
-			t.Errorf("got %#v, want Seq=0", termEv)
+		// 11 >> disconnect, << disconnect
+		client.DisconnectRequest()
+		dResp := client.ExpectDisconnectResponse(t)
+		if dResp.Seq != 0 || dResp.RequestSeq != 11 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=11", dResp)
 		}
+	})
+}
 
+// Like the test above, except the program is configured to continue on entry.
+func TestContinueOnEntry(t *testing.T) {
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		// 1 >> initialize, << initialize
+		client.InitializeRequest()
+		client.ExpectInitializeResponse(t)
+
+		// 2 >> launch, << initialized, << launch
+		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
+		client.ExpectInitializedEvent(t)
+		client.ExpectLaunchResponse(t)
+
+		// 3 >> setBreakpoints, << setBreakpoints
+		client.SetBreakpointsRequest(fixture.Source, nil)
+		client.ExpectSetBreakpointsResponse(t)
+
+		// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
+		client.SetExceptionBreakpointsRequest()
+		client.ExpectSetExceptionBreakpointsResponse(t)
+
+		// 5 >> configurationDone, << configurationDone
+		client.ConfigurationDoneRequest()
+		client.ExpectConfigurationDoneResponse(t)
+		// "Continue" happens behind the scenes
+
+		// For now continue is blocking and runs until a stop or
+		// termination. But once we upgrade the server to be async,
+		// a simultaneous threads request can be made while continue
+		// is running. Note that vscode-go just keeps track of the
+		// continue state and would just return a dummy response
+		// without talking to debugger if continue was in progress.
+		// TODO(polina): test this once it is possible
+
+		client.ExpectTerminatedEvent(t)
+
+		// It is possible for the program to terminate before the initial
+		// threads request is processed.
+
+		// 6 >> threads, << threads
+		client.ThreadsRequest()
+		tResp := client.ExpectThreadsResponse(t)
+		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 0 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=0", tResp)
+		}
+
+		// 7 >> disconnect, << disconnect
 		client.DisconnectRequest()
 		dResp := client.ExpectDisconnectResponse(t)
-		if dResp.Seq != 0 || dResp.RequestSeq != 5 {
-			t.Errorf("got %#v, want Seq=0, RequestSeq=5", dResp)
+		if dResp.Seq != 0 || dResp.RequestSeq != 7 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7", dResp)
 		}
 	})
 }
 
+// TestSetBreakpoint corresponds to a debug session that is configured to
+// continue on entry with a pre-set breakpoint.
 func TestSetBreakpoint(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		client.InitializeRequest()
@@ -133,10 +259,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
 		client.ExpectInitializedEvent(t)
-		launchResp := client.ExpectLaunchResponse(t)
-		if launchResp.RequestSeq != 1 {
-			t.Errorf("got %#v, want RequestSeq=1", launchResp)
-		}
+		client.ExpectLaunchResponse(t)
 
 		client.SetBreakpointsRequest(fixture.Source, []int{8, 100})
 		sResp := client.ExpectSetBreakpointsResponse(t)
@@ -152,31 +275,50 @@ func TestSetBreakpoint(t *testing.T) {
 		client.ExpectSetExceptionBreakpointsResponse(t)
 
 		client.ConfigurationDoneRequest()
-		cdResp := client.ExpectConfigurationDoneResponse(t)
-		if cdResp.RequestSeq != 4 {
-			t.Errorf("got %#v, want RequestSeq=4", cdResp)
-		}
+		client.ExpectConfigurationDoneResponse(t)
+		// This triggers "continue"
+
+		// TODO(polina): add a no-op threads request
+		// with dummy response here once server becomes async
+		// to match what happens in VS Code.
 
-		client.ContinueRequest(1)
 		stopEvent1 := client.ExpectStoppedEvent(t)
 		if stopEvent1.Body.Reason != "breakpoint" ||
 			stopEvent1.Body.ThreadId != 1 ||
 			!stopEvent1.Body.AllThreadsStopped {
 			t.Errorf("got %#v, want Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", stopEvent1)
 		}
-		client.ExpectContinueResponse(t)
+
+		client.ThreadsRequest()
+		tResp := client.ExpectThreadsResponse(t)
+		if len(tResp.Body.Threads) < 2 { // 1 main + runtime
+			t.Errorf("\ngot  %#v\nwant len(Threads)>1", tResp.Body.Threads)
+		}
+		// TODO(polina): can we reliably test for these values?
+		wantMain := dap.Thread{Id: 1, Name: "main.Increment"}
+		wantRuntime := dap.Thread{Id: 2, Name: "runtime.gopark"}
+		for _, got := range tResp.Body.Threads {
+			if !reflect.DeepEqual(got, wantMain) && !strings.HasPrefix(got.Name, "runtime") {
+				t.Errorf("\ngot  %#v\nwant []dap.Thread{%#v, %#v, ...}", tResp.Body.Threads, wantMain, wantRuntime)
+			}
+		}
+
+		// TODO(polina): add other status checking requests
+		// that are not yet supported (stackTrace, scopes, variables)
 
 		client.ContinueRequest(1)
-		client.ExpectTerminatedEvent(t)
 		client.ExpectContinueResponse(t)
+		// "Continue" is triggered after the response is sent
 
+		client.ExpectTerminatedEvent(t)
 		client.DisconnectRequest()
 		client.ExpectDisconnectResponse(t)
 	})
 }
 
 // runDebugSesion is a helper for executing the standard init and shutdown
-// sequences while specifying unique launch criteria via parameters.
+// sequences for a program that does not stop on entry
+// while specifying unique launch criteria via parameters.
 func runDebugSession(t *testing.T, client *daptest.Client, launchRequest func()) {
 	client.InitializeRequest()
 	client.ExpectInitializeResponse(t)
@@ -185,9 +327,14 @@ func runDebugSession(t *testing.T, client *daptest.Client, launchRequest func())
 	client.ExpectInitializedEvent(t)
 	client.ExpectLaunchResponse(t)
 
+	// Skip no-op setBreakpoints
+	// Skip no-op setExceptionBreakpoints
+
 	client.ConfigurationDoneRequest()
 	client.ExpectConfigurationDoneResponse(t)
 
+	// Program automatically continues to completion
+
 	client.ExpectTerminatedEvent(t)
 	client.DisconnectRequest()
 	client.ExpectDisconnectResponse(t)
@@ -218,7 +365,7 @@ func TestLaunchTestRequest(t *testing.T) {
 
 func TestBadLaunchRequests(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
-		seqCnt := 0
+		seqCnt := 1
 		expectFailedToLaunch := func(response *dap.ErrorResponse) {
 			t.Helper()
 			if response.RequestSeq != seqCnt {
diff --git a/service/test/integration1_test.go b/service/test/integration1_test.go
index 27e3d051..980f0cb6 100644
--- a/service/test/integration1_test.go
+++ b/service/test/integration1_test.go
@@ -919,8 +919,13 @@ func Test1Disasm(t *testing.T) {
 			t.Fatal("PC instruction not found")
 		}
 
-		startinstr := getCurinstr(d3)
+		if runtime.GOARCH == "386" && buildMode == "pie" {
+			// Skip the rest of the test because on intel 386 with PIE build mode
+			// the compiler will insert calls to __x86.get_pc_thunk which do not have DIEs and we can't resolve.
+			return
+		}
 
+		startinstr := getCurinstr(d3)
 		count := 0
 		for {
 			if count > 20 {
diff --git a/service/test/integration2_test.go b/service/test/integration2_test.go
index 6a635de4..9cf441c1 100644
--- a/service/test/integration2_test.go
+++ b/service/test/integration2_test.go
@@ -1026,8 +1026,13 @@ func TestDisasm(t *testing.T) {
 			t.Fatal("PC instruction not found")
 		}
 
-		startinstr := getCurinstr(d3)
+		if runtime.GOARCH == "386" && buildMode == "pie" {
+			// Skip the rest of the test because on intel 386 with PIE build mode
+			// the compiler will insert calls to __x86.get_pc_thunk which do not have DIEs and we can't resolve.
+			return
+		}
 
+		startinstr := getCurinstr(d3)
 		count := 0
 		for {
 			if count > 20 {
@@ -1154,13 +1159,14 @@ func TestSkipPrologue2(t *testing.T) {
 		callme3 := findLocationHelper(t, c, "main.callme3", false, 1, 0)[0]
 		callme3Z := uint64(clientEvalVariable(t, c, "main.callme3").Addr)
 		ver, _ := goversion.Parse(runtime.Version())
-		if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVer18Beta) {
+
+		if (ver.Major < 0 || ver.AfterOrEqual(goversion.GoVer18Beta)) && runtime.GOARCH != "386" {
 			findLocationHelper(t, c, "callme.go:19", false, 1, callme3)
 		} else {
 			// callme3 does not have local variables therefore the first line of the
 			// function is immediately after the prologue
-			// This is only true before 1.8 where frame pointer chaining introduced a
-			// bit of prologue even for functions without local variables
+			// This is only true before go1.8 or on Intel386 where frame pointer chaining
+			// introduced a bit of prologue even for functions without local variables
 			findLocationHelper(t, c, "callme.go:19", false, 1, callme3Z)
 		}
 		if callme3 == callme3Z {
diff --git a/service/test/variables_test.go b/service/test/variables_test.go
index 00cf95ae..418fcf3b 100644
--- a/service/test/variables_test.go
+++ b/service/test/variables_test.go
@@ -1001,13 +1001,11 @@ func TestPackageRenames(t *testing.T) {
 		// Renamed imports
 		{"badexpr", true, `interface {}(*go/ast.BadExpr) *{From: 1, To: 2}`, "", "interface {}", nil},
 		{"req", true, `interface {}(*net/http.Request) *{Method: "amethod", …`, "", "interface {}", nil},
-		{"amap", true, "interface {}(map[go/ast.BadExpr]net/http.Request) [{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},
 
 		// Package name that doesn't match import path
 		{"iface3", true, `interface {}(*github.com/go-delve/delve/_fixtures/internal/dir0/renamedpackage.SomeType) *{A: true}`, "", "interface {}", nil},
 
 		// Interfaces to anonymous types
-		{"amap2", true, "interface {}(*map[go/ast.BadExpr]net/http.Request) *[{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},
 		{"dir0someType", true, "interface {}(*github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType) *{X: 3}", "", "interface {}", nil},
 		{"dir1someType", true, "interface {}(github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType) {X: 1, Y: 2}", "", "interface {}", nil},
 		{"amap3", true, "interface {}(map[github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType]github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType) [{X: 4}: {X: 5, Y: 6}, ]", "", "interface {}", nil},
@@ -1022,6 +1020,16 @@ func TestPackageRenames(t *testing.T) {
 		{`"dir1/pkg".A`, false, "1", "", "int", nil},
 	}
 
+	testcases_i386 := []varTest{
+		{"amap", true, "interface {}(map[go/ast.BadExpr]net/http.Request) [{From: 2, To: 3}: {Method: \"othermethod\", …", "", "interface {}", nil},
+		{"amap2", true, "interface {}(*map[go/ast.BadExpr]net/http.Request) *[{From: 2, To: 3}: {Method: \"othermethod\", …", "", "interface {}", nil},
+	}
+
+	testcases_64bit := []varTest{
+		{"amap", true, "interface {}(map[go/ast.BadExpr]net/http.Request) [{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},
+		{"amap2", true, "interface {}(*map[go/ast.BadExpr]net/http.Request) *[{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},
+	}
+
 	testcases1_8 := []varTest{
 		// before 1.9 embedded struct fields have fieldname == type
 		{"astruct2", true, `interface {}(*struct { github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType; X int }) *{github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType: github.com/go-delve/delve/_fixtures/internal/dir1/pkg.SomeType {X: 1, Y: 2}, X: 10}`, "", "interface {}", nil},
@@ -1054,6 +1062,12 @@ func TestPackageRenames(t *testing.T) {
 		if goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) {
 			testPackageRenamesHelper(t, p, testcases1_13)
 		}
+
+		if runtime.GOARCH == "386" {
+			testPackageRenamesHelper(t, p, testcases_i386)
+		} else {
+			testPackageRenamesHelper(t, p, testcases_64bit)
+		}
 	})
 }