github.com/neilgarb/delve@v1.9.2-nobreaks/Documentation/internal/portnotes.md (about) 1 # Notes on porting Delve to other architectures 2 3 ## Continuous Integration requirements 4 5 Code that isn't tested doesn't work, we like to run CI on all supported platforms. Currently our CI is done on an [instance of TeamCity cloud provided by JetBrains](https://delve.teamcity.com/), with the exception of the FreeBSD port, which is tested by Cirrus-CI. 6 7 TeamCity settings are in `.teamcity/settings.kts` which in turn runs one of `_scripts/test_linux.sh`, `_scripts/test_mac.sh` or `_scripts/test_windows.ps1`. 8 All test scripts eventually end up calling into our main test program `_scripts/make.go`, which makes the appropriate `go test` calls. 9 10 If you plan to port Delve to a new platform you should first figure out how we are going to add your port to our CI, there are three possible solutions: 11 12 1. the platform can be run on existing agents we have on TeamCity (linux/amd64, linux/arm64, windows/amd64, darwin/amd64, darwin/arm64) through Docker or similar solutions. 13 2. there is a free CI service that integrates with GitHub that we can use 14 3. you provide the hardware to be added to TeamCity to test it 15 16 Exception to this requirement can be discussed in special cases. 17 18 ## General code organization 19 20 An introduction to the architecture of Delve can be found in the 2018 Gophercon Iceland talk: [slides](https://speakerdeck.com/aarzilli/internal-architecture-of-delve), [video](https://www.youtube.com/watch?v=IKnTr7Zms1k). 21 22 ### Packages you shouldn't worry about 23 24 * `cmd/dlv/...` implements the command line program 25 * `pkg/terminal/...` implements the command line user interface 26 * `service/...` with the exception of `service/test`, implements our API as well as DAP 27 * `pkg/dwarf/...` with the exception of `pkg/dwarf/regnum`, implements DWARF features not covered by the standard library 28 * anything else in `pkg` that isn't inside `pkg/proc` 29 30 ### pkg/proc 31 32 `pkg/proc` is the symbolic layer of Delve, its job is to bridge the distance between the API and low level Operating System and CPU features. Almost all features of Delve are implemented here, **except for the interaction with the OS/CPU**, which is provided by one of three backends: `pkg/proc/native`, `pkg/proc/core` or `pkg/proc/gdbserial`. 33 34 This package also contains the main test suite for Delve's backends in `pkg/proc/proc_test.go`. The tests for reading variables, however, are inside `service/test` for historical reasons. 35 36 When porting Delve to a new CPU a new instance of the `proc.Arch` structure should be filled, see `pkg/proc/arch.go` and `pkg/proc/amd64_arch.go` as an example. To do this you will have to: 37 38 - provide a disassembler for the port architecture 39 - provide a mapping between DWARF register numbers and hardware registers in `pkg/dwarf/regnum` (see `pkg/dwarf/regnum/amd64.go` as an example). This mapping *is not arbitrary* it needs to be described in some standard document which should be linked to in the documentation of `pkg/dwarf/regnum`, for example the mapping for amd64 is described by the System V ABI AMD64 Architecture Processor Supplement v. 1.0 on page 61 figure 3.36. 40 - if you don't know what `proc.Arch.fixFrameUnwindContext` or `porc.Arch.switchStack` should do *leave them empty* 41 - the `proc.Arch.prologues` field needs to be filled by looking at the relevant parts of the Go linker (`cmd/link`), the code is somewhere inside `$GOROOT/src/cmd/internal/obj`, usually the function is called `stacksplit`. 42 43 If your target OS uses an executable file format other than ELF, Mach-O or PE you will also have to change `pkg/proc/bininfo.go` . 44 45 **See also** the note on [build tags](#buildtags). 46 47 ### pkg/proc/gdbserial 48 49 This implements GDB remote serial protocol, it is used as the main backend on macOS as well as connecting to [rr](https://rr-project.org/). 50 51 Unless you are making a macOS port you shouldn't worry about this. 52 53 ### pkg/proc/core 54 55 This implements code for reading core files. You don't need to support this for the port platform, see the note on [skippable features](#skippable). 56 If you decide to do it anyway see the note on [build tags](#buildtags). 57 58 ### pkg/proc/native 59 60 This is the interface between Delve and the OS/CPU, you will definitely want to work on this and it will be the bulk of the port job. The tests for this code however are not in this directory, they are in `pkg/proc` and `service/test`. 61 62 ## General port process 63 64 1. Edit `pkg/proc/native/support_sentinel.go` to disable it in the port platform 65 2. Fill `proc.Arch` struct for target architecture if it isn't supported already 66 3. Go in `pkg/proc` 67 * run `go test -v` 68 * fix compiler errors 69 * repeat until compilation succeeds 70 * fix test failures 71 * repeat until almost all tests pass (see note on [skippable tests and features](#skippable)) 72 5. Go in `service/test` 73 * run `go test -v` 74 * fix compiler errors 75 * repeat until compilation succeeds 76 * fix test failures 77 * repeat until almost all tests pass (see note on [skippable tests and features](#skippable)) 78 6. Go to the root directory of the project 79 * run `go run _scripts/make.go test` 80 * fix compiler errors 81 * repeat until compilation succeeds 82 * fix test failures 83 * repeat until almost all tests pass (see note on [skippable tests and features](#skippable)) 84 85 ## Miscellaneous 86 87 ### <a name='buildtags'> Uses of build tags, runtime.GOOS and runtime.GOARCH 88 89 Delve has the ability to read cross-platform core files: you can read a core file of any supported platform with Delve running on any other supported platform. 90 For example, a core file produced by linux/arm64 can be read using Delve running under windows/amd64. 91 This feature has far reaching consequences, for example the stack unwinding code in `pkg/proc/stack.go` could be asked to unwind a stack for an architecture different from the one its running under. 92 93 What this means in practice is that, in general, using build tags (like `_amd64.go`) or checking `runtime.GOOS` and `runtime.GOARCH` is forbidden throughout Delve's source tree, with two important exceptions: 94 95 * `pkg/proc/native` is allowed to check runtime.GOOS/runtime.GOARCH as well as using build tags 96 * test files are allowed to check runtime.GOOS/runtime.GOARCH as well as using build tags 97 98 Other exceptions can be considered, but in general code outside of `pkg/proc/native` should: 99 100 * use `proc.BinaryInfo.GOOS` instead of `runtime.GOOS` 101 * use `proc.BinaryInfo.Arch.Name` instead of `runtime.GOARCH` 102 * use `proc.BinaryInfo.Arch.PtrSize()` instead of determining the pointer size with `unsafe.Sizeof` 103 * use `uint64` wherever an address-sized integer is needed, instead of `uintptr` 104 * use `amd64_filename.go` instead of the build tag version, `filename_amd64.go` 105 106 ### <a name='skippable'> Features and tests that can be skipped by a port 107 108 Delve offers many features, however not all of them are necessary for a useful port of Delve. The following features are optional to implement for a port: 109 110 - Reading core files (i.e. `pkg/proc/core`) 111 - Writing core files (i.e. `pkg/proc/native/dump_*.go`) 112 - Watchpoints (`(*nativeThread).writeHardwareBreakpoint` etc) 113 - Supporting CGO calls (`proc.Arch.switchStack`) 114 - eBPF (`pkg/proc/internal/ebpf`) 115 - Working with Position Independent Executables (PIE), unless the default buildmode for the port platform is PIE 116 - Function call injection (`pkg/proc/fncall.go` -- it is probably not supported on the port architecture anyway) 117 118 For all these features it is acceptable (and possibly advisable) to either leave the implementation empty or to write a stub that always returns an "not implemented" error. Tests relative to these features can be skipped, `proc_test.go` has a `skipOn` utility function that can be called to skip a specific test on some architectures. 119 120 Other tests should pass reliably, it is acceptable to skip some of them as long as most of them will pass. 121 The following tests should not be skipped even if you will be tempted to: 122 123 * `proc.TestNextConcurrent` 124 * `proc.TestNextConcurrentVariant2` 125 * `proc.TestBreakpointCounts` (enable `proc.TestBreakpointCountsWithDetection` if you have problems with this) 126 * `proc.TestStepConcurrentDirect` 127 * `proc.TestStepConcurrentPtr` 128 129 ### Porting to Big Endian architectures 130 131 Delve was initially written for amd64 and assumed 64bit and little endianness everywhere. The assumption on pointer size has been removed throughout the codebase, the assumption about endianness hasn't. Both `pkg/dwarf/loclist` and `pkg/dwarf/frame` incorrectly assume little endian encoding. Other parts of the code might do the same. 132 133 ### Porting to ARM32, MIPS and Software Single Stepping 134 135 When resuming a thread stopped at a breakpoint Delve will: 136 137 1. remove the breakpoint temporarily 138 2. make the thread execute a single instruction 139 3. write the breakpoint back 140 141 Step 2 is implemented using the hardware single step feature that many CPUs have and that is exposed via PTRACE_SINGLESTEP or similar features. 142 ARM32 and MIPS do not have a hardware single step implemented, this means that it has to be implemented in software. 143 The linux kernel used to have a software implementation of PTRACE_SINGLESTEP but those have been removed because they were too burdensome to maintain and delegated the feature to userspace debuggers entirely. 144 145 A software singlestep implementation would work like this: 146 147 1. decode the current instruction 148 2. figure out all the possible places where the PC registers could be after executing the current instruction 149 3. set a breakpoint on all of them 150 4. resume the thread normally 151 5. clear all breakpoints created on step 3 152 153 Delve does not currently have any infrastructure to help implement this, which means that porting to architectures without hardware singlestep is even more complicated. 154