From c4536f7e051e8cf8e72e569cfcc1fe8384995094 Mon Sep 17 00:00:00 2001 From: wehub-resource-sync Date: Mon, 13 Jul 2026 12:30:31 +0800 Subject: [PATCH] chore: import upstream snapshot with attribution --- .github/workflows/go.yml | 66 ++ .gitignore | 24 + LICENSE | 21 + README.md | 79 ++ README.wehub.md | 7 + data/download.sh | 62 ++ debug.go | 12 + fileui.go | 785 ++++++++++++++ fileui_comments.go | 36 + fileui_navigation_test.go | 102 ++ fileui_settings.go | 229 +++++ fileui_tabs.go | 295 ++++++ go.mod | 23 + go.sum | 41 + icons.go | 12 + internal/README.md | 1 + internal/asmcheck/main.go | 92 ++ internal/asmhelp/asmhelp.go | 964 ++++++++++++++++++ internal/asmhelp/asmhelp_test.go | 388 +++++++ internal/asmref/README.md | 54 + internal/asmref/asmref.go | 94 ++ internal/asmref/asmref_test.go | 17 + internal/asmref/gen/arm.go | 226 ++++ internal/asmref/gen/arm_test.go | 66 ++ internal/asmref/gen/build.go | 88 ++ internal/asmref/gen/coverage.go | 57 ++ internal/asmref/gen/main.go | 69 ++ .../gen/testdata/arm/AArch64-shared.xml | 9 + .../asmref/gen/testdata/arm/abs_advsimd.xml | 19 + internal/asmref/gen/testdata/arm/add_imm.xml | 35 + internal/asmref/gen/testdata/arm/ldr_imm.xml | 30 + .../gen/testdata/arm/sysreg_ctr_el0.xml | 9 + .../asmref/gen/testdata/arm/ubfiz_ubfm.xml | 28 + .../asmref/gen/testdata/x86/instructions.xml | 42 + internal/asmref/gen/x86.go | 139 +++ internal/asmref/gen/x86_test.go | 89 ++ internal/asmref/table.json.gz | Bin 0 -> 650128 bytes internal/atomicfile/atomicfile.go | 53 + internal/codeview/codeview.go | 152 +++ internal/codeview/codeview_test.go | 111 ++ internal/codeview/comments.go | 104 ++ internal/codeview/draw.go | 343 +++++++ internal/codeview/geometry.go | 124 +++ internal/codeview/help.go | 113 ++ internal/codeview/input.go | 296 ++++++ internal/codeview/selection.go | 188 ++++ internal/codeview/selection_test.go | 44 + internal/comments/comments.go | 477 +++++++++ internal/comments/comments_test.go | 246 +++++ internal/disasm/code.go | 70 ++ internal/disasm/file.go | 24 + internal/disasm/linerange.go | 14 + internal/disasm/lineset.go | 62 ++ internal/f32color/color.go | 127 +++ internal/go/LICENSE | 27 + internal/go/expose.go_ | 49 + internal/go/src/abi/abi.go | 104 ++ internal/go/src/abi/abi_amd64.go | 18 + internal/go/src/abi/abi_arm64.go | 17 + internal/go/src/abi/abi_generic.go | 38 + internal/go/src/abi/abi_loong64.go | 17 + internal/go/src/abi/abi_ppc64x.go | 19 + internal/go/src/abi/abi_riscv64.go | 17 + internal/go/src/abi/abi_s390x.go | 19 + internal/go/src/abi/bounds.go | 113 ++ internal/go/src/abi/compiletype.go | 28 + internal/go/src/abi/escape.go | 65 ++ internal/go/src/abi/funcpc.go | 31 + internal/go/src/abi/funcpc_gccgo.go | 21 + internal/go/src/abi/iface.go | 41 + internal/go/src/abi/map.go | 64 ++ internal/go/src/abi/rangefuncconsts.go | 18 + internal/go/src/abi/runtime.go | 8 + internal/go/src/abi/stack.go | 33 + internal/go/src/abi/stub.s | 7 + internal/go/src/abi/switch.go | 58 ++ internal/go/src/abi/symtab.go | 143 +++ internal/go/src/abi/type.go | 777 ++++++++++++++ internal/go/src/archive/archive.go | 503 +++++++++ internal/go/src/bio/buf.go | 148 +++ internal/go/src/bio/buf_mmap.go | 62 ++ internal/go/src/bio/buf_nommap.go | 11 + internal/go/src/bisect/bisect.go | 778 ++++++++++++++ internal/go/src/buildcfg/cfg.go | 467 +++++++++ internal/go/src/buildcfg/exp.go | 203 ++++ internal/go/src/buildcfg/zbootstrap.go | 22 + internal/go/src/disasm/disasm.go | 470 +++++++++ internal/go/src/disasm/expose.go | 51 + internal/go/src/goarch/gengoarch.go | 60 ++ internal/go/src/goarch/goarch.go | 65 ++ internal/go/src/goarch/goarch_386.go | 13 + internal/go/src/goarch/goarch_amd64.go | 13 + internal/go/src/goarch/goarch_arm.go | 13 + internal/go/src/goarch/goarch_arm64.go | 13 + internal/go/src/goarch/goarch_loong64.go | 15 + internal/go/src/goarch/goarch_mips.go | 13 + internal/go/src/goarch/goarch_mips64.go | 13 + internal/go/src/goarch/goarch_mips64le.go | 13 + internal/go/src/goarch/goarch_mipsle.go | 13 + internal/go/src/goarch/goarch_ppc64.go | 13 + internal/go/src/goarch/goarch_ppc64le.go | 13 + internal/go/src/goarch/goarch_riscv64.go | 13 + internal/go/src/goarch/goarch_s390x.go | 13 + internal/go/src/goarch/goarch_wasm.go | 13 + internal/go/src/goarch/zgoarch_386.go | 32 + internal/go/src/goarch/zgoarch_amd64.go | 32 + internal/go/src/goarch/zgoarch_arm.go | 32 + internal/go/src/goarch/zgoarch_arm64.go | 32 + internal/go/src/goarch/zgoarch_arm64be.go | 32 + internal/go/src/goarch/zgoarch_armbe.go | 32 + internal/go/src/goarch/zgoarch_loong64.go | 32 + internal/go/src/goarch/zgoarch_mips.go | 32 + internal/go/src/goarch/zgoarch_mips64.go | 32 + internal/go/src/goarch/zgoarch_mips64le.go | 32 + internal/go/src/goarch/zgoarch_mips64p32.go | 32 + internal/go/src/goarch/zgoarch_mips64p32le.go | 32 + internal/go/src/goarch/zgoarch_mipsle.go | 32 + internal/go/src/goarch/zgoarch_ppc.go | 32 + internal/go/src/goarch/zgoarch_ppc64.go | 32 + internal/go/src/goarch/zgoarch_ppc64le.go | 32 + internal/go/src/goarch/zgoarch_riscv.go | 32 + internal/go/src/goarch/zgoarch_riscv64.go | 32 + internal/go/src/goarch/zgoarch_s390.go | 32 + internal/go/src/goarch/zgoarch_s390x.go | 32 + internal/go/src/goarch/zgoarch_sparc.go | 32 + internal/go/src/goarch/zgoarch_sparc64.go | 32 + internal/go/src/goarch/zgoarch_wasm.go | 32 + .../go/src/goexperiment/exp_arenas_off.go | 8 + internal/go/src/goexperiment/exp_arenas_on.go | 8 + .../src/goexperiment/exp_boringcrypto_off.go | 8 + .../src/goexperiment/exp_boringcrypto_on.go | 8 + .../go/src/goexperiment/exp_cgocheck2_off.go | 8 + .../go/src/goexperiment/exp_cgocheck2_on.go | 8 + .../go/src/goexperiment/exp_dwarf5_off.go | 8 + internal/go/src/goexperiment/exp_dwarf5_on.go | 8 + .../go/src/goexperiment/exp_fieldtrack_off.go | 8 + .../go/src/goexperiment/exp_fieldtrack_on.go | 8 + .../exp_goroutineleakprofile_off.go | 8 + .../exp_goroutineleakprofile_on.go | 8 + .../go/src/goexperiment/exp_greenteagc_off.go | 8 + .../go/src/goexperiment/exp_greenteagc_on.go | 8 + .../goexperiment/exp_heapminimum512kib_off.go | 8 + .../goexperiment/exp_heapminimum512kib_on.go | 8 + .../go/src/goexperiment/exp_jsonv2_off.go | 8 + internal/go/src/goexperiment/exp_jsonv2_on.go | 8 + .../go/src/goexperiment/exp_loopvar_off.go | 8 + .../go/src/goexperiment/exp_loopvar_on.go | 8 + .../go/src/goexperiment/exp_newinliner_off.go | 8 + .../go/src/goexperiment/exp_newinliner_on.go | 8 + .../goexperiment/exp_preemptibleloops_off.go | 8 + .../goexperiment/exp_preemptibleloops_on.go | 8 + .../exp_randomizedheapbase64_off.go | 8 + .../exp_randomizedheapbase64_on.go | 8 + .../go/src/goexperiment/exp_regabiargs_off.go | 8 + .../go/src/goexperiment/exp_regabiargs_on.go | 8 + .../goexperiment/exp_regabiwrappers_off.go | 8 + .../src/goexperiment/exp_regabiwrappers_on.go | 8 + .../src/goexperiment/exp_runtimefreegc_off.go | 8 + .../src/goexperiment/exp_runtimefreegc_on.go | 8 + .../src/goexperiment/exp_runtimesecret_off.go | 8 + .../src/goexperiment/exp_runtimesecret_on.go | 8 + internal/go/src/goexperiment/exp_simd_off.go | 8 + internal/go/src/goexperiment/exp_simd_on.go | 8 + .../exp_sizespecializedmalloc_off.go | 8 + .../exp_sizespecializedmalloc_on.go | 8 + .../goexperiment/exp_staticlockranking_off.go | 8 + .../goexperiment/exp_staticlockranking_on.go | 8 + internal/go/src/goexperiment/flags.go | 131 +++ internal/go/src/goexperiment/mkconsts.go | 72 ++ internal/go/src/goobj/builtin.go | 47 + internal/go/src/goobj/builtinlist.go | 275 +++++ internal/go/src/goobj/funcinfo.go | 145 +++ internal/go/src/goobj/mkbuiltin.go | 160 +++ internal/go/src/goobj/objfile.go | 874 ++++++++++++++++ internal/go/src/objabi/autotype.go | 38 + internal/go/src/objabi/flag.go | 374 +++++++ internal/go/src/objabi/funcid.go | 52 + internal/go/src/objabi/head.go | 114 +++ internal/go/src/objabi/line.go | 134 +++ internal/go/src/objabi/path.go | 78 ++ internal/go/src/objabi/pkgspecial.go | 136 +++ internal/go/src/objabi/reloctype.go | 544 ++++++++++ internal/go/src/objabi/reloctype_string.go | 126 +++ internal/go/src/objabi/stack.go | 32 + internal/go/src/objabi/symkind.go | 98 ++ internal/go/src/objabi/symkind_string.go | 48 + internal/go/src/objabi/util.go | 33 + internal/go/src/objabi/zbootstrap.go | 3 + internal/go/src/objfile/elf.go | 174 ++++ internal/go/src/objfile/goobj.go | 342 +++++++ internal/go/src/objfile/macho.go | 129 +++ internal/go/src/objfile/objfile.go | 185 ++++ internal/go/src/objfile/pe.go | 194 ++++ internal/go/src/objfile/plan9obj.go | 150 +++ internal/go/src/objfile/xcoff.go | 162 +++ internal/go/src/saferio/io.go | 132 +++ internal/go/src/src/pos.go | 478 +++++++++ internal/go/src/src/xpos.go | 183 ++++ internal/go/src/sys/arch.go | 287 ++++++ internal/go/src/sys/args.go | 13 + internal/go/src/unsafeheader/unsafeheader.go | 37 + internal/go/src/xcoff/ar.go | 226 ++++ internal/go/src/xcoff/file.go | 693 +++++++++++++ internal/go/src/xcoff/xcoff.go | 367 +++++++ internal/go/update.go | 78 ++ internal/goobj/code.go | 254 +++++ internal/goobj/file.go | 119 +++ internal/goobj/file_test.go | 55 + internal/gui/filterlist.go | 144 +++ internal/gui/focus.go | 41 + internal/gui/geom.go | 22 + internal/gui/scroll.go | 68 ++ internal/gui/selectlist.go | 208 ++++ internal/gui/theme.go | 109 ++ internal/gui/widgets.go | 172 ++++ internal/gui/windows.go | 55 + internal/mcp/app.go | 272 +++++ internal/mcp/export.go | 173 ++++ internal/mcp/mcp.go | 538 ++++++++++ internal/mcp/mcp_test.go | 113 ++ internal/mcp/session.go | 72 ++ internal/syntax/syntax.go | 453 ++++++++ internal/syntax/syntax_test.go | 107 ++ internal/wasmobj/file.go | 140 +++ internal/wasmobj/file_test.go | 35 + loader.go | 136 +++ main.go | 95 ++ navigation.go | 100 ++ navigation_test.go | 51 + packaging/macos/Info.plist | 30 + screenshot.gif | Bin 0 -> 184589 bytes scripts/build-macos-dmg.sh | 69 ++ settings.go | 124 +++ settings_test.go | 35 + testdata/c-wasm/.gitignore | 2 + testdata/c-wasm/Makefile | 16 + testdata/c-wasm/example-clang.wasm | Bin 0 -> 1016 bytes testdata/c-wasm/example.c | 14 + testdata/c-wasm/example.wasm | Bin 0 -> 908 bytes 239 files changed, 23836 insertions(+) create mode 100644 .github/workflows/go.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README.wehub.md create mode 100755 data/download.sh create mode 100644 debug.go create mode 100644 fileui.go create mode 100644 fileui_comments.go create mode 100644 fileui_navigation_test.go create mode 100644 fileui_settings.go create mode 100644 fileui_tabs.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 icons.go create mode 100644 internal/README.md create mode 100644 internal/asmcheck/main.go create mode 100644 internal/asmhelp/asmhelp.go create mode 100644 internal/asmhelp/asmhelp_test.go create mode 100644 internal/asmref/README.md create mode 100644 internal/asmref/asmref.go create mode 100644 internal/asmref/asmref_test.go create mode 100644 internal/asmref/gen/arm.go create mode 100644 internal/asmref/gen/arm_test.go create mode 100644 internal/asmref/gen/build.go create mode 100644 internal/asmref/gen/coverage.go create mode 100644 internal/asmref/gen/main.go create mode 100644 internal/asmref/gen/testdata/arm/AArch64-shared.xml create mode 100644 internal/asmref/gen/testdata/arm/abs_advsimd.xml create mode 100644 internal/asmref/gen/testdata/arm/add_imm.xml create mode 100644 internal/asmref/gen/testdata/arm/ldr_imm.xml create mode 100644 internal/asmref/gen/testdata/arm/sysreg_ctr_el0.xml create mode 100644 internal/asmref/gen/testdata/arm/ubfiz_ubfm.xml create mode 100644 internal/asmref/gen/testdata/x86/instructions.xml create mode 100644 internal/asmref/gen/x86.go create mode 100644 internal/asmref/gen/x86_test.go create mode 100644 internal/asmref/table.json.gz create mode 100644 internal/atomicfile/atomicfile.go create mode 100644 internal/codeview/codeview.go create mode 100644 internal/codeview/codeview_test.go create mode 100644 internal/codeview/comments.go create mode 100644 internal/codeview/draw.go create mode 100644 internal/codeview/geometry.go create mode 100644 internal/codeview/help.go create mode 100644 internal/codeview/input.go create mode 100644 internal/codeview/selection.go create mode 100644 internal/codeview/selection_test.go create mode 100644 internal/comments/comments.go create mode 100644 internal/comments/comments_test.go create mode 100644 internal/disasm/code.go create mode 100644 internal/disasm/file.go create mode 100644 internal/disasm/linerange.go create mode 100644 internal/disasm/lineset.go create mode 100644 internal/f32color/color.go create mode 100644 internal/go/LICENSE create mode 100644 internal/go/expose.go_ create mode 100755 internal/go/src/abi/abi.go create mode 100755 internal/go/src/abi/abi_amd64.go create mode 100755 internal/go/src/abi/abi_arm64.go create mode 100755 internal/go/src/abi/abi_generic.go create mode 100755 internal/go/src/abi/abi_loong64.go create mode 100755 internal/go/src/abi/abi_ppc64x.go create mode 100755 internal/go/src/abi/abi_riscv64.go create mode 100755 internal/go/src/abi/abi_s390x.go create mode 100755 internal/go/src/abi/bounds.go create mode 100755 internal/go/src/abi/compiletype.go create mode 100755 internal/go/src/abi/escape.go create mode 100755 internal/go/src/abi/funcpc.go create mode 100755 internal/go/src/abi/funcpc_gccgo.go create mode 100755 internal/go/src/abi/iface.go create mode 100755 internal/go/src/abi/map.go create mode 100755 internal/go/src/abi/rangefuncconsts.go create mode 100755 internal/go/src/abi/runtime.go create mode 100755 internal/go/src/abi/stack.go create mode 100755 internal/go/src/abi/stub.s create mode 100755 internal/go/src/abi/switch.go create mode 100755 internal/go/src/abi/symtab.go create mode 100755 internal/go/src/abi/type.go create mode 100755 internal/go/src/archive/archive.go create mode 100755 internal/go/src/bio/buf.go create mode 100755 internal/go/src/bio/buf_mmap.go create mode 100755 internal/go/src/bio/buf_nommap.go create mode 100755 internal/go/src/bisect/bisect.go create mode 100755 internal/go/src/buildcfg/cfg.go create mode 100755 internal/go/src/buildcfg/exp.go create mode 100755 internal/go/src/buildcfg/zbootstrap.go create mode 100755 internal/go/src/disasm/disasm.go create mode 100644 internal/go/src/disasm/expose.go create mode 100755 internal/go/src/goarch/gengoarch.go create mode 100755 internal/go/src/goarch/goarch.go create mode 100755 internal/go/src/goarch/goarch_386.go create mode 100755 internal/go/src/goarch/goarch_amd64.go create mode 100755 internal/go/src/goarch/goarch_arm.go create mode 100755 internal/go/src/goarch/goarch_arm64.go create mode 100755 internal/go/src/goarch/goarch_loong64.go create mode 100755 internal/go/src/goarch/goarch_mips.go create mode 100755 internal/go/src/goarch/goarch_mips64.go create mode 100755 internal/go/src/goarch/goarch_mips64le.go create mode 100755 internal/go/src/goarch/goarch_mipsle.go create mode 100755 internal/go/src/goarch/goarch_ppc64.go create mode 100755 internal/go/src/goarch/goarch_ppc64le.go create mode 100755 internal/go/src/goarch/goarch_riscv64.go create mode 100755 internal/go/src/goarch/goarch_s390x.go create mode 100755 internal/go/src/goarch/goarch_wasm.go create mode 100755 internal/go/src/goarch/zgoarch_386.go create mode 100755 internal/go/src/goarch/zgoarch_amd64.go create mode 100755 internal/go/src/goarch/zgoarch_arm.go create mode 100755 internal/go/src/goarch/zgoarch_arm64.go create mode 100755 internal/go/src/goarch/zgoarch_arm64be.go create mode 100755 internal/go/src/goarch/zgoarch_armbe.go create mode 100755 internal/go/src/goarch/zgoarch_loong64.go create mode 100755 internal/go/src/goarch/zgoarch_mips.go create mode 100755 internal/go/src/goarch/zgoarch_mips64.go create mode 100755 internal/go/src/goarch/zgoarch_mips64le.go create mode 100755 internal/go/src/goarch/zgoarch_mips64p32.go create mode 100755 internal/go/src/goarch/zgoarch_mips64p32le.go create mode 100755 internal/go/src/goarch/zgoarch_mipsle.go create mode 100755 internal/go/src/goarch/zgoarch_ppc.go create mode 100755 internal/go/src/goarch/zgoarch_ppc64.go create mode 100755 internal/go/src/goarch/zgoarch_ppc64le.go create mode 100755 internal/go/src/goarch/zgoarch_riscv.go create mode 100755 internal/go/src/goarch/zgoarch_riscv64.go create mode 100755 internal/go/src/goarch/zgoarch_s390.go create mode 100755 internal/go/src/goarch/zgoarch_s390x.go create mode 100755 internal/go/src/goarch/zgoarch_sparc.go create mode 100755 internal/go/src/goarch/zgoarch_sparc64.go create mode 100755 internal/go/src/goarch/zgoarch_wasm.go create mode 100755 internal/go/src/goexperiment/exp_arenas_off.go create mode 100755 internal/go/src/goexperiment/exp_arenas_on.go create mode 100755 internal/go/src/goexperiment/exp_boringcrypto_off.go create mode 100755 internal/go/src/goexperiment/exp_boringcrypto_on.go create mode 100755 internal/go/src/goexperiment/exp_cgocheck2_off.go create mode 100755 internal/go/src/goexperiment/exp_cgocheck2_on.go create mode 100755 internal/go/src/goexperiment/exp_dwarf5_off.go create mode 100755 internal/go/src/goexperiment/exp_dwarf5_on.go create mode 100755 internal/go/src/goexperiment/exp_fieldtrack_off.go create mode 100755 internal/go/src/goexperiment/exp_fieldtrack_on.go create mode 100755 internal/go/src/goexperiment/exp_goroutineleakprofile_off.go create mode 100755 internal/go/src/goexperiment/exp_goroutineleakprofile_on.go create mode 100755 internal/go/src/goexperiment/exp_greenteagc_off.go create mode 100755 internal/go/src/goexperiment/exp_greenteagc_on.go create mode 100755 internal/go/src/goexperiment/exp_heapminimum512kib_off.go create mode 100755 internal/go/src/goexperiment/exp_heapminimum512kib_on.go create mode 100755 internal/go/src/goexperiment/exp_jsonv2_off.go create mode 100755 internal/go/src/goexperiment/exp_jsonv2_on.go create mode 100755 internal/go/src/goexperiment/exp_loopvar_off.go create mode 100755 internal/go/src/goexperiment/exp_loopvar_on.go create mode 100755 internal/go/src/goexperiment/exp_newinliner_off.go create mode 100755 internal/go/src/goexperiment/exp_newinliner_on.go create mode 100755 internal/go/src/goexperiment/exp_preemptibleloops_off.go create mode 100755 internal/go/src/goexperiment/exp_preemptibleloops_on.go create mode 100755 internal/go/src/goexperiment/exp_randomizedheapbase64_off.go create mode 100755 internal/go/src/goexperiment/exp_randomizedheapbase64_on.go create mode 100755 internal/go/src/goexperiment/exp_regabiargs_off.go create mode 100755 internal/go/src/goexperiment/exp_regabiargs_on.go create mode 100755 internal/go/src/goexperiment/exp_regabiwrappers_off.go create mode 100755 internal/go/src/goexperiment/exp_regabiwrappers_on.go create mode 100755 internal/go/src/goexperiment/exp_runtimefreegc_off.go create mode 100755 internal/go/src/goexperiment/exp_runtimefreegc_on.go create mode 100755 internal/go/src/goexperiment/exp_runtimesecret_off.go create mode 100755 internal/go/src/goexperiment/exp_runtimesecret_on.go create mode 100755 internal/go/src/goexperiment/exp_simd_off.go create mode 100755 internal/go/src/goexperiment/exp_simd_on.go create mode 100755 internal/go/src/goexperiment/exp_sizespecializedmalloc_off.go create mode 100755 internal/go/src/goexperiment/exp_sizespecializedmalloc_on.go create mode 100755 internal/go/src/goexperiment/exp_staticlockranking_off.go create mode 100755 internal/go/src/goexperiment/exp_staticlockranking_on.go create mode 100755 internal/go/src/goexperiment/flags.go create mode 100755 internal/go/src/goexperiment/mkconsts.go create mode 100755 internal/go/src/goobj/builtin.go create mode 100755 internal/go/src/goobj/builtinlist.go create mode 100755 internal/go/src/goobj/funcinfo.go create mode 100755 internal/go/src/goobj/mkbuiltin.go create mode 100755 internal/go/src/goobj/objfile.go create mode 100755 internal/go/src/objabi/autotype.go create mode 100755 internal/go/src/objabi/flag.go create mode 100755 internal/go/src/objabi/funcid.go create mode 100755 internal/go/src/objabi/head.go create mode 100755 internal/go/src/objabi/line.go create mode 100755 internal/go/src/objabi/path.go create mode 100755 internal/go/src/objabi/pkgspecial.go create mode 100755 internal/go/src/objabi/reloctype.go create mode 100755 internal/go/src/objabi/reloctype_string.go create mode 100755 internal/go/src/objabi/stack.go create mode 100755 internal/go/src/objabi/symkind.go create mode 100755 internal/go/src/objabi/symkind_string.go create mode 100755 internal/go/src/objabi/util.go create mode 100755 internal/go/src/objabi/zbootstrap.go create mode 100755 internal/go/src/objfile/elf.go create mode 100755 internal/go/src/objfile/goobj.go create mode 100755 internal/go/src/objfile/macho.go create mode 100755 internal/go/src/objfile/objfile.go create mode 100755 internal/go/src/objfile/pe.go create mode 100755 internal/go/src/objfile/plan9obj.go create mode 100755 internal/go/src/objfile/xcoff.go create mode 100755 internal/go/src/saferio/io.go create mode 100755 internal/go/src/src/pos.go create mode 100755 internal/go/src/src/xpos.go create mode 100755 internal/go/src/sys/arch.go create mode 100755 internal/go/src/sys/args.go create mode 100755 internal/go/src/unsafeheader/unsafeheader.go create mode 100755 internal/go/src/xcoff/ar.go create mode 100755 internal/go/src/xcoff/file.go create mode 100755 internal/go/src/xcoff/xcoff.go create mode 100644 internal/go/update.go create mode 100644 internal/goobj/code.go create mode 100644 internal/goobj/file.go create mode 100644 internal/goobj/file_test.go create mode 100644 internal/gui/filterlist.go create mode 100644 internal/gui/focus.go create mode 100644 internal/gui/geom.go create mode 100644 internal/gui/scroll.go create mode 100644 internal/gui/selectlist.go create mode 100644 internal/gui/theme.go create mode 100644 internal/gui/widgets.go create mode 100644 internal/gui/windows.go create mode 100644 internal/mcp/app.go create mode 100644 internal/mcp/export.go create mode 100644 internal/mcp/mcp.go create mode 100644 internal/mcp/mcp_test.go create mode 100644 internal/mcp/session.go create mode 100644 internal/syntax/syntax.go create mode 100644 internal/syntax/syntax_test.go create mode 100644 internal/wasmobj/file.go create mode 100644 internal/wasmobj/file_test.go create mode 100644 loader.go create mode 100644 main.go create mode 100644 navigation.go create mode 100644 navigation_test.go create mode 100644 packaging/macos/Info.plist create mode 100644 screenshot.gif create mode 100755 scripts/build-macos-dmg.sh create mode 100644 settings.go create mode 100644 settings_test.go create mode 100644 testdata/c-wasm/.gitignore create mode 100644 testdata/c-wasm/Makefile create mode 100755 testdata/c-wasm/example-clang.wasm create mode 100644 testdata/c-wasm/example.c create mode 100755 testdata/c-wasm/example.wasm diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..67d07cb --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,66 @@ +name: CI + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + - run: sudo apt-get update && sudo apt-get install -y gcc pkg-config libwayland-dev libx11-dev libx11-xcb-dev libxkbcommon-x11-dev libgles2-mesa-dev libegl1-mesa-dev libffi-dev libxcursor-dev libvulkan-dev + - run: go test ./... + + build-macos: + if: github.event_name == 'workflow_dispatch' || (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) + needs: test + name: macOS ${{ matrix.goarch }} + strategy: + fail-fast: false + matrix: + include: + - runner: macos-15 + goarch: arm64 + - runner: macos-15-intel + goarch: amd64 + runs-on: ${{ matrix.runner }} + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + - name: Build application DMG + shell: bash + run: | + if [[ "$GITHUB_REF_TYPE" == "tag" && "$GITHUB_REF_NAME" == v* ]]; then + # CFBundleShortVersionString accepts only digits and dots; + # strip any pre-release/build suffix (v1.2.3-rc1 -> 1.2.3). + version="${GITHUB_REF_NAME#v}" + version="${version%%-*}" + version="${version%%+*}" + else + version="0.0.${GITHUB_RUN_NUMBER}" + fi + ./scripts/build-macos-dmg.sh "$version" "$GITHUB_RUN_NUMBER" "${{ matrix.goarch }}" + - name: Upload DMG + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + # Artifact names are immutable in upload-artifact v4+; without a + # per-arch name both matrix jobs upload to "artifact" and the + # second one fails. + name: lensm-dmg-${{ matrix.goarch }} + path: dist/*.dmg + archive: false + if-no-files-found: error diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94fb43c --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +data/arm64/ +data/x86/ + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +.idea/ + +lensm +/output/ +/dist/ +/tmp/ +*.lensm-comments.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8d57f72 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Egon Elbre + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..71cb8f8 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# lensm + +A tool for viewing assembly and source. + +![Screenshot](./screenshot.gif) + +Install with the usual Go commands: + +``` +go install loov.dev/lensm@main +``` + +_For Linux you may need to add some [additional dependencies](https://gioui.org/doc/install/linux). You can use `go install --tags nowayland loov.dev/lensm@main` or `go install --tags nox11 loov.dev/lensm@main` respectively to skip building Wayland or X11 version._ + +Build this checkout locally: + +``` +go build -o lensm . +``` + +GitHub Actions also builds installable macOS disk images for Apple Silicon +and Intel. Download the DMG artifact from a successful CI run, open it, and +drag `Lensm.app` to Applications. CI builds use an ad-hoc signature, so macOS +may require the first launch through **Control-click → Open**. + +To build the DMG locally on macOS: + +``` +./scripts/build-macos-dmg.sh 0.0.0 1 "$(go env GOARCH)" +``` + +On Linux, use the same Gio tags if you want to skip a windowing backend: + +``` +go build -tags nowayland -o lensm . +go build -tags nox11 -o lensm . +``` + +Run the program with an executable path, or start it without arguments +and load an executable from the top bar. On macOS, use Choose... to select +the executable with the native Finder file dialog. The function filter is +available inside the UI. `-watch` allows to automatically reload the +executable and information when it changes. + +``` +lensm -watch lensm +``` + +Inside the code view: + +- follow call targets and use `Alt+Left/Right` (or `Cmd/Ctrl+[` and + `Cmd/Ctrl+]`) to navigate between functions; +- hover an assembly instruction to see its reference and a simplified + explanation when Lensm has a matching rule; +- drag across Go assembly, native assembly, or source lines to select a block, + then use `Cmd/Ctrl+C` to copy it. `Shift` extends a selection and + `Escape` clears it. + +Run lensm as an MCP server over stdio: + +``` +lensm mcp [-comments ./lensm.lensm-comments.json] ./lensm +``` + +The MCP server exposes tools for listing functions, reading a function's +Go source, Go assembly and native assembly, and reading or writing comments. +By default comments are stored in a sidecar file named +`.lensm-comments.json`. + +When the GUI and a separate `lensm mcp` process write the same sidecar, +saves merge per comment: each process's additions, edits, and deletions +survive, and only conflicting edits to the same comment resolve to the +last writer. + +Note: The program requires a binary that is built on your computer, otherwise the source code for the functions cannot be loaded. + +## Why? + +I wrote a blog post at https://www.storj.io/blog/lensm on why and how the core functionality works. diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..cc2bbbf --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`loov/lensm` +- 原始仓库:https://github.com/loov/lensm +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/data/download.sh b/data/download.sh new file mode 100755 index 0000000..f3ecc97 --- /dev/null +++ b/data/download.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# Downloads the CPU ISA source dumps that internal/asmref/gen parses into +# table.json. Both files are large and gitignored (see .gitignore); this script +# fetches them into .data/ so the generator can be pointed at them. +# +# Usage: +# .data/download.sh # download anything missing +# FORCE=1 .data/download.sh # re-download even if present +# +# After it finishes it prints the exact `go run ./internal/asmref/gen ...` +# command with the resolved paths. +set -euo pipefail + +DATA_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FORCE="${FORCE:-0}" + +# x86: uops.info (XED-derived) instruction + port data. ~140MB. +X86_URL="https://uops.info/instructions.xml" +X86_FILE="$DATA_DIR/x86/instructions.xml" + +# arm64: ARM's official A64 ISA XML. URL and version are pinned to match the +# release golang.org/x/arch/arm64/instgen/xmlspec expects (its ExpectedURL). +ARM_VERSION="ISA_A64_xml_A_profile-2025-12" +ARM_URL="https://developer.arm.com/-/cdn-downloads/permalink/Exploration-Tools-A64-ISA/ISA_A64/${ARM_VERSION}.tar.gz" +ARM_TARBALL="$DATA_DIR/arm64/${ARM_VERSION}.tar.gz" +ARM_DIR="$DATA_DIR/arm64/${ARM_VERSION}" + +fetch() { # url dest + echo " downloading $1" + curl -fL --progress-bar -o "$2" "$1" +} + +echo "==> x86 (uops.info)" +if [[ "$FORCE" != "1" && -s "$X86_FILE" ]]; then + echo " present, skipping: $X86_FILE" +else + mkdir -p "$(dirname "$X86_FILE")" + fetch "$X86_URL" "$X86_FILE" +fi + +echo "==> arm64 (ARM A64 ISA XML, ${ARM_VERSION})" +if [[ "$FORCE" != "1" && -d "$ARM_DIR" ]]; then + echo " present, skipping: $ARM_DIR" +else + mkdir -p "$DATA_DIR/arm64" + fetch "$ARM_URL" "$ARM_TARBALL" + echo " extracting" + tar xzf "$ARM_TARBALL" -C "$DATA_DIR/arm64" + rm -f "$ARM_TARBALL" + xml_count=$(find "$ARM_DIR" -name '*.xml' | wc -l | tr -d ' ') + echo " extracted ${xml_count} xml files" +fi + +echo +echo "Sources ready. Generate the real table with:" +echo +echo " go run ./internal/asmref/gen \\" +echo " -arm $ARM_DIR \\" +echo " -x86 $X86_FILE \\" +echo " -x86arch ADL-P \\" +echo " -out internal/asmref/table.json" diff --git a/debug.go b/debug.go new file mode 100644 index 0000000..9a38104 --- /dev/null +++ b/debug.go @@ -0,0 +1,12 @@ +package main + +const N = 44 + +func Fibonacci(n int) int { + if n <= 1 { + return n + } + return Fibonacci(n-1) + Fibonacci(n-2) +} + +var _ = Fibonacci(0) diff --git a/fileui.go b/fileui.go new file mode 100644 index 0000000..81763bb --- /dev/null +++ b/fileui.go @@ -0,0 +1,785 @@ +package main + +import ( + "errors" + "fmt" + "image" + "io" + "os" + "slices" + "strconv" + "strings" + "time" + + "gioui.org/app" + "gioui.org/font" + "gioui.org/io/clipboard" + "gioui.org/io/event" + "gioui.org/io/key" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" + "gioui.org/x/component" + "gioui.org/x/explorer" + + "loov.dev/lensm/internal/codeview" + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" + "loov.dev/lensm/internal/mcp" + "loov.dev/lensm/internal/syntax" +) + +var workInProgressWASM bool + +type FileUIConfig struct { + Path string + Watch bool + Context int + CommentsPath string +} + +type FileUI struct { + Windows *gui.Windows + Theme *gui.Theme + + Config FileUIConfig + Settings AppSettings + + LoadError error + + // Currently loaded executable. + File disasm.File + Funcs *gui.FilterList[disasm.Func] + + // Active code view. + CodeTabs []*CodeTab + ActiveTab int + Tabs widget.List + + // split divides the function list from the code view. + split component.Resize + + // Other FileUI elements. + BrowseButton widget.Clickable + SettingsButton widget.Clickable + Dark widget.Bool + SyntaxStyle widget.Enum + ShowNativeAsm widget.Bool + ShowAsmHelp widget.Bool + Comment widget.Editor + TextSizeEditor widget.Editor + StartMCP widget.Clickable + StopMCP widget.Clickable + + Comments *comments.Store + MCP *mcp.AppServer + + picker *explorer.Explorer + loader *loader + invalidate chan struct{} + settingsEvents chan event.Event + settingsAcks chan struct{} + pickerResults chan pickerResult + exited chan struct{} + flushTimer *time.Timer + sessionDirty bool + commentKey string + copyStatus string + saveError string + settingsWindowOpen bool + pickerOpen bool + loadGeneration uint64 + loadedPath string + Navigation NavigationHistory + navigatingHistory bool +} + +type pickerResult struct { + path string + ok bool + err error +} + +func NewFileUI(windows *gui.Windows, theme *material.Theme) *FileUI { + ui := &FileUI{} + ui.Windows = windows + settings, err := LoadAppSettings() + if err != nil { + fmt.Fprintf(os.Stderr, "unable to load settings: %v\n", err) + } + ui.Settings = settings + ui.Theme = gui.NewTheme(theme, settings.Dark) + ui.SyntaxStyle.Value = settings.SyntaxStyle + ui.Dark.Value = settings.Dark + ui.Funcs = gui.NewFilterList[disasm.Func](ui.Theme) + ui.ActiveTab = -1 + ui.Navigation.Reset() + ui.Tabs.List.Axis = layout.Horizontal + ui.ShowNativeAsm.Value = settings.ShowNativeAsm + ui.ShowAsmHelp.Value = settings.ShowAsmHelp + ui.split.Axis = layout.Horizontal + ui.split.Ratio = settings.SidebarRatio + ui.Comment.SingleLine = true + ui.TextSizeEditor.SingleLine = true + ui.TextSizeEditor.Submit = true + ui.TextSizeEditor.SetText(strconv.Itoa(settings.TextSize)) + ui.Comments, _ = comments.Open("", "") + return ui +} + +func (ui *FileUI) Run(w *app.Window) error { + var ops op.Ops + + exited := make(chan struct{}) + defer close(exited) + defer func() { + if ui.MCP != nil { + _ = ui.MCP.Close() + } + }() + + loader := newLoader(loadDisasmFile, ui.Config.Watch) + ui.loader = loader + defer loader.Close() + picker := explorer.NewExplorer(w) + ui.picker = picker + invalidate := make(chan struct{}, 1) + ui.invalidate = invalidate + settingsEvents := make(chan event.Event) + settingsAcks := make(chan struct{}) + ui.settingsEvents = settingsEvents + ui.settingsAcks = settingsAcks + pickerResults := make(chan pickerResult, 1) + ui.pickerResults = pickerResults + ui.exited = exited + flushTimer := time.NewTimer(time.Hour) + flushTimer.Stop() + ui.flushTimer = flushTimer + var settingsOps op.Ops + defer ui.flushPending() + defer func() { + ui.picker = nil + ui.loader = nil + ui.invalidate = nil + ui.settingsEvents = nil + ui.settingsAcks = nil + ui.pickerResults = nil + ui.exited = nil + ui.flushTimer = nil + }() + + if ui.Config.Path != "" { + ui.requestLoad(ui.Config.Path) + } + + events := make(chan event.Event) + acks := make(chan struct{}) + + go func() { + for { + ev := w.Event() + events <- ev + <-acks + if _, ok := ev.(app.DestroyEvent); ok { + return + } + } + }() + + for { + select { + case result := <-loader.Results(): + if result.generation != ui.loadGeneration { + if result.file != nil { + _ = result.file.Close() + } + continue + } + if result.err != nil { + ui.LoadError = result.err + if ui.MCP != nil { + ui.MCP.SetPath("", ui.Comments) + } + w.Invalidate() + continue + } + ui.SetFile(result.file) + w.Invalidate() + case <-invalidate: + w.Invalidate() + case <-flushTimer.C: + ui.flushPending() + w.Invalidate() + case res := <-pickerResults: + ui.pickerOpen = false + if res.err != nil { + ui.LoadError = res.err + } else if res.ok { + ui.Config.Path = res.path + ui.LoadError = nil + ui.copyStatus = "" + ui.requestLoad(res.path) + } + w.Invalidate() + case ev := <-settingsEvents: + switch e := ev.(type) { + case app.FrameEvent: + gtx := app.NewContext(&settingsOps, e) + ui.layoutSettingsWindow(gtx) + e.Frame(gtx.Ops) + case app.DestroyEvent: + ui.settingsWindowOpen = false + } + settingsAcks <- struct{}{} + case e := <-events: + picker.ListenEvents(e) + switch e := e.(type) { + case app.FrameEvent: + gtx := app.NewContext(&ops, e) + ui.Layout(gtx) + e.Frame(gtx.Ops) + + case app.DestroyEvent: + acks <- struct{}{} + return e.Err + } + acks <- struct{}{} + } + } +} + +func (ui *FileUI) SetFile(file disasm.File) { + ui.navigatingHistory = true + defer func() { + ui.navigatingHistory = false + if tab := ui.activeTab(); tab != nil { + ui.Navigation.Visit(tab.Name) + } + }() + // Keep history across watch-mode reloads of the same binary; open + // tabs are preserved there too. Entries whose functions vanished are + // skipped during navigation. + path := comments.CleanPath(ui.Config.Path) + if ui.File == nil || path != ui.loadedPath { + ui.Navigation.Reset() + } + ui.loadedPath = path + + initialLoad := ui.File == nil + if ui.File != nil { + _ = ui.File.Close() + } + + openNames := make([]string, 0, len(ui.CodeTabs)) + for _, tab := range ui.CodeTabs { + openNames = append(openNames, tab.Name) + } + activeName := ui.Funcs.Selected + if tab := ui.activeTab(); tab != nil { + activeName = tab.Name + } + if initialLoad && comments.CleanPath(ui.Config.Path) == ui.Settings.LastPath && len(ui.Settings.OpenTabs) > 0 { + openNames = append(openNames[:0], ui.Settings.OpenTabs...) + activeName = ui.Settings.ActiveTab + if activeName == "" && len(openNames) > 0 { + activeName = openNames[0] + } + } + + ui.File = file + ui.LoadError = nil + ui.loadCommentsForPath(ui.Config.Path) + ui.CodeTabs = nil + ui.ActiveTab = -1 + ui.commentKey = "" + ui.Funcs.SetItems(file.Funcs()) + + if activeName != "" { + ui.selectFuncByName(activeName) + } + for _, name := range openNames { + fn := ui.findFunc(name) + if fn == nil { + continue + } + tab := ui.appendCodeTab(fn) + if name == activeName { + ui.ActiveTab = len(ui.CodeTabs) - 1 + ui.selectFuncByName(tab.Name) + } + } + + if len(ui.CodeTabs) == 0 { + if ui.Funcs.SelectedItem == nil && len(ui.Funcs.Filtered) > 0 { + ui.Funcs.SelectIndex(0) + } + ui.openTab(ui.Funcs.SelectedItem, false) + ui.afterFileLoaded() + return + } + if !gui.InRange(ui.ActiveTab, len(ui.CodeTabs)) { + ui.ActiveTab = 0 + ui.selectFuncByName(ui.CodeTabs[ui.ActiveTab].Name) + } + ui.afterFileLoaded() +} + +func (ui *FileUI) loadOptions() disasm.Options { + return disasm.Options{Context: ui.Config.Context} +} + +func (ui *FileUI) findFunc(name string) disasm.Func { + if ui.File == nil { + return nil + } + for _, fn := range ui.File.Funcs() { + if fn.Name() == name { + return fn + } + } + return nil +} + +func (ui *FileUI) selectFuncByName(name string) { + ui.Funcs.Selected = name + ui.Funcs.List.Selected = -1 + var zero disasm.Func + ui.Funcs.SelectedItem = zero + + for i, fn := range ui.Funcs.Filtered { + if fn.Name() == name { + ui.Funcs.List.Selected = i + ui.Funcs.SelectedItem = fn + return + } + } + for _, fn := range ui.Funcs.All { + if fn.Name() == name { + ui.Funcs.SelectedItem = fn + return + } + } +} + +func (ui *FileUI) Layout(gtx layout.Context) { + colors := ui.Theme.Colors + paint.FillShape(gtx.Ops, colors.Background, clip.Rect{Max: gtx.Constraints.Max}.Op()) + + event.Op(gtx.Ops, ui) + ui.handleActions(gtx) + + layout.Flex{ + Axis: layout.Vertical, + }.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutToolbar(gtx, colors) + }), + layout.Rigid(gui.HorizontalLine{Height: 1, Color: colors.Splitter}.Layout), + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + return ui.layoutContent(gtx, colors) + }), + ) +} + +func (ui *FileUI) handleActions(gtx layout.Context) { + // widget.Editor claims Option+arrow word-jumps on macOS, and this + // unfocused poll runs before any editor's Update, so it would steal + // them from a focused editor. Only listen for Alt+arrows while no + // text editor has focus. + editorFocused := gtx.Focused(&ui.Comment) || gtx.Focused(&ui.Funcs.Filter) + filters := []event.Filter{ + key.Filter{Required: key.ModShortcut, Name: key.Name("[")}, + key.Filter{Required: key.ModShortcut, Name: key.Name("]")}, + key.Filter{Required: key.ModShortcut, Name: key.Name("W")}, + } + if !editorFocused { + filters = append(filters, + key.Filter{Required: key.ModAlt, Name: key.NameLeftArrow}, + key.Filter{Required: key.ModAlt, Name: key.NameRightArrow}, + ) + } + for { + ev, ok := gtx.Event(filters...) + if !ok { + break + } + keyEvent, ok := ev.(key.Event) + if !ok || keyEvent.State != key.Press { + continue + } + switch keyEvent.Name { + case key.NameLeftArrow, key.Name("["): + ui.navigateBack() + case key.NameRightArrow, key.Name("]"): + ui.navigateForward() + case key.Name("W"): + ui.closeTab(ui.ActiveTab) + } + } + for ui.BrowseButton.Clicked(gtx) { + ui.chooseFile() + } + for ui.SettingsButton.Clicked(gtx) { + ui.openSettingsWindow() + } +} + +func (ui *FileUI) layoutToolbar(gtx layout.Context, colors gui.UIColors) layout.Dimensions { + paint.FillShape(gtx.Ops, colors.SecondaryBackground, clip.Rect{Max: gtx.Constraints.Max}.Op()) + + inset := layout.Inset{Top: 4, Right: 6, Bottom: 4, Left: 6} + return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + button := material.Button(ui.Theme.Theme, &ui.BrowseButton, "Choose...") + button.Inset = layout.Inset{Top: 6, Right: 10, Bottom: 6, Left: 10} + return layout.Inset{Right: 6}.Layout(gtx, button.Layout) + }), + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + if ui.Config.Path == "" { + return layout.Dimensions{Size: image.Pt(gtx.Constraints.Max.X, 0)} + } + label := ui.Theme.Muted(ui.Config.Path, 0.8) + label.MaxLines = 1 + return layout.W.Layout(gtx, label.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + button := material.IconButton(ui.Theme.Theme, &ui.SettingsButton, SettingsIcon, "Settings") + button.Size = 18 + button.Inset = layout.UniformInset(8) + return layout.Inset{Left: 4}.Layout(gtx, button.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.copyStatus == "" { + return layout.Dimensions{} + } + label := ui.Theme.Muted(ui.copyStatus, 0.75) + return layout.Inset{Left: 2}.Layout(gtx, label.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.saveError == "" { + return layout.Dimensions{} + } + label := ui.Theme.ErrorLabel(ui.saveError, 0.75) + return layout.Inset{Left: 2}.Layout(gtx, label.Layout) + }), + ) + }) +} + +func (ui *FileUI) layoutSyntaxSelector(gtx layout.Context, colors gui.UIColors) layout.Dimensions { + return layout.Inset{Left: 10}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := ui.Theme.Muted("Syntax", 0.85) + return layout.Inset{Right: 3}.Layout(gtx, label.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutSyntaxRadio(gtx, colors, syntax.StyleGoLand) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutSyntaxRadio(gtx, colors, syntax.StyleDarcula) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutSyntaxRadio(gtx, colors, syntax.StyleMono) + }), + ) + }) +} + +func (ui *FileUI) layoutSyntaxRadio(gtx layout.Context, colors gui.UIColors, style string) layout.Dimensions { + radio := material.RadioButton(ui.Theme.Theme, &ui.SyntaxStyle, style, syntax.StyleLabel(style)) + radio.Color = colors.MutedText + radio.IconColor = ui.Theme.ContrastBg + radio.TextSize = ui.Theme.TextSize * 0.78 + radio.Size = unit.Dp(18) + return layout.Inset{Left: 2}.Layout(gtx, radio.Layout) +} + +// scheduleFlush arranges for buffered comment and settings changes to +// reach disk once the user pauses, instead of on every keystroke or tab +// switch. Main event loop only. +func (ui *FileUI) scheduleFlush() { + if ui.flushTimer == nil { + ui.flushPending() + return + } + ui.flushTimer.Reset(time.Second) +} + +func (ui *FileUI) flushPending() { + ui.saveError = "" + if ui.sessionDirty { + if err := SaveAppSettings(ui.Settings); err != nil { + ui.saveError = "settings not saved: " + err.Error() + fmt.Fprintf(os.Stderr, "unable to save settings: %v\n", err) + } else { + ui.sessionDirty = false + } + } + if err := ui.Comments.Flush(); err != nil { + ui.saveError = "comments not saved: " + err.Error() + fmt.Fprintf(os.Stderr, "unable to save comments: %v\n", err) + } +} + +func (ui *FileUI) invalidateMain() { + if ui.invalidate == nil { + return + } + select { + case ui.invalidate <- struct{}{}: + default: + } +} + +func (ui *FileUI) chooseFile() { + if ui.pickerOpen || ui.picker == nil || ui.pickerResults == nil { + return + } + // The native picker blocks until dismissed; running it here would + // stall the frame in progress and freeze the window. + ui.pickerOpen = true + picker, results, exited := ui.picker, ui.pickerResults, ui.exited + go func() { + var res pickerResult + // No extension filter: Go executables usually have none. + file, err := picker.ChooseFile() + switch { + case err == nil: + // The disassembler needs a path, not a reader; on desktop + // platforms the explorer hands back an *os.File. + if f, ok := file.(*os.File); ok { + res = pickerResult{path: f.Name(), ok: true} + } else { + res.err = errors.New("file picker did not return a local file path") + } + _ = file.Close() + case errors.Is(err, explorer.ErrUserDecline): + default: + res.err = err + } + select { + case results <- res: + case <-exited: + } + }() +} + +func (ui *FileUI) layoutContent(gtx layout.Context, colors gui.UIColors) layout.Dimensions { + active := ui.activeCode() + if active == nil || !active.Loaded() || active.Name != ui.Funcs.Selected { + selected := ui.Funcs.SelectedItem + if selected != nil { + ui.previewTab(selected) + } + } + + // Keep both panes usable at any window size; the ratio from a drag + // or from settings is clamped, not trusted. + total := float32(gtx.Constraints.Max.X) + minSidebar := float32(gtx.Metric.Dp(120)) + minContent := float32(gtx.Metric.Dp(240)) + ui.split.Ratio = min(max(ui.split.Ratio, minSidebar/total), 1-minContent/total) + + defer func() { + if ui.split.Ratio != ui.Settings.SidebarRatio { + settings := ui.Settings + settings.SidebarRatio = ui.split.Ratio + ui.saveSettings(settings) + } + }() + + return ui.split.Layout(gtx, + func(gtx layout.Context) layout.Dimensions { + gtx.Constraints = layout.Exact(gtx.Constraints.Max) + return ui.Funcs.Layout(ui.Theme, gtx) + }, + func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.LoadError != nil { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + txt := ui.Theme.ErrorLabel(ui.LoadError.Error(), 1) + return layout.UniformInset(6).Layout(gtx, txt.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.File == nil { + return layout.Dimensions{} + } + return ui.layoutCodeTabs(gtx, colors) + }), + ) + } + return ui.layoutCodeTabs(gtx, colors) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + code := ui.activeCode() + if code == nil || !code.Loaded() { + return layout.Dimensions{} + } + txt := ui.Theme.Muted("file: "+code.Code.File, 1) + txt.Font.Style = font.Italic + + inset := layout.Inset{Top: 2, Left: 4, Right: 4, Bottom: 4} + return inset.Layout(gtx, txt.Layout) + }), + layout.Rigid(gui.HorizontalLine{Height: 1, Color: colors.Splitter}.Layout), + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + if ui.LoadError != nil && ui.File == nil { + return layout.Dimensions{} + } + code := ui.activeCode() + if code == nil { + return layout.Dimensions{} + } + + gtx.Constraints = layout.Exact(gtx.Constraints.Max) + return layout.Stack{ + Alignment: layout.SE, + }.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { + return codeview.Style{ + UI: code, + + TryOpen: ui.tryOpen, + OnInteract: ui.keepActiveTab, + CopyText: func(gtx layout.Context, text string) { + ui.writeClipboardText(gtx, text, "Copied selection") + }, + + Comments: ui.Comments, + SetComment: ui.setBufferedComment, + CommentKey: &ui.commentKey, + CommentEditor: &ui.Comment, + + Theme: ui.Theme, + Syntax: syntax.PaletteFor(ui.Settings.SyntaxStyle, colors.SyntaxColors()), + ShowNative: ui.ShowNativeAsm.Value, + ShowHelp: ui.ShowAsmHelp.Value, + TextHeight: ui.Theme.TextSize, + }.Layout(gtx) + }), + ) + }), + ) + }, + func(gtx layout.Context) layout.Dimensions { + size := image.Pt(gtx.Metric.Dp(8), gtx.Constraints.Max.Y) + paint.FillShape(gtx.Ops, colors.Splitter, clip.Rect{ + Min: image.Pt(size.X/2, 0), + Max: image.Pt(size.X/2+1, size.Y), + }.Op()) + return layout.Dimensions{Size: size} + }, + ) +} + +func (ui *FileUI) requestLoad(path string) { + if ui.loader == nil { + return + } + ui.loadGeneration++ + ui.loader.Request(ui.loadGeneration, path) +} + +func (ui *FileUI) afterFileLoaded() { + ui.saveSessionState() + if ui.MCP != nil { + ui.MCP.SetPath(ui.Config.Path, ui.Comments) + } +} + +func (ui *FileUI) saveSessionState() { + if ui.File == nil { + return + } + path := comments.CleanPath(ui.Config.Path) + if path == "" { + return + } + + openTabs := make([]string, 0, len(ui.CodeTabs)) + for _, tab := range ui.CodeTabs { + if tab.Name != "" && !tab.Preview { + openTabs = append(openTabs, tab.Name) + } + } + activeTab := "" + if tab := ui.activeTab(); tab != nil { + activeTab = tab.Name + } + + settings := ui.Settings + settings.LastPath = path + settings.OpenTabs = cleanFuncNames(openTabs) + settings.ActiveTab = activeTab + if settings.ActiveTab != "" && !slices.Contains(settings.OpenTabs, settings.ActiveTab) { + settings.ActiveTab = "" + } + if ui.Settings.LastPath == settings.LastPath && + slices.Equal(ui.Settings.OpenTabs, settings.OpenTabs) && + ui.Settings.ActiveTab == settings.ActiveTab { + return + } + + ui.Settings = settings + ui.sessionDirty = true + ui.scheduleFlush() +} + +func (ui *FileUI) writeClipboardText(gtx layout.Context, text, status string) { + if text == "" { + return + } + gtx.Execute(clipboard.WriteCmd{ + Type: "text/plain", + Data: io.NopCloser(strings.NewReader(text)), + }) + ui.copyStatus = status +} + +func (ui *FileUI) tryOpen(gtx layout.Context, call string) { + fn := ui.findFunc(call) + if fn == nil { + return + } + + ui.openTab(fn, true) + gtx.Execute(op.InvalidateCmd{}) +} + +func (ui *FileUI) startMCP() { + if ui.MCP != nil { + return + } + server, err := mcp.StartAppServer(loadDisasmFile, ui.Config.CommentsPath) + if err != nil { + ui.LoadError = fmt.Errorf("unable to start MCP server: %w", err) + ui.invalidateMain() + return + } + ui.MCP = server + if ui.File != nil { + ui.MCP.SetPath(ui.Config.Path, ui.Comments) + } + fmt.Fprintf(os.Stderr, "lensm MCP server listening at %s\n", server.URL()) + ui.invalidateMain() +} + +func (ui *FileUI) stopMCP() { + if ui.MCP == nil { + return + } + if err := ui.MCP.Close(); err != nil { + fmt.Fprintf(os.Stderr, "unable to stop MCP server: %v\n", err) + } + ui.MCP = nil + ui.invalidateMain() +} diff --git a/fileui_comments.go b/fileui_comments.go new file mode 100644 index 0000000..cc0eb1c --- /dev/null +++ b/fileui_comments.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "os" + + "loov.dev/lensm/internal/comments" +) + +func (ui *FileUI) loadCommentsForPath(exePath string) { + // Write out anything buffered for the previous binary first. + if err := ui.Comments.Flush(); err != nil { + fmt.Fprintf(os.Stderr, "unable to save comments: %v\n", err) + } + commentsPath := ui.Config.CommentsPath + if commentsPath == "" { + commentsPath = comments.DefaultPath(exePath) + } + store, err := comments.Open(commentsPath, exePath) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to load comments from %q: %v\n", commentsPath, err) + store, _ = comments.Open("", exePath) + } + ui.Comments = store +} + +// setBufferedComment records the comment in memory and schedules the +// disk write, so typing doesn't rewrite the sidecar per keystroke. +func (ui *FileUI) setBufferedComment(coord comments.Coord, text string) { + if err := ui.Comments.SetBuffered(coord, text); err != nil { + ui.saveError = "comment not saved: " + err.Error() + fmt.Fprintln(os.Stderr, err) + return + } + ui.scheduleFlush() +} diff --git a/fileui_navigation_test.go b/fileui_navigation_test.go new file mode 100644 index 0000000..244d856 --- /dev/null +++ b/fileui_navigation_test.go @@ -0,0 +1,102 @@ +package main + +import ( + "testing" + + "gioui.org/widget/material" + + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" +) + +type navigationTestFunc string + +func (fn navigationTestFunc) Name() string { return string(fn) } +func (fn navigationTestFunc) Load(disasm.Options) (*disasm.Code, error) { + return &disasm.Code{Name: string(fn)}, nil +} + +type navigationTestFile struct{ funcs []disasm.Func } + +func (file navigationTestFile) Close() error { return nil } +func (file navigationTestFile) Funcs() []disasm.Func { return file.funcs } + +func TestFileUINavigationBackAndForward(t *testing.T) { + functions := []disasm.Func{ + navigationTestFunc("main.A"), + navigationTestFunc("main.B"), + navigationTestFunc("main.C"), + } + theme := gui.NewTheme(material.NewTheme(), false) + ui := &FileUI{ + Theme: theme, + File: navigationTestFile{funcs: functions}, + Funcs: gui.NewFilterList[disasm.Func](theme), + ActiveTab: -1, + } + ui.Navigation.Reset() + ui.Funcs.SetItems(functions) + + ui.openTab(functions[0], false) + ui.openTab(functions[1], true) + ui.openTab(functions[2], true) + if got := ui.Navigation.Current(); got != "main.C" { + t.Fatalf("current history entry = %q", got) + } + + ui.navigateBack() + if got := ui.activeTab().Name; got != "main.B" { + t.Fatalf("active tab after Back = %q", got) + } + ui.navigateForward() + if got := ui.activeTab().Name; got != "main.C" { + t.Fatalf("active tab after Forward = %q", got) + } +} + +func TestFileUIPreviewTab(t *testing.T) { + functions := []disasm.Func{ + navigationTestFunc("main.A"), + navigationTestFunc("main.B"), + navigationTestFunc("main.C"), + } + theme := gui.NewTheme(material.NewTheme(), false) + ui := &FileUI{ + Theme: theme, + File: navigationTestFile{funcs: functions}, + Funcs: gui.NewFilterList[disasm.Func](theme), + ActiveTab: -1, + } + ui.Navigation.Reset() + ui.Funcs.SetItems(functions) + + // Browsing the list only ever keeps a single preview tab. + ui.previewTab(functions[0]) + ui.previewTab(functions[1]) + ui.previewTab(functions[2]) + if len(ui.CodeTabs) != 1 { + t.Fatalf("preview browsing tabs = %d, want 1", len(ui.CodeTabs)) + } + if got := ui.activeTab().Name; got != "main.C" { + t.Fatalf("preview tab name = %q", got) + } + + // Clicking the tab keeps it; the next preview opens a second tab. + ui.selectTab(0) + if ui.CodeTabs[0].Preview { + t.Fatalf("tab still preview after selectTab") + } + ui.previewTab(functions[0]) + if len(ui.CodeTabs) != 2 { + t.Fatalf("tabs after keeping = %d, want 2", len(ui.CodeTabs)) + } + + // Acting on the preview content keeps it too. + if !ui.activeTab().Preview { + t.Fatalf("expected active tab to be preview") + } + ui.keepActiveTab() + if ui.activeTab().Preview { + t.Fatalf("keepActiveTab did not clear Preview") + } +} diff --git a/fileui_settings.go b/fileui_settings.go new file mode 100644 index 0000000..e2af8fa --- /dev/null +++ b/fileui_settings.go @@ -0,0 +1,229 @@ +package main + +import ( + "image" + "strconv" + "strings" + + "gioui.org/app" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/gui" + "loov.dev/lensm/internal/syntax" +) + +func (ui *FileUI) openSettingsWindow() { + if ui.settingsWindowOpen || ui.settingsEvents == nil { + return + } + ui.settingsWindowOpen = true + events, acks, exited := ui.settingsEvents, ui.settingsAcks, ui.exited + ui.Windows.Open("lensm settings", image.Pt(520, 360), func(w *app.Window) error { + // Only pump events here: the settings window is laid out on the + // main event loop, because layout reads and mutates state shared + // with the main window (Settings, MCP, Config, widget state) and + // shapes text through the Theme's Shaper, which is not safe for + // concurrent use. + for { + ev := w.Event() + select { + case events <- ev: + case <-exited: + return nil + } + select { + case <-acks: + case <-exited: + return nil + } + if e, ok := ev.(app.DestroyEvent); ok { + return e.Err + } + } + }) +} + +func (ui *FileUI) layoutSettingsWindow(gtx layout.Context) layout.Dimensions { + colors := ui.Theme.Colors + paint.FillShape(gtx.Ops, colors.Background, clip.Rect{Max: gtx.Constraints.Max}.Op()) + ui.handleSettingsActions(gtx) + + return layout.UniformInset(14).Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + title := material.H6(ui.Theme.Theme, "Settings") + return layout.Inset{Bottom: 12}.Layout(gtx, title.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutSettingsSection(gtx, "Visual", []layout.FlexChild{ + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(material.Switch(ui.Theme.Theme, &ui.Dark, "Dark theme").Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := ui.Theme.Label("Dark theme", 1) + return layout.Inset{Left: 6}.Layout(gtx, label.Layout) + }), + ) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + check := material.CheckBox(ui.Theme.Theme, &ui.ShowNativeAsm, "Native asm") + return check.Layout(gtx) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + check := material.CheckBox(ui.Theme.Theme, &ui.ShowAsmHelp, "Show instruction help") + return check.Layout(gtx) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutSyntaxSelector(gtx, colors) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return ui.layoutLabeledEditor(gtx, "Text size", &ui.TextSizeEditor) + }), + }) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return layout.Inset{Top: 14}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return ui.layoutSettingsSection(gtx, "MCP", []layout.FlexChild{ + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + status := "Stopped" + if ui.MCP != nil { + status = "Running: " + ui.MCP.URL() + } + label := ui.Theme.Muted(status, 1) + return layout.Inset{Top: 6, Bottom: 6}.Layout(gtx, label.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.MCP != nil { + gtx = gtx.Disabled() + } + button := material.Button(ui.Theme.Theme, &ui.StartMCP, "Start MCP") + return button.Layout(gtx) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.MCP == nil { + gtx = gtx.Disabled() + } + button := material.Button(ui.Theme.Theme, &ui.StopMCP, "Stop MCP") + return layout.Inset{Left: 8}.Layout(gtx, button.Layout) + }), + ) + }), + }) + }) + }), + ) + }) +} + +func (ui *FileUI) layoutSettingsSection(gtx layout.Context, title string, children []layout.FlexChild) layout.Dimensions { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + append([]layout.FlexChild{ + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := ui.Theme.Muted(title, 0.9) + return layout.Inset{Bottom: 6}.Layout(gtx, label.Layout) + }), + }, children...)..., + ) +} + +func (ui *FileUI) layoutLabeledEditor(gtx layout.Context, labelText string, editor *widget.Editor) layout.Dimensions { + return layout.Inset{Top: 6}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := ui.Theme.Label(labelText, 1) + return layout.Inset{Right: 8}.Layout(gtx, label.Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + gtx.Constraints = layout.Exact(image.Pt(gtx.Metric.Dp(80), gtx.Metric.Dp(34))) + return gui.FocusBorder(ui.Theme.Theme, gtx.Focused(editor)).Layout(gtx, + material.Editor(ui.Theme.Theme, editor, "").Layout) + }), + ) + }) +} + +func (ui *FileUI) handleSettingsActions(gtx layout.Context) { + changedVisual := false + for ui.Dark.Update(gtx) { + ui.Theme.SetDark(ui.Dark.Value) + changedVisual = true + } + for ui.ShowNativeAsm.Update(gtx) { + changedVisual = true + } + for ui.ShowAsmHelp.Update(gtx) { + changedVisual = true + } + for ui.SyntaxStyle.Update(gtx) { + ui.SyntaxStyle.Value = syntax.NormalizeStyle(ui.SyntaxStyle.Value) + changedVisual = true + } + if ui.updatePositiveIntEditor(gtx, &ui.TextSizeEditor, func(value int) { + ui.Theme.TextSize = unit.Sp(value) + }) { + changedVisual = true + } + if changedVisual { + ui.saveVisualSettings() + gtx.Execute(op.InvalidateCmd{}) + ui.invalidateMain() + } + + for ui.StartMCP.Clicked(gtx) { + ui.startMCP() + gtx.Execute(op.InvalidateCmd{}) + } + for ui.StopMCP.Clicked(gtx) { + ui.stopMCP() + gtx.Execute(op.InvalidateCmd{}) + } +} + +func (ui *FileUI) updatePositiveIntEditor(gtx layout.Context, editor *widget.Editor, apply func(int)) bool { + changed := false + for { + ev, ok := editor.Update(gtx) + if !ok { + break + } + switch ev.(type) { + case widget.ChangeEvent, widget.SubmitEvent: + changed = true + } + } + if !changed { + return false + } + value, err := strconv.Atoi(strings.TrimSpace(editor.Text())) + if err != nil || value <= 0 { + return false + } + apply(value) + return true +} + +func (ui *FileUI) saveVisualSettings() { + settings := ui.Settings + settings.Dark = ui.Dark.Value + settings.ShowNativeAsm = ui.ShowNativeAsm.Value + settings.ShowAsmHelp = ui.ShowAsmHelp.Value + settings.SyntaxStyle = syntax.NormalizeStyle(ui.SyntaxStyle.Value) + if value, err := strconv.Atoi(strings.TrimSpace(ui.TextSizeEditor.Text())); err == nil && value > 0 { + settings.TextSize = value + } + ui.saveSettings(settings) +} + +func (ui *FileUI) saveSettings(settings AppSettings) { + ui.Settings = settings + ui.sessionDirty = true + ui.scheduleFlush() +} diff --git a/fileui_tabs.go b/fileui_tabs.go new file mode 100644 index 0000000..6587b32 --- /dev/null +++ b/fileui_tabs.go @@ -0,0 +1,295 @@ +package main + +import ( + "image" + + "gioui.org/font" + "gioui.org/layout" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/widget" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/codeview" + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" +) + +type CodeTab struct { + Name string + Func disasm.Func + Code codeview.UI + Preview bool + Tab widget.Clickable + Close widget.Clickable +} + +func (ui *FileUI) activeTab() *CodeTab { + if !gui.InRange(ui.ActiveTab, len(ui.CodeTabs)) { + return nil + } + return ui.CodeTabs[ui.ActiveTab] +} + +func (ui *FileUI) activeCode() *codeview.UI { + tab := ui.activeTab() + if tab == nil { + return nil + } + return &tab.Code +} + +func (ui *FileUI) loadTabCode(tab *CodeTab, fn disasm.Func) { + tab.Name = fn.Name() + tab.Func = fn + tab.Code = codeview.UI{} + tab.Code.Code, ui.LoadError = fn.Load(ui.loadOptions()) + tab.Code.SelectedAsm = -1 + tab.Code.SelectedView = codeview.ViewGoAsm + tab.Code.ResetScroll() +} + +func (ui *FileUI) appendCodeTab(fn disasm.Func) *CodeTab { + if fn == nil { + return nil + } + tab := &CodeTab{} + ui.loadTabCode(tab, fn) + ui.CodeTabs = append(ui.CodeTabs, tab) + return tab +} + +// previewIndex returns the index of the single preview tab, or -1. +func (ui *FileUI) previewIndex() int { + for i, tab := range ui.CodeTabs { + if tab.Preview { + return i + } + } + return -1 +} + +// previewTab shows fn in a single reusable preview tab. Browsing the function +// list (keyboard up/down or clicking a name) replaces the preview in place +// instead of piling up permanent tabs. Clicking the tab keeps it open, see +// selectTab, which clears Preview. +func (ui *FileUI) previewTab(fn disasm.Func) *CodeTab { + if fn == nil { + return nil + } + name := fn.Name() + tab := ui.findCodeTab(name, fn) + if tab == nil { + if slot := ui.previewIndex(); slot >= 0 { + tab = ui.CodeTabs[slot] + ui.loadTabCode(tab, fn) + ui.ActiveTab = slot + } else { + tab = ui.appendCodeTab(fn) + if tab == nil { + return nil + } + ui.ActiveTab = len(ui.CodeTabs) - 1 + } + tab.Preview = true + } + ui.selectFuncByName(name) + ui.commentKey = "" + ui.recordNavigation(name) + ui.saveSessionState() + return tab +} + +// openTab opens fn in a tab and activates it. When next is true a newly +// created tab is inserted right after the active tab instead of appended +// at the end; an already-open tab is activated in place regardless. +func (ui *FileUI) openTab(fn disasm.Func, next bool) *CodeTab { + if fn == nil { + return nil + } + name := fn.Name() + tab := ui.findCodeTab(name, fn) + if tab == nil { + tab = ui.appendCodeTab(fn) + if tab == nil { + return nil + } + index := len(ui.CodeTabs) - 1 + if next && gui.InRange(ui.ActiveTab, index) { + at := ui.ActiveTab + 1 + if at < index { + copy(ui.CodeTabs[at+1:], ui.CodeTabs[at:index]) + ui.CodeTabs[at] = tab + index = at + } + } + ui.ActiveTab = index + } + tab.Preview = false + ui.selectFuncByName(name) + ui.commentKey = "" + ui.recordNavigation(name) + ui.saveSessionState() + return tab +} + +// findCodeTab returns the open tab for name, refreshing its Func and +// making it active, or nil if no tab is open for it. +func (ui *FileUI) findCodeTab(name string, fn disasm.Func) *CodeTab { + for i, tab := range ui.CodeTabs { + if tab.Name == name { + tab.Func = fn + ui.ActiveTab = i + return tab + } + } + return nil +} + +// keepActiveTab promotes the active preview tab to a permanent one, called +// when the user acts on the tab's content. +func (ui *FileUI) keepActiveTab() { + if tab := ui.activeTab(); tab != nil && tab.Preview { + tab.Preview = false + ui.saveSessionState() + } +} + +func (ui *FileUI) selectTab(index int) { + if !gui.InRange(index, len(ui.CodeTabs)) { + return + } + ui.ActiveTab = index + ui.CodeTabs[index].Preview = false + ui.commentKey = "" + ui.selectFuncByName(ui.CodeTabs[index].Name) + ui.recordNavigation(ui.CodeTabs[index].Name) + ui.saveSessionState() +} + +func (ui *FileUI) closeTab(index int) { + if !gui.InRange(index, len(ui.CodeTabs)) { + return + } + ui.CodeTabs = append(ui.CodeTabs[:index], ui.CodeTabs[index+1:]...) + switch { + case len(ui.CodeTabs) == 0: + ui.ActiveTab = -1 + ui.commentKey = "" + ui.Funcs.Selected = "" + ui.Funcs.SelectedItem = nil + ui.Funcs.List.Selected = -1 + case ui.ActiveTab > index: + ui.ActiveTab-- + case ui.ActiveTab == index && ui.ActiveTab >= len(ui.CodeTabs): + ui.ActiveTab = len(ui.CodeTabs) - 1 + } + if tab := ui.activeTab(); tab != nil { + ui.selectFuncByName(tab.Name) + ui.recordNavigation(tab.Name) + } else { + ui.commentKey = "" + } + ui.saveSessionState() +} + +func (ui *FileUI) layoutCodeTabs(gtx layout.Context, colors gui.UIColors) layout.Dimensions { + for i := 0; i < len(ui.CodeTabs); i++ { + tab := ui.CodeTabs[i] + for tab.Tab.Clicked(gtx) { + ui.selectTab(i) + } + closed := false + for tab.Close.Clicked(gtx) { + ui.closeTab(i) + closed = true + } + if closed { + i-- + } + } + + if len(ui.CodeTabs) == 0 { + return layout.Dimensions{} + } + + height := max(gtx.Metric.Dp(22), 20) + availableWidth := gtx.Constraints.Max.X + gtx.Constraints = layout.Exact(image.Pt(availableWidth, height)) + paint.FillShape(gtx.Ops, colors.SecondaryBackground, clip.Rect{Max: gtx.Constraints.Max}.Op()) + + tabWidth := gtx.Metric.Dp(220) + minTabWidth := gtx.Metric.Dp(120) + if len(ui.CodeTabs) <= 3 && availableWidth > 0 { + tabWidth = min(tabWidth, max(minTabWidth, availableWidth/len(ui.CodeTabs))) + } + if tabWidth < minTabWidth { + tabWidth = minTabWidth + } + + list := material.List(ui.Theme.Theme, &ui.Tabs) + list.AnchorStrategy = material.Overlay + return list.Layout(gtx, len(ui.CodeTabs), func(gtx layout.Context, index int) layout.Dimensions { + gtx.Constraints = layout.Exact(image.Pt(tabWidth, height)) + return ui.layoutCodeTab(gtx, colors, ui.CodeTabs[index], index == ui.ActiveTab) + }) +} + +func (ui *FileUI) layoutCodeTab(gtx layout.Context, colors gui.UIColors, tab *CodeTab, active bool) layout.Dimensions { + size := gtx.Constraints.Max + defer clip.Rect{Max: size}.Push(gtx.Ops).Pop() + + bg := colors.SecondaryBackground + if active { + bg = colors.Background + } else if tab.Tab.Hovered() { + bg = colors.Selection + } + paint.FillShape(gtx.Ops, bg, clip.Rect{Max: size}.Op()) + if active { + paint.FillShape(gtx.Ops, ui.Theme.ContrastBg, clip.Rect{Max: image.Pt(size.X, 2)}.Op()) + } + paint.FillShape(gtx.Ops, colors.Splitter, clip.Rect{ + Min: image.Pt(size.X-1, 0), + Max: size, + }.Op()) + + layout.Inset{Top: 1, Right: 4, Bottom: 1, Left: 8}.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + gtx.Constraints = layout.Exact(gtx.Constraints.Max) + return tab.Tab.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + size := gtx.Constraints.Max + defer clip.Rect{Max: size}.Push(gtx.Ops).Pop() + + label := ui.Theme.Label(tab.Name, 0.8) + label.MaxLines = 1 + if active { + label.Font.Weight = font.Black + } + if tab.Preview { + label.Font.Style = font.Italic + } + dims := layout.W.Layout(gtx, label.Layout) + return layout.Dimensions{Size: size, Baseline: dims.Baseline} + }) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + closeWidth := gtx.Metric.Dp(22) + gtx.Constraints = layout.Exact(image.Pt(closeWidth, gtx.Constraints.Max.Y)) + return tab.Close.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + size := gtx.Constraints.Max + if tab.Close.Hovered() { + paint.FillShape(gtx.Ops, colors.Selection, clip.Rect{Max: size}.Op()) + } + label := ui.Theme.Muted("x", 0.8) + label.MaxLines = 1 + dims := layout.Center.Layout(gtx, label.Layout) + return layout.Dimensions{Size: size, Baseline: dims.Baseline} + }) + }), + ) + }) + + return layout.Dimensions{Size: size} +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cc104ed --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module loov.dev/lensm + +go 1.26 + +require ( + gioui.org v0.10.1 + gioui.org/x v0.10.0 + github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 + golang.org/x/arch v0.28.0 + golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96 +) + +require ( + gioui.org/shader v1.0.8 // indirect + git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 // indirect + github.com/go-text/typesetting v0.3.4 // indirect + github.com/godbus/dbus/v5 v5.0.6 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/image v0.35.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c769d9d --- /dev/null +++ b/go.sum @@ -0,0 +1,41 @@ +eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= +eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= +gioui.org v0.10.1 h1:Dvp6iDk9RKuZk19jxhOmb4p673CLVvb656LyMxQ+uO0= +gioui.org v0.10.1/go.mod h1:MZJZsdEPkTBzChdqeE8CiiQhreUQBj43qusDxQNDf7k= +gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= +gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= +gioui.org/x v0.10.0 h1:+oXnsUsyqQEldUR+4l+U25hHv6lU6TagZxXi9YDWEXE= +gioui.org/x v0.10.0/go.mod h1:ruS8Rj06tvag88dJmb8mrWbgmrcahiPZcBLd2ZKyQ6Q= +git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr9PPB5aw8SjcOWWila7ZI= +git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-text/typesetting v0.3.4 h1:YYurUOtEb9kGSOz4uE3k4OpBGsp1dDL8+fjCeaFamAU= +github.com/go-text/typesetting v0.3.4/go.mod h1:4qZCQphq4KSgGTAeI0uMEkVbROgfah8BuyF5LRYr7XY= +github.com/go-text/typesetting-utils v0.0.0-20260223113751-2d88ac90dae3 h1:drBZzMgdYPbmyXqOto4YhhJGrFIQCX94FpR4MzTCsos= +github.com/go-text/typesetting-utils v0.0.0-20260223113751-2d88ac90dae3/go.mod h1:3/62I4La/HBRX9TcTpBj4eipLiwzf+vhI+7whTc9V7o= +github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q= +github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk= +golang.org/x/arch v0.28.0 h1:wVwVdqsTuUbJvhYVCspQYwZXHNYeLSoZnmHD+ggddpQ= +golang.org/x/arch v0.28.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= +golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96 h1:wJ3cDLvYRAWzRt6f3e2VwVlziH3httfx2PGMa8hqqWo= +golang.org/x/exp/shiny v0.0.0-20260112195511-716be5621a96/go.mod h1:hq/Ge0xSczE7aHicXVhn3Kd0j3hOtWQR4KEgAwemgdk= +golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= +golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/icons.go b/icons.go new file mode 100644 index 0000000..a8af644 --- /dev/null +++ b/icons.go @@ -0,0 +1,12 @@ +package main + +import ( + "gioui.org/widget" + "golang.org/x/exp/shiny/materialdesign/icons" +) + +// SettingsIcon is used for application settings. +var SettingsIcon = func() *widget.Icon { + icon, _ := widget.NewIcon(icons.ActionSettings) + return icon +}() diff --git a/internal/README.md b/internal/README.md new file mode 100644 index 0000000..7593d90 --- /dev/null +++ b/internal/README.md @@ -0,0 +1 @@ +`go` folder contains code vendored from [Go project](https://go.googlesource.com/go/). \ No newline at end of file diff --git a/internal/asmcheck/main.go b/internal/asmcheck/main.go new file mode 100644 index 0000000..188d463 --- /dev/null +++ b/internal/asmcheck/main.go @@ -0,0 +1,92 @@ +// Command asmcheck reads `go tool objdump` output on stdin, extracts every Go +// (Plan 9) instruction mnemonic, and reports which ones asmhelp fails to +// resolve to real reference data. Throwaway analysis tool. +package main + +import ( + "bufio" + "fmt" + "os" + "sort" + "strings" + + "loov.dev/lensm/internal/asmhelp" +) + +func main() { + arch := "arm64" + if len(os.Args) > 1 { + arch = os.Args[1] + } + + count := map[string]int{} // mnemonic -> occurrences + sample := map[string]string{} // mnemonic -> one full instruction line + + sc := bufio.NewScanner(os.Stdin) + sc.Buffer(make([]byte, 1<<20), 1<<20) + for sc.Scan() { + line := sc.Text() + if strings.HasPrefix(strings.TrimSpace(line), "TEXT ") { + continue // function header pseudo-op, not an instruction + } + // objdump instruction lines are tab-separated (with empty padding + // fields): file:line, addr, hex, instr. The instruction is the last + // non-empty field. + var instr string + for _, f := range strings.Split(line, "\t") { + if f = strings.TrimSpace(f); f != "" { + instr = f + } + } + if !strings.HasPrefix(strings.TrimSpace(line), " ") && !strings.Contains(line, "0x") { + continue // function header line, not an instruction + } + toks := strings.Fields(instr) + if len(toks) == 0 { + continue + } + m := toks[0] + count[m]++ + if sample[m] == "" { + sample[m] = instr + } + } + + var unresolved, notPlausible []string + resolved := 0 + for m := range count { + help, ok := asmhelp.ForInstruction(arch, "", sample[m]) + switch { + case ok && help.Note == "": + resolved++ + case ok && help.Note != "": + unresolved = append(unresolved, m) + default: + notPlausible = append(notPlausible, m) + } + } + + byCountDesc := func(list []string) { + sort.Slice(list, func(i, j int) bool { + if count[list[i]] != count[list[j]] { + return count[list[i]] > count[list[j]] + } + return list[i] < list[j] + }) + } + byCountDesc(unresolved) + byCountDesc(notPlausible) + + fmt.Printf("unique mnemonics: %d resolved: %d unresolved: %d not-plausible: %d\n\n", + len(count), resolved, len(unresolved), len(notPlausible)) + fmt.Println("UNRESOLVED (plausible mnemonics with no reference data), by frequency:") + for _, m := range unresolved { + fmt.Printf(" %6d %-12s %s\n", count[m], m, sample[m]) + } + if len(notPlausible) > 0 { + fmt.Println("\nNOT-PLAUSIBLE (skipped: directives, undecodable, etc.):") + for _, m := range notPlausible { + fmt.Printf(" %6d %s\n", count[m], m) + } + } +} diff --git a/internal/asmhelp/asmhelp.go b/internal/asmhelp/asmhelp.go new file mode 100644 index 0000000..6139def --- /dev/null +++ b/internal/asmhelp/asmhelp.go @@ -0,0 +1,964 @@ +package asmhelp + +import ( + "regexp" + "slices" + "strings" + + "loov.dev/lensm/internal/asmref" +) + +type Help struct { + Mnemonic string + Description string + Explanation string + // Ports lists execution-port usage (uops.info notation) for x86 mnemonics, + // sourced from the generated reference. Empty for other architectures. + Ports []string + // Note flags that the tooltip has no real reference data for this mnemonic; + // the Description is a generic placeholder. Empty when reference data exists. + Note string +} + +type asmInstructionRule struct { + Prefixes []string + Description string + Explain func([]string) string + // ExplainArch is used instead of Explain when the operand meaning + // depends on the architecture. + ExplainArch func(arch string, operands []string) string +} + +// asmInstructionRules is intentionally data-driven: add a prefix and, when +// useful, a small operand rewrite to extend the built-in reference. +var asmInstructionRules = []asmInstructionRule{ + {Prefixes: []string{"MOVZX", "MOVSX", "MOVSXD"}, Description: "Move a value while extending it to a wider size.", Explain: explainMove}, + {Prefixes: []string{"MOV"}, Description: "Move or copy data between registers and memory.", Explain: explainMove}, + {Prefixes: []string{"LEA"}, Description: "Load an effective address without reading memory.", Explain: explainLEA}, + {Prefixes: []string{"ADD", "ADC"}, Description: "Add values; ADC also includes the carry flag.", Explain: explainBinary("+")}, + {Prefixes: []string{"SUB", "SBB"}, Description: "Subtract values; SBB also includes the borrow flag.", Explain: explainBinary("-")}, + {Prefixes: []string{"MUL", "IMUL"}, Description: "Multiply values (IMUL is signed multiplication).", Explain: explainBinary("*")}, + {Prefixes: []string{"MADD"}, Description: "Multiply two values and add a third.", Explain: explainMADD}, + {Prefixes: []string{"MSUB"}, Description: "Multiply two values and subtract the product from a third.", Explain: explainMSUB}, + {Prefixes: []string{"DIV", "IDIV", "SDIV", "UDIV"}, Description: "Divide values (signed or unsigned according to the mnemonic).", Explain: explainBinary("/")}, + {Prefixes: []string{"CQO", "CDQ", "CWD", "CBW"}, Description: "Sign-extend the accumulator for a wider signed operation."}, + {Prefixes: []string{"AND"}, Description: "Compute a bitwise AND.", Explain: explainBinary("&")}, + {Prefixes: []string{"OR", "ORR"}, Description: "Compute a bitwise OR.", Explain: explainBinary("|")}, + {Prefixes: []string{"BIC"}, Description: "Clear selected bits of a value.", Explain: explainBinary("&^")}, + {Prefixes: []string{"XOR", "EOR"}, Description: "Compute a bitwise exclusive OR.", Explain: explainBinary("^")}, + {Prefixes: []string{"SHL", "SAL", "LSL"}, Description: "Shift bits left.", Explain: explainBinary("<<")}, + {Prefixes: []string{"SHR", "LSR"}, Description: "Shift bits right, filling with zeroes.", Explain: explainBinary(">>")}, + {Prefixes: []string{"SAR", "ASR"}, Description: "Arithmetic right shift, preserving the sign.", Explain: explainBinary(">>")}, + {Prefixes: []string{"INC"}, Description: "Increment a value by one.", Explain: explainUnaryDelta("+", "1")}, + {Prefixes: []string{"DEC"}, Description: "Decrement a value by one.", Explain: explainUnaryDelta("-", "1")}, + {Prefixes: []string{"NEG"}, Description: "Negate a signed value.", Explain: explainUnaryPrefix("-")}, + {Prefixes: []string{"NOT", "MVN"}, Description: "Invert every bit in a value.", Explain: explainUnaryPrefix("^")}, + {Prefixes: []string{"CMP"}, Description: "Compare values and update condition flags.", ExplainArch: explainCompare}, + {Prefixes: []string{"CMOV"}, Description: "Conditionally move a value when the selected flags match.", Explain: explainMove}, + {Prefixes: []string{"SET"}, Description: "Set a byte to 0 or 1 according to condition flags."}, + {Prefixes: []string{"CSEL", "CSET"}, Description: "Select or set a value according to condition flags."}, + {Prefixes: []string{"TEST", "TST"}, Description: "Test bits and update condition flags without storing a result.", Explain: explainTest}, + {Prefixes: []string{"FMADD"}, Description: "Fused floating-point multiply-add with one rounding step.", Explain: explainFMADD}, + {Prefixes: []string{"FADD"}, Description: "Add floating-point values.", Explain: explainBinary("+")}, + {Prefixes: []string{"FSUB"}, Description: "Subtract floating-point values.", Explain: explainBinary("-")}, + {Prefixes: []string{"FMUL"}, Description: "Multiply floating-point values.", Explain: explainBinary("*")}, + {Prefixes: []string{"FMOV"}, Description: "Move a floating-point value.", Explain: explainMove}, + {Prefixes: []string{"LDR", "LDUR", "LOAD"}, Description: "Load a value from memory into a register.", Explain: explainLoad}, + {Prefixes: []string{"STR", "STUR", "STORE"}, Description: "Store a register value in memory.", Explain: explainStore}, + {Prefixes: []string{"ADR", "ADRP"}, Description: "Form a PC-relative address."}, + {Prefixes: []string{"LDP"}, Description: "Load a pair of registers from memory."}, + {Prefixes: []string{"STP"}, Description: "Store a pair of registers to memory."}, + {Prefixes: []string{"XCHG", "SWAP"}, Description: "Exchange two values."}, + {Prefixes: []string{"BSWAP", "REV"}, Description: "Reverse the byte order of a value."}, + {Prefixes: []string{"BSF", "BSR"}, Description: "Scan for the position of the first or last set bit."}, + {Prefixes: []string{"CLZ", "CTZ"}, Description: "Count leading or trailing zero bits."}, + {Prefixes: []string{"POPCNT"}, Description: "Count the one bits in a value."}, + {Prefixes: []string{"PUSH"}, Description: "Push a value onto the stack."}, + {Prefixes: []string{"POP"}, Description: "Pop the top stack value."}, + {Prefixes: []string{"CALL", "BL", "BLR", "JAL", "JALR"}, Description: "Call a function and save a return address."}, + {Prefixes: []string{"LOCK"}, Description: "Make the following read-modify-write instruction atomic."}, + {Prefixes: []string{"FS", "GS"}, Description: "Segment override prefix for the following memory access."}, + {Prefixes: []string{"SYSCALL", "SVC"}, Description: "Enter the operating system or supervisor."}, + {Prefixes: []string{"INT"}, Description: "Raise a software interrupt."}, + {Prefixes: []string{"ICEBP"}, Description: "Raise a debug exception (undocumented INT1)."}, + {Prefixes: []string{"RET", "LRET"}, Description: "Return to the caller."}, + {Prefixes: []string{"JMP", "B", "BR"}, Description: "Jump to another instruction."}, + {Prefixes: []string{"JE", "JZ", "BEQ"}, Description: "Jump when values are equal (zero flag set)."}, + {Prefixes: []string{"JNE", "JNZ", "BNE"}, Description: "Jump when values are not equal."}, + {Prefixes: []string{"JG", "JGE", "JL", "JLE", "BGT", "BGE", "BLT", "BLE"}, Description: "Conditional jump after a signed comparison."}, + {Prefixes: []string{"JA", "JAE", "JB", "JBE", "BHI", "BHS", "BLO", "BLS", "BCS", "BCC"}, Description: "Conditional jump after an unsigned comparison."}, + {Prefixes: []string{"BMI", "BPL", "BVS", "BVC"}, Description: "Conditional jump testing the sign or overflow flag."}, + {Prefixes: []string{"CBZ"}, Description: "Jump when a register is zero."}, + {Prefixes: []string{"CBNZ"}, Description: "Jump when a register is not zero."}, + {Prefixes: []string{"TBZ"}, Description: "Jump when a selected bit is zero."}, + {Prefixes: []string{"TBNZ"}, Description: "Jump when a selected bit is not zero."}, + {Prefixes: []string{"NOP", "NOOP"}, Description: "Do nothing for one instruction slot."}, + {Prefixes: []string{"HLT", "WFI"}, Description: "Stop or wait for an external event."}, + {Prefixes: []string{"PAUSE", "YIELD"}, Description: "Hint that the processor is in a spin-wait loop."}, + {Prefixes: []string{"MFENCE", "LFENCE", "SFENCE", "DMB", "DSB", "ISB"}, Description: "Order memory or instruction accesses across this barrier."}, +} + +// NativeAssemblyInstructionHelp returns reference text and a syntax-correct +// effect for the native (GNU) spelling of an instruction. GNU x86/AT&T uses +// source-first operands while ARM-family GNU syntax uses destination-first. +func ForNative(arch, canonical, text string) (Help, bool) { + mnemonic, operands := splitAssemblyInstruction(text) + lookup := canonicalNativeMnemonic(mnemonic) + // Arch is irrelevant here: only the description is kept, the + // explanation is replaced with a native-syntax rewrite below. + help, ok := knownAssemblyInstructionHelp("", lookup, nil) + if !ok { + return Help{}, false + } + help.Mnemonic = mnemonic + help.Explanation = explainNativeInstruction(lookup, operands) + if help.Explanation == "" { + help.Explanation = explainNativeEffect(lookup, operands) + } + if ref, ok := referenceEntry(arch, canonical, mnemonic); ok && isX86(arch) { + help.Ports = referencePorts(ref) + } + return help, true +} + +func canonicalNativeMnemonic(mnemonic string) string { + switch { + case mnemonic == "ADDS": + return "ADD" + case mnemonic == "SUBS": + return "SUB" + case mnemonic == "ANDS": + return "AND" + case strings.HasPrefix(mnemonic, "LDR"): + return "LDR" + case strings.HasPrefix(mnemonic, "LDUR"): + return "LDUR" + case strings.HasPrefix(mnemonic, "STR"): + return "STR" + case strings.HasPrefix(mnemonic, "STUR"): + return "STUR" + case strings.HasPrefix(mnemonic, "CMOV"): + return "CMOV" + case strings.HasPrefix(mnemonic, "SET"): + return "SET" + case strings.HasPrefix(mnemonic, "MOVZ"), strings.HasPrefix(mnemonic, "MOVSX"), strings.HasPrefix(mnemonic, "MOVSXD"): + return "MOVZX" + case mnemonic == "CQTO": + return "CQO" + case strings.HasPrefix(mnemonic, "B."): + condition := strings.TrimPrefix(mnemonic, "B.") + switch condition { + case "EQ": + return "BEQ" + case "NE": + return "BNE" + case "GT": + return "BGT" + case "GE": + return "BGE" + case "LT": + return "BLT" + case "LE": + return "BLE" + case "HI": + return "BHI" + case "HS", "CS": + return "BHS" + case "LO", "CC": + return "BLO" + case "LS": + return "BLS" + case "MI": + return "BMI" + case "PL": + return "BPL" + case "VS": + return "BVS" + case "VC": + return "BVC" + case "AL", "NV": + // Both encode "always" on modern cores. + return "B" + } + } + for _, base := range []string{"PUSH", "POP", "CALL", "JMP", "RET", "XCHG", "POPCNT", "BSWAP", "INC", "DEC", "NEG", "NOT"} { + if mnemonic == base+"B" || mnemonic == base+"W" || mnemonic == base+"L" || mnemonic == base+"Q" { + return base + } + } + return mnemonic +} + +func explainNativeEffect(mnemonic string, operands []string) string { + operand := func(index int) string { + if index >= len(operands) { + return "target" + } + return strings.TrimSpace(strings.TrimPrefix(operands[index], "#")) + } + switch { + case mnemonic == "PUSH": + return "stack := push(stack, " + operand(0) + ")" + case mnemonic == "POP": + return operand(0) + " := pop(stack)" + case mnemonic == "CALL" || mnemonic == "BL" || mnemonic == "BLR" || mnemonic == "JAL" || mnemonic == "JALR": + return "call " + operand(0) + " and save the return address" + case mnemonic == "RET": + return "PC := saved return address" + case mnemonic == "JMP" || mnemonic == "B" || mnemonic == "BR": + return "PC := " + operand(0) + case mnemonic == "JE" || mnemonic == "JZ" || mnemonic == "BEQ": + return "if the compared values are equal (Z == 1), PC := " + operand(0) + case mnemonic == "JNE" || mnemonic == "JNZ" || mnemonic == "BNE": + return "if the compared values are not equal (Z == 0), PC := " + operand(0) + case mnemonic == "BGT": + return "if signed greater than (Z == 0 and N == V), PC := " + operand(0) + case mnemonic == "BGE": + return "if signed greater than or equal (N == V), PC := " + operand(0) + case mnemonic == "BLT": + return "if signed less than (N != V), PC := " + operand(0) + case mnemonic == "BLE": + return "if signed less than or equal (Z == 1 or N != V), PC := " + operand(0) + case mnemonic == "JG": + return "if signed greater than (ZF == 0 and SF == OF), PC := " + operand(0) + case mnemonic == "JGE": + return "if signed greater than or equal (SF == OF), PC := " + operand(0) + case mnemonic == "JL": + return "if signed less than (SF != OF), PC := " + operand(0) + case mnemonic == "JLE": + return "if signed less than or equal (ZF == 1 or SF != OF), PC := " + operand(0) + case mnemonic == "BHI": + return "if unsigned higher than (C == 1 and Z == 0), PC := " + operand(0) + case mnemonic == "BHS": + return "if unsigned higher than or equal (C == 1), PC := " + operand(0) + case mnemonic == "BLO": + return "if unsigned lower than (C == 0), PC := " + operand(0) + case mnemonic == "BLS": + return "if unsigned lower than or equal (C == 0 or Z == 1), PC := " + operand(0) + case mnemonic == "BMI": + return "if negative (N == 1), PC := " + operand(0) + case mnemonic == "BPL": + return "if positive or zero (N == 0), PC := " + operand(0) + case mnemonic == "BVS": + return "if overflow (V == 1), PC := " + operand(0) + case mnemonic == "BVC": + return "if no overflow (V == 0), PC := " + operand(0) + case mnemonic == "JA": + return "if unsigned above (CF == 0 and ZF == 0), PC := " + operand(0) + case mnemonic == "JAE": + return "if unsigned above or equal (CF == 0), PC := " + operand(0) + case mnemonic == "JB": + return "if unsigned below (CF == 1), PC := " + operand(0) + case mnemonic == "JBE": + return "if unsigned below or equal (CF == 1 or ZF == 1), PC := " + operand(0) + case mnemonic == "CBZ": + return "if " + operand(0) + " == 0, PC := " + operand(1) + case mnemonic == "CBNZ": + return "if " + operand(0) + " != 0, PC := " + operand(1) + case mnemonic == "TBZ": + return "if bit " + operand(1) + " of " + operand(0) + " == 0, PC := " + operand(2) + case mnemonic == "TBNZ": + return "if bit " + operand(1) + " of " + operand(0) + " != 0, PC := " + operand(2) + case mnemonic == "INC": + return operand(0) + " := " + operand(0) + " + 1" + case mnemonic == "DEC": + return operand(0) + " := " + operand(0) + " - 1" + case mnemonic == "NEG": + return operand(0) + " := -" + operand(0) + case mnemonic == "NOT": + return operand(0) + " := ^" + operand(0) + case mnemonic == "SET": + return operand(0) + " := condition ? 1 : 0" + case mnemonic == "NOP": + return "state is unchanged" + case mnemonic == "SYSCALL" || mnemonic == "SVC" || mnemonic == "INT": + return "transfer control to the operating system" + case mnemonic == "MFENCE" || mnemonic == "LFENCE" || mnemonic == "SFENCE" || mnemonic == "DMB" || mnemonic == "DSB" || mnemonic == "ISB": + return "wait for the ordered accesses to complete" + case mnemonic == "PAUSE" || mnemonic == "YIELD": + return "temporarily yield processor execution resources" + case mnemonic == "HLT" || mnemonic == "WFI": + return "wait until an interrupt or event occurs" + case mnemonic == "CQO" || mnemonic == "CDQ" || mnemonic == "CWD" || mnemonic == "CBW": + return "extend the accumulator's sign into the high half" + case mnemonic == "XCHG" || mnemonic == "SWAP": + return operand(0) + ", " + operand(1) + " := " + operand(1) + ", " + operand(0) + } + return "" +} + +func explainNativeInstruction(mnemonic string, operands []string) string { + if len(operands) == 0 { + return "" + } + // Registers prefixed with % and immediates prefixed with $ identify GNU + // x86/AT&T syntax, whose source-first operand order agrees with the + // source-first Plan 9 rewrites (hence the empty arch: the "amd64" CMP + // rewrite is natural-order, which would be wrong for AT&T). + if strings.Contains(strings.Join(operands, " "), "%") || strings.HasPrefix(strings.TrimSpace(operands[0]), "$") { + if help, ok := ForInstruction("", "", mnemonic+" "+strings.Join(operands, ", ")); ok { + if help.Explanation != "" { + return help.Explanation + } + } + return explainNativeX86Effect(mnemonic, operands) + } + + value := formatNativeValue + if len(operands) >= 2 { + destination := value(operands[0]) + switch { + case mnemonic == "MOVZX", strings.HasPrefix(mnemonic, "MOV"): + return destination + " := " + value(operands[1]) + case mnemonic == "LDR" || mnemonic == "LDUR" || mnemonic == "LOAD": + return explainNativeLoad(destination, operands[1:]) + case mnemonic == "STR" || mnemonic == "STUR" || mnemonic == "STORE": + return explainNativeStore(value(operands[0]), operands[1:]) + case mnemonic == "LDP" && len(operands) >= 3: + return explainNativePairLoad(value(operands[0]), value(operands[1]), operands[2:]) + case mnemonic == "STP" && len(operands) >= 3: + return explainNativePairStore(value(operands[0]), value(operands[1]), operands[2:]) + case mnemonic == "LEA", mnemonic == "ADR", mnemonic == "ADRP": + return destination + " := address(" + value(operands[1]) + ")" + case mnemonic == "ADD" || mnemonic == "ADC": + return explainNativeDestinationFirst(destination, operands[1:], "+", value) + case mnemonic == "SUB" || mnemonic == "SBB": + return explainNativeDestinationFirst(destination, operands[1:], "-", value) + case mnemonic == "MUL" || mnemonic == "IMUL" || mnemonic == "FMUL": + return explainNativeDestinationFirst(destination, operands[1:], "*", value) + case mnemonic == "MADD" && len(operands) >= 4: + return destination + " := " + value(operands[1]) + " * " + value(operands[2]) + " + " + value(operands[3]) + case mnemonic == "MSUB" && len(operands) >= 4: + return destination + " := " + value(operands[3]) + " - " + value(operands[1]) + " * " + value(operands[2]) + case mnemonic == "NEG": + // arm64 two-operand form: neg x0, x1 is x0 := -x1. + return destination + " := -" + value(operands[1]) + case mnemonic == "NOT" || mnemonic == "MVN": + return destination + " := ^" + value(operands[1]) + case mnemonic == "DIV" || mnemonic == "IDIV" || mnemonic == "SDIV" || mnemonic == "UDIV": + return explainNativeDestinationFirst(destination, operands[1:], "/", value) + case mnemonic == "AND": + return explainNativeDestinationFirst(destination, operands[1:], "&", value) + case mnemonic == "BIC": + return explainNativeDestinationFirst(destination, operands[1:], "&^", value) + case mnemonic == "OR" || mnemonic == "ORR": + return explainNativeDestinationFirst(destination, operands[1:], "|", value) + case mnemonic == "XOR" || mnemonic == "EOR": + return explainNativeDestinationFirst(destination, operands[1:], "^", value) + case mnemonic == "SHL" || mnemonic == "SAL" || mnemonic == "LSL": + return explainNativeDestinationFirst(destination, operands[1:], "<<", value) + case mnemonic == "SHR" || mnemonic == "LSR" || mnemonic == "SAR" || mnemonic == "ASR": + return explainNativeDestinationFirst(destination, operands[1:], ">>", value) + case mnemonic == "FADD": + return explainNativeDestinationFirst(destination, operands[1:], "+", value) + case mnemonic == "FSUB": + return explainNativeDestinationFirst(destination, operands[1:], "-", value) + case mnemonic == "FMADD" && len(operands) >= 4: + return destination + " := " + value(operands[1]) + " * " + value(operands[2]) + " + " + value(operands[3]) + case mnemonic == "CMP": + return "flags := compare(" + value(operands[0]) + ", " + value(operands[1]) + ")" + case mnemonic == "TEST" || mnemonic == "TST": + return "flags := " + value(operands[0]) + " & " + value(operands[1]) + case mnemonic == "CSEL" && len(operands) >= 4: + return destination + " := condition(" + value(operands[3]) + ") ? " + value(operands[1]) + " : " + value(operands[2]) + case mnemonic == "CSET": + return destination + " := condition(" + value(operands[1]) + ") ? 1 : 0" + case mnemonic == "POPCNT": + return destination + " := countOneBits(" + value(operands[1]) + ")" + case mnemonic == "BSWAP" || mnemonic == "REV": + return destination + " := reverseBytes(" + value(operands[1]) + ")" + case mnemonic == "BSF" || mnemonic == "BSR" || mnemonic == "CLZ" || mnemonic == "CTZ": + return destination + " := significantBitPosition(" + value(operands[1]) + ")" + } + } + return "" +} + +func explainNativeX86Effect(mnemonic string, operands []string) string { + operand := func(index int) string { + if index >= len(operands) { + return "target" + } + return formatNativeValue(operands[index]) + } + destination := operand(len(operands) - 1) + switch mnemonic { + case "POPCNT": + return destination + " := countOneBits(" + operand(0) + ")" + case "BSWAP": + return destination + " := reverseBytes(" + destination + ")" + case "XCHG": + return operand(0) + ", " + operand(1) + " := " + operand(1) + ", " + operand(0) + case "SET": + return destination + " := condition ? 1 : 0" + case "CMOV": + return "if condition, " + destination + " := " + operand(0) + } + return explainNativeEffect(mnemonic, operands) +} + +func formatNativeValue(operand string) string { + operand = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(operand), "#")) + if strings.HasPrefix(operand, "[") { + return formatNativeMemory(operand) + } + return operand +} + +func formatNativeMemory(operand string) string { + _, address, _ := parseNativeMemory(operand) + return "memory[" + address + "]" +} + +func explainNativeLoad(destination string, addressOperands []string) string { + base, address, preIndex := parseNativeMemory(addressOperands[0]) + if preIndex { + return base + " := " + address + "; " + destination + " := memory[" + base + "]" + } + effect := destination + " := memory[" + address + "]" + if len(addressOperands) > 1 { + effect += "; " + base + " := " + addNativeOffset(base, addressOperands[1]) + } + return effect +} + +func explainNativeStore(source string, addressOperands []string) string { + base, address, preIndex := parseNativeMemory(addressOperands[0]) + if preIndex { + return base + " := " + address + "; memory[" + base + "] := " + source + } + effect := "memory[" + address + "] := " + source + if len(addressOperands) > 1 { + effect += "; " + base + " := " + addNativeOffset(base, addressOperands[1]) + } + return effect +} + +func explainNativePairLoad(first, second string, addressOperands []string) string { + base, address, preIndex := parseNativeMemory(addressOperands[0]) + load := first + ", " + second + " := pair(memory[" + address + "])" + if preIndex { + return base + " := " + address + "; " + first + ", " + second + " := pair(memory[" + base + "])" + } + if len(addressOperands) > 1 { + load += "; " + base + " := " + addNativeOffset(base, addressOperands[1]) + } + return load +} + +func explainNativePairStore(first, second string, addressOperands []string) string { + base, address, preIndex := parseNativeMemory(addressOperands[0]) + store := "memory[" + address + "] := pair(" + first + ", " + second + ")" + if preIndex { + return base + " := " + address + "; memory[" + base + "] := pair(" + first + ", " + second + ")" + } + if len(addressOperands) > 1 { + store += "; " + base + " := " + addNativeOffset(base, addressOperands[1]) + } + return store +} + +func parseNativeMemory(operand string) (base, address string, preIndex bool) { + operand = strings.TrimSpace(operand) + preIndex = strings.HasSuffix(operand, "!") + inside := strings.TrimSpace(strings.TrimSuffix(strings.TrimSuffix(strings.TrimPrefix(operand, "["), "!"), "]")) + parts := splitAssemblyOperands(inside) + base = strings.TrimSpace(parts[0]) + address = base + if len(parts) > 1 { + address = addNativeOffset(base, strings.Join(parts[1:], ", ")) + } + return base, address, preIndex +} + +func addNativeOffset(base, offset string) string { + offset = strings.TrimSpace(strings.ReplaceAll(offset, "#", "")) + if after, ok := strings.CutPrefix(offset, "-"); ok { + return base + " - " + after + } + return base + " + " + offset +} + +func explainNativeDestinationFirst(destination string, sources []string, operator string, value func(string) string) string { + if len(sources) == 1 { + return destination + " := " + destination + " " + operator + " " + value(sources[0]) + } + return destination + " := " + value(sources[0]) + " " + operator + " " + value(sources[1]) +} + +// ForInstruction returns help for a Go-assembly instruction. canonical is the +// decoder's mnemonic (e.g. "LD1" for the Go spelling "VLD1"); pass "" when it is +// unavailable and the mnemonic is recovered from text instead. +func ForInstruction(arch, canonical, text string) (Help, bool) { + display, operands := splitAssemblyInstruction(text) + if display == "" { + return Help{}, false + } + // Strip the Go assembler's .P/.W index marker before matching, so post- and + // pre-index forms (MOVBU.P, FLDPQ.P) resolve like their base mnemonic. + mnemonic := display + if i := strings.IndexByte(mnemonic, '.'); i > 0 { + mnemonic = mnemonic[:i] + } + ref, hasRef := referenceEntry(arch, canonical, mnemonic) + + help, ok := knownAssemblyInstructionHelp(arch, mnemonic, operands) + switch { + case ok: + // Curated rules own the bespoke Explanation semantics; keep them. + case hasRef: + // For mnemonics the rules don't cover, fall back to the generated + // reference so the tooltip shows real ARM/x86 text. + help, ok = Help{Description: referenceBrief(ref)}, true + case plausibleMnemonic(mnemonic): + help = Help{ + Description: "Execute the " + mnemonic + " instruction.", + Note: noReferenceNote, + } + default: + return Help{}, false + } + // Ports are x86-only in the reference; the table merges ARM and x86 under + // one mnemonic key, so gate by arch to avoid showing x86 ports for arm64. + if hasRef && isX86(arch) { + help.Ports = referencePorts(ref) + } + help.Mnemonic = display + return help, true +} + +func isX86(arch string) bool { + return arch == "386" || arch == "amd64" +} + +// referenceArch is the microarchitecture whose port usage is summarised in the +// tooltip. The table stores every measured microarchitecture; the hover shows +// one recent, well-covered core. +const referenceArch = "ADL-P" + +// referencePorts returns the distinct port usages measured for the reference +// microarchitecture across the mnemonic's operand forms. +func referencePorts(ref asmref.Entry) []string { + var out []string + for _, p := range ref.PerfFor(referenceArch) { + if p.Ports != "" && !slices.Contains(out, p.Ports) { + out = append(out, p.Ports) + } + } + return out +} + +// noReferenceNote is shown on tooltips that have no real reference data — the +// Description is only a generic placeholder. +const noReferenceNote = "No reference information available for this instruction." + +// referenceEntry looks up the generated reference for an instruction. It tries +// the canonical decoder mnemonic first (authoritative), then falls back to +// spellings recovered from the displayed text: the table is keyed by base +// mnemonic (ADD, LD1, CRC32) while the disassembly shows ADDQ, CRC32Q (x86 size +// suffixes) or VLD1.P, VMOV (arm64 SIMD V-prefix and .P post-index marker). +func referenceEntry(arch, canonical, textMnemonic string) (asmref.Entry, bool) { + for _, m := range []string{canonical, textMnemonic} { + if m == "" { + continue + } + for _, candidate := range mnemonicCandidates(arch, m) { + if e, ok := asmref.Lookup(candidate); ok { + return e, true + } + } + } + return asmref.Entry{}, false +} + +// mnemonicCandidates returns the base mnemonics to try, most specific first. +func mnemonicCandidates(arch, mnemonic string) []string { + candidates := []string{mnemonic} + base := mnemonic + // Go assembler dot-suffixes mark writeback/post-index (VLD1.P -> VLD1). + if i := strings.IndexByte(base, '.'); i > 0 { + base = base[:i] + candidates = append(candidates, base) + } + if isX86(arch) { + // Go disambiguates some SSE ops with a _XMM tag (MOVSD_XMM -> MOVSD). + if i := strings.IndexByte(base, '_'); i > 0 { + base = base[:i] + candidates = append(candidates, base) + } + if s := trimGoAsmSuffix(base); s != base { + candidates = append(candidates, s) + } + return candidates + } + // arm64: expand the Go spelling into candidate ARM base mnemonics. Only + // table hits are used, so over-eager candidates that match nothing are + // harmless; rule-covered mnemonics (e.g. branches) never reach here. + forms := []string{base} + // Go spells SIMD ops with a leading V (VLD1 -> LD1, VMOV -> MOV). + if strings.HasPrefix(base, "V") && len(base) > 1 { + forms = append(forms, base[1:]) + } + for _, f := range append([]string(nil), forms...) { + // Trailing type/width suffixes: W (32-bit), B/H/S/D/Q (element size), + // and two-letter combos on converts (FCVTZSDW -> FCVTZS). + for n := 1; n <= 2; n++ { + if len(f) > n && isArmSizeSuffix(f[len(f)-n:]) { + forms = append(forms, f[:len(f)-n]) + } + } + // "2" upper-half variants (PMULL2, UMLAL2) are documented under the base. + if strings.HasSuffix(f, "2") && len(f) > 1 { + forms = append(forms, f[:len(f)-1]) + } + // SIMD&FP register-pair load/store: FLDP/FSTP -> LDP/STP. + if strings.HasPrefix(f, "FLDP") { + forms = append(forms, "LDP") + } else if strings.HasPrefix(f, "FSTP") { + forms = append(forms, "STP") + } + } + return append(candidates, forms...) +} + +func isArmSizeSuffix(s string) bool { + for _, r := range s { + switch r { + case 'B', 'H', 'W', 'S', 'D', 'Q': + default: + return false + } + } + return true +} + +// trimGoAsmSuffix removes a single trailing Go-assembler operand-size suffix. +// Longer suffixes (BU/HU/WU) are tried before single letters. Only used after +// an exact lookup misses, so stripping a genuine trailing letter is harmless. +func trimGoAsmSuffix(mnemonic string) string { + for _, suffix := range []string{"BU", "HU", "WU", "B", "W", "L", "Q", "D", "H", "S"} { + if len(mnemonic) > len(suffix) && strings.HasSuffix(mnemonic, suffix) { + return mnemonic[:len(mnemonic)-len(suffix)] + } + } + return mnemonic +} + +// referenceBrief returns a short human description for an entry, preferring the +// brief title over the full first paragraph. +func referenceBrief(ref asmref.Entry) string { + if ref.Brief != "" { + return ref.Brief + } + return ref.Description +} + +// plausibleMnemonic reports whether a token looks like an instruction +// mnemonic. Undecodable bytes render as "?", which must not get an +// authoritative-sounding "Execute the ? instruction." fallback. +func plausibleMnemonic(mnemonic string) bool { + if r := mnemonic[0]; r < 'A' || r > 'Z' { + return false + } + for _, r := range mnemonic { + switch { + case 'A' <= r && r <= 'Z', '0' <= r && r <= '9', r == '.', r == '_': + default: + return false + } + } + return true +} + +func knownAssemblyInstructionHelp(arch, mnemonic string, operands []string) (Help, bool) { + // Resolve exact mnemonics first. Otherwise BLS can be mistaken for BL with + // an S size suffix, and similar prefix collisions produce wrong semantics. + for _, rule := range asmInstructionRules { + if slices.Contains(rule.Prefixes, mnemonic) { + return assemblyHelpFromRule(arch, mnemonic, operands, rule), true + } + } + for _, rule := range asmInstructionRules { + for _, prefix := range rule.Prefixes { + if mnemonicMatches(mnemonic, prefix) { + return assemblyHelpFromRule(arch, mnemonic, operands, rule), true + } + } + } + return Help{}, false +} + +func assemblyHelpFromRule(arch, mnemonic string, operands []string, rule asmInstructionRule) Help { + help := Help{Mnemonic: mnemonic, Description: rule.Description} + switch { + case rule.ExplainArch != nil: + help.Explanation = rule.ExplainArch(arch, operands) + case rule.Explain != nil: + help.Explanation = rule.Explain(operands) + } + return help +} + +func mnemonicMatches(mnemonic, prefix string) bool { + if mnemonic == prefix { + return true + } + if !strings.HasPrefix(mnemonic, prefix) { + return false + } + suffix := strings.TrimPrefix(mnemonic, prefix) + // CMOV/SET take an x86 condition-code suffix (CMOVNE, SETA) rather than a + // size suffix. + if conditionFamilies[prefix] { + return x86ConditionCodes[suffix] + } + return nativeSizeSuffixes(prefix)[suffix] +} + +// conditionFamilies are mnemonics whose suffix is a condition code, not a size. +var conditionFamilies = map[string]bool{"CMOV": true, "SET": true} + +var x86ConditionCodes = map[string]bool{ + "E": true, "NE": true, "Z": true, "NZ": true, "A": true, "AE": true, + "B": true, "BE": true, "G": true, "GE": true, "L": true, "LE": true, + "O": true, "NO": true, "S": true, "NS": true, "P": true, "PE": true, + "NP": true, "PO": true, "C": true, "NC": true, +} + +// Suffix sets are package-level: nativeSizeSuffixes runs in the per-frame +// hover lookup and must not allocate. +var ( + x86IntegerSuffixes = map[string]bool{"B": true, "W": true, "L": true, "Q": true} + floatOrVectorSuffixes = map[string]bool{"S": true, "D": true} + // arm64 Go assembly sizes moves with D/H/W/B plus zero-extending + // BU/HU/WU: MOVD, MOVWU, ... The x86 sizes stay valid alongside. + movSuffixes = map[string]bool{ + "B": true, "W": true, "L": true, "Q": true, + "D": true, "H": true, "BU": true, "HU": true, "WU": true, + } + // arm64 32-bit register variants: SDIVW, MADDW, LSLW, ... + arm64WordSuffixes = map[string]bool{"W": true} +) + +func nativeSizeSuffixes(mnemonic string) map[string]bool { + switch mnemonic { + case "MOV": + return movSuffixes + case "FMOV": + return floatOrVectorSuffixes + case "LEA", "ADD", "ADC", "SUB", "SBB", "MUL", "IMUL", "DIV", "IDIV", + "AND", "OR", "XOR", "SHL", "SAL", "SHR", "SAR", "INC", "DEC", "NEG", "NOT", + "CMP", "TEST", "PUSH", "POP", "CALL", "RET", "XCHG", "BSWAP", "POPCNT": + return x86IntegerSuffixes + case "SDIV", "UDIV", "MADD", "MSUB", "ORR", "EOR", "LSL", "LSR", "ASR", "BIC", "MVN": + return arm64WordSuffixes + case "FADD", "FSUB", "FMUL", "FMADD": + return floatOrVectorSuffixes + default: + return nil + } +} + +func splitAssemblyInstruction(text string) (string, []string) { + text = strings.TrimSpace(text) + if text == "" { + return "", nil + } + if index := strings.Index(text, "\t"); index >= 0 { + text = text[:index] + } + fields := strings.Fields(text) + if len(fields) == 0 { + return "", nil + } + mnemonic := strings.ToUpper(strings.TrimSpace(fields[0])) + rest := strings.TrimSpace(strings.TrimPrefix(text, fields[0])) + if rest == "" { + return mnemonic, nil + } + parts := splitAssemblyOperands(rest) + operands := make([]string, 0, len(parts)) + for _, part := range parts { + operands = append(operands, strings.TrimSpace(part)) + } + return mnemonic, operands +} + +func splitAssemblyOperands(text string) []string { + var parts []string + start, depth := 0, 0 + for index, char := range text { + switch char { + case '(', '[': + depth++ + case ')', ']': + if depth > 0 { + depth-- + } + case ',': + if depth == 0 { + parts = append(parts, text[start:index]) + start = index + 1 + } + } + } + return append(parts, text[start:]) +} + +func explainMove(operands []string) string { + if len(operands) < 2 { + return "" + } + return formatDestination(operands[len(operands)-1]) + " := " + formatValue(operands[0]) +} + +func explainLEA(operands []string) string { + if len(operands) < 2 { + return "" + } + return formatDestination(operands[len(operands)-1]) + " := " + formatAddress(operands[0]) +} + +func explainBinary(operator string) func([]string) string { + return func(operands []string) string { + if len(operands) < 2 { + return "" + } + destination := formatDestination(operands[len(operands)-1]) + if len(operands) == 2 { + return destination + " := " + destination + " " + operator + " " + formatValue(operands[0]) + } + // Three-operand Go assembly computes dst := second op first: + // SUB R1, R5, R3 is R3 = R5 - R1, LSL $3, R5, R3 is R3 = R5 << 3. + return destination + " := " + formatValue(operands[len(operands)-2]) + " " + operator + " " + formatValue(operands[0]) + } +} + +func explainUnaryDelta(operator, amount string) func([]string) string { + return func(operands []string) string { + if len(operands) == 0 { + return "" + } + destination := formatDestination(operands[len(operands)-1]) + return destination + " := " + destination + " " + operator + " " + amount + } +} + +func explainUnaryPrefix(operator string) func([]string) string { + return func(operands []string) string { + if len(operands) == 0 { + return "" + } + destination := formatDestination(operands[len(operands)-1]) + return destination + " := " + operator + destination + } +} + +func explainCompare(arch string, operands []string) string { + if len(operands) < 2 { + return "" + } + // Go assembly for x86 deliberately keeps CMP operands in natural + // left-to-right order (CMPQ AX, BX sets flags from AX-BX; see + // go.dev/issue/60920), while arm64 and the other ports use + // source-first order (CMP R1, R4 sets flags from R4-R1). + if arch == "386" || arch == "amd64" { + return "flags := compare(" + formatValue(operands[0]) + ", " + formatValue(operands[1]) + ")" + } + return "flags := compare(" + formatValue(operands[1]) + ", " + formatValue(operands[0]) + ")" +} + +func explainTest(operands []string) string { + if len(operands) < 2 { + return "" + } + return "flags := " + formatValue(operands[1]) + " & " + formatValue(operands[0]) +} + +func explainFMADD(operands []string) string { + if len(operands) < 4 { + return "" + } + destination := formatDestination(operands[3]) + return destination + " := " + formatValue(operands[0]) + " * " + formatValue(operands[2]) + " + " + formatValue(operands[1]) +} + +// Go arm64 assembly orders MADD/MSUB as "Rm, Ra, Rn, Rd": +// MADD R2, R3, R1, R0 computes R0 = R1*R2 + R3. +func explainMADD(operands []string) string { + if len(operands) < 4 { + return "" + } + destination := formatDestination(operands[3]) + return destination + " := " + formatValue(operands[2]) + " * " + formatValue(operands[0]) + " + " + formatValue(operands[1]) +} + +func explainMSUB(operands []string) string { + if len(operands) < 4 { + return "" + } + destination := formatDestination(operands[3]) + return destination + " := " + formatValue(operands[1]) + " - " + formatValue(operands[2]) + " * " + formatValue(operands[0]) +} + +func explainLoad(operands []string) string { + if len(operands) < 2 { + return "" + } + return formatDestination(operands[len(operands)-1]) + " := " + formatValue(operands[0]) +} + +func explainStore(operands []string) string { + if len(operands) < 2 { + return "" + } + return "memory[" + formatAddress(operands[len(operands)-1]) + "] := " + formatValue(operands[0]) +} + +var plan9Address = regexp.MustCompile(`^([^()]*)\(([^)]+)\)(?:\(([^)]+)\))?$`) + +func formatValue(operand string) string { + operand = strings.TrimSpace(operand) + if after, ok := strings.CutPrefix(operand, "$"); ok { + return after + } + if plan9Address.MatchString(operand) { + return "memory[" + formatAddress(operand) + "]" + } + return operand +} + +func formatDestination(operand string) string { + operand = strings.TrimSpace(operand) + if plan9Address.MatchString(operand) { + return "memory[" + formatAddress(operand) + "]" + } + return operand +} + +func formatAddress(operand string) string { + operand = strings.TrimSpace(strings.TrimPrefix(operand, "$")) + match := plan9Address.FindStringSubmatch(operand) + if len(match) == 0 { + return operand + } + displacement := strings.TrimSpace(match[1]) + base := strings.TrimSpace(match[2]) + index := strings.TrimSpace(match[3]) + + var terms []string + if base != "" && base != "SB" { + terms = append(terms, base) + } + if index != "" { + index = strings.ReplaceAll(index, "*", " * ") + terms = append(terms, index) + } + if displacement != "" && displacement != "0" { + terms = append(terms, displacement) + } + if len(terms) == 0 { + return displacement + } + return strings.Join(terms, " + ") +} diff --git a/internal/asmhelp/asmhelp_test.go b/internal/asmhelp/asmhelp_test.go new file mode 100644 index 0000000..8bebefa --- /dev/null +++ b/internal/asmhelp/asmhelp_test.go @@ -0,0 +1,388 @@ +package asmhelp + +import ( + "strings" + "testing" +) + +func TestAssemblyInstructionExplanations(t *testing.T) { + tests := []struct { + arch string + instruction string + want string + }{ + {"arm64", "MOV (R2), R1", "R1 := memory[R2]"}, + {"amd64", "ADDQ $1, R1", "R1 := R1 + 1"}, + {"amd64", "LEAQ 0(R1)(R2*4), R3", "R3 := R1 + R2 * 4"}, + {"arm64", "FMADDS F0, F2, F1, F2", "F2 := F0 * F1 + F2"}, + // x86 Go assembly keeps CMP operands in natural order: flags are + // computed from AX - 0x10 (go.dev/issue/60920). + {"amd64", "CMPQ AX, $0x10", "flags := compare(AX, 0x10)"}, + // arm64 Go assembly is source-first: CMP R1, R4 is R4 - R1. + {"arm64", "CMP R1, R4", "flags := compare(R4, R1)"}, + // Three-operand arm64 forms compute dst := second op first. + {"arm64", "SUB R1, R5, R3", "R3 := R5 - R1"}, + {"arm64", "LSL $3, R5, R3", "R3 := R5 << 3"}, + // arm64 sized moves and 32-bit variants must keep matching their + // base rules. + {"arm64", "MOVD (R1), R0", "R0 := memory[R1]"}, + {"arm64", "MOVWU (R1), R0", "R0 := memory[R1]"}, + {"arm64", "SDIVW R1, R2, R0", "R0 := R2 / R1"}, + {"arm64", "FMOVD F1, F0", "F0 := F1"}, + // MADD R2, R3, R1, R0 computes R0 = R1*R2 + R3. + {"arm64", "MADD R2, R3, R1, R0", "R0 := R1 * R2 + R3"}, + {"arm64", "MSUB R2, R3, R1, R0", "R0 := R3 - R1 * R2"}, + } + for _, test := range tests { + help, ok := ForInstruction(test.arch, "", test.instruction) + if !ok { + t.Fatalf("no help for %q", test.instruction) + } + if help.Explanation != test.want { + t.Errorf("explanation for %q (%s) = %q, want %q", test.instruction, test.arch, help.Explanation, test.want) + } + } +} + +func TestAssemblyInstructionReference(t *testing.T) { + help, ok := ForInstruction("amd64", "", "JNE 12(PC)") + if !ok || help.Description == "" { + t.Fatalf("JNE help = %#v, %v", help, ok) + } + if help.Explanation != "" { + t.Fatalf("unexpected JNE explanation: %q", help.Explanation) + } +} + +func TestUnknownGoAssemblyInstructionHasFallbackReference(t *testing.T) { + // A plausible mnemonic that is in neither the curated rules nor the + // generated reference falls back to the generic line, flagged with a note. + help, ok := ForInstruction("amd64", "", "ZZUNKNOWNOP $49, Y1, Y2, Y3") + if !ok { + t.Fatal("no fallback help for Go assembly instruction") + } + if help.Mnemonic != "ZZUNKNOWNOP" || help.Description != "Execute the ZZUNKNOWNOP instruction." { + t.Fatalf("fallback help = %#v", help) + } + if help.Note == "" { + t.Error("generic fallback should carry a missing-reference note") + } +} + +// TestGoAssemblerSpellingsResolve covers the mnemonic spellings the Go +// assembler emits that differ from the ARM/x86 reference names, found by +// disassembling the lensm binary. Each must resolve to real help (no +// missing-reference note). +func TestGoAssemblerSpellingsResolve(t *testing.T) { + cases := []struct{ arch, mnemonic string }{ + // arm64 width/type suffixes, V-prefix, .P index, F-pair, "2" variant. + {"arm64", "LDPW"}, {"arm64", "STPW"}, {"arm64", "CBZW"}, {"arm64", "TSTW"}, + {"arm64", "REV16W"}, {"arm64", "MOVKW"}, {"arm64", "LDARW"}, + {"arm64", "FCMPS"}, {"arm64", "FDIVD"}, {"arm64", "FMOVQ"}, + {"arm64", "SCVTFWD"}, {"arm64", "FCVTZSDW"}, {"arm64", "UCVTFD"}, + {"arm64", "FLDPQ"}, {"arm64", "FSTPS"}, {"arm64", "FLDPQ.P"}, + {"arm64", "VLD1.P"}, {"arm64", "VMOV"}, {"arm64", "VPMULL2"}, + {"arm64", "MOVBU.P"}, {"arm64", "BCC"}, {"arm64", "NOOP"}, + // amd64 condition families, prefixes, SSE tag, obscure ops. + {"amd64", "CMOVNE"}, {"amd64", "CMOVAE"}, {"amd64", "SETA"}, {"amd64", "SETNE"}, + {"amd64", "LOCK"}, {"amd64", "MOVSD_XMM"}, {"amd64", "ICEBP"}, + {"amd64", "FS"}, {"amd64", "LRET"}, + } + for _, tc := range cases { + help, ok := ForInstruction(tc.arch, "", tc.mnemonic) + if !ok || help.Note != "" || help.Description == "" { + t.Errorf("%s %s did not resolve: ok=%v %#v", tc.arch, tc.mnemonic, ok, help) + } + } +} + +func TestCanonicalMnemonicIsAuthoritative(t *testing.T) { + // The decoder's canonical op name resolves the reference directly, even when + // the displayed mnemonic is unrecognisable and no heuristic would map it. + help, ok := ForInstruction("arm64", "FMOV", "ZZZ F0, F1") + if !ok || help.Note != "" || help.Description == "" { + t.Fatalf("canonical FMOV did not resolve: ok=%v %#v", ok, help) + } + if help.Mnemonic != "ZZZ" { + t.Errorf("display mnemonic = %q, want ZZZ (from text)", help.Mnemonic) + } +} + +func TestReferenceResolvesArm64GoSpellings(t *testing.T) { + // The Go arm64 assembler spells SIMD ops with a V prefix and a .P + // post-index marker; the reference is keyed by the ARM base name (LD1, MOV). + for _, instruction := range []string{ + "VLD1 (R0), [V0.B16]", + "VLD1.P 16(R0), [V0.B16]", + "VMOV V1.B16, V2.B16", + } { + help, ok := ForInstruction("arm64", "", instruction) + if !ok { + t.Fatalf("no help for %q", instruction) + } + if help.Description == "" || help.Note != "" { + t.Errorf("%q should resolve to real reference text, got %#v", instruction, help) + } + } +} + +func TestGeneratedReferenceReplacesGenericFallback(t *testing.T) { + // ABS and CRC32 are absent from the curated rules, so they used to get the + // generic "Execute the X instruction." line. The generated asmref table now + // supplies real reference text, while the bespoke Explanation stays empty + // (no rule fabricates semantics for them). Asserted structurally so the test + // holds against whatever prose the current ISA release ships. + for _, tc := range []struct{ arch, instruction string }{ + {"arm64", "ABS V0.8B, V1.8B"}, + {"amd64", "CRC32 AX, BL"}, + } { + mnemonic, _ := splitAssemblyInstruction(tc.instruction) + help, ok := ForInstruction(tc.arch, "", tc.instruction) + if !ok { + t.Fatalf("no help for %q", tc.instruction) + } + if help.Description == "" || help.Description == "Execute the "+mnemonic+" instruction." { + t.Errorf("%q got generic/empty description %q", tc.instruction, help.Description) + } + if help.Explanation != "" { + t.Errorf("unexpected explanation for %q: %q", tc.instruction, help.Explanation) + } + } +} + +func TestReferenceLookupToleratesPlan9Suffixes(t *testing.T) { + // lensm shows Plan 9 spellings with size suffixes (CRC32Q), but the table + // is keyed by the base mnemonic (CRC32). The fallback must still resolve it. + help, ok := ForInstruction("amd64", "", "CRC32Q AX, BX") + if !ok { + t.Fatal("no help for CRC32Q") + } + if !strings.Contains(strings.ToUpper(help.Description), "CRC32") { + t.Errorf("CRC32Q description = %q, want it to mention CRC32", help.Description) + } + if len(help.Ports) == 0 { + t.Errorf("CRC32Q should carry ports from the reference") + } +} + +func TestPortsAttachToRuleCoveredX86ButNotArm(t *testing.T) { + // ADDQ is covered by the curated rules; ports still come from the reference. + x86, ok := ForInstruction("amd64", "", "ADDQ $1, AX") + if !ok { + t.Fatal("no help for ADDQ") + } + if len(x86.Ports) == 0 { + t.Errorf("amd64 ADDQ should carry ports, got %#v", x86) + } + // The same merged ADD entry must not leak its x86 ports onto arm64. + arm, ok := ForInstruction("arm64", "", "ADD R1, R2, R3") + if !ok { + t.Fatal("no help for arm64 ADD") + } + if len(arm.Ports) != 0 { + t.Errorf("arm64 ADD must not carry x86 ports, got %#v", arm.Ports) + } +} + +func TestUndecodableInstructionHasNoFallback(t *testing.T) { + // Undecodable bytes render as "?" in the Go column. + if help, ok := ForInstruction("amd64", "", "?"); ok { + t.Fatalf("unexpected fallback for undecodable instruction: %#v", help) + } + if help, ok := ForInstruction("amd64", "", "// pseudo"); ok { + t.Fatalf("unexpected fallback for non-mnemonic token: %#v", help) + } +} + +func TestUnknownNativeAssemblyInstructionHasNoGoFallback(t *testing.T) { + if help, ok := ForNative("", "", "unknownop %rax"); ok { + t.Fatalf("unexpected native fallback: %#v", help) + } +} + +func TestNativeAssemblyInstructionHelpUsesNativeRewrite(t *testing.T) { + help, ok := ForNative("", "", "addq $1, %rax") + if !ok { + t.Fatal("no native help for ADDQ") + } + if help.Explanation != "%rax := %rax + 1" { + t.Fatalf("native explanation = %q", help.Explanation) + } +} + +func TestNativeARMAssemblyInstructionExplanation(t *testing.T) { + help, ok := ForNative("", "", "add x0, x1, #8") + if !ok { + t.Fatal("no native help for ARM ADD") + } + if help.Explanation != "x0 := x1 + 8" { + t.Fatalf("native ARM explanation = %q", help.Explanation) + } +} + +func TestNativeARMStoreInstructionExplanation(t *testing.T) { + help, ok := ForNative("", "", "str x0, [sp, #16]") + if !ok { + t.Fatal("no native help for ARM STR") + } + if help.Explanation != "memory[sp + 16] := x0" { + t.Fatalf("native ARM STR explanation = %q", help.Explanation) + } +} + +func TestNativeARMIndexedMemoryExplanations(t *testing.T) { + tests := map[string]string{ + "str x30, [sp, #-112]!": "sp := sp - 112; memory[sp] := x30", + "ldr x0, [sp], #16": "x0 := memory[sp]; sp := sp + 16", + "stp x29, x30, [sp, #-16]!": "sp := sp - 16; memory[sp] := pair(x29, x30)", + } + for instruction, want := range tests { + help, ok := ForNative("", "", instruction) + if !ok || help.Explanation != want { + t.Errorf("%q explanation = %q, want %q", instruction, help.Explanation, want) + } + } +} + +func TestNativeDirectJumpHasHelp(t *testing.T) { + // x86 GNU syntax spells direct jumps jmpq. + help, ok := ForNative("", "", "jmpq .+0x100") + if !ok { + t.Fatal("no native help for jmpq") + } + if help.Explanation != "PC := .+0x100" { + t.Fatalf("jmpq explanation = %q", help.Explanation) + } +} + +func TestNativeARMTwoOperandNeg(t *testing.T) { + help, ok := ForNative("", "", "neg x0, x1") + if !ok { + t.Fatal("no native help for neg") + } + if help.Explanation != "x0 := -x1" { + t.Fatalf("neg explanation = %q", help.Explanation) + } + help, ok = ForNative("", "", "mvn x0, x1") + if !ok { + t.Fatal("no native help for mvn") + } + if help.Explanation != "x0 := ^x1" { + t.Fatalf("mvn explanation = %q", help.Explanation) + } +} + +func TestNativeARMUnsignedConditionalBranchIsNotCall(t *testing.T) { + help, ok := ForNative("", "", "b.ls .+0x1bc") + if !ok { + t.Fatal("no native help for B.LS") + } + if help.Description != "Conditional jump after an unsigned comparison." { + t.Fatalf("B.LS description = %q", help.Description) + } + if help.Explanation != "if unsigned lower than or equal (C == 0 or Z == 1), PC := .+0x1bc" { + t.Fatalf("B.LS explanation = %q", help.Explanation) + } +} + +func TestAssemblyHelpRuleTableHasNoMnemonicCollisions(t *testing.T) { + type owner struct { + description string + prefix string + } + exact := make(map[string]owner) + for _, rule := range asmInstructionRules { + for _, prefix := range rule.Prefixes { + if previous, exists := exact[prefix]; exists { + t.Errorf("duplicate mnemonic %s in %s and %s", prefix, previous.prefix, prefix) + } + exact[prefix] = owner{description: rule.Description, prefix: prefix} + } + } + + for _, rule := range asmInstructionRules { + for _, prefix := range rule.Prefixes { + for suffix := range nativeSizeSuffixes(prefix) { + mnemonic := prefix + suffix + if exactOwner, isExactMnemonic := exact[mnemonic]; isExactMnemonic { + if help, ok := ForInstruction("", "", mnemonic); !ok || help.Description != exactOwner.description { + t.Errorf("exact %s was captured as a suffix of %s", mnemonic, prefix) + } + continue + } + help, ok := ForInstruction("", "", mnemonic) + if !ok || help.Description != rule.Description { + t.Errorf("%s resolved to %#v; want rule for %s", mnemonic, help, prefix) + } + } + } + } +} + +func TestAllNativeARMConditionBranchesResolveAsBranches(t *testing.T) { + conditions := map[string]string{ + "eq": "if the compared values are equal (Z == 1), PC := .+4", + "ne": "if the compared values are not equal (Z == 0), PC := .+4", + "gt": "if signed greater than (Z == 0 and N == V), PC := .+4", + "ge": "if signed greater than or equal (N == V), PC := .+4", + "lt": "if signed less than (N != V), PC := .+4", + "le": "if signed less than or equal (Z == 1 or N != V), PC := .+4", + "hi": "if unsigned higher than (C == 1 and Z == 0), PC := .+4", + "hs": "if unsigned higher than or equal (C == 1), PC := .+4", + "cs": "if unsigned higher than or equal (C == 1), PC := .+4", + "lo": "if unsigned lower than (C == 0), PC := .+4", + "cc": "if unsigned lower than (C == 0), PC := .+4", + "ls": "if unsigned lower than or equal (C == 0 or Z == 1), PC := .+4", + } + for condition, want := range conditions { + instruction := "b." + condition + " .+4" + help, ok := ForNative("", "", instruction) + if !ok { + t.Errorf("no help for %s", instruction) + continue + } + if strings.Contains(help.Description, "Call a function") { + t.Errorf("%s incorrectly resolved as call: %#v", instruction, help) + } + if help.Explanation != want { + t.Errorf("%s effect = %q, want %q", instruction, help.Explanation, want) + } + } +} + +func TestAssemblyInstructionReferenceCoverage(t *testing.T) { + for _, instruction := range []string{ + "movzbl (%rax), %eax", "cmovne %rax, %rbx", "sete %al", + "cqto", "syscall", "mfence", "adrp x0, 0x1000", "stp x0, x1, [sp]", + } { + if help, ok := ForNative("", "", instruction); !ok || help.Description == "" || help.Explanation == "" { + t.Errorf("no native help for %q", instruction) + } + } +} + +func TestNativeInstructionFamiliesHaveConcreteExplanations(t *testing.T) { + instructions := []string{ + "movq %rax, %rbx", "movzbl (%rax), %ebx", "leaq 8(%rax), %rbx", + "addq $1, %rax", "sub x0, x1, x2", "madd x0, x1, x2, x3", + "msub x0, x1, x2, x3", "udiv x0, x1, x2", "and x0, x1, x2", + "orr x0, x1, x2", "eor x0, x1, x2", "lsl x0, x1, #3", + "cmp x0, x1", "tst x0, x1", "fadd d0, d1, d2", + "fsub d0, d1, d2", "fmul d0, d1, d2", "fmadd d0, d1, d2, d3", + "ldrb w0, [x1, #4]", "strh w0, [sp, #8]", "ldp x0, x1, [sp]", + "stp x0, x1, [sp, #-16]!", "adrp x0, 0x1000", "csel x0, x1, x2, eq", + "cset x0, ne", "rev x0, x1", "clz x0, x1", "popcnt %rax, %rbx", + "pushq %rax", "popq %rax", "callq 0x1000", "ret", "jmp 0x1000", + "b.eq 0x1000", "cbz x0, 0x1000", "tbnz x0, #2, 0x1000", + "nop", "syscall", "dmb ish", "yield", "xchgq %rax, %rbx", + } + for _, instruction := range instructions { + help, ok := ForNative("", "", instruction) + if !ok || help.Explanation == "" { + t.Errorf("native %q help has no concrete explanation: %#v", instruction, help) + } + if strings.HasPrefix(help.Explanation, "execute ") { + t.Errorf("native %q uses generic explanation %q", instruction, help.Explanation) + } + } +} diff --git a/internal/asmref/README.md b/internal/asmref/README.md new file mode 100644 index 0000000..dd92877 --- /dev/null +++ b/internal/asmref/README.md @@ -0,0 +1,54 @@ +# asmref + +A generated, flattened instruction reference for hover tooltips. `table.json.gz` +(gzip-compressed JSON) is embedded at build time and looked up by mnemonic via +`asmref.Lookup`. + +The table holds the factual "API" — brief title, description, syntax forms and +per-operand meanings — plus x86 per-microarchitecture performance data (uops, +ports, latency, throughput). Bit encodings and execution pseudocode are dropped. +The Go-pseudocode effects shown in tooltips come from `internal/asmhelp`, not +from here; `asmref` only fills in reference text `asmhelp` doesn't already cover. + +The full table (all mnemonics, all measured microarchitectures) is ~15MB of JSON +that gzips to ~0.6MB, hence the compressed embed. + +## Regenerating + +``` +go generate ./internal/asmref +``` + +With no arguments the generator reads the small checked-in fixtures under +`gen/testdata`, so a bare run stays reproducible. To build the full table, +download the sources (`data/download.sh`) and point it at them: + +``` +go run ./internal/asmref/gen \ + -arm data/arm64/ISA_A64_xml_A_profile-2025-12 \ + -x86 data/x86/instructions.xml \ + -out internal/asmref/table.json.gz +``` + +The generator also prints (to stderr) any mnemonics that `golang.org/x/arch`'s +decoders know but the table is missing — a coverage hint, not an error. + +## Sources + +- **ARM AArch64** — the official "Exploration tools / ISA XML" release from + developer.arm.com (the `ISA_A64_xml_*` bundle, one XML file per instruction). + Files starting with `sysreg_` (system registers) and `AArch64-` (shared + pseudocode) are skipped. +- **x86 / AMD64** — `instructions.xml` from https://uops.info (XED-derived, + ~140MB). It is a benchmark dataset, not a manual: there are no description or + syntax elements, so the `summary` attribute becomes the brief and the `string` + attribute (e.g. `ADD (R32, M32)`) becomes a syntax form. Per-operand + descriptions are not emitted — they restate the token already in the form. + For every operand form and every measured microarchitecture, the first + `` yields uops, ports (uops.info notation, e.g. `1*p0156`), + throughput and worst-case latency; `` estimate nodes are skipped in + favour of the real measurements. + +`data/download.sh` fetches both dumps (URLs/version pinned to match +`golang.org/x/arch/arm64/instgen/xmlspec`). They are large and gitignored; +record the release version you used so regeneration stays reproducible. diff --git a/internal/asmref/asmref.go b/internal/asmref/asmref.go new file mode 100644 index 0000000..d6d130b --- /dev/null +++ b/internal/asmref/asmref.go @@ -0,0 +1,94 @@ +// Package asmref holds a generated, flattened instruction reference used for +// hover tooltips. The table is produced by ./gen from CPU ISA XML (ARM's +// official AArch64 release and the uops.info XED-derived dump) and embedded as +// gzip-compressed JSON — see README.md for how to regenerate. +// +// asmref is deliberately just reference text (brief, description, syntax, +// operand meanings) plus x86 performance data. The bespoke Go-pseudocode +// effects live in internal/asmhelp; nothing here overwrites them. +package asmref + +import ( + "bytes" + "compress/gzip" + _ "embed" + "encoding/json" + "io" + "strings" + "sync" +) + +// Entry is the flattened reference for a single mnemonic. It matches the JSON +// schema written by the generator; both gen and runtime share this type. +type Entry struct { + Brief string `json:"brief,omitempty"` + Description string `json:"description,omitempty"` + Syntax []string `json:"syntax,omitempty"` + Operands map[string]string `json:"operands,omitempty"` + // Variants holds x86 per-operand-form performance data across all measured + // microarchitectures. Empty for ARM. + Variants []Variant `json:"variants,omitempty"` +} + +// Variant is one x86 operand form (e.g. "ADD (R32, R32)") and its measured +// performance on each microarchitecture. +type Variant struct { + Form string `json:"form"` + Perf []ArchPerf `json:"perf,omitempty"` +} + +// ArchPerf is the uops.info measurement for one microarchitecture. +type ArchPerf struct { + Arch string `json:"arch"` + Uops int `json:"uops,omitempty"` + Ports string `json:"ports,omitempty"` // uops.info notation, e.g. "1*p0156" + Latency int `json:"lat,omitempty"` // worst-case cycles across operand pairs + TP float64 `json:"tp,omitempty"` // throughput (cycles per instruction) +} + +// PerfFor returns the per-variant measurements for one microarchitecture. +func (e Entry) PerfFor(arch string) []ArchPerf { + var out []ArchPerf + for _, v := range e.Variants { + for _, p := range v.Perf { + if p.Arch == arch { + out = append(out, p) + } + } + } + return out +} + +// Regenerating needs the real ISA dumps under data/ (run data/download.sh +// first). The ARM directory name tracks the pinned release; bump both here and +// in data/download.sh when updating. +//go:generate go run ./gen -arm ../../data/arm64/ISA_A64_xml_A_profile-2025-12 -x86 ../../data/x86/instructions.xml -out table.json.gz + +//go:embed table.json.gz +var tableGz []byte + +var ( + tableOnce sync.Once + table map[string]Entry +) + +func load() { + // A malformed embed should surface as "no data" rather than panic in the + // UI hover path; the generator is what guarantees validity. + if r, err := gzip.NewReader(bytes.NewReader(tableGz)); err == nil { + if data, err := io.ReadAll(r); err == nil { + _ = json.Unmarshal(data, &table) + } + } + if table == nil { + table = map[string]Entry{} + } +} + +// Lookup returns the reference entry for a mnemonic (case-insensitive). The +// table is loaded once on first use. +func Lookup(mnemonic string) (Entry, bool) { + tableOnce.Do(load) + e, ok := table[strings.ToUpper(strings.TrimSpace(mnemonic))] + return e, ok +} diff --git a/internal/asmref/asmref_test.go b/internal/asmref/asmref_test.go new file mode 100644 index 0000000..9de304f --- /dev/null +++ b/internal/asmref/asmref_test.go @@ -0,0 +1,17 @@ +package asmref + +import "testing" + +func TestLookup(t *testing.T) { + // The embedded table is generated from gen/testdata; ADD is always present. + e, ok := Lookup("add") + if !ok { + t.Fatal("ADD not found (is table.json generated?)") + } + if e.Brief == "" || len(e.Syntax) == 0 { + t.Fatalf("ADD entry looks empty: %#v", e) + } + if _, ok := Lookup("NOTAREALINSTRUCTION"); ok { + t.Fatal("unexpected hit for bogus mnemonic") + } +} diff --git a/internal/asmref/gen/arm.go b/internal/asmref/gen/arm.go new file mode 100644 index 0000000..d18efc3 --- /dev/null +++ b/internal/asmref/gen/arm.go @@ -0,0 +1,226 @@ +package main + +import ( + "encoding/xml" + "fmt" + "io" + "os" + "path/filepath" + "slices" + "strings" +) + +// armCollected holds the syntaxes and operand meanings gathered for one +// mnemonic within a single instruction file. +type armCollected struct { + Syntax []string + Operands map[string]string +} + +// armFile is the reference extracted from one AArch64 instruction XML file. +type armFile struct { + Brief string // short title, from + Description string // first authored paragraph + Mnemonics map[string]*armCollected +} + +// ParseARMDir walks an ARM AArch64 ISA XML directory and feeds every +// instruction file into the builder. Files whose basename starts with +// "sysreg_" (system registers) or "AArch64-" (shared pseudocode, not +// instructions) are skipped per the ARM release layout. +func ParseARMDir(b *Builder, dir string) error { + return filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + name := d.Name() + if !strings.HasSuffix(name, ".xml") { + return nil + } + if strings.HasPrefix(name, "sysreg_") || strings.HasPrefix(name, "AArch64-") { + return nil + } + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + parsed, err := parseARMFile(f) + if err != nil { + return fmt.Errorf("%s: %w", name, err) + } + for mnemonic, c := range parsed.Mnemonics { + b.Add(mnemonic, parsed.Brief, parsed.Description, c.Syntax, nil, c.Operands) + } + return nil + }) +} + +// parseARMFile extracts the factual reference from one instruction file. It is +// a token walk rather than struct unmarshaling because is mixed +// content (literal text interleaved with operand links) and because +// (pseudocode) and (bit encodings) must be skipped +// wholesale. +func parseARMFile(r io.Reader) (armFile, error) { + dec := xml.NewDecoder(r) + out := armFile{Mnemonics: map[string]*armCollected{}} + + var ( + curMnemonic string + aliasMnemonic string // set for alias files (e.g. UBFIZ aliasing UBFM) + title string // instructionsection title attr, a fallback brief + + inBrief bool // inside + inBriefPara bool // capturing the first of + haveBrief bool + briefBuf strings.Builder + + inAuthored bool // inside + inDescPara bool // capturing the first of + haveDesc bool + descBuf strings.Builder + + inAsm bool // inside an + synBuf strings.Builder + inA bool // inside an operand link within an asmtemplate + aHover string + aTextBuf strings.Builder + ) + + // An alias file documents the alias (UBFIZ), not its underlying base + // (UBFM) — the mnemonic docvar holds the base, so the alias_mnemonic wins. + mnemonic := func() string { + if aliasMnemonic != "" { + return aliasMnemonic + } + return curMnemonic + } + + collected := func() *armCollected { + key := mnemonic() + c := out.Mnemonics[key] + if c == nil { + c = &armCollected{Operands: map[string]string{}} + out.Mnemonics[key] = c + } + return c + } + + for { + tok, err := dec.Token() + if err == io.EOF { + break + } + if err != nil { + return armFile{}, err + } + switch t := tok.(type) { + case xml.StartElement: + switch t.Name.Local { + case "instructionsection": + title = attr(t, "title") + case "ps_section", "regdiagram": + if err := dec.Skip(); err != nil { + return armFile{}, err + } + case "docvar": + switch attr(t, "key") { + case "mnemonic": + curMnemonic = attr(t, "value") + case "alias_mnemonic": + aliasMnemonic = attr(t, "value") + } + case "brief": + inBrief = true + case "authored": + inAuthored = true + case "para": + if inBrief && !haveBrief { + inBriefPara = true + briefBuf.Reset() + } else if inAuthored && !haveDesc { + inDescPara = true + descBuf.Reset() + } + case "asmtemplate": + inAsm = true + synBuf.Reset() + case "a": + if inAsm { + inA = true + aHover = attr(t, "hover") + aTextBuf.Reset() + } + } + case xml.CharData: + if inBriefPara { + briefBuf.Write(t) + } + if inDescPara { + descBuf.Write(t) + } + if inAsm { + synBuf.Write(t) + if inA { + aTextBuf.Write(t) + } + } + case xml.EndElement: + switch t.Name.Local { + case "brief": + inBrief = false + case "authored": + inAuthored = false + case "para": + if inBriefPara { + out.Brief = briefBuf.String() + haveBrief = true + inBriefPara = false + } else if inDescPara { + out.Description = descBuf.String() + haveDesc = true + inDescPara = false + } + case "a": + if inA { + name := strings.TrimSpace(aTextBuf.String()) + if name != "" && aHover != "" && mnemonic() != "" { + collected().Operands[name] = aHover + } + inA = false + } + case "asmtemplate": + inAsm = false + if mnemonic() != "" { + if syn := normalizeSpace(synBuf.String()); syn != "" { + c := collected() + if !slices.Contains(c.Syntax, syn) { + c.Syntax = append(c.Syntax, syn) + } + } + } + } + } + } + // Fall back to the title attr (minus its " -- A64" profile suffix) when a + // file has no paragraph. + if out.Brief == "" { + if i := strings.Index(title, " -- "); i >= 0 { + title = title[:i] + } + out.Brief = title + } + return out, nil +} + +func attr(e xml.StartElement, key string) string { + for _, a := range e.Attr { + if a.Name.Local == key { + return a.Value + } + } + return "" +} diff --git a/internal/asmref/gen/arm_test.go b/internal/asmref/gen/arm_test.go new file mode 100644 index 0000000..8123b9b --- /dev/null +++ b/internal/asmref/gen/arm_test.go @@ -0,0 +1,66 @@ +package main + +import ( + "strings" + "testing" +) + +func TestParseARMDir(t *testing.T) { + b := NewBuilder() + if err := ParseARMDir(b, "testdata/arm"); err != nil { + t.Fatal(err) + } + table := b.Table() + + // sysreg_ and AArch64- files must be skipped entirely. + if _, ok := table["SHOULDNOTAPPEAR"]; ok { + t.Fatal("skipped file leaked into the table") + } + + add, ok := table["ADD"] + if !ok { + t.Fatal("ADD missing") + } + // Brief comes from , not the noisy "-- A64" title attr. + if add.Brief != "Add (immediate)" { + t.Errorf("ADD brief = %q", add.Brief) + } + // Description is the first paragraph only; the second must be dropped. + if !strings.HasPrefix(add.Description, "Add (immediate) adds a register value") { + t.Errorf("ADD description = %q", add.Description) + } + if strings.Contains(add.Description, "second paragraph") { + t.Errorf("ADD description leaked a later paragraph: %q", add.Description) + } + if len(add.Syntax) != 1 || add.Syntax[0] != "ADD , , #" { + t.Errorf("ADD syntax = %#v", add.Syntax) + } + if got := add.Operands[""]; !strings.Contains(got, "destination register") { + t.Errorf("ADD operand = %q", got) + } + if _, ok := add.Operands[""]; !ok { + t.Errorf("ADD operand missing: %#v", add.Operands) + } + + // Two LDR encodings in one file must merge into distinct syntaxes. + ldr := table["LDR"] + if len(ldr.Syntax) != 2 { + t.Errorf("LDR should have 2 syntaxes, got %#v", ldr.Syntax) + } + + // Alias files must be keyed by the alias mnemonic (UBFIZ), not the base + // (UBFM) from the mnemonic docvar. + ubfiz, ok := table["UBFIZ"] + if !ok { + t.Fatal("UBFIZ alias missing") + } + if ubfiz.Brief != "Unsigned Bitfield Insert in Zero" { + t.Errorf("UBFIZ brief = %q", ubfiz.Brief) + } + if len(ubfiz.Syntax) != 1 || !strings.HasPrefix(ubfiz.Syntax[0], "UBFIZ ") { + t.Errorf("UBFIZ syntax = %#v", ubfiz.Syntax) + } + if _, leaked := table["UBFM"]; leaked { + t.Error("alias content leaked into base UBFM key") + } +} diff --git a/internal/asmref/gen/build.go b/internal/asmref/gen/build.go new file mode 100644 index 0000000..2a58f4d --- /dev/null +++ b/internal/asmref/gen/build.go @@ -0,0 +1,88 @@ +package main + +import ( + "regexp" + "slices" + "strings" + + "loov.dev/lensm/internal/asmref" +) + +// Builder accumulates parsed fragments and merges everything that shares a +// mnemonic into a single Entry. Both the ARM and x86 parsers feed the same +// builder so, e.g., an ADD documented in several files collapses to one key. +type Builder struct { + entries map[string]*asmref.Entry +} + +func NewBuilder() *Builder { + return &Builder{entries: map[string]*asmref.Entry{}} +} + +// Add merges one parsed fragment. Merge rules are first-non-empty for the prose +// fields, dedup-append for syntax (document order preserved), and union for +// operands (first meaning for a name wins). This keeps regeneration +// deterministic given the same input. +func (b *Builder) Add(mnemonic, brief, description string, syntax []string, variants []asmref.Variant, operands map[string]string) { + key := strings.ToUpper(strings.TrimSpace(mnemonic)) + if key == "" { + return + } + e := b.entries[key] + if e == nil { + e = &asmref.Entry{Operands: map[string]string{}} + b.entries[key] = e + } + if e.Brief == "" { + e.Brief = clean(brief) + } + if e.Description == "" { + e.Description = clean(description) + } + for _, s := range syntax { + s = normalizeSpace(s) + if s != "" && !slices.Contains(e.Syntax, s) { + e.Syntax = append(e.Syntax, s) + } + } + for _, v := range variants { + if i := slices.IndexFunc(e.Variants, func(x asmref.Variant) bool { return x.Form == v.Form }); i >= 0 { + e.Variants[i].Perf = append(e.Variants[i].Perf, v.Perf...) + } else { + e.Variants = append(e.Variants, v) + } + } + for name, meaning := range operands { + name = strings.TrimSpace(name) + if name != "" && e.Operands[name] == "" { + e.Operands[name] = clean(meaning) + } + } +} + +// Table returns the merged result. Empty operand maps are dropped so the JSON +// stays clean (omitempty handles nil, not empty maps). +func (b *Builder) Table() map[string]asmref.Entry { + out := make(map[string]asmref.Entry, len(b.entries)) + for k, e := range b.entries { + if len(e.Operands) == 0 { + e.Operands = nil + } + out[k] = *e + } + return out +} + +var tagPattern = regexp.MustCompile(`<[^>]+>`) + +// clean strips any residual HTML/XML tags and collapses whitespace to a single +// space. Operand text like "" reaches clean already decoded, so only real +// markup (from description paragraphs) is removed. +func clean(s string) string { + s = tagPattern.ReplaceAllString(s, "") + return normalizeSpace(s) +} + +func normalizeSpace(s string) string { + return strings.Join(strings.Fields(s), " ") +} diff --git a/internal/asmref/gen/coverage.go b/internal/asmref/gen/coverage.go new file mode 100644 index 0000000..13e8e6c --- /dev/null +++ b/internal/asmref/gen/coverage.go @@ -0,0 +1,57 @@ +package main + +import ( + "sort" + "strings" + + "golang.org/x/arch/arm64/arm64asm" + "golang.org/x/arch/x86/x86asm" +) + +// knownMnemonics enumerates every mnemonic x/arch's decoders can name. There is +// no exported count, so we probe Op values until a long run returns the +// "Op(N)" fallback, which marks the end of the name table. +func knownMnemonics() map[string]bool { + out := map[string]bool{} + add := func(s string) { + if s == "" || strings.HasPrefix(s, "Op(") { + return + } + out[strings.ToUpper(s)] = true + } + miss := 0 + for i := 1; i < 65000 && miss < 1000; i++ { + s := x86asm.Op(i).String() + if s == "" || strings.HasPrefix(s, "Op(") { + miss++ + continue + } + miss = 0 + add(s) + } + miss = 0 + for i := 1; i < 65000 && miss < 1000; i++ { + s := arm64asm.Op(i).String() + if s == "" || strings.HasPrefix(s, "Op(") { + miss++ + continue + } + miss = 0 + add(s) + } + return out +} + +// missingCoverage returns mnemonics x/arch knows that the generated table does +// not cover. It is advisory: a gap usually just means an ISA XML file was not +// present, not that anything is wrong. +func missingCoverage(table map[string]bool) []string { + var missing []string + for name := range knownMnemonics() { + if !table[name] { + missing = append(missing, name) + } + } + sort.Strings(missing) + return missing +} diff --git a/internal/asmref/gen/main.go b/internal/asmref/gen/main.go new file mode 100644 index 0000000..d2adf19 --- /dev/null +++ b/internal/asmref/gen/main.go @@ -0,0 +1,69 @@ +// Command gen flattens CPU ISA XML into internal/asmref/table.json.gz. +// +// It is run via `go generate ./internal/asmref`. By default it reads the small +// checked-in fixtures so a bare run stays reproducible; point -arm and -x86 at +// a real ARM AArch64 ISA XML release and a uops.info instructions.xml to +// produce the full table. See ../README.md for source versions and URLs. +package main + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "flag" + "fmt" + "log" + "os" + "strings" +) + +func main() { + armDir := flag.String("arm", "gen/testdata/arm", "directory of ARM AArch64 ISA XML files") + x86File := flag.String("x86", "gen/testdata/x86/instructions.xml", "uops.info instructions.xml path") + out := flag.String("out", "table.json.gz", "output path (gzip-compressed JSON)") + flag.Parse() + + b := NewBuilder() + if *armDir != "" { + if err := ParseARMDir(b, *armDir); err != nil { + log.Fatalf("arm: %v", err) + } + } + if *x86File != "" { + if err := ParseX86File(b, *x86File); err != nil { + log.Fatalf("x86: %v", err) + } + } + + table := b.Table() + + covered := make(map[string]bool, len(table)) + for k := range table { + covered[k] = true + } + if missing := missingCoverage(covered); len(missing) > 0 { + sample := missing + if len(sample) > 20 { + sample = sample[:20] + } + fmt.Fprintf(os.Stderr, "asmref: %d mnemonic(s) known to x/arch are not in the table (e.g. %s)\n", + len(missing), strings.Join(sample, ", ")) + } + + data, err := json.Marshal(table) + if err != nil { + log.Fatalf("marshal: %v", err) + } + var buf bytes.Buffer + zw, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression) + if _, err := zw.Write(data); err != nil { + log.Fatalf("gzip: %v", err) + } + if err := zw.Close(); err != nil { + log.Fatalf("gzip: %v", err) + } + if err := os.WriteFile(*out, buf.Bytes(), 0o644); err != nil { + log.Fatalf("write %s: %v", *out, err) + } + fmt.Fprintf(os.Stderr, "asmref: wrote %d mnemonics to %s (%d KB gzipped)\n", len(table), *out, buf.Len()/1024) +} diff --git a/internal/asmref/gen/testdata/arm/AArch64-shared.xml b/internal/asmref/gen/testdata/arm/AArch64-shared.xml new file mode 100644 index 0000000..8052c20 --- /dev/null +++ b/internal/asmref/gen/testdata/arm/AArch64-shared.xml @@ -0,0 +1,9 @@ + + + + + + SHOULDNOTAPPEAR + + + diff --git a/internal/asmref/gen/testdata/arm/abs_advsimd.xml b/internal/asmref/gen/testdata/arm/abs_advsimd.xml new file mode 100644 index 0000000..7ed39b3 --- /dev/null +++ b/internal/asmref/gen/testdata/arm/abs_advsimd.xml @@ -0,0 +1,19 @@ + + + + Absolute value (vector) + + Absolute value (vector) computes the absolute value of each vector element and writes the result to the destination vector. + + + + + + 0 + + + ABS <Vd>, <Vn> + + + + diff --git a/internal/asmref/gen/testdata/arm/add_imm.xml b/internal/asmref/gen/testdata/arm/add_imm.xml new file mode 100644 index 0000000..ed6a036 --- /dev/null +++ b/internal/asmref/gen/testdata/arm/add_imm.xml @@ -0,0 +1,35 @@ + + + + + + + Add (immediate) + + Add (immediate) adds a register value and an optionally-shifted immediate value, and writes the result to the destination register. + This second paragraph must be ignored. + + + + + + + + + 1 + 0 + + + + + + ADD <Wd>, <Wn>, #<imm> + + + + + + if d == 31 then SP[] = result; else X[d] = result; + + + diff --git a/internal/asmref/gen/testdata/arm/ldr_imm.xml b/internal/asmref/gen/testdata/arm/ldr_imm.xml new file mode 100644 index 0000000..e6bbbc9 --- /dev/null +++ b/internal/asmref/gen/testdata/arm/ldr_imm.xml @@ -0,0 +1,30 @@ + + + + Load register (immediate) + + Load Register (immediate) loads a word or doubleword from memory and writes it to a register. + + + + + + + + 1 + + + LDR <Wt>, [<Xn|SP>], #<simm> + + + + + + + + + LDR <Wt>, [<Xn|SP>, #<simm>]! + + + + diff --git a/internal/asmref/gen/testdata/arm/sysreg_ctr_el0.xml b/internal/asmref/gen/testdata/arm/sysreg_ctr_el0.xml new file mode 100644 index 0000000..9dff2c3 --- /dev/null +++ b/internal/asmref/gen/testdata/arm/sysreg_ctr_el0.xml @@ -0,0 +1,9 @@ + + + + + + MRS SHOULDNOTAPPEAR + + + diff --git a/internal/asmref/gen/testdata/arm/ubfiz_ubfm.xml b/internal/asmref/gen/testdata/arm/ubfiz_ubfm.xml new file mode 100644 index 0000000..2626410 --- /dev/null +++ b/internal/asmref/gen/testdata/arm/ubfiz_ubfm.xml @@ -0,0 +1,28 @@ + + + + + + + + + Unsigned Bitfield Insert in Zero + + Unsigned Bitfield Insert in Zero copies a bitfield of a given width to the least significant bits, with the destination bits to the left and right set to zero. + + + + + + + + + 0 + + + UBFIZ <Wd>, <Wn>, #<lsb>, #<width> + + + + diff --git a/internal/asmref/gen/testdata/x86/instructions.xml b/internal/asmref/gen/testdata/x86/instructions.xml new file mode 100644 index 0000000..3809b39 --- /dev/null +++ b/internal/asmref/gen/testdata/x86/instructions.xml @@ -0,0 +1,42 @@ + + + + + EAX,ECX,EDX,EBX + EAX,ECX,EDX,EBX + + + + + + + + + + + + + + + + + EAX,ECX,EDX,EBX + + + + + + + + + + EAX,ECX,EDX,EBX + AL,CL,DL,BL + + + + + + + + diff --git a/internal/asmref/gen/x86.go b/internal/asmref/gen/x86.go new file mode 100644 index 0000000..af271c7 --- /dev/null +++ b/internal/asmref/gen/x86.go @@ -0,0 +1,139 @@ +package main + +import ( + "bufio" + "encoding/xml" + "fmt" + "io" + "os" + "strconv" + "strings" + + "loov.dev/lensm/internal/asmref" +) + +// ParseX86File parses a uops.info instructions.xml and feeds every instruction +// into the builder, keyed by its base mnemonic (the asm attribute). +// +// uops.info is a benchmark dataset, not an ISA manual: there are no or +// elements. The usable "API" lives in attributes — summary (a +// human title, the brief) and string (the operand-form). The children +// are not emitted (their derived descriptions just restate the token already in +// the form). +// +// Performance data is captured per operand form across every measured +// microarchitecture: for each the first yields +// uops, ports, throughput and worst-case latency. estimate nodes are +// skipped in favour of the real measurements. The file is ~140MB, so this is a +// streaming token walk. +func ParseX86File(b *Builder, path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + dec := xml.NewDecoder(bufio.NewReaderSize(f, 1<<20)) + var ( + cur *x86Inst + curArch string + took bool // a measurement was already taken for curArch + building bool // inside the measurement we are keeping + m asmref.ArchPerf + ) + for { + tok, err := dec.Token() + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("%s: %w", path, err) + } + switch t := tok.(type) { + case xml.StartElement: + switch t.Name.Local { + case "instruction": + cur = &x86Inst{asm: attr(t, "asm"), summary: attr(t, "summary"), form: attr(t, "string")} + case "architecture": + curArch, took = attr(t, "name"), false + case "IACA": + _ = dec.Skip() // estimate, not a measurement + case "measurement": + if cur == nil || curArch == "" || took { + _ = dec.Skip() + break + } + building = true + m = asmref.ArchPerf{ + Arch: curArch, + Uops: atoi(attr(t, "uops")), + Ports: strings.TrimSpace(attr(t, "ports")), + TP: measuredThroughput(t), + } + case "latency": + if building { + if c := atoi(attr(t, "cycles")); c > m.Latency { + m.Latency = c + } + } + } + case xml.EndElement: + switch t.Name.Local { + case "measurement": + if building { + cur.perf = append(cur.perf, m) + building, took = false, true + } + case "instruction": + if cur != nil { + addX86(b, cur) + cur = nil + } + } + } + } + return nil +} + +type x86Inst struct { + asm string + summary string + form string + perf []asmref.ArchPerf +} + +func addX86(b *Builder, inst *x86Inst) { + mnemonic := strings.ToUpper(strings.TrimSpace(inst.asm)) + if mnemonic == "" { + return + } + form := strings.TrimSpace(inst.form) + var syntax []string + var variants []asmref.Variant + if form != "" { + syntax = []string{form} + if len(inst.perf) > 0 { + variants = []asmref.Variant{{Form: form, Perf: inst.perf}} + } + } + // summary is the only human text; it serves as the brief. + b.Add(mnemonic, inst.summary, "", syntax, variants, nil) +} + +// measuredThroughput prefers the loop-measured throughput, falling back to the +// unrolled measurement. +func measuredThroughput(t xml.StartElement) float64 { + for _, key := range []string{"TP_loop", "TP_unrolled", "TP"} { + if v := attr(t, key); v != "" { + if f, err := strconv.ParseFloat(v, 64); err == nil { + return f + } + } + } + return 0 +} + +func atoi(s string) int { + n, _ := strconv.Atoi(strings.TrimSpace(s)) + return n +} diff --git a/internal/asmref/gen/x86_test.go b/internal/asmref/gen/x86_test.go new file mode 100644 index 0000000..4b1100e --- /dev/null +++ b/internal/asmref/gen/x86_test.go @@ -0,0 +1,89 @@ +package main + +import ( + "strings" + "testing" + + "loov.dev/lensm/internal/asmref" +) + +func TestParseX86File(t *testing.T) { + b := NewBuilder() + if err := ParseX86File(b, "testdata/x86/instructions.xml"); err != nil { + t.Fatal(err) + } + table := b.Table() + + add, ok := table["ADD"] + if !ok { + t.Fatal("ADD missing") + } + if add.Brief != "Add" { + t.Errorf("ADD brief = %q", add.Brief) + } + // Both operand forms merge as syntax and as perf variants. + wantSyntax := []string{"ADD (R32, R32)", "ADD (M32, R32)"} + if strings.Join(add.Syntax, "|") != strings.Join(wantSyntax, "|") { + t.Errorf("ADD syntax = %#v", add.Syntax) + } + if len(add.Variants) != 2 { + t.Fatalf("ADD should have 2 variants, got %d", len(add.Variants)) + } + + // First form carries all measured microarchitectures (IACA nodes ignored). + regReg := findVariant(add, "ADD (R32, R32)") + if regReg == nil { + t.Fatal("ADD (R32, R32) variant missing") + } + if got := archNames(regReg.Perf); strings.Join(got, ",") != "ADL-P,ZEN5" { + t.Errorf("ADD (R32, R32) arches = %v", got) + } + adl := findPerf(regReg.Perf, "ADL-P") + if adl == nil || adl.Uops != 1 || adl.Ports != "1*p0156" || adl.Latency != 1 || adl.TP != 0.25 { + t.Errorf("ADD (R32, R32) ADL-P perf = %#v", adl) + } + + // The memory form has higher latency and load ports. + memReg := findVariant(add, "ADD (M32, R32)") + adlMem := findPerf(memReg.Perf, "ADL-P") + if adlMem == nil || adlMem.Latency != 6 || adlMem.Uops != 2 { + t.Errorf("ADD (M32, R32) ADL-P perf = %#v", adlMem) + } + + crc, ok := table["CRC32"] + if !ok { + t.Fatal("CRC32 missing") + } + if crc.Brief != "Accumulate CRC32 Value" { + t.Errorf("CRC32 brief = %q", crc.Brief) + } + if p := crc.PerfFor("ADL-P"); len(p) != 1 || p[0].Latency != 3 { + t.Errorf("CRC32 ADL-P perf = %#v", p) + } +} + +func findVariant(e asmref.Entry, form string) *asmref.Variant { + for i := range e.Variants { + if e.Variants[i].Form == form { + return &e.Variants[i] + } + } + return nil +} + +func findPerf(perf []asmref.ArchPerf, arch string) *asmref.ArchPerf { + for i := range perf { + if perf[i].Arch == arch { + return &perf[i] + } + } + return nil +} + +func archNames(perf []asmref.ArchPerf) []string { + var out []string + for _, p := range perf { + out = append(out, p.Arch) + } + return out +} diff --git a/internal/asmref/table.json.gz b/internal/asmref/table.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..95e9798baba2b9dab93db419401979c1dc205f77 GIT binary patch literal 650128 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⋙P6#!n!k{=Q-?&I(f5ac2nyRH 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

v7Fe)yXl?eD__a4{*oo9?{so9KzG&T~9 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

-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

_Lcl$0hVPSoDWP&w%bzsV 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 literal 0 HcmV?d00001 diff --git a/internal/atomicfile/atomicfile.go b/internal/atomicfile/atomicfile.go new file mode 100644 index 0000000..b627804 --- /dev/null +++ b/internal/atomicfile/atomicfile.go @@ -0,0 +1,53 @@ +package atomicfile + +import ( + "os" + "path/filepath" +) + +// atomicWriteFile replaces path with data via a temp file in the same +// directory: the temp file is chmodded (CreateTemp creates it 0600), +// fsynced so a crash cannot leave a truncated file behind the +// already-journaled rename, renamed over the target, and the parent +// directory is best-effort synced so the rename itself survives a crash. +func Write(path string, data []byte, perm os.FileMode) error { + dir := filepath.Dir(path) + tmp, err := os.CreateTemp(dir, "."+filepath.Base(path)+"-*.tmp") + if err != nil { + return err + } + tmpName := tmp.Name() + defer os.Remove(tmpName) + + if err := tmp.Chmod(perm); err != nil { + _ = tmp.Close() + return err + } + if _, err := tmp.Write(data); err != nil { + _ = tmp.Close() + return err + } + if err := tmp.Sync(); err != nil { + _ = tmp.Close() + return err + } + if err := tmp.Close(); err != nil { + return err + } + if err := os.Rename(tmpName, path); err != nil { + return err + } + syncDir(dir) + return nil +} + +// syncDir best-effort flushes directory metadata so a rename survives a +// crash. Errors are ignored: not all platforms support syncing directories. +func syncDir(dir string) { + d, err := os.Open(dir) + if err != nil { + return + } + _ = d.Sync() + _ = d.Close() +} diff --git a/internal/codeview/codeview.go b/internal/codeview/codeview.go new file mode 100644 index 0000000..9990991 --- /dev/null +++ b/internal/codeview/codeview.go @@ -0,0 +1,152 @@ +package codeview + +import ( + "image" + "strings" + + "gioui.org/f32" + "gioui.org/io/pointer" + "gioui.org/layout" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" + "gioui.org/widget" + + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" + "loov.dev/lensm/internal/syntax" +) + +type UI struct { + *disasm.Code + + asm gui.ScrollRegion + src gui.ScrollRegion + + hl highlightCache + + mousePosition f32.Point + SelectedAsm int + SelectedView View + SelectedFile string + SelectedLine int + Selection TextSelection + + selecting bool + selectionPointer pointer.ID + selectionStart f32.Point + selectionMoved bool +} + +// highlightCache holds the syntax highlight spans for a Code. Layout +// runs every frame; highlighting is a pure function of the immutable +// code and the palette, so it is rebuilt only when either changes — +// re-highlighting per frame allocates a go/scanner per source line. +type highlightCache struct { + code *disasm.Code + syntax syntax.Palette + + asm [][]syntax.Span + nativeText []string + native [][]syntax.Span + // source is indexed by source file, block, and line within the block. + source [][][][]syntax.Span +} + +func (hl *highlightCache) update(code *disasm.Code, palette syntax.Palette) { + if hl.code == code && hl.syntax == palette { + return + } + hl.code = code + hl.syntax = palette + + hl.asm = make([][]syntax.Span, len(code.Insts)) + hl.nativeText = make([]string, len(code.Insts)) + hl.native = make([][]syntax.Span, len(code.Insts)) + for i := range code.Insts { + ix := &code.Insts[i] + hl.asm[i] = syntax.HighlightAsm(ix.Text, ix.Call, palette) + hl.nativeText[i] = strings.ToUpper(ix.NativeText) + hl.native[i] = syntax.HighlightAsm(hl.nativeText[i], "", palette) + } + + hl.source = make([][][][]syntax.Span, len(code.Source)) + for i, src := range code.Source { + blocks := make([][][]syntax.Span, len(src.Blocks)) + for j, block := range src.Blocks { + lines := make([][]syntax.Span, len(block.Lines)) + for k, line := range block.Lines { + lines[k] = syntax.HighlightSource(block.From+k, line, palette) + } + blocks[j] = lines + } + hl.source[i] = blocks + } +} + +func (ui *UI) Loaded() bool { + return ui.Code != nil +} + +func (ui *UI) ResetScroll() { + ui.asm.Offset = 100000 + ui.src.Offset = 100000 +} + +type Style struct { + *UI + + TryOpen func(gtx layout.Context, funcname string) + CopyText func(gtx layout.Context, text string) + // OnInteract fires on a primary press in the content, used to keep a + // preview tab open once the user acts on it. + OnInteract func() + + // Comments backs inline comment display and editing. Reads go through + // the store directly; SetComment records an edit (buffered write plus + // flush scheduling), and CommentKey/CommentEditor drive the single + // shared inline editor. + Comments *comments.Store + SetComment func(comments.Coord, string) + CommentKey *string + CommentEditor *widget.Editor + Theme *gui.Theme + Syntax syntax.Palette + + ShowNative bool + ShowHelp bool + TextHeight unit.Sp +} + +func (ui Style) Layout(gtx layout.Context) layout.Dimensions { + gtx.Constraints = layout.Exact(gtx.Constraints.Max) + if ui.Code == nil { + return layout.Dimensions{Size: gtx.Constraints.Max} + } + ui.UI.hl.update(ui.Code, ui.Syntax) + + paint.FillShape(gtx.Ops, ui.Theme.Colors.Background, clip.Rect{Max: gtx.Constraints.Max}.Op()) + defer clip.Rect{Max: gtx.Constraints.Max}.Push(gtx.Ops).Pop() + + c := ui.columns(gtx) + mouseClicked := ui.handleInput(gtx, c) + + // draw gutter + paint.FillShape(gtx.Ops, ui.Theme.Colors.Gutter, clip.Rect{ + Min: image.Pt(int(c.gutter.Min), 0), + Max: image.Pt(int(c.gutter.Max), gtx.Constraints.Max.Y), + }.Op()) + if scroll, ok := ui.asm.Anim.Update(gtx); ok { + ui.asm.Offset = scroll + } + + hover := ui.resolveHover(gtx, c, mouseClicked) + highlightRanges := ui.layoutRelations(gtx, c, hover) + ui.layoutAssembly(gtx, c, hover, highlightRanges) + sourceContentHeight := ui.layoutSource(gtx, c, hover, mouseClicked) + ui.layoutScrollbars(gtx, c, sourceContentHeight) + ui.layoutHelp(gtx, c, hover) + + return layout.Dimensions{Size: gtx.Constraints.Max} +} diff --git a/internal/codeview/codeview_test.go b/internal/codeview/codeview_test.go new file mode 100644 index 0000000..ee6f3bd --- /dev/null +++ b/internal/codeview/codeview_test.go @@ -0,0 +1,111 @@ +package codeview + +import ( + "image" + "testing" + "time" + + "gioui.org/f32" + "gioui.org/io/input" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/text" + "gioui.org/unit" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" + "loov.dev/lensm/internal/syntax" +) + +func TestCodeUIStyleLayoutWithHelpAndSelection(t *testing.T) { + base := material.NewTheme() + base.Shaper = text.NewShaper(text.WithCollection(gui.LoadFonts(""))) + base.TextSize = unit.Sp(12) + theme := gui.NewTheme(base, false) + state := &UI{Code: &disasm.Code{ + Name: "main.example", + File: "main.go", + MaxJump: 1, + Insts: []disasm.Inst{ + {Text: "MOV (R2), R1", NativeText: "mov (%r2), %r1"}, + {Text: "ADDQ $1, R1", NativeText: "addq $1, %r1"}, + }, + Source: []disasm.Source{{ + File: "main.go", + Blocks: []disasm.SourceBlock{{ + LineRange: disasm.LineRange{From: 1, To: 2}, + Lines: []string{"func example() {", "}"}, + }}, + }}, + }} + state.mousePosition = f32.Pt(100, 22) + + var router input.Router + var operations op.Ops + newContext := func() layout.Context { + operations.Reset() + return layout.Context{ + Ops: &operations, + Source: router.Source(), + Metric: unit.Metric{PxPerDp: 1, PxPerSp: 1}, + Now: time.Now(), + Constraints: layout.Exact(image.Pt(1200, 600)), + } + } + copied := "" + style := Style{ + UI: state, + Theme: theme, + Syntax: syntax.PaletteFor(syntax.StyleGoLand, theme.Colors.SyntaxColors()), + ShowNative: true, + TextHeight: theme.TextSize, + CopyText: func(_ layout.Context, text string) { + copied = text + }, + } + gtx := newContext() + if got := style.Layout(gtx).Size; got != gtx.Constraints.Max { + t.Fatalf("Layout size = %v, want %v", got, gtx.Constraints.Max) + } + router.Frame(gtx.Ops) + + router.Queue( + pointer.Event{Source: pointer.Mouse, PointerID: 1, Buttons: pointer.ButtonPrimary, Kind: pointer.Press, Position: f32.Pt(100, 20)}, + pointer.Event{Source: pointer.Mouse, PointerID: 1, Buttons: pointer.ButtonPrimary, Kind: pointer.Move, Position: f32.Pt(100, 40)}, + pointer.Event{Source: pointer.Mouse, PointerID: 1, Buttons: pointer.ButtonPrimary, Kind: pointer.Release, Position: f32.Pt(100, 40)}, + ) + gtx = newContext() + style.Layout(gtx) + router.Frame(gtx.Ops) + if from, to, ok := state.Selection.Range(); !ok || from != 0 || to != 1 || state.Selection.View != ViewGoAsm { + t.Fatalf("drag selection = %#v, range %d..%d", state.Selection, from, to) + } + if !gtx.Focused(state) { + t.Fatal("code view did not retain keyboard focus after drag selection") + } + + router.Queue(key.Event{Name: key.Name("C"), Modifiers: key.ModShortcut, State: key.Press}) + gtx = newContext() + style.Layout(gtx) + router.Frame(gtx.Ops) + if want := "MOV (R2), R1\nADDQ $1, R1\n"; copied != want { + t.Fatalf("copied text = %q, want %q", copied, want) + } + + // A fast drag past the bottom edge must clamp to the last line, not + // stop at the last in-range drag sample. + router.Queue( + pointer.Event{Source: pointer.Mouse, PointerID: 2, Buttons: pointer.ButtonPrimary, Kind: pointer.Press, Position: f32.Pt(100, 20)}, + pointer.Event{Source: pointer.Mouse, PointerID: 2, Buttons: pointer.ButtonPrimary, Kind: pointer.Move, Position: f32.Pt(100, 5000)}, + pointer.Event{Source: pointer.Mouse, PointerID: 2, Buttons: pointer.ButtonPrimary, Kind: pointer.Release, Position: f32.Pt(100, 5000)}, + ) + gtx = newContext() + style.Layout(gtx) + router.Frame(gtx.Ops) + if from, to, ok := state.Selection.Range(); !ok || from != 0 || to != len(state.Code.Insts)-1 { + t.Fatalf("overshoot drag selection = %#v, range %d..%d", state.Selection, from, to) + } +} diff --git a/internal/codeview/comments.go b/internal/codeview/comments.go new file mode 100644 index 0000000..b4709b2 --- /dev/null +++ b/internal/codeview/comments.go @@ -0,0 +1,104 @@ +package codeview + +import ( + "image" + "strconv" + + "gioui.org/layout" + "gioui.org/op" + "gioui.org/widget" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" +) + +func (ui Style) asmCoord(view View, inst disasm.Inst) comments.Coord { + name := "" + if ui.Code != nil { + name = ui.Code.Name + } + cview, _ := view.CommentView() + return comments.Coord{Function: name, View: cview, PC: inst.PC} +} + +func (ui Style) sourceCoord(file string, line int) comments.Coord { + name := "" + if ui.Code != nil { + name = ui.Code.Name + } + return comments.Coord{Function: name, View: comments.ViewSource, File: file, Line: line} +} + +// commentEditKey identifies which comment the shared inline editor is +// bound to, so moving to a different line reloads the editor text. +func commentEditKey(coord comments.Coord) string { + if coord.View == comments.ViewSource { + return string(coord.View) + ":" + coord.Function + ":" + coord.File + ":" + strconv.Itoa(coord.Line) + } + return string(coord.View) + ":" + coord.Function + ":" + comments.FormatPC(coord.PC) +} + +// layoutInlineCommentEditor draws the comment for coord, either as an +// editable field (when the line is selected and editing is wired up) or +// read-only. prefix is the leading marker: ";" for assembly, "//" for +// source. +func (ui Style) layoutInlineCommentEditor(gtx layout.Context, coord comments.Coord, prefix string, top, left, width, lineHeight int) { + if ui.CommentEditor == nil || ui.CommentKey == nil || ui.SetComment == nil { + comment := ui.Comments.Get(coord) + if comment == "" { + return + } + gui.SourceLine{ + TopLeft: image.Pt(left, top), + Width: width, + Text: prefix + " " + comment, + TextHeight: ui.TextHeight, + Italic: true, + Color: ui.Theme.Colors.MutedText, + }.Layout(ui.Theme.Theme, gtx) + return + } + + key := commentEditKey(coord) + if key != *ui.CommentKey { + *ui.CommentKey = key + ui.CommentEditor.SetText(ui.Comments.Get(coord)) + } + + changed := false + for { + ev, ok := ui.CommentEditor.Update(gtx) + if !ok { + break + } + switch ev.(type) { + case widget.ChangeEvent, widget.SubmitEvent: + changed = true + } + } + if changed { + ui.SetComment(coord, ui.CommentEditor.Text()) + } + + prefixWidth := lineHeight + gui.SourceLine{ + TopLeft: image.Pt(left, top), + Width: prefixWidth, + Text: prefix, + TextHeight: ui.TextHeight, + Color: ui.Theme.Colors.MutedText, + }.Layout(ui.Theme.Theme, gtx) + + editorLeft := left + prefixWidth + editorWidth := max(width-prefixWidth, 0) + stack := op.Offset(image.Pt(editorLeft, top)).Push(gtx.Ops) + gtx.Constraints = layout.Exact(image.Pt(editorWidth, lineHeight)) + editor := material.Editor(ui.Theme.Theme, ui.CommentEditor, "comment") + editor.TextSize = ui.TextHeight + editor.Font.Typeface = "override-monospace,Go,monospace" + editor.Color = ui.Theme.Colors.Text + editor.Layout(gtx) + stack.Pop() +} diff --git a/internal/codeview/draw.go b/internal/codeview/draw.go new file mode 100644 index 0000000..15cc82e --- /dev/null +++ b/internal/codeview/draw.go @@ -0,0 +1,343 @@ +package codeview + +import ( + "image" + "math" + + "gioui.org/f32" + "gioui.org/io/key" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/f32color" + "loov.dev/lensm/internal/gui" +) + +// layoutRelations draws the curved bands linking source lines to the +// assembly ranges they compiled to, and returns the assembly ranges under +// the pointer so the jump lines can highlight them. +func (ui Style) layoutRelations(gtx layout.Context, c codeColumns, hover codeHover) []disasm.LineRange { + lineHeight := c.lineHeight + gutter, source, asm := c.gutter, c.source, c.asm + mousePosition := hover.position + mouseInAsm, mouseInSource := hover.inAsm, hover.inSource + + var highlightRanges []disasm.LineRange + top := int(ui.src.Offset) + var highlightPaths []clip.PathSpec + relationStroke := ui.Theme.Colors.RelationStroke + relationFill := relationStroke + relationFill.A /= 2 + for i, src := range ui.Code.Source { + if i > 0 { + top += lineHeight + } + top += lineHeight + for i, block := range src.Blocks { + if i > 0 { + top += lineHeight + } + for _, ranges := range block.Related { + if len(ranges) > 0 { + highlight := mouseInSource && float32(top) <= mousePosition.Y && mousePosition.Y < float32(top+lineHeight) + if !highlight && mouseInAsm { + for _, r := range ranges { + if float32(r.From*lineHeight)+ui.asm.Offset <= mousePosition.Y && mousePosition.Y < float32(r.To*lineHeight)+ui.asm.Offset { + highlight = true + break + } + } + } + if highlight { + highlightRanges = ranges + + var p clip.Path + p.Begin(gtx.Ops) + p.MoveTo(f32.Pt(gutter.Max, float32(top+lineHeight))) + p.LineTo(f32.Pt(source.Max, float32(top+lineHeight))) + p.LineTo(f32.Pt(source.Max, float32(top))) + p.LineTo(f32.Pt(gutter.Max, float32(top))) + pin := float32(top) + for i, r := range ranges { + const S = 0.1 + p.CubeTo( + f32.Pt(gutter.Lerp(0.5-S), pin), + f32.Pt(gutter.Lerp(0.5+S), float32(r.From*lineHeight)+ui.asm.Offset), + f32.Pt(gutter.Min, float32(r.From*lineHeight)+ui.asm.Offset)) + p.LineTo(f32.Pt(asm.Min, float32(r.From*lineHeight)+ui.asm.Offset)) + p.LineTo(f32.Pt(asm.Min, float32(r.To*lineHeight)+ui.asm.Offset)) + p.LineTo(f32.Pt(gutter.Min, float32(r.To*lineHeight)+ui.asm.Offset)) + pin = float32(top) + float32(lineHeight)*float32(i+1)/float32(len(ranges)) + p.CubeTo( + f32.Pt(gutter.Lerp(0.5+S), float32(r.To*lineHeight)+ui.asm.Offset), + f32.Pt(gutter.Lerp(0.5-S), pin), + f32.Pt(gutter.Max, pin)) + } + highlightPaths = append(highlightPaths, p.End()) + } + } + top += lineHeight + } + } + } + for _, path := range highlightPaths { + paint.FillShape(gtx.Ops, relationFill, clip.Outline{Path: path}.Op()) + paint.FillShape(gtx.Ops, relationStroke, clip.Stroke{Path: path, Width: 1}.Op()) + } + return highlightRanges +} + +// layoutAssembly draws the Go and native assembly columns: selection +// highlights, instruction text, inline comments, and jump lines. +func (ui Style) layoutAssembly(gtx layout.Context, c codeColumns, hover codeHover, highlightRanges []disasm.LineRange) { + hl := &ui.UI.hl + lineHeight := c.lineHeight + pad, jumpStep := c.pad, c.jumpStep + jump, asm, native, gutter := c.jump, c.asm, c.native, c.gutter + highlightAsmIndex := hover.asmIndex + + asmClip := clip.Rect{ + Min: image.Pt(int(jump.Min), 0), + Max: image.Pt(int(gutter.Min), gtx.Constraints.Max.Y), + }.Push(gtx.Ops) + if ui.ShowNative { + paint.FillShape(gtx.Ops, ui.Theme.Colors.Splitter, clip.Rect{ + Min: image.Pt(int(native.Min)-pad/2, 0), + Max: image.Pt(int(native.Min)-pad/2+1, gtx.Constraints.Max.Y), + }.Op()) + } + for i, ix := range ui.Code.Insts { + if ui.Selection.Contains(ViewGoAsm, i) { + paint.FillShape(gtx.Ops, ui.Theme.Colors.Selection, clip.Rect{ + Min: image.Pt(int(asm.Min), i*lineHeight+int(ui.asm.Offset)), + Max: image.Pt(int(asm.Max), (i+1)*lineHeight+int(ui.asm.Offset)), + }.Op()) + } + if ui.ShowNative && ui.Selection.Contains(ViewNativeAsm, i) { + paint.FillShape(gtx.Ops, ui.Theme.Colors.Selection, clip.Rect{ + Min: image.Pt(int(native.Min), i*lineHeight+int(ui.asm.Offset)), + Max: image.Pt(int(native.Max), (i+1)*lineHeight+int(ui.asm.Offset)), + }.Op()) + } + if ui.SelectedAsm == i { + paint.FillShape(gtx.Ops, ui.Theme.Colors.Selection, clip.Rect{ + Min: image.Pt(int(asm.Min), i*lineHeight+int(ui.asm.Offset)), + Max: image.Pt(int(gutter.Min), (i+1)*lineHeight+int(ui.asm.Offset)), + }.Op()) + } + gui.SourceLine{ + TopLeft: image.Pt(c.goTextLeft, i*lineHeight+int(ui.asm.Offset)), + Width: c.goInstructionWidth, + Text: ix.Text, + Spans: hl.asm[i], + TextHeight: ui.TextHeight, + Italic: ix.Call != "", + Bold: highlightAsmIndex == i || ui.SelectedAsm == i, + Color: ui.Syntax.Plain, + }.Layout(ui.Theme.Theme, gtx) + if c.commentWidth > 0 && ix.Text != "" { + comment := ui.Comments.Get(ui.asmCoord(ViewGoAsm, ix)) + if ui.SelectedAsm == i && ui.SelectedView == ViewGoAsm { + ui.layoutInlineCommentEditor(gtx, ui.asmCoord(ViewGoAsm, ix), ";", i*lineHeight+int(ui.asm.Offset), c.commentLeft, c.commentWidth, lineHeight) + } else if comment != "" { + gui.SourceLine{ + TopLeft: image.Pt(c.commentLeft, i*lineHeight+int(ui.asm.Offset)), + Width: c.commentWidth, + Text: "; " + comment, + TextHeight: ui.TextHeight, + Italic: true, + Color: ui.Theme.Colors.MutedText, + }.Layout(ui.Theme.Theme, gtx) + } + } + if ui.ShowNative { + nativeComment := ui.Comments.Get(ui.asmCoord(ViewNativeAsm, ix)) + width := c.nativeTextWidth + if (nativeComment != "" || (ui.SelectedAsm == i && ui.SelectedView == ViewNativeAsm)) && c.nativeCommentWidth > 0 { + width = c.nativeInstructionWidth + } + gui.SourceLine{ + TopLeft: image.Pt(c.nativeTextLeft, i*lineHeight+int(ui.asm.Offset)), + Width: width, + Text: hl.nativeText[i], + Spans: hl.native[i], + TextHeight: ui.TextHeight, + Bold: highlightAsmIndex == i || ui.SelectedAsm == i, + Color: ui.Syntax.Plain, + }.Layout(ui.Theme.Theme, gtx) + if ui.SelectedAsm == i && ui.SelectedView == ViewNativeAsm && c.nativeCommentWidth > 0 { + ui.layoutInlineCommentEditor(gtx, ui.asmCoord(ViewNativeAsm, ix), ";", i*lineHeight+int(ui.asm.Offset), c.nativeCommentLeft, c.nativeCommentWidth, lineHeight) + } else if nativeComment != "" && c.nativeCommentWidth > 0 { + gui.SourceLine{ + TopLeft: image.Pt(c.nativeCommentLeft, i*lineHeight+int(ui.asm.Offset)), + Width: c.nativeCommentWidth, + Text: "; " + nativeComment, + TextHeight: ui.TextHeight, + Italic: true, + Color: ui.Theme.Colors.MutedText, + }.Layout(ui.Theme.Theme, gtx) + } + } + + // jump line + if ix.RefOffset != 0 { + lineWidth := gtx.Metric.Dp(1) + align := float32(lineWidth%2) / 2 + stack := op.Affine(f32.Affine2D{}.Offset( + f32.Pt(jump.Max+align, float32(i*lineHeight)+align+ui.asm.Offset))).Push(gtx.Ops) + + var path clip.Path + path.Begin(gtx.Ops) + path.MoveTo(f32.Pt(float32(pad/2), float32(lineHeight*2/3))) + path.LineTo(f32.Pt(float32(-jumpStep*ix.RefStack), float32(lineHeight*2/3))) + path.LineTo(f32.Pt(float32(-jumpStep*ix.RefStack), float32(lineHeight/3+ix.RefOffset*lineHeight))) + path.LineTo(f32.Pt(float32(-jumpStep/2), float32(lineHeight/3+ix.RefOffset*lineHeight))) + // draw arrow + path.Line(f32.Pt(0, float32(lineHeight/4))) + path.Line(f32.Pt(float32(lineHeight/3), float32(-lineHeight/4))) + path.Line(f32.Pt(float32(-lineHeight/3), float32(-lineHeight/4))) + path.Line(f32.Pt(0, float32(lineHeight/4))) + + width := float32(lineWidth) + alpha := float32(0.7) + if highlightAsmIndex >= 0 && (highlightAsmIndex == i || highlightAsmIndex == i+ix.RefOffset) { + width *= 3 + alpha = 1 + } else if disasm.LineRangesContain(highlightRanges, i, i+ix.RefOffset) { + width *= 3 + } + jumpColor := f32color.HSLA(float32(math.Mod(float64(ix.PC)*math.Phi, 1)), 0.8, 0.4, alpha) + paint.FillShape(gtx.Ops, jumpColor, clip.Stroke{Path: path.End(), Width: width}.Op()) + + stack.Pop() + } + } + asmClip.Pop() +} + +// layoutSource draws the source column: file headers, source lines, +// selection highlights, and inline comments. It returns the total pixel +// height of the source content for the scrollbar. +func (ui Style) layoutSource(gtx layout.Context, c codeColumns, hover codeHover, mouseClicked bool) int { + hl := &ui.UI.hl + lineHeight := c.lineHeight + source := c.source + mousePosition := hover.position + mouseInSource := hover.inSource + + sourceClip := clip.Rect{ + Min: image.Pt(int(source.Min), 0), + Max: image.Pt(int(source.Max), gtx.Constraints.Max.Y), + }.Push(gtx.Ops) + top := int(ui.src.Offset) + sourceRow := 0 + paintSourceSelection := func(row, rowTop int) { + if ui.Selection.Contains(ViewSource, row) { + paint.FillShape(gtx.Ops, ui.Theme.Colors.Selection, clip.Rect{ + Min: image.Pt(int(source.Min), rowTop), + Max: image.Pt(int(source.Max), rowTop+lineHeight), + }.Op()) + } + } + for i, src := range ui.Code.Source { + if i > 0 { + paintSourceSelection(sourceRow, top) + top += lineHeight + sourceRow++ + } + paintSourceSelection(sourceRow, top) + gui.SourceLine{ + TopLeft: image.Pt(int(source.Min), top), + Text: src.File, + TextHeight: ui.TextHeight, + Bold: hover.asmIndex == i, + Color: ui.Theme.Colors.MutedText, + }.Layout(ui.Theme.Theme, gtx) + top += lineHeight + sourceRow++ + for blockIndex, block := range src.Blocks { + if blockIndex > 0 { + paintSourceSelection(sourceRow, top) + top += lineHeight + sourceRow++ + } + for off := range block.Lines { + paintSourceSelection(sourceRow, top) + highlight := mouseInSource && float32(top) <= mousePosition.Y && mousePosition.Y < float32(top+lineHeight) + lineNo := block.From + off + if highlight && mouseClicked { + ui.SelectedAsm = -1 + ui.SelectedView = ViewSource + ui.SelectedFile = src.File + ui.SelectedLine = lineNo + if ui.CommentEditor != nil { + gtx.Execute(key.FocusCmd{Tag: ui.CommentEditor}) + } + } + sourceComment := ui.Comments.Get(ui.sourceCoord(src.File, lineNo)) + width := c.sourceTextWidth + selectedSource := ui.SelectedView == ViewSource && ui.SelectedFile == src.File && ui.SelectedLine == lineNo + if (sourceComment != "" || selectedSource) && c.sourceCommentWidth > 0 { + width = c.sourceCodeWidth + } + gui.SourceLine{ + TopLeft: image.Pt(int(source.Min), top), + Width: width, + Spans: hl.source[i][blockIndex][off], + TextHeight: ui.TextHeight, + Bold: highlight, + Color: ui.Syntax.Plain, + }.Layout(ui.Theme.Theme, gtx) + if selectedSource && c.sourceCommentWidth > 0 { + ui.layoutInlineCommentEditor(gtx, ui.sourceCoord(src.File, lineNo), "//", top, c.sourceCommentLeft, c.sourceCommentWidth, lineHeight) + } else if sourceComment != "" && c.sourceCommentWidth > 0 { + gui.SourceLine{ + TopLeft: image.Pt(c.sourceCommentLeft, top), + Width: c.sourceCommentWidth, + Text: "// " + sourceComment, + TextHeight: ui.TextHeight, + Italic: true, + Color: ui.Theme.Colors.MutedText, + }.Layout(ui.Theme.Theme, gtx) + } + top += lineHeight + sourceRow++ + } + } + } + sourceClip.Pop() + return top - int(ui.src.Offset) +} + +// layoutScrollbars draws and services the assembly and source scrollbars, +// clamping each column's scroll offset to its content. +func (ui Style) layoutScrollbars(gtx layout.Context, c codeColumns, sourceContentHeight int) { + lineHeight := c.lineHeight + pad := c.pad + jump, gutter, source := c.jump, c.gutter, c.source + + overflow := float32(lineHeight) + + { + stack := clip.Rect{ + Min: image.Pt(int(jump.Min)-pad, 0), + Max: image.Pt(int(gutter.Min), gtx.Constraints.Max.Y), + }.Push(gtx.Ops) + ui.asm.LayoutBar(gtx, ui.Theme.Theme, int(jump.Min)-pad, pad, + -overflow, float32(len(ui.Code.Insts)*lineHeight)+overflow) + stack.Pop() + } + + { + stack := clip.Rect{ + Min: image.Pt(int(source.Min), 0), + Max: image.Pt(int(source.Max)+pad, gtx.Constraints.Max.Y), + }.Push(gtx.Ops) + ui.src.LayoutBar(gtx, ui.Theme.Theme, int(source.Max), pad, + -overflow, float32(sourceContentHeight)+overflow) + stack.Pop() + } +} diff --git a/internal/codeview/geometry.go b/internal/codeview/geometry.go new file mode 100644 index 0000000..8947289 --- /dev/null +++ b/internal/codeview/geometry.go @@ -0,0 +1,124 @@ +package codeview + +import ( + "gioui.org/f32" + "gioui.org/layout" + + "loov.dev/lensm/internal/gui" +) + +// codeColumns is the horizontal geometry of the code view: the pixel +// bounds of each column and the derived text/comment sub-regions. It all +// derives from the viewport width, line height, and whether the native +// assembly column is shown. +type codeColumns struct { + lineHeight int + pad int + jumpStep int + + jump gui.Bounds + asm gui.Bounds + native gui.Bounds + gutter gui.Bounds + source gui.Bounds + + goTextLeft int + goInstructionWidth int + commentLeft int + commentWidth int + + nativeTextLeft int + nativeTextWidth int + nativeCommentLeft int + nativeCommentWidth int + nativeInstructionWidth int + + sourceTextLeft int + sourceTextWidth int + sourceCommentLeft int + sourceCommentWidth int + sourceCodeWidth int +} + +// codeHover is the transient pointer state for one frame: where the mouse +// is and which instruction, if any, it hovers. +type codeHover struct { + position f32.Point + inAsm bool + inSource bool + asmIndex int +} + +func (ui Style) columns(gtx layout.Context) codeColumns { + // The layout has the following sections: + // pad | Jump | pad/2 | Go asm | pad | Native asm | pad | Gutter | pad | Source | pad + lineHeight := gui.CodeLineHeightPx(gtx, ui.TextHeight) + pad := lineHeight + jumpStep := lineHeight / 2 + jumpWidth := jumpStep * ui.Code.MaxJump + gutterWidth := lineHeight * 8 + fixedWidth := gutterWidth + jumpWidth + 4*pad + pad/2 + if ui.ShowNative { + fixedWidth += pad + } + blocksWidth := max(0, gtx.Constraints.Max.X-fixedWidth) + + jump := gui.BoundsWidth(pad, jumpWidth) + asmWidth := blocksWidth * 40 / 100 + if ui.ShowNative { + asmWidth = blocksWidth * 28 / 100 + } + asm := gui.BoundsWidth(int(jump.Max)+pad/2, asmWidth) + native := gui.BoundsWidth(int(asm.Max), 0) + gutter := gui.BoundsWidth(int(asm.Max)+pad, gutterWidth) + sourceWidth := blocksWidth - int(asm.Width()) + if ui.ShowNative { + native = gui.BoundsWidth(int(asm.Max)+pad, blocksWidth*28/100) + gutter = gui.BoundsWidth(int(native.Max)+pad, gutterWidth) + sourceWidth -= int(native.Width()) + } + source := gui.BoundsWidth(int(gutter.Max)+pad, max(0, sourceWidth)) + + c := codeColumns{ + lineHeight: lineHeight, + pad: pad, + jumpStep: jumpStep, + jump: jump, + asm: asm, + native: native, + gutter: gutter, + source: source, + } + minimumCommentWidth := lineHeight * 4 + + c.sourceTextLeft = int(source.Min) + c.sourceTextWidth = max(int(source.Max)-c.sourceTextLeft, 0) + c.sourceCommentLeft = c.sourceTextLeft + c.sourceTextWidth*70/100 + c.sourceCommentWidth = int(source.Max) - c.sourceCommentLeft + c.sourceCodeWidth = c.sourceCommentLeft - c.sourceTextLeft - pad/2 + if c.sourceCodeWidth < 0 || c.sourceCommentWidth < minimumCommentWidth { + c.sourceCodeWidth = c.sourceTextWidth + c.sourceCommentWidth = 0 + } + + c.goTextLeft = int(asm.Min) + pad/2 + goTextWidth := max(int(asm.Max)-c.goTextLeft, 0) + c.nativeTextLeft = int(native.Min) + c.nativeTextWidth = max(int(native.Max)-c.nativeTextLeft, 0) + c.nativeCommentLeft = c.nativeTextLeft + c.nativeTextWidth*62/100 + c.nativeCommentWidth = int(native.Max) - c.nativeCommentLeft + c.nativeInstructionWidth = c.nativeCommentLeft - c.nativeTextLeft - pad/2 + if c.nativeInstructionWidth < 0 || c.nativeCommentWidth < minimumCommentWidth { + c.nativeInstructionWidth = c.nativeTextWidth + c.nativeCommentWidth = 0 + } + c.commentLeft = max(c.goTextLeft+goTextWidth*62/100, c.goTextLeft) + c.commentWidth = max(int(asm.Max)-c.commentLeft, 0) + c.goInstructionWidth = c.commentLeft - c.goTextLeft - pad/2 + if c.goInstructionWidth < 0 || c.commentWidth < minimumCommentWidth { + c.goInstructionWidth = goTextWidth + c.commentWidth = 0 + } + + return c +} diff --git a/internal/codeview/help.go b/internal/codeview/help.go new file mode 100644 index 0000000..35da3e0 --- /dev/null +++ b/internal/codeview/help.go @@ -0,0 +1,113 @@ +package codeview + +import ( + "image" + "strings" + + "gioui.org/f32" + "gioui.org/font" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/asmhelp" + "loov.dev/lensm/internal/gui" +) + +// layoutHelp draws the instruction help tooltip for the hovered assembly +// line, when help is enabled and the user is not selecting or editing. +func (ui Style) layoutHelp(gtx layout.Context, c codeColumns, hover codeHover) { + commentEditing := ui.CommentEditor != nil && gtx.Focused(ui.CommentEditor) + if !ui.ShowHelp || ui.selecting || commentEditing || !gui.InRange(hover.asmIndex, len(ui.Code.Insts)) { + return + } + inst := ui.Code.Insts[hover.asmIndex] + nativeHovered := ui.ShowNative && c.native.Contains(hover.position.X) + var help asmhelp.Help + var ok bool + if nativeHovered { + help, ok = asmhelp.ForNative(ui.Code.Arch, inst.Mnemonic, inst.NativeText) + } else { + help, ok = asmhelp.ForInstruction(ui.Code.Arch, inst.Mnemonic, inst.Text) + } + if ok { + ui.layoutAssemblyHelp(gtx, help, hover.position) + } +} + +func (ui Style) layoutAssemblyHelp(gtx layout.Context, help asmhelp.Help, position f32.Point) { + maxWidth := gtx.Metric.Dp(460) + if maxWidth > gtx.Constraints.Max.X-16 { + maxWidth = max(0, gtx.Constraints.Max.X-16) + } + if maxWidth == 0 { + return + } + + contentContext := gtx + contentContext.Constraints.Min = image.Point{} + contentContext.Constraints.Max = image.Pt(maxWidth, gtx.Metric.Dp(140)) + macro := op.Record(gtx.Ops) + dims := layout.UniformInset(8).Layout(contentContext, func(gtx layout.Context) layout.Dimensions { + children := []layout.FlexChild{ + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := material.Body1(ui.Theme.Theme, help.Mnemonic+" — "+help.Description) + label.Font.Weight = font.Bold + label.Color = ui.Theme.Colors.Text + label.TextSize = ui.TextHeight * 9 / 10 + return label.Layout(gtx) + }), + } + if help.Explanation != "" { + children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := material.Body1(ui.Theme.Theme, help.Explanation) + label.Font.Typeface = "override-monospace,Go,monospace" + label.Color = ui.Syntax.Plain + label.TextSize = ui.TextHeight * 9 / 10 + return layout.Inset{Top: 5}.Layout(gtx, label.Layout) + })) + } + if len(help.Ports) > 0 { + children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := material.Body1(ui.Theme.Theme, "ports: "+strings.Join(help.Ports, ", ")) + label.Font.Typeface = "override-monospace,Go,monospace" + label.Color = ui.Syntax.Comment + label.TextSize = ui.TextHeight * 8 / 10 + return layout.Inset{Top: 5}.Layout(gtx, label.Layout) + })) + } + if help.Note != "" { + children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions { + label := material.Body1(ui.Theme.Theme, help.Note) + label.Font.Style = font.Italic + label.Color = ui.Syntax.Comment + label.TextSize = ui.TextHeight * 8 / 10 + return layout.Inset{Top: 5}.Layout(gtx, label.Layout) + })) + } + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, children...) + }) + call := macro.Stop() + + left := int(position.X) + gtx.Metric.Dp(12) + top := int(position.Y) + gtx.Metric.Dp(18) + if left+dims.Size.X > gtx.Constraints.Max.X-4 { + left = gtx.Constraints.Max.X - dims.Size.X - 4 + } + if top+dims.Size.Y > gtx.Constraints.Max.Y-4 { + top = int(position.Y) - dims.Size.Y - gtx.Metric.Dp(8) + } + left = max(4, left) + top = max(4, top) + + stack := op.Offset(image.Pt(left, top)).Push(gtx.Ops) + paint.FillShape(gtx.Ops, ui.Theme.Colors.SecondaryBackground, clip.UniformRRect(image.Rectangle{Max: dims.Size}, 5).Op(gtx.Ops)) + paint.FillShape(gtx.Ops, ui.Theme.Colors.Splitter, clip.Stroke{ + Path: clip.UniformRRect(image.Rectangle{Max: dims.Size}, 5).Path(gtx.Ops), + Width: 1, + }.Op()) + call.Add(gtx.Ops) + stack.Pop() +} diff --git a/internal/codeview/input.go b/internal/codeview/input.go new file mode 100644 index 0000000..d3dc358 --- /dev/null +++ b/internal/codeview/input.go @@ -0,0 +1,296 @@ +package codeview + +import ( + "image" + "math" + "strings" + "time" + + "gioui.org/f32" + "gioui.org/font" + "gioui.org/io/event" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/widget" + + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/gui" +) + +// handleInput processes pointer and keyboard events for the frame, +// updating selection and scroll state, and reports whether the release +// was a click (as opposed to a drag). +func (ui Style) handleInput(gtx layout.Context, c codeColumns) (mouseClicked bool) { + lineHeight := c.lineHeight + event.Op(gtx.Ops, ui.UI) + selectionAt := func(position f32.Point) (View, int, bool) { + if c.asm.Contains(position.X) { + relative := position.Y - ui.asm.Offset + if relative < 0 { + return ViewNone, -1, false + } + line := int(relative) / lineHeight + return ViewGoAsm, line, gui.InRange(line, len(ui.Code.Insts)) + } + if ui.ShowNative && c.native.Contains(position.X) { + relative := position.Y - ui.asm.Offset + if relative < 0 { + return ViewNone, -1, false + } + line := int(relative) / lineHeight + return ViewNativeAsm, line, gui.InRange(line, len(ui.Code.Insts)) + } + if c.source.Contains(position.X) { + line := sourceRowAtY(ui.Code, ui.src.Offset, lineHeight, position.Y) + return ViewSource, line, line >= 0 + } + return ViewNone, -1, false + } + // selectionDragLine clamps a drag position to the selection view's + // content, so a fast drag past an edge selects through to the first + // or last line instead of stopping at the last in-range sample. + selectionDragLine := func(view View, position f32.Point) (int, bool) { + var line, count int + switch view { + case ViewGoAsm, ViewNativeAsm: + count = len(ui.Code.Insts) + line = int(position.Y-ui.asm.Offset) / lineHeight + case ViewSource: + count = sourceRowCount(ui.Code) + line = int(position.Y-ui.src.Offset) / lineHeight + default: + return -1, false + } + if count == 0 { + return -1, false + } + return min(max(line, 0), count-1), true + } + for { + ev, ok := gtx.Event(pointer.Filter{ + Target: ui.UI, + Kinds: pointer.Move | pointer.Leave | pointer.Press | pointer.Drag | pointer.Release | pointer.Cancel | pointer.Scroll, + ScrollY: pointer.ScrollRange{ + Min: int(ui.asm.Offset) - lineHeight, + Max: len(ui.Code.Insts)*lineHeight + lineHeight - int(ui.asm.Offset), + }, + }) + if !ok { + break + } + if ev, ok := ev.(pointer.Event); ok { + switch ev.Kind { + case pointer.Move: + ui.mousePosition = ev.Position + case pointer.Leave: + if !ui.selecting { + ui.mousePosition = f32.Pt(-1, -1) + } + case pointer.Press: + ui.mousePosition = ev.Position + if ev.Buttons.Contain(pointer.ButtonPrimary) { + if ui.OnInteract != nil { + ui.OnInteract() + } + view, line, selectable := selectionAt(ev.Position) + if selectable { + ui.Selection.Begin(view, line, ev.Modifiers.Contain(key.ModShift)) + ui.selecting = true + ui.selectionPointer = ev.PointerID + ui.selectionStart = ev.Position + ui.selectionMoved = false + gtx.Execute(pointer.GrabCmd{Tag: ui.UI, ID: ev.PointerID}) + gtx.Execute(key.FocusCmd{Tag: ui.UI}) + } else { + ui.Selection.Clear() + } + } + case pointer.Drag: + ui.mousePosition = ev.Position + if ui.selecting && ev.PointerID == ui.selectionPointer { + if math.Abs(float64(ev.Position.X-ui.selectionStart.X)) > 3 || math.Abs(float64(ev.Position.Y-ui.selectionStart.Y)) > 3 { + ui.selectionMoved = true + } + if line, ok := selectionDragLine(ui.Selection.View, ev.Position); ok { + ui.Selection.Extend(ui.Selection.View, line) + } + } + case pointer.Release: + ui.mousePosition = ev.Position + if ui.selecting && ev.PointerID == ui.selectionPointer { + if line, ok := selectionDragLine(ui.Selection.View, ev.Position); ok { + ui.Selection.Extend(ui.Selection.View, line) + } + mouseClicked = !ui.selectionMoved + ui.selecting = false + } + case pointer.Cancel: + ui.selecting = false + case pointer.Scroll: + ui.mousePosition = ev.Position + switch { + case c.asm.Contains(ev.Position.X): + ui.asm.Offset -= ev.Scroll.Y + case ui.ShowNative && c.native.Contains(ev.Position.X): + ui.asm.Offset -= ev.Scroll.Y + case c.source.Contains(ev.Position.X): + ui.src.Offset -= ev.Scroll.Y + } + } + } + } + selectionFocus := event.Tag(ui.UI) + commentFocused := ui.CommentEditor != nil && gtx.Focused(ui.CommentEditor) + if ui.Selection.Active && !commentFocused { + // A drag can leave keyboard focus on a surrounding widget on macOS. + // An active line selection still owns Cmd/Ctrl+C, Cmd/Ctrl+A, and + // Escape unless the user is editing comment text. (A focused text + // editor elsewhere still wins: it polls earlier in layout order.) + selectionFocus = nil + } + for { + ev, ok := gtx.Event(key.Filter{Focus: selectionFocus, Required: key.ModShortcut, Name: key.Name("C")}) + if !ok { + break + } + keyEvent, ok := ev.(key.Event) + if ok && keyEvent.State == key.Press { + if text := ui.Selection.Text(ui.Code); text != "" && ui.CopyText != nil { + ui.CopyText(gtx, text) + } + } + } + for { + ev, ok := gtx.Event( + key.FocusFilter{Target: ui.UI}, + key.Filter{Focus: selectionFocus, Required: key.ModShortcut, Name: key.Name("A")}, + key.Filter{Focus: selectionFocus, Name: key.NameEscape}, + ) + if !ok { + break + } + keyEvent, ok := ev.(key.Event) + if !ok || keyEvent.State != key.Press { + continue + } + switch keyEvent.Name { + case key.Name("A"): + view := ui.Selection.View + if view == ViewNone { + view = ViewGoAsm + } + lineCount := len(ui.Code.Insts) + if view == ViewSource { + lineCount = sourceRowCount(ui.Code) + } + if lineCount > 0 { + ui.Selection = TextSelection{View: view, Anchor: 0, Head: lineCount - 1, Active: true} + } + case key.NameEscape: + ui.Selection.Clear() + } + } + return mouseClicked +} + +// resolveHover computes the pointer hover state and handles clicks on the +// assembly column: following call targets, selecting a line for comment +// editing, and activating jump animations. +func (ui Style) resolveHover(gtx layout.Context, c codeColumns, mouseClicked bool) codeHover { + lineHeight := c.lineHeight + mousePosition := ui.mousePosition + mouseInAsm := c.asm.Contains(mousePosition.X) || (ui.ShowNative && c.native.Contains(mousePosition.X)) + mouseInSource := c.source.Contains(mousePosition.X) + if mouseInAsm || mouseInSource { + pointer.CursorText.Add(gtx.Ops) + } + highlightAsmIndex := -1 + if relative := mousePosition.Y - ui.asm.Offset; mouseInAsm && relative >= 0 { + highlightAsmIndex = int(relative) / lineHeight + } + + if gui.InRange(highlightAsmIndex, len(ui.Code.Insts)) { + activateClicked := mouseClicked && ui.SelectedAsm == highlightAsmIndex + ix := &ui.Code.Insts[highlightAsmIndex] + callTargetHovered := ui.TryOpen != nil && + ix.Call != "" && + c.asm.Contains(mousePosition.X) && + mousePosition.X <= float32(c.goTextLeft+c.goInstructionWidth) && + ui.callTargetHit(gtx, *ix, c.goTextLeft, mousePosition.X) + if callTargetHovered { + pointer.CursorPointer.Add(gtx.Ops) + if mouseClicked { + ui.SelectedAsm = highlightAsmIndex + ui.SelectedView = ViewGoAsm + ui.SelectedFile = "" + ui.SelectedLine = 0 + ui.TryOpen(gtx, ix.Call) + } + } else if mouseClicked && ix.Text != "" { + // Spacer rows (empty synthetic instructions) have no inline + // editor; focusing it would swallow subsequent typing. + ui.SelectedAsm = highlightAsmIndex + ui.SelectedFile = "" + ui.SelectedLine = 0 + if ui.ShowNative && c.native.Contains(mousePosition.X) { + ui.SelectedView = ViewNativeAsm + } else { + ui.SelectedView = ViewGoAsm + } + if ui.CommentEditor != nil { + gtx.Execute(key.FocusCmd{Tag: ui.CommentEditor}) + } + } + if ix.Call == "" && ix.RefOffset != 0 { + pointer.CursorPointer.Add(gtx.Ops) + if activateClicked { + // TODO: smooth scroll + // highlightAsmIndex -= ix.RefOffset + ui.asm.Anim.Start(gtx, ui.asm.Offset, ui.asm.Offset-float32(ix.RefOffset*lineHeight), 150*time.Millisecond) + } + } + } + if !gui.InRange(ui.SelectedAsm, len(ui.Code.Insts)) { + ui.SelectedAsm = -1 + } + + return codeHover{ + position: mousePosition, + inAsm: mouseInAsm, + inSource: mouseInSource, + asmIndex: highlightAsmIndex, + } +} + +func (ui Style) callTargetHit(gtx layout.Context, inst disasm.Inst, left int, x float32) bool { + if inst.Call == "" { + return false + } + start := strings.Index(inst.Text, inst.Call) + if start < 0 { + return false + } + // A hovered call line is drawn bold and italic; measure with the + // same style, otherwise the hitbox drifts from the visible text + // whenever the fallback font is proportional. + f := font.Font{Typeface: "override-monospace,Go,monospace", Weight: font.Black, Style: font.Italic} + end := start + len(inst.Call) + targetLeft := left + ui.measureAsmTextWidth(gtx, f, inst.Text[:start]) + targetRight := left + ui.measureAsmTextWidth(gtx, f, inst.Text[:end]) + return float32(targetLeft) <= x && x <= float32(targetRight) +} + +func (ui Style) measureAsmTextWidth(gtx layout.Context, f font.Font, text string) int { + if text == "" { + return 0 + } + gtx.Constraints.Min = image.Point{} + gtx.Constraints.Max = image.Pt(gui.MaxLineWidth, gui.MaxLineWidth) + + macro := op.Record(gtx.Ops) + dims := widget.Label{MaxLines: 1}.Layout(gtx, ui.Theme.Shaper, f, ui.TextHeight, text, op.CallOp{}) + _ = macro.Stop() + return dims.Size.X +} diff --git a/internal/codeview/selection.go b/internal/codeview/selection.go new file mode 100644 index 0000000..ccd0402 --- /dev/null +++ b/internal/codeview/selection.go @@ -0,0 +1,188 @@ +package codeview + +import ( + "strings" + + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/disasm" +) + +type View uint8 + +const ( + ViewNone View = iota + ViewGoAsm + ViewNativeAsm + ViewSource +) + +// CommentView maps a pane to its comment-store view. ok is false for +// CodeViewNone, which has no comments. +func (v View) CommentView() (view comments.View, ok bool) { + switch v { + case ViewGoAsm: + return comments.ViewGoAsm, true + case ViewNativeAsm: + return comments.ViewNativeAsm, true + case ViewSource: + return comments.ViewSource, true + default: + return "", false + } +} + +type TextSelection struct { + View View + Anchor int + Head int + Active bool +} + +func (s *TextSelection) Clear() { + *s = TextSelection{} +} + +func (s *TextSelection) Begin(view View, line int, extend bool) { + if view == ViewNone || line < 0 { + s.Clear() + return + } + if !extend || !s.Active || s.View != view { + s.View = view + s.Anchor = line + } + s.Head = line + s.Active = true +} + +func (s *TextSelection) Extend(view View, line int) { + if !s.Active || s.View != view || line < 0 { + return + } + s.Head = line +} + +func (s TextSelection) Range() (from, to int, ok bool) { + if !s.Active { + return 0, 0, false + } + from, to = s.Anchor, s.Head + if from > to { + from, to = to, from + } + return from, to, true +} + +func (s TextSelection) Contains(view View, line int) bool { + if s.View != view { + return false + } + from, to, ok := s.Range() + return ok && from <= line && line <= to +} + +type sourceTextRow struct { + Text string +} + +func sourceTextRows(code *disasm.Code) []sourceTextRow { + if code == nil { + return nil + } + var rows []sourceTextRow + for sourceIndex, source := range code.Source { + if sourceIndex > 0 { + rows = append(rows, sourceTextRow{}) + } + rows = append(rows, sourceTextRow{Text: "// " + source.File}) + for blockIndex, block := range source.Blocks { + if blockIndex > 0 { + rows = append(rows, sourceTextRow{}) + } + for _, line := range block.Lines { + rows = append(rows, sourceTextRow{Text: line}) + } + } + } + return rows +} + +// sourceRowCount mirrors the rows produced by sourceTextRows without +// building them; it runs on every pointer event. +func sourceRowCount(code *disasm.Code) int { + if code == nil { + return 0 + } + count := 0 + for sourceIndex, source := range code.Source { + if sourceIndex > 0 { + count++ + } + count++ + for blockIndex, block := range source.Blocks { + if blockIndex > 0 { + count++ + } + count += len(block.Lines) + } + } + return count +} + +func sourceRowAtY(code *disasm.Code, scroll float32, lineHeight int, y float32) int { + if code == nil || lineHeight <= 0 { + return -1 + } + relative := y - scroll + if relative < 0 { + return -1 + } + row := int(relative / float32(lineHeight)) + if row < 0 || row >= sourceRowCount(code) { + return -1 + } + return row +} + +func (s TextSelection) Text(code *disasm.Code) string { + if code == nil { + return "" + } + from, to, ok := s.Range() + if !ok { + return "" + } + + var lines []string + switch s.View { + case ViewGoAsm, ViewNativeAsm: + if from < 0 { + from = 0 + } + if to >= len(code.Insts) { + to = len(code.Insts) - 1 + } + for i := from; i <= to; i++ { + text := code.Insts[i].Text + if s.View == ViewNativeAsm { + text = strings.ToUpper(code.Insts[i].NativeText) + } + lines = append(lines, text) + } + case ViewSource: + rows := sourceTextRows(code) + if from < 0 { + from = 0 + } + if to >= len(rows) { + to = len(rows) - 1 + } + for i := from; i <= to; i++ { + lines = append(lines, rows[i].Text) + } + } + if len(lines) == 0 { + return "" + } + return strings.Join(lines, "\n") + "\n" +} diff --git a/internal/codeview/selection_test.go b/internal/codeview/selection_test.go new file mode 100644 index 0000000..a612845 --- /dev/null +++ b/internal/codeview/selection_test.go @@ -0,0 +1,44 @@ +package codeview + +import ( + "testing" + + "loov.dev/lensm/internal/disasm" +) + +func TestTextSelectionAssembly(t *testing.T) { + code := &disasm.Code{Insts: []disasm.Inst{ + {Text: "MOV (R2), R1", NativeText: "mov (%r2), %r1"}, + {Text: "ADDQ $1, R1", NativeText: "addq $1, %r1"}, + {Text: "RET", NativeText: "ret"}, + }} + selection := TextSelection{View: ViewGoAsm, Anchor: 1, Head: 0, Active: true} + if got, want := selection.Text(code), "MOV (R2), R1\nADDQ $1, R1\n"; got != want { + t.Fatalf("Text() = %q, want %q", got, want) + } + selection.View = ViewNativeAsm + if got, want := selection.Text(code), "MOV (%R2), %R1\nADDQ $1, %R1\n"; got != want { + t.Fatalf("native Text() = %q, want %q", got, want) + } +} + +func TestTextSelectionSource(t *testing.T) { + code := &disasm.Code{Source: []disasm.Source{{ + File: "main.go", + Blocks: []disasm.SourceBlock{{Lines: []string{"func main() {", "}"}}}, + }}} + selection := TextSelection{View: ViewSource, Anchor: 0, Head: 2, Active: true} + if got, want := selection.Text(code), "// main.go\nfunc main() {\n}\n"; got != want { + t.Fatalf("Text() = %q, want %q", got, want) + } +} + +func TestSourceRowAtYRejectsSpaceAboveContent(t *testing.T) { + code := &disasm.Code{Source: []disasm.Source{{File: "main.go"}}} + if got := sourceRowAtY(code, 10, 20, 9); got != -1 { + t.Fatalf("sourceRowAtY() = %d, want -1", got) + } + if got := sourceRowAtY(code, 10, 20, 10); got != 0 { + t.Fatalf("sourceRowAtY() = %d, want 0", got) + } +} diff --git a/internal/comments/comments.go b/internal/comments/comments.go new file mode 100644 index 0000000..8467b61 --- /dev/null +++ b/internal/comments/comments.go @@ -0,0 +1,477 @@ +package comments + +import ( + "cmp" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "slices" + "strings" + "sync" + "time" + + "loov.dev/lensm/internal/atomicfile" +) + +const commentsFileVersion = 1 + +type View string + +const ( + ViewSource View = "source" + ViewGoAsm View = "go_asm" + ViewNativeAsm View = "native_asm" +) + +type Coord struct { + Binary string `json:"binary,omitempty"` + Function string `json:"function"` + View View `json:"view"` + File string `json:"file,omitempty"` + Line int `json:"line,omitempty"` + PC uint64 `json:"pc,omitempty"` + PCHex string `json:"pc_hex,omitempty"` +} + +type Record struct { + Coord + Text string `json:"text"` + UpdatedAt time.Time `json:"updated_at"` +} + +type commentsDiskFile struct { + Version int `json:"version"` + Binary string `json:"binary,omitempty"` + Comments []json.RawMessage `json:"comments"` +} + +type Store struct { + mu sync.RWMutex + path string + binary string + records map[string]Record + // touched marks keys mutated since the last sync with the file; + // saves merge the on-disk state for all other keys, so another lensm + // process writing the same sidecar loses only conflicting edits to + // the same record, not its whole set of comments. + touched map[string]bool + // dirty marks in-memory changes not yet written by Flush. + dirty bool + // preserved holds records this version doesn't understand (e.g. views + // added by a newer lensm), kept verbatim so saving doesn't destroy them. + preserved []json.RawMessage +} + +func DefaultPath(binaryPath string) string { + if binaryPath == "" { + return "" + } + return binaryPath + ".lensm-comments.json" +} + +func Open(path, binaryPath string) (*Store, error) { + binaryPath = CleanPath(binaryPath) + store := &Store{ + path: path, + binary: binaryPath, + records: map[string]Record{}, + touched: map[string]bool{}, + } + if path == "" { + return store, nil + } + if err := store.load(); err != nil { + return nil, err + } + return store, nil +} + +func (store *Store) Path() string { + if store == nil { + return "" + } + return store.path +} + +func (store *Store) Get(coord Coord) string { + if store == nil { + return "" + } + store.mu.RLock() + defer store.mu.RUnlock() + rec, ok := store.records[store.key(coord)] + if !ok { + return "" + } + return rec.Text +} + +func (store *Store) Set(coord Coord, text string) error { + return store.set(coord, text, true) +} + +// SetBuffered updates the comment in memory and defers the disk write +// until Flush, so typing in an editor doesn't rewrite the sidecar on +// every keystroke. +func (store *Store) SetBuffered(coord Coord, text string) error { + return store.set(coord, text, false) +} + +func (store *Store) set(coord Coord, text string, persist bool) error { + if store == nil { + return errors.New("comment store is not initialized") + } + text = strings.TrimSpace(text) + if err := coord.validate(); err != nil { + return err + } + + store.mu.Lock() + defer store.mu.Unlock() + key := store.key(coord) + existing, exists := store.records[key] + if text == "" { + // Even when the record isn't in memory it may exist on disk, + // written by another process sharing the sidecar; fall through so + // the deletion is recorded and the next save drops it instead of + // re-adopting it. + delete(store.records, key) + } else { + coord = store.Normalize(coord) + if exists && existing.Text == text { + return nil + } + store.records[key] = Record{ + Coord: coord, + Text: text, + UpdatedAt: time.Now().UTC(), + } + } + wasTouched := store.touched[key] + store.touched[key] = true + wasDirty := store.dirty + store.dirty = true + if !persist { + return nil + } + if err := store.saveLocked(); err != nil { + // Roll back so the in-memory store keeps matching the file. + if exists { + store.records[key] = existing + } else { + delete(store.records, key) + } + if !wasTouched { + delete(store.touched, key) + } + store.dirty = wasDirty + return err + } + store.dirty = false + return nil +} + +// Flush writes buffered changes to disk. +func (store *Store) Flush() error { + if store == nil { + return nil + } + store.mu.Lock() + defer store.mu.Unlock() + if !store.dirty { + return nil + } + if err := store.saveLocked(); err != nil { + return err + } + store.dirty = false + return nil +} + +func (store *Store) All() []Record { + if store == nil { + return nil + } + store.mu.RLock() + defer store.mu.RUnlock() + out := make([]Record, 0, len(store.records)) + for _, rec := range store.records { + out = append(out, rec) + } + sortCommentRecords(out) + return out +} + +func (store *Store) Filter(function string, view View) []Record { + if store == nil { + return nil + } + store.mu.RLock() + defer store.mu.RUnlock() + var out []Record + for _, rec := range store.records { + if function != "" && rec.Function != function { + continue + } + if view != "" && rec.View != view { + continue + } + out = append(out, rec) + } + sortCommentRecords(out) + return out +} + +func (store *Store) ForSource(function, file string, line int) string { + return store.Get(Coord{ + Function: function, + View: ViewSource, + File: file, + Line: line, + }) +} + +func (store *Store) ForAsm(function string, view View, pc uint64) string { + return store.Get(Coord{ + Function: function, + View: view, + PC: pc, + }) +} + +func (store *Store) SetSource(function, file string, line int, text string) error { + return store.Set(Coord{ + Function: function, + View: ViewSource, + File: file, + Line: line, + }, text) +} + +func (store *Store) SetAsm(function string, view View, pc uint64, text string) error { + return store.Set(Coord{ + Function: function, + View: view, + PC: pc, + }, text) +} + +func (store *Store) load() error { + data, err := os.ReadFile(store.path) + if errors.Is(err, os.ErrNotExist) { + return nil + } + if err != nil { + return err + } + + var disk commentsDiskFile + if err := json.Unmarshal(data, &disk); err != nil { + return fmt.Errorf("load comments: %w", err) + } + if disk.Version != 0 && disk.Version != commentsFileVersion { + return fmt.Errorf("unsupported comments file version %d", disk.Version) + } + for _, raw := range disk.Comments { + var rec Record + if err := json.Unmarshal(raw, &rec); err != nil { + store.preserved = append(store.preserved, raw) + continue + } + if rec.Binary == "" { + rec.Binary = cmp.Or(disk.Binary, store.binary) + } + rec.PCHex = commentPCHex(rec.Coord) + if err := rec.Coord.validate(); err != nil { + store.preserved = append(store.preserved, raw) + continue + } + if rec.Text == "" { + continue + } + store.records[store.key(rec.Coord)] = rec + } + return nil +} + +func (store *Store) saveLocked() error { + if store.path == "" { + return nil + } + store.mergeExternalLocked() + records := make([]Record, 0, len(store.records)) + for _, rec := range store.records { + records = append(records, rec) + } + sortCommentRecords(records) + + // Don't create a file just to record that nothing exists, e.g. after + // deleting a comment that was never saved. + if len(records) == 0 && len(store.preserved) == 0 { + if _, err := os.Stat(store.path); errors.Is(err, os.ErrNotExist) { + clear(store.touched) + return nil + } + } + + comments := make([]json.RawMessage, 0, len(records)+len(store.preserved)) + for _, rec := range records { + raw, err := json.Marshal(rec) + if err != nil { + return err + } + comments = append(comments, raw) + } + comments = append(comments, store.preserved...) + + data, err := json.MarshalIndent(commentsDiskFile{ + Version: commentsFileVersion, + Binary: store.binary, + Comments: comments, + }, "", " ") + if err != nil { + return err + } + data = append(data, '\n') + + if err := os.MkdirAll(filepath.Dir(store.path), 0o755); err != nil { + return err + } + if err := atomicfile.Write(store.path, data, 0o644); err != nil { + return err + } + // The in-memory state now matches the file again. + clear(store.touched) + return nil +} + +// mergeExternalLocked folds records another lensm process wrote to the +// sidecar into the in-memory store before a full-file rewrite. Keys +// mutated locally since the last sync win; everything else — additions, +// edits, and deletions from the other process — is adopted, so two +// processes sharing a sidecar lose at most conflicting edits to the +// same record instead of each other's whole comment sets. +func (store *Store) mergeExternalLocked() { + data, err := os.ReadFile(store.path) + if err != nil { + return // nothing on disk to merge + } + var disk commentsDiskFile + if err := json.Unmarshal(data, &disk); err != nil { + return + } + if disk.Version != 0 && disk.Version != commentsFileVersion { + return + } + + merged := make(map[string]Record, len(store.records)) + for key, rec := range store.records { + if store.touched[key] { + merged[key] = rec + } + } + store.preserved = nil + for _, raw := range disk.Comments { + var rec Record + if err := json.Unmarshal(raw, &rec); err != nil { + store.preserved = append(store.preserved, raw) + continue + } + if rec.Binary == "" { + rec.Binary = cmp.Or(disk.Binary, store.binary) + } + rec.PCHex = commentPCHex(rec.Coord) + if err := rec.Coord.validate(); err != nil { + store.preserved = append(store.preserved, raw) + continue + } + if rec.Text == "" { + continue + } + key := store.key(rec.Coord) + if store.touched[key] { + continue + } + merged[key] = rec + } + store.records = merged +} + +func (store *Store) Normalize(coord Coord) Coord { + coord.Binary = cmp.Or(coord.Binary, store.binary) + coord.Binary = CleanPath(coord.Binary) + coord.PCHex = commentPCHex(coord) + return coord +} + +func (store *Store) key(coord Coord) string { + coord = store.Normalize(coord) + switch coord.View { + case ViewSource: + return fmt.Sprintf("%s\x00%s\x00%s\x00%d", coord.Function, coord.View, CleanPath(coord.File), coord.Line) + case ViewGoAsm, ViewNativeAsm: + return fmt.Sprintf("%s\x00%s\x00%x", coord.Function, coord.View, coord.PC) + default: + return fmt.Sprintf("%s\x00%s\x00%s\x00%d\x00%x", coord.Function, coord.View, CleanPath(coord.File), coord.Line, coord.PC) + } +} + +func (coord Coord) validate() error { + if coord.Function == "" { + return errors.New("function is required") + } + switch coord.View { + case ViewSource: + if coord.File == "" { + return errors.New("file is required for source comments") + } + if coord.Line <= 0 { + return errors.New("line must be positive for source comments") + } + case ViewGoAsm, ViewNativeAsm: + default: + return fmt.Errorf("unsupported comment view %q", coord.View) + } + return nil +} + +func sortCommentRecords(records []Record) { + slices.SortFunc(records, func(a, b Record) int { + if c := cmp.Compare(a.Function, b.Function); c != 0 { + return c + } + if c := cmp.Compare(a.View, b.View); c != 0 { + return c + } + if c := cmp.Compare(a.File, b.File); c != 0 { + return c + } + if c := cmp.Compare(a.Line, b.Line); c != 0 { + return c + } + return cmp.Compare(a.PC, b.PC) + }) +} + +func CleanPath(path string) string { + if path == "" { + return "" + } + if abs, err := filepath.Abs(path); err == nil { + return filepath.Clean(abs) + } + return filepath.Clean(path) +} + +func FormatPC(pc uint64) string { + return fmt.Sprintf("0x%x", pc) +} + +func commentPCHex(coord Coord) string { + if coord.View == ViewGoAsm || coord.View == ViewNativeAsm || coord.PC != 0 { + return FormatPC(coord.PC) + } + return "" +} diff --git a/internal/comments/comments_test.go b/internal/comments/comments_test.go new file mode 100644 index 0000000..fb00aa0 --- /dev/null +++ b/internal/comments/comments_test.go @@ -0,0 +1,246 @@ +package comments + +import ( + "bytes" + "os" + "path/filepath" + "testing" + "time" +) + +func TestCommentStoreRoundTrip(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + + if err := store.SetSource("main.add", "/tmp/main.go", 12, "source note"); err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "go asm note"); err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewNativeAsm, 0x1000, "native asm note"); err != nil { + t.Fatal(err) + } + + reloaded, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if got := reloaded.ForSource("main.add", "/tmp/main.go", 12); got != "source note" { + t.Fatalf("source comment = %q", got) + } + if got := reloaded.ForAsm("main.add", ViewGoAsm, 0x1000); got != "go asm note" { + t.Fatalf("go asm comment = %q", got) + } + if got := reloaded.ForAsm("main.add", ViewNativeAsm, 0x1000); got != "native asm note" { + t.Fatalf("native asm comment = %q", got) + } +} + +func TestCommentStoreDeletesEmptyComment(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "note"); err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, " "); err != nil { + t.Fatal(err) + } + + reloaded, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if got := reloaded.ForAsm("main.add", ViewGoAsm, 0x1000); got != "" { + t.Fatalf("deleted comment = %q", got) + } + if got := len(reloaded.All()); got != 0 { + t.Fatalf("comment count = %d", got) + } +} + +func TestCommentStoreSkipsUnchangedCommentWrite(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "note"); err != nil { + t.Fatal(err) + } + + before, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + records := store.All() + if len(records) != 1 { + t.Fatalf("comment count = %d", len(records)) + } + updatedAt := records[0].UpdatedAt + + time.Sleep(time.Millisecond) + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "note"); err != nil { + t.Fatal(err) + } + after, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(before, after) { + t.Fatal("unchanged comment rewrote comments file") + } + records = store.All() + if len(records) != 1 { + t.Fatalf("comment count = %d", len(records)) + } + if !records[0].UpdatedAt.Equal(updatedAt) { + t.Fatalf("updated_at changed from %s to %s", updatedAt, records[0].UpdatedAt) + } +} + +func TestCommentStorePreservesUnknownRecords(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + seed := `{ + "version": 1, + "comments": [ + {"function": "main.add", "view": "hologram", "pc": 16, "text": "from the future"}, + {"function": "main.add", "view": "go_asm", "pc_hex": "0x1000", "pc": 4096, "text": "known"} + ] +}` + if err := os.WriteFile(path, []byte(seed), 0o644); err != nil { + t.Fatal(err) + } + + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + // Trigger a full rewrite of the file. + if err := store.SetAsm("main.add", ViewGoAsm, 0x2000, "new note"); err != nil { + t.Fatal(err) + } + + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(data, []byte("hologram")) { + t.Fatalf("unknown-view record dropped on save:\n%s", data) + } + if !bytes.Contains(data, []byte("known")) || !bytes.Contains(data, []byte("new note")) { + t.Fatalf("known records missing after save:\n%s", data) + } +} + +func TestCommentStoreSaveKeepsFileShareable(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "note"); err != nil { + t.Fatal(err) + } + info, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + if got := info.Mode().Perm(); got != 0o644 { + t.Fatalf("comments file mode = %o, want 644", got) + } +} + +func TestCommentStoreSkipsMissingCommentDelete(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, ""); err != nil { + t.Fatal(err) + } + if _, err := os.Stat(path); !os.IsNotExist(err) { + t.Fatalf("comments file exists after deleting missing comment: %v", err) + } +} + +func TestCommentStoreMergesConcurrentProcessWrites(t *testing.T) { + path := filepath.Join(t.TempDir(), "comments.json") + first, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + second, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + + // Each store simulates a separate lensm process holding a stale + // full-file snapshot of the same sidecar. + if err := first.SetAsm("main.add", ViewGoAsm, 0x1000, "from first"); err != nil { + t.Fatal(err) + } + if err := second.SetAsm("main.add", ViewGoAsm, 0x2000, "from second"); err != nil { + t.Fatal(err) + } + + reloaded, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if got := reloaded.ForAsm("main.add", ViewGoAsm, 0x1000); got != "from first" { + t.Fatalf("first process's comment = %q, want it to survive the second save", got) + } + if got := reloaded.ForAsm("main.add", ViewGoAsm, 0x2000); got != "from second" { + t.Fatalf("second process's comment = %q", got) + } + + // A deletion must not be resurrected by the other process's stale copy. + if err := first.SetAsm("main.add", ViewGoAsm, 0x2000, ""); err != nil { + t.Fatal(err) + } + reloaded, err = Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if got := reloaded.ForAsm("main.add", ViewGoAsm, 0x2000); got != "" { + t.Fatalf("deleted comment resurrected: %q", got) + } +} + +func TestCommentStoreRollsBackFailedSave(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "comments.json") + store, err := Open(path, "/tmp/app") + if err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "old"); err != nil { + t.Fatal(err) + } + + store.path = filepath.Join(dir, "missing", "comments.json") + if err := os.WriteFile(filepath.Join(dir, "missing"), []byte("not a directory"), 0o644); err != nil { + t.Fatal(err) + } + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, "new"); err == nil { + t.Fatal("updating comment unexpectedly succeeded") + } + if got := store.ForAsm("main.add", ViewGoAsm, 0x1000); got != "old" { + t.Fatalf("comment after failed update = %q, want old", got) + } + + if err := store.SetAsm("main.add", ViewGoAsm, 0x1000, ""); err == nil { + t.Fatal("deleting comment unexpectedly succeeded") + } + if got := store.ForAsm("main.add", ViewGoAsm, 0x1000); got != "old" { + t.Fatalf("comment after failed delete = %q, want old", got) + } +} diff --git a/internal/disasm/code.go b/internal/disasm/code.go new file mode 100644 index 0000000..0d42c1b --- /dev/null +++ b/internal/disasm/code.go @@ -0,0 +1,70 @@ +package disasm + +// Code combines the disassembly and the source code mapping. +type Code struct { + // Name is the name of the code block, e.g. function or method name. + Name string + // File is where the code is located. + File string + // Arch is the GOARCH of the disassembled binary. + Arch string + + // Insts is the slice of a all instructions in the code. + Insts []Inst + // MaxJump is the maximum layers of jumps for the codeblock. + // This is used to determine how much padding is created for + // the jump lines. + MaxJump int + + // Source is the slice of a codeblocks that were used to create the instructions. + Source []Source +} + +// Inst represents a single instruction. +type Inst struct { + // PC is the program counter, usually offset in the binary. + PC uint64 + // Text is the textual representation of this instruction. + Text string + // NativeText is the native assembler syntax for this instruction. + NativeText string + // Mnemonic is the canonical decoder mnemonic (e.g. "LD1" where Text spells + // it "VLD1"), used for reference lookups. Empty for undecodable bytes. + Mnemonic string + // File is the location where this instruction was compiled from. + File string + // Line is the line in the file where this instruction was compiled from. + Line int + + // RefPC is a reference to another program counter, e.g. a call. + RefPC uint64 + // RefOffset is a reference to a relative jump. + RefOffset int + // RefStack is the depth that the jump line should be drawn at. + RefStack int + + // Call is a named target that should be present in Funcs. + // This is used to make the instruction clickable and follow to the + // called target. + Call string +} + +// Source represents code from a single file. +type Source struct { + // File is the file name for the source code. + File string + // Blocks is a slice of blocks that were used for compiling the instructions. + Blocks []SourceBlock +} + +// SourceBlock represents a single sequential codeblock that references the instructions. +type SourceBlock struct { + // LineRange is the range of lines that it references from the file. + LineRange + // Lines are textual representation of the source starting from `LineRange.From`. + Lines []string + // Related contains a set of ranges in the instructions. + // e.g. for source code Lines[5] there will be drawn relation shapes to each + // instructions `for _, r := range Related[5] { draw(Insts[r.From:r.To]) }` + Related [][]LineRange +} diff --git a/internal/disasm/file.go b/internal/disasm/file.go new file mode 100644 index 0000000..6338f19 --- /dev/null +++ b/internal/disasm/file.go @@ -0,0 +1,24 @@ +package disasm + +// File represents an object file, a module or anything that contains functions. +type File interface { + // Close closes the underlying data. + Close() error + // Funcs enumerates all the visualizable code blocks. + Funcs() []Func +} + +// Func represents a function or method that can be independently rendered. +type Func interface { + // Name is the name of the func. + Name() string + // Load loads the source code and disassembles it. + Load(opt Options) (*Code, error) +} + +// Options defines configuration for loading the func. +type Options struct { + // Context is the number of lines that should be additionally included for context. + // This can often contain function documentation. + Context int +} diff --git a/internal/disasm/linerange.go b/internal/disasm/linerange.go new file mode 100644 index 0000000..c76b0b5 --- /dev/null +++ b/internal/disasm/linerange.go @@ -0,0 +1,14 @@ +package disasm + +// LineRange represents a list of lines. +type LineRange struct{ From, To int } + +// LineRangesContain checks whether line a or line b is contained in the ranges. +func LineRangesContain(ranges []LineRange, a, b int) bool { + for _, r := range ranges { + if (r.From <= a && a < r.To) || (r.From <= b && b < r.To) { + return true + } + } + return false +} diff --git a/internal/disasm/lineset.go b/internal/disasm/lineset.go new file mode 100644 index 0000000..1480cf1 --- /dev/null +++ b/internal/disasm/lineset.go @@ -0,0 +1,62 @@ +package disasm + +import "slices" + +// LineSet represents a set of needed lines. +type LineSet struct { + list []int +} + +// Add adds line to the needed set. +func (rs *LineSet) Add(line int) { + if at, found := slices.BinarySearch(rs.list, line); !found { + rs.list = slices.Insert(rs.list, at, line) + } +} + +// Ranges converts line set to line ranges and adds context for extra information. +func (rs *LineSet) Ranges(context int) []LineRange { + if len(rs.list) == 0 { + return nil + } + + var all []LineRange + + current := LineRange{From: rs.list[0] - context, To: rs.list[0] + context + 1} + if current.From < 1 { + current.From = 1 + } + for _, line := range rs.list { + if line-context <= current.To { + current.To = line + context + 1 + } else { + all = append(all, current) + current = LineRange{From: line - context, To: line + context + 1} + } + } + all = append(all, current) + + return all +} + +// RangesZero returns a ranges without expanding by context. +func (rs *LineSet) RangesZero() []LineRange { + if len(rs.list) == 0 { + return nil + } + + var all []LineRange + + current := LineRange{From: rs.list[0], To: rs.list[0] + 1} + for _, line := range rs.list { + if line <= current.To { + current.To = line + 1 + } else { + all = append(all, current) + current = LineRange{From: line, To: line + 1} + } + } + all = append(all, current) + + return all +} diff --git a/internal/f32color/color.go b/internal/f32color/color.go new file mode 100644 index 0000000..59de7a5 --- /dev/null +++ b/internal/f32color/color.go @@ -0,0 +1,127 @@ +package f32color + +import ( + "image/color" + "math" +) + +var ( + White = color.NRGBA{0xFF, 0xFF, 0xFF, 0xFF} + Black = color.NRGBA{0x00, 0x00, 0x00, 0xFF} + Red = color.NRGBA{0xFF, 0x00, 0x00, 0xFF} + Green = color.NRGBA{0x00, 0xFF, 0x00, 0xFF} + Blue = color.NRGBA{0x00, 0x00, 0xFF, 0xFF} + Yellow = color.NRGBA{0xFF, 0xFF, 0x00, 0xFF} + + Transparent = color.NRGBA{0xFF, 0xFF, 0xFF, 0x00} +) + +func NRGBAHex(hex uint32) color.NRGBA { + return color.NRGBA{ + R: uint8(hex >> 24), + G: uint8(hex >> 16), + B: uint8(hex >> 8), + A: uint8(hex >> 0), + } +} + +// Gray8 returns the corresponding gray value. +func Gray8(v byte) color.NRGBA { + return color.NRGBA{R: v, G: v, B: v, A: 0xFF} +} + +// RGB returns color based on RGB in range 0..1 +func RGB(r, g, b float32) color.NRGBA { + return color.NRGBA{R: sat8(r), G: sat8(g), B: sat8(b), A: 0xFF} +} + +// RGBA returns color based on RGBA in range 0..1 +func RGBA(r, g, b, a float32) color.NRGBA { + return color.NRGBA{R: sat8(r), G: sat8(g), B: sat8(b), A: sat8(a)} +} + +// HSLA returns color based on HSLA in range 0..1 +func HSLA(h, s, l, a float32) color.NRGBA { return RGBA(hsla(h, s, l, a)) } + +// HSL returns color based on HSL in range 0..1 +func HSL(h, s, l float32) color.NRGBA { return HSLA(h, s, l, 1) } + +// RGBAFloat returns RGBA scaled to 0..1 +func RGBAFloat(c color.NRGBA) (r, g, b, a float32) { + return float32(c.R) / 0xFF, float32(c.G) / 0xFF, float32(c.B) / 0xFF, float32(c.A) / 0xFF +} + +// Lerp linearly interpolates each RGBA component separately +func RGBALerp(a, b color.NRGBA, p float32) color.NRGBA { + ar, ag, ab, aa := RGBAFloat(a) + br, bg, bb, ba := RGBAFloat(b) + return RGBA( + lerpClamp(ar, br, p), + lerpClamp(ag, bg, p), + lerpClamp(ab, bb, p), + lerpClamp(aa, ba, p), + ) +} + +func hue(v1, v2, h float32) float32 { + if h < 0 { + h += 1 + } + if h > 1 { + h -= 1 + } + if 6*h < 1 { + return v1 + (v2-v1)*6*h + } else if 2*h < 1 { + return v2 + } else if 3*h < 2 { + return v1 + (v2-v1)*(2.0/3.0-h)*6 + } + + return v1 +} + +func hsla(h, s, l, a float32) (r, g, b, ra float32) { + if s == 0 { + return l, l, l, a + } + + h = mod32(h, 1) + + var v2 float32 + if l < 0.5 { + v2 = l * (1 + s) + } else { + v2 = (l + s) - s*l + } + + v1 := 2*l - v2 + r = hue(v1, v2, h+1.0/3.0) + g = hue(v1, v2, h) + b = hue(v1, v2, h-1.0/3.0) + ra = a + + return +} + +// sat8 converts 0..1 float to 0..255 uint8 +func sat8(v float32) uint8 { + v *= 255.0 + if v >= 255 { + return 255 + } else if v <= 0 { + return 0 + } + return uint8(v) +} + +func lerpClamp(p, min, max float32) float32 { + if p < 0 { + return min + } else if p > 1 { + return max + } + return min + (max-min)*p +} + +func mod32(x, y float32) float32 { return float32(math.Mod(float64(x), float64(y))) } diff --git a/internal/go/LICENSE b/internal/go/LICENSE new file mode 100644 index 0000000..ea5ea89 --- /dev/null +++ b/internal/go/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/internal/go/expose.go_ b/internal/go/expose.go_ new file mode 100644 index 0000000..3d0a4a5 --- /dev/null +++ b/internal/go/expose.go_ @@ -0,0 +1,49 @@ +package disasm + +import ( + "strings" + + "loov.dev/lensm/internal/go/src/objfile" +) + +func (d *Disasm) Syms() []objfile.Sym { return d.syms } +func (d *Disasm) TextStart() uint64 { return d.textStart } +func (d *Disasm) TextEnd() uint64 { return d.textEnd } +func (d *Disasm) PCLN() objfile.Liner { return d.pcln } +func (d *Disasm) GOARCH() string { return d.goarch } + +// DecodeSyntax disassembles the text segment range [start, end), calling f for +// each instruction with Go assembler syntax and native (GNU) syntax separately. +// +// This is a lensm addition, re-applied on every `go generate` because upstream +// Decode only yields a single combined syntax. It relies on Decode's gnuAsm=true +// format of "%-36s // %s" (goText // gnuText) to split the two apart. +func (d *Disasm) DecodeSyntax(start, end uint64, relocs []objfile.Reloc, f func(pc, size uint64, file string, line int, goText, nativeText string)) { + if start < d.textStart { + start = d.textStart + } + if end > d.textEnd { + end = d.textEnd + } + code := d.text[:end-d.textStart] + lookup := d.lookup + for pc := start; pc < end; { + i := pc - d.textStart + combined, size := d.disasm(code[i:], pc, lookup, d.byteOrder, true) + goText, nativeText := combined, combined + if j := strings.Index(combined, " // "); j >= 0 { + goText = strings.TrimRight(combined[:j], " ") + nativeText = combined[j+len(" // "):] + } + file, line, _ := d.pcln.PCToLine(pc) + reloc := "" + sep := "\t" + for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) { + reloc += sep + relocs[0].Stringer.String(pc-start) + sep = " " + relocs = relocs[1:] + } + f(pc, uint64(size), file, line, goText+reloc, nativeText+reloc) + pc += uint64(size) + } +} diff --git a/internal/go/src/abi/abi.go b/internal/go/src/abi/abi.go new file mode 100755 index 0000000..a3a7c50 --- /dev/null +++ b/internal/go/src/abi/abi.go @@ -0,0 +1,104 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +import ( + "loov.dev/lensm/internal/go/src/goarch" + "unsafe" +) + +// RegArgs is a struct that has space for each argument +// and return value register on the current architecture. +// +// Assembly code knows the layout of the first two fields +// of RegArgs. +// +// RegArgs also contains additional space to hold pointers +// when it may not be safe to keep them only in the integer +// register space otherwise. +type RegArgs struct { + // Values in these slots should be precisely the bit-by-bit + // representation of how they would appear in a register. + // + // This means that on big endian arches, integer values should + // be in the top bits of the slot. Floats are usually just + // directly represented, but some architectures treat narrow + // width floating point values specially (e.g. they're promoted + // first, or they need to be NaN-boxed). + Ints [IntArgRegs]uintptr // untyped integer registers + Floats [FloatArgRegs]uint64 // untyped float registers + + // Fields above this point are known to assembly. + + // Ptrs is a space that duplicates Ints but with pointer type, + // used to make pointers passed or returned in registers + // visible to the GC by making the type unsafe.Pointer. + Ptrs [IntArgRegs]unsafe.Pointer + + // ReturnIsPtr is a bitmap that indicates which registers + // contain or will contain pointers on the return path from + // a reflectcall. The i'th bit indicates whether the i'th + // register contains or will contain a valid Go pointer. + ReturnIsPtr IntArgRegBitmap +} + +func (r *RegArgs) Dump() { + print("Ints:") + for _, x := range r.Ints { + print(" ", x) + } + println() + print("Floats:") + for _, x := range r.Floats { + print(" ", x) + } + println() + print("Ptrs:") + for _, x := range r.Ptrs { + print(" ", x) + } + println() +} + +// IntRegArgAddr returns a pointer inside of r.Ints[reg] that is appropriately +// offset for an argument of size argSize. +// +// argSize must be non-zero, fit in a register, and a power-of-two. +// +// This method is a helper for dealing with the endianness of different CPU +// architectures, since sub-word-sized arguments in big endian architectures +// need to be "aligned" to the upper edge of the register to be interpreted +// by the CPU correctly. +func (r *RegArgs) IntRegArgAddr(reg int, argSize uintptr) unsafe.Pointer { + if argSize > goarch.PtrSize || argSize == 0 || argSize&(argSize-1) != 0 { + panic("invalid argSize") + } + offset := uintptr(0) + if goarch.BigEndian { + offset = goarch.PtrSize - argSize + } + return unsafe.Pointer(uintptr(unsafe.Pointer(&r.Ints[reg])) + offset) +} + +// IntArgRegBitmap is a bitmap large enough to hold one bit per +// integer argument/return register. +type IntArgRegBitmap [(IntArgRegs + 7) / 8]uint8 + +// Set sets the i'th bit of the bitmap to 1. +func (b *IntArgRegBitmap) Set(i int) { + b[i/8] |= uint8(1) << (i % 8) +} + +// Get returns whether the i'th bit of the bitmap is set. +// +// nosplit because it's called in extremely sensitive contexts, like +// on the reflectcall return path. +// +//go:nosplit +func (b *IntArgRegBitmap) Get(i int) bool { + // Compute p=&b[i/8], but without a bounds check. We don't have the stack for it. + p := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(b)) + uintptr(i/8))) + return *p&(uint8(1)<<(i%8)) != 0 +} diff --git a/internal/go/src/abi/abi_amd64.go b/internal/go/src/abi/abi_amd64.go new file mode 100755 index 0000000..d3c5678 --- /dev/null +++ b/internal/go/src/abi/abi_amd64.go @@ -0,0 +1,18 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +const ( + // See abi_generic.go. + + // RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11. + IntArgRegs = 9 + + // X0 -> X14. + FloatArgRegs = 15 + + // We use SSE2 registers which support 64-bit float operations. + EffectiveFloatRegSize = 8 +) diff --git a/internal/go/src/abi/abi_arm64.go b/internal/go/src/abi/abi_arm64.go new file mode 100755 index 0000000..4dc5143 --- /dev/null +++ b/internal/go/src/abi/abi_arm64.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +const ( + // See abi_generic.go. + + // R0 - R15. + IntArgRegs = 16 + + // F0 - F15. + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/internal/go/src/abi/abi_generic.go b/internal/go/src/abi/abi_generic.go new file mode 100755 index 0000000..a08d320 --- /dev/null +++ b/internal/go/src/abi/abi_generic.go @@ -0,0 +1,38 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !goexperiment.regabiargs && !amd64 && !arm64 && !loong64 && !ppc64 && !ppc64le && !riscv64 + +package abi + +const ( + // ABI-related constants. + // + // In the generic case, these are all zero + // which lets them gracefully degrade to ABI0. + + // IntArgRegs is the number of registers dedicated + // to passing integer argument values. Result registers are identical + // to argument registers, so this number is used for those too. + IntArgRegs = 0 + + // FloatArgRegs is the number of registers dedicated + // to passing floating-point argument values. Result registers are + // identical to argument registers, so this number is used for + // those too. + FloatArgRegs = 0 + + // EffectiveFloatRegSize describes the width of floating point + // registers on the current platform from the ABI's perspective. + // + // Since Go only supports 32-bit and 64-bit floating point primitives, + // this number should be either 0, 4, or 8. 0 indicates no floating + // point registers for the ABI or that floating point values will be + // passed via the softfloat ABI. + // + // For platforms that support larger floating point register widths, + // such as x87's 80-bit "registers" (not that we support x87 currently), + // use 8. + EffectiveFloatRegSize = 0 +) diff --git a/internal/go/src/abi/abi_loong64.go b/internal/go/src/abi/abi_loong64.go new file mode 100755 index 0000000..10ad898 --- /dev/null +++ b/internal/go/src/abi/abi_loong64.go @@ -0,0 +1,17 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +const ( + // See abi_generic.go. + + // R4 - R19 + IntArgRegs = 16 + + // F0 - F15 + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/internal/go/src/abi/abi_ppc64x.go b/internal/go/src/abi/abi_ppc64x.go new file mode 100755 index 0000000..73416d7 --- /dev/null +++ b/internal/go/src/abi/abi_ppc64x.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ppc64 || ppc64le + +package abi + +const ( + // See abi_generic.go. + + // R3 - R10, R14 - R17. + IntArgRegs = 12 + + // F1 - F12. + FloatArgRegs = 12 + + EffectiveFloatRegSize = 8 +) diff --git a/internal/go/src/abi/abi_riscv64.go b/internal/go/src/abi/abi_riscv64.go new file mode 100755 index 0000000..2bcd9d6 --- /dev/null +++ b/internal/go/src/abi/abi_riscv64.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +const ( + // See abi_generic.go. + + // X8 - X23 + IntArgRegs = 16 + + // F8 - F23. + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/internal/go/src/abi/abi_s390x.go b/internal/go/src/abi/abi_s390x.go new file mode 100755 index 0000000..9b07f27 --- /dev/null +++ b/internal/go/src/abi/abi_s390x.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.regabiargs + +package abi + +const ( + // See abi_generic.go. + + // R2 - R9. + IntArgRegs = 8 + + // F0 - F15 + FloatArgRegs = 16 + + EffectiveFloatRegSize = 8 +) diff --git a/internal/go/src/abi/bounds.go b/internal/go/src/abi/bounds.go new file mode 100755 index 0000000..d685980 --- /dev/null +++ b/internal/go/src/abi/bounds.go @@ -0,0 +1,113 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +// This type and constants are for encoding different +// kinds of bounds check failures. +type BoundsErrorCode uint8 + +const ( + BoundsIndex BoundsErrorCode = iota // s[x], 0 <= x < len(s) failed + BoundsSliceAlen // s[?:x], 0 <= x <= len(s) failed + BoundsSliceAcap // s[?:x], 0 <= x <= cap(s) failed + BoundsSliceB // s[x:y], 0 <= x <= y failed (but boundsSliceA didn't happen) + BoundsSlice3Alen // s[?:?:x], 0 <= x <= len(s) failed + BoundsSlice3Acap // s[?:?:x], 0 <= x <= cap(s) failed + BoundsSlice3B // s[?:x:y], 0 <= x <= y failed (but boundsSlice3A didn't happen) + BoundsSlice3C // s[x:y:?], 0 <= x <= y failed (but boundsSlice3A/B didn't happen) + BoundsConvert // (*[x]T)(s), 0 <= x <= len(s) failed + numBoundsCodes +) + +const ( + BoundsMaxReg = 15 + BoundsMaxConst = 31 +) + +// Here's how we encode PCDATA_PanicBounds entries: + +// We allow 16 registers (0-15) and 32 constants (0-31). +// Encode the following constant c: +// bits use +// ----------------------------- +// 0 x is in a register +// 1 y is in a register +// +// if x is in a register +// 2 x is signed +// [3:6] x's register number +// else +// [2:6] x's constant value +// +// if y is in a register +// [7:10] y's register number +// else +// [7:11] y's constant value +// +// The final integer is c * numBoundsCode + code + +// TODO: 32-bit + +// Encode bounds failure information into an integer for PCDATA_PanicBounds. +// Register numbers must be in 0-15. Constants must be in 0-31. +func BoundsEncode(code BoundsErrorCode, signed, xIsReg, yIsReg bool, xVal, yVal int) int { + c := int(0) + if xIsReg { + c |= 1 << 0 + if signed { + c |= 1 << 2 + } + if xVal < 0 || xVal > BoundsMaxReg { + panic("bad xReg") + } + c |= xVal << 3 + } else { + if xVal < 0 || xVal > BoundsMaxConst { + panic("bad xConst") + } + c |= xVal << 2 + } + if yIsReg { + c |= 1 << 1 + if yVal < 0 || yVal > BoundsMaxReg { + panic("bad yReg") + } + c |= yVal << 7 + } else { + if yVal < 0 || yVal > BoundsMaxConst { + panic("bad yConst") + } + c |= yVal << 7 + } + return c*int(numBoundsCodes) + int(code) +} +func BoundsDecode(v int) (code BoundsErrorCode, signed, xIsReg, yIsReg bool, xVal, yVal int) { + code = BoundsErrorCode(v % int(numBoundsCodes)) + c := v / int(numBoundsCodes) + xIsReg = c&1 != 0 + c >>= 1 + yIsReg = c&1 != 0 + c >>= 1 + if xIsReg { + signed = c&1 != 0 + c >>= 1 + xVal = c & 0xf + c >>= 4 + } else { + xVal = c & 0x1f + c >>= 5 + } + if yIsReg { + yVal = c & 0xf + c >>= 4 + } else { + yVal = c & 0x1f + c >>= 5 + } + if c != 0 { + panic("BoundsDecode decoding error") + } + return +} diff --git a/internal/go/src/abi/compiletype.go b/internal/go/src/abi/compiletype.go new file mode 100755 index 0000000..f00a69c --- /dev/null +++ b/internal/go/src/abi/compiletype.go @@ -0,0 +1,28 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +// These functions are the build-time version of the Go type data structures. + +// Their contents must be kept in sync with their definitions. +// Because the host and target type sizes can differ, the compiler and +// linker cannot use the host information that they might get from +// either unsafe.Sizeof and Alignof, nor runtime, reflect, or reflectlite. + +// CommonSize returns sizeof(Type) for a compilation target with a given ptrSize +func CommonSize(ptrSize int) int { return 4*ptrSize + 8 + 8 } + +// StructFieldSize returns sizeof(StructField) for a compilation target with a given ptrSize +func StructFieldSize(ptrSize int) int { return 3 * ptrSize } + +// UncommonSize returns sizeof(UncommonType). This currently does not depend on ptrSize. +// This exported function is in an internal package, so it may change to depend on ptrSize in the future. +func UncommonSize() uint64 { return 4 + 2 + 2 + 4 + 4 } + +// TFlagOff returns the offset of Type.TFlag for a compilation target with a given ptrSize +func TFlagOff(ptrSize int) int { return 2*ptrSize + 4 } + +// ITabTypeOff returns the offset of ITab.Type for a compilation target with a given ptrSize +func ITabTypeOff(ptrSize int) int { return ptrSize } diff --git a/internal/go/src/abi/escape.go b/internal/go/src/abi/escape.go new file mode 100755 index 0000000..d37be01 --- /dev/null +++ b/internal/go/src/abi/escape.go @@ -0,0 +1,65 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +import "unsafe" + +// NoEscape hides the pointer p from escape analysis, preventing it +// from escaping to the heap. It compiles down to nothing. +// +// WARNING: This is very subtle to use correctly. The caller must +// ensure that it's truly safe for p to not escape to the heap by +// maintaining runtime pointer invariants (for example, that globals +// and the heap may not generally point into a stack). +// +//go:nosplit +//go:nocheckptr +func NoEscape(p unsafe.Pointer) unsafe.Pointer { + x := uintptr(p) + return unsafe.Pointer(x ^ 0) +} + +var alwaysFalse bool +var escapeSink any + +// Escape forces any pointers in x to escape to the heap. +func Escape[T any](x T) T { + if alwaysFalse { + escapeSink = x + } + return x +} + +// EscapeNonString forces v to be on the heap, if v contains a +// non-string pointer. +// +// This is used in hash/maphash.Comparable. We cannot hash pointers +// to local variables on stack, as their addresses might change on +// stack growth. Strings are okay as the hash depends on only the +// content, not the pointer. +// +// This is essentially +// +// if hasNonStringPointers(T) { Escape(v) } +// +// Implemented as a compiler intrinsic. +func EscapeNonString[T any](v T) { panic("intrinsic") } + +// EscapeToResultNonString models a data flow edge from v to the result, +// if v contains a non-string pointer. If v contains only string pointers, +// it returns a copy of v, but is not modeled as a data flow edge +// from the escape analysis's perspective. +// +// This is used in unique.clone, to model the data flow edge on the +// value with strings excluded, because strings are cloned (by +// content). +// +// TODO: probably we should define this as a intrinsic and EscapeNonString +// could just be "heap = EscapeToResultNonString(v)". This way we can model +// an edge to the result but not necessarily heap. +func EscapeToResultNonString[T any](v T) T { + EscapeNonString(v) + return *(*T)(NoEscape(unsafe.Pointer(&v))) +} diff --git a/internal/go/src/abi/funcpc.go b/internal/go/src/abi/funcpc.go new file mode 100755 index 0000000..54b0735 --- /dev/null +++ b/internal/go/src/abi/funcpc.go @@ -0,0 +1,31 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !gccgo + +package abi + +// FuncPC* intrinsics. +// +// CAREFUL: In programs with plugins, FuncPC* can return different values +// for the same function (because there are actually multiple copies of +// the same function in the address space). To be safe, don't use the +// results of this function in any == expression. It is only safe to +// use the result as an address at which to start executing code. + +// FuncPCABI0 returns the entry PC of the function f, which must be a +// direct reference of a function defined as ABI0. Otherwise it is a +// compile-time error. +// +// Implemented as a compile intrinsic. +func FuncPCABI0(f any) uintptr + +// FuncPCABIInternal returns the entry PC of the function f. If f is a +// direct reference of a function, it must be defined as ABIInternal. +// Otherwise it is a compile-time error. If f is not a direct reference +// of a defined function, it assumes that f is a func value. Otherwise +// the behavior is undefined. +// +// Implemented as a compile intrinsic. +func FuncPCABIInternal(f any) uintptr diff --git a/internal/go/src/abi/funcpc_gccgo.go b/internal/go/src/abi/funcpc_gccgo.go new file mode 100755 index 0000000..ad5fa52 --- /dev/null +++ b/internal/go/src/abi/funcpc_gccgo.go @@ -0,0 +1,21 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// For bootstrapping with gccgo. + +//go:build gccgo + +package abi + +import "unsafe" + +func FuncPCABI0(f interface{}) uintptr { + words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f)) + return *(*uintptr)(unsafe.Pointer(words[1])) +} + +func FuncPCABIInternal(f interface{}) uintptr { + words := (*[2]unsafe.Pointer)(unsafe.Pointer(&f)) + return *(*uintptr)(unsafe.Pointer(words[1])) +} diff --git a/internal/go/src/abi/iface.go b/internal/go/src/abi/iface.go new file mode 100755 index 0000000..f53d7e1 --- /dev/null +++ b/internal/go/src/abi/iface.go @@ -0,0 +1,41 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +import "unsafe" + +// The first word of every non-empty interface type contains an *ITab. +// It records the underlying concrete type (Type), the interface type it +// is implementing (Inter), and some ancillary information. +// +// allocated in non-garbage-collected memory +type ITab struct { + Inter *InterfaceType + Type *Type + Hash uint32 // copy of Type.Hash. Used for type switches. + Fun [1]uintptr // variable sized. fun[0]==0 means Type does not implement Inter. +} + +// EmptyInterface describes the layout of a "interface{}" or a "any." +// These are represented differently than non-empty interface, as the first +// word always points to an abi.Type. +type EmptyInterface struct { + Type *Type + Data unsafe.Pointer +} + +// NonEmptyInterface describes the layout of an interface that contains any methods. +type NonEmptyInterface struct { + ITab *ITab + Data unsafe.Pointer +} + +// CommonInterface describes the layout of both [EmptyInterface] and [NonEmptyInterface]. +type CommonInterface struct { + // Either an *ITab or a *Type, unexported to avoid accidental use. + _ unsafe.Pointer + + Data unsafe.Pointer +} diff --git a/internal/go/src/abi/map.go b/internal/go/src/abi/map.go new file mode 100755 index 0000000..4476dda --- /dev/null +++ b/internal/go/src/abi/map.go @@ -0,0 +1,64 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +import ( + "unsafe" +) + +// Map constants common to several packages +// runtime/runtime-gdb.py:MapTypePrinter contains its own copy +const ( + // Number of bits in the group.slot count. + MapGroupSlotsBits = 3 + + // Number of slots in a group. + MapGroupSlots = 1 << MapGroupSlotsBits // 8 + + // Maximum key or elem size to keep inline (instead of mallocing per element). + // Must fit in a uint8. + MapMaxKeyBytes = 128 + MapMaxElemBytes = 128 + + ctrlEmpty = 0b10000000 + bitsetLSB = 0x0101010101010101 + + // Value of control word with all empty slots. + MapCtrlEmpty = bitsetLSB * uint64(ctrlEmpty) +) + +type MapType struct { + Type + Key *Type + Elem *Type + Group *Type // internal type representing a slot group + // function for hashing keys (ptr to key, seed) -> hash + Hasher func(unsafe.Pointer, uintptr) uintptr + GroupSize uintptr // == Group.Size_ + SlotSize uintptr // size of key/elem slot + ElemOff uintptr // offset of elem in key/elem slot + Flags uint32 +} + +// Flag values +const ( + MapNeedKeyUpdate = 1 << iota + MapHashMightPanic + MapIndirectKey + MapIndirectElem +) + +func (mt *MapType) NeedKeyUpdate() bool { // true if we need to update key on an overwrite + return mt.Flags&MapNeedKeyUpdate != 0 +} +func (mt *MapType) HashMightPanic() bool { // true if hash function might panic + return mt.Flags&MapHashMightPanic != 0 +} +func (mt *MapType) IndirectKey() bool { // store ptr to key instead of key itself + return mt.Flags&MapIndirectKey != 0 +} +func (mt *MapType) IndirectElem() bool { // store ptr to elem instead of elem itself + return mt.Flags&MapIndirectElem != 0 +} diff --git a/internal/go/src/abi/rangefuncconsts.go b/internal/go/src/abi/rangefuncconsts.go new file mode 100755 index 0000000..4e9248d --- /dev/null +++ b/internal/go/src/abi/rangefuncconsts.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +type RF_State int + +// These constants are shared between the compiler, which uses them for state functions +// and panic indicators, and the runtime, which turns them into more meaningful strings +// For best code generation, RF_DONE and RF_READY should be 0 and 1. +const ( + RF_DONE = RF_State(iota) // body of loop has exited in a non-panic way + RF_READY // body of loop has not exited yet, is not running -- this is not a panic index + RF_PANIC // body of loop is either currently running, or has panicked + RF_EXHAUSTED // iterator function return, i.e., sequence is "exhausted" + RF_MISSING_PANIC = 4 // body of loop panicked but iterator function defer-recovered it away +) diff --git a/internal/go/src/abi/runtime.go b/internal/go/src/abi/runtime.go new file mode 100755 index 0000000..9b91cdf --- /dev/null +++ b/internal/go/src/abi/runtime.go @@ -0,0 +1,8 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +// ZeroValSize is the size in bytes of runtime.zeroVal. +const ZeroValSize = 1024 diff --git a/internal/go/src/abi/stack.go b/internal/go/src/abi/stack.go new file mode 100755 index 0000000..8e3327e --- /dev/null +++ b/internal/go/src/abi/stack.go @@ -0,0 +1,33 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +const ( + // StackNosplitBase is the base maximum number of bytes that a chain of + // NOSPLIT functions can use. + // + // This value must be multiplied by the stack guard multiplier, so do not + // use it directly. See runtime/stack.go:stackNosplit and + // cmd/internal/objabi/stack.go:StackNosplit. + StackNosplitBase = 800 + + // We have three different sequences for stack bounds checks, depending on + // whether the stack frame of a function is small, big, or huge. + + // After a stack split check the SP is allowed to be StackSmall bytes below + // the stack guard. + // + // Functions that need frames <= StackSmall can perform the stack check + // using a single comparison directly between the stack guard and the SP + // because we ensure that StackSmall bytes of stack space are available + // beyond the stack guard. + StackSmall = 128 + + // Functions that need frames <= StackBig can assume that neither + // SP-framesize nor stackGuard-StackSmall will underflow, and thus use a + // more efficient check. In order to ensure this, StackBig must be <= the + // size of the unmapped space at zero. + StackBig = 4096 +) diff --git a/internal/go/src/abi/stub.s b/internal/go/src/abi/stub.s new file mode 100755 index 0000000..5bad98d --- /dev/null +++ b/internal/go/src/abi/stub.s @@ -0,0 +1,7 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file silences errors about body-less functions +// that are provided by intrinsics in the latest version of the compiler, +// but may not be known to the bootstrap compiler. diff --git a/internal/go/src/abi/switch.go b/internal/go/src/abi/switch.go new file mode 100755 index 0000000..81c2b5b --- /dev/null +++ b/internal/go/src/abi/switch.go @@ -0,0 +1,58 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +import "loov.dev/lensm/internal/go/src/goarch" + +type InterfaceSwitch struct { + Cache *InterfaceSwitchCache + NCases int + + // Array of NCases elements. + // Each case must be a non-empty interface type. + Cases [1]*InterfaceType +} + +type InterfaceSwitchCache struct { + Mask uintptr // mask for index. Must be a power of 2 minus 1 + Entries [1]InterfaceSwitchCacheEntry // Mask+1 entries total +} + +type InterfaceSwitchCacheEntry struct { + // type of source value (a *Type) + Typ uintptr + // case # to dispatch to + Case int + // itab to use for resulting case variable (a *runtime.itab) + Itab uintptr +} + +func UseInterfaceSwitchCache(arch goarch.ArchFamilyType) bool { + // We need an atomic load instruction to make the cache multithreaded-safe. + // (AtomicLoadPtr needs to be implemented in cmd/compile/internal/ssa/_gen/ARCH.rules.) + switch arch { + case goarch.AMD64, goarch.ARM64, goarch.LOONG64, goarch.MIPS, goarch.MIPS64, goarch.PPC64, goarch.RISCV64, goarch.S390X: + return true + default: + return false + } +} + +type TypeAssert struct { + Cache *TypeAssertCache + Inter *InterfaceType + CanFail bool +} +type TypeAssertCache struct { + Mask uintptr + Entries [1]TypeAssertCacheEntry +} +type TypeAssertCacheEntry struct { + // type of source value (a *runtime._type) + Typ uintptr + // itab to use for result (a *runtime.itab) + // nil if CanFail is set and conversion would fail. + Itab uintptr +} diff --git a/internal/go/src/abi/symtab.go b/internal/go/src/abi/symtab.go new file mode 100755 index 0000000..10033e7 --- /dev/null +++ b/internal/go/src/abi/symtab.go @@ -0,0 +1,143 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +// PCLnTabMagic is the version at the start of the PC/line table. +// This is the start of the .pclntab section, and is also runtime.pcHeader. +// The magic numbers are chosen such that reading the value with +// a different endianness does not result in the same value. +// That lets us the magic number to determine the endianness. +type PCLnTabMagic uint32 + +const ( + // Initial PCLnTabMagic value used in Go 1.2 through Go 1.15. + Go12PCLnTabMagic PCLnTabMagic = 0xfffffffb + // PCLnTabMagic value used in Go 1.16 through Go 1.17. + // Several fields added to header (CL 241598). + Go116PCLnTabMagic PCLnTabMagic = 0xfffffffa + // PCLnTabMagic value used in Go 1.18 through Go 1.19. + // Entry PC of func data changed from address to offset (CL 351463). + Go118PCLnTabMagic PCLnTabMagic = 0xfffffff0 + // PCLnTabMagic value used in Go 1.20 and later. + // A ":" was added to generated symbol names (#37762). + Go120PCLnTabMagic PCLnTabMagic = 0xfffffff1 + + // CurrentPCLnTabMagic is the value emitted by the current toolchain. + // This is written by the linker to the pcHeader and read by the + // runtime and debug/gosym (and external tools like Delve). + // + // Change this value when updating the pclntab version. + // Changing this exported value is OK because is an + // internal package. + CurrentPCLnTabMagic = Go120PCLnTabMagic +) + +// A FuncFlag records bits about a function, passed to the runtime. +type FuncFlag uint8 + +const ( + // FuncFlagTopFrame indicates a function that appears at the top of its stack. + // The traceback routine stop at such a function and consider that a + // successful, complete traversal of the stack. + // Examples of TopFrame functions include goexit, which appears + // at the top of a user goroutine stack, and mstart, which appears + // at the top of a system goroutine stack. + FuncFlagTopFrame FuncFlag = 1 << iota + + // FuncFlagSPWrite indicates a function that writes an arbitrary value to SP + // (any write other than adding or subtracting a constant amount). + // The traceback routines cannot encode such changes into the + // pcsp tables, so the function traceback cannot safely unwind past + // SPWrite functions. Stopping at an SPWrite function is considered + // to be an incomplete unwinding of the stack. In certain contexts + // (in particular garbage collector stack scans) that is a fatal error. + FuncFlagSPWrite + + // FuncFlagAsm indicates that a function was implemented in assembly. + FuncFlagAsm +) + +// A FuncID identifies particular functions that need to be treated +// specially by the runtime. +// Note that in some situations involving plugins, there may be multiple +// copies of a particular special runtime function. +type FuncID uint8 + +const ( + // If you add a FuncID, you probably also want to add an entry to the map in + // ../../cmd/internal/objabi/funcid.go + + FuncIDNormal FuncID = iota // not a special function + FuncID_abort + FuncID_asmcgocall + FuncID_asyncPreempt + FuncID_cgocallback + FuncID_corostart + FuncID_debugCallV2 + FuncID_gcBgMarkWorker + FuncID_goexit + FuncID_gogo + FuncID_gopanic + FuncID_handleAsyncEvent + FuncID_mcall + FuncID_morestack + FuncID_mstart + FuncID_panicwrap + FuncID_rt0_go + FuncID_runtime_main + FuncID_runFinalizers + FuncID_runCleanups + FuncID_sigpanic + FuncID_systemstack + FuncID_systemstack_switch + FuncIDWrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) +) + +// ArgsSizeUnknown is set in Func.argsize to mark all functions +// whose argument size is unknown (C vararg functions, and +// assembly code without an explicit specification). +// This value is generated by the compiler, assembler, or linker. +const ArgsSizeUnknown = -0x80000000 + +// IDs for PCDATA and FUNCDATA tables in Go binaries. +// +// These must agree with ../../../runtime/funcdata.h. +const ( + PCDATA_UnsafePoint = 0 + PCDATA_StackMapIndex = 1 + PCDATA_InlTreeIndex = 2 + PCDATA_ArgLiveIndex = 3 + PCDATA_PanicBounds = 4 + + FUNCDATA_ArgsPointerMaps = 0 + FUNCDATA_LocalsPointerMaps = 1 + FUNCDATA_StackObjects = 2 + FUNCDATA_InlTree = 3 + FUNCDATA_OpenCodedDeferInfo = 4 + FUNCDATA_ArgInfo = 5 + FUNCDATA_ArgLiveInfo = 6 + FUNCDATA_WrapInfo = 7 +) + +// Special values for the PCDATA_UnsafePoint table. +const ( + UnsafePointSafe = -1 // Safe for async preemption + UnsafePointUnsafe = -2 // Unsafe for async preemption + + // UnsafePointRestart1(2) apply on a sequence of instructions, within + // which if an async preemption happens, we should back off the PC + // to the start of the sequence when resuming. + // We need two so we can distinguish the start/end of the sequence + // in case that two sequences are next to each other. + UnsafePointRestart1 = -3 + UnsafePointRestart2 = -4 + + // Like UnsafePointRestart1, but back to function entry if async preempted. + UnsafePointRestartAtEntry = -5 +) + +const MINFUNC = 16 // minimum size for a function + +const FuncTabBucketSize = 256 * MINFUNC // size of bucket in the pc->func lookup table diff --git a/internal/go/src/abi/type.go b/internal/go/src/abi/type.go new file mode 100755 index 0000000..243b787 --- /dev/null +++ b/internal/go/src/abi/type.go @@ -0,0 +1,777 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package abi + +import ( + "unsafe" +) + +// Type is the runtime representation of a Go type. +// +// Be careful about accessing this type at build time, as the version +// of this type in the compiler/linker may not have the same layout +// as the version in the target binary, due to pointer width +// differences and any experiments. Use cmd/compile/internal/rttype +// or the functions in compiletype.go to access this type instead. +// (TODO: this admonition applies to every type in this package. +// Put it in some shared location?) +type Type struct { + Size_ uintptr + PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers + Hash uint32 // hash of type; avoids computation in hash tables + TFlag TFlag // extra type information flags + Align_ uint8 // alignment of variable with this type + FieldAlign_ uint8 // alignment of struct field with this type + Kind_ Kind // what kind of type this is (string, int, ...) + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + Equal func(unsafe.Pointer, unsafe.Pointer) bool + // GCData stores the GC type data for the garbage collector. + // Normally, GCData points to a bitmask that describes the + // ptr/nonptr fields of the type. The bitmask will have at + // least PtrBytes/ptrSize bits. + // If the TFlagGCMaskOnDemand bit is set, GCData is instead a + // **byte and the pointer to the bitmask is one dereference away. + // The runtime will build the bitmask if needed. + // (See runtime/type.go:getGCMask.) + // Note: multiple types may have the same value of GCData, + // including when TFlagGCMaskOnDemand is set. The types will, of course, + // have the same pointer layout (but not necessarily the same size). + GCData *byte + Str NameOff // string form + PtrToThis TypeOff // type for pointer to this type, may be zero +} + +// A Kind represents the specific kind of type that a Type represents. +// The zero Kind is not a valid kind. +type Kind uint8 + +const ( + Invalid Kind = iota + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + Array + Chan + Func + Interface + Map + Pointer + Slice + String + Struct + UnsafePointer +) + +// TFlag is used by a Type to signal what extra type information is +// available in the memory directly following the Type value. +type TFlag uint8 + +const ( + // TFlagUncommon means that there is a data with a type, UncommonType, + // just beyond the shared-per-type common data. That is, the data + // for struct types will store their UncommonType at one offset, the + // data for interface types will store their UncommonType at a different + // offset. UncommonType is always accessed via a pointer that is computed + // using trust-us-we-are-the-implementors pointer arithmetic. + // + // For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0, + // then t has UncommonType data and it can be accessed as: + // + // type structTypeUncommon struct { + // structType + // u UncommonType + // } + // u := &(*structTypeUncommon)(unsafe.Pointer(t)).u + TFlagUncommon TFlag = 1 << 0 + + // TFlagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + TFlagExtraStar TFlag = 1 << 1 + + // TFlagNamed means the type has a name. + TFlagNamed TFlag = 1 << 2 + + // TFlagRegularMemory means that equal and hash functions can treat + // this type as a single region of t.size bytes. + TFlagRegularMemory TFlag = 1 << 3 + + // TFlagGCMaskOnDemand means that the GC pointer bitmask will be + // computed on demand at runtime instead of being precomputed at + // compile time. If this flag is set, the GCData field effectively + // has type **byte instead of *byte. The runtime will store a + // pointer to the GC pointer bitmask in *GCData. + TFlagGCMaskOnDemand TFlag = 1 << 4 + + // TFlagDirectIface means that a value of this type is stored directly + // in the data field of an interface, instead of indirectly. + // This flag is just a cached computation of Size_ == PtrBytes == goarch.PtrSize. + TFlagDirectIface TFlag = 1 << 5 + + // Leaving this breadcrumb behind for dlv. It should not be used, and no + // Kind should be big enough to set this bit. + KindDirectIface Kind = 1 << 5 +) + +// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime. +type NameOff int32 + +// TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime. +type TypeOff int32 + +// TextOff is an offset from the top of a text section. See (rtype).textOff in runtime. +type TextOff int32 + +// String returns the name of k. +func (k Kind) String() string { + if int(k) < len(kindNames) { + return kindNames[k] + } + return kindNames[0] +} + +var kindNames = []string{ + Invalid: "invalid", + Bool: "bool", + Int: "int", + Int8: "int8", + Int16: "int16", + Int32: "int32", + Int64: "int64", + Uint: "uint", + Uint8: "uint8", + Uint16: "uint16", + Uint32: "uint32", + Uint64: "uint64", + Uintptr: "uintptr", + Float32: "float32", + Float64: "float64", + Complex64: "complex64", + Complex128: "complex128", + Array: "array", + Chan: "chan", + Func: "func", + Interface: "interface", + Map: "map", + Pointer: "ptr", + Slice: "slice", + String: "string", + Struct: "struct", + UnsafePointer: "unsafe.Pointer", +} + +// TypeOf returns the abi.Type of some value. +func TypeOf(a any) *Type { + eface := *(*EmptyInterface)(unsafe.Pointer(&a)) + // Types are either static (for compiler-created types) or + // heap-allocated but always reachable (for reflection-created + // types, held in the central map). So there is no need to + // escape types. noescape here help avoid unnecessary escape + // of v. + return (*Type)(NoEscape(unsafe.Pointer(eface.Type))) +} + +// TypeFor returns the abi.Type for a type parameter. +func TypeFor[T any]() *Type { + return (*PtrType)(unsafe.Pointer(TypeOf((*T)(nil)))).Elem +} + +func (t *Type) Kind() Kind { return t.Kind_ } + +func (t *Type) HasName() bool { + return t.TFlag&TFlagNamed != 0 +} + +// Pointers reports whether t contains pointers. +func (t *Type) Pointers() bool { return t.PtrBytes != 0 } + +// IsDirectIface reports whether t is stored directly in an interface value. +func (t *Type) IsDirectIface() bool { + return t.TFlag&TFlagDirectIface != 0 +} + +func (t *Type) GcSlice(begin, end uintptr) []byte { + if t.TFlag&TFlagGCMaskOnDemand != 0 { + panic("GcSlice can't handle on-demand gcdata types") + } + return unsafe.Slice(t.GCData, int(end))[begin:] +} + +// Method on non-interface type +type Method struct { + Name NameOff // name of method + Mtyp TypeOff // method type (without receiver) + Ifn TextOff // fn used in interface call (one-word receiver) + Tfn TextOff // fn used for normal method call +} + +// UncommonType is present only for defined types or types with methods +// (if T is a defined type, the uncommonTypes for T and *T have methods). +// Using a pointer to this struct reduces the overall size required +// to describe a non-defined type with no methods. +type UncommonType struct { + PkgPath NameOff // import path; empty for built-in types like int, string + Mcount uint16 // number of methods + Xcount uint16 // number of exported methods + Moff uint32 // offset from this uncommontype to [mcount]Method + _ uint32 // unused +} + +func (t *UncommonType) Methods() []Method { + if t.Mcount == 0 { + return nil + } + return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount] +} + +func (t *UncommonType) ExportedMethods() []Method { + if t.Xcount == 0 { + return nil + } + return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.Xcount] +} + +// addChecked returns p+x. +// +// The whySafe string is ignored, so that the function still inlines +// as efficiently as p+x, but all call sites should use the string to +// record why the addition is safe, which is to say why the addition +// does not cause x to advance to the very end of p's allocation +// and therefore point incorrectly at the next block in memory. +func addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// Imethod represents a method on an interface type +type Imethod struct { + Name NameOff // name of method + Typ TypeOff // .(*FuncType) underneath +} + +// ArrayType represents a fixed array type. +type ArrayType struct { + Type + Elem *Type // array element type + Slice *Type // slice type + Len uintptr +} + +// Len returns the length of t if t is an array type, otherwise 0 +func (t *Type) Len() int { + if t.Kind() == Array { + return int((*ArrayType)(unsafe.Pointer(t)).Len) + } + return 0 +} + +func (t *Type) Common() *Type { + return t +} + +type ChanDir int + +const ( + RecvDir ChanDir = 1 << iota // <-chan + SendDir // chan<- + BothDir = RecvDir | SendDir // chan + InvalidDir ChanDir = 0 +) + +// ChanType represents a channel type +type ChanType struct { + Type + Elem *Type + Dir ChanDir +} + +type structTypeUncommon struct { + StructType + u UncommonType +} + +// ChanDir returns the direction of t if t is a channel type, otherwise InvalidDir (0). +func (t *Type) ChanDir() ChanDir { + if t.Kind() == Chan { + ch := (*ChanType)(unsafe.Pointer(t)) + return ch.Dir + } + return InvalidDir +} + +// Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil +func (t *Type) Uncommon() *UncommonType { + if t.TFlag&TFlagUncommon == 0 { + return nil + } + switch t.Kind() { + case Struct: + return &(*structTypeUncommon)(unsafe.Pointer(t)).u + case Pointer: + type u struct { + PtrType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Func: + type u struct { + FuncType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Slice: + type u struct { + SliceType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Array: + type u struct { + ArrayType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Chan: + type u struct { + ChanType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Map: + type u struct { + MapType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + case Interface: + type u struct { + InterfaceType + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + default: + type u struct { + Type + u UncommonType + } + return &(*u)(unsafe.Pointer(t)).u + } +} + +// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil. +func (t *Type) Elem() *Type { + switch t.Kind() { + case Array: + tt := (*ArrayType)(unsafe.Pointer(t)) + return tt.Elem + case Chan: + tt := (*ChanType)(unsafe.Pointer(t)) + return tt.Elem + case Map: + tt := (*MapType)(unsafe.Pointer(t)) + return tt.Elem + case Pointer: + tt := (*PtrType)(unsafe.Pointer(t)) + return tt.Elem + case Slice: + tt := (*SliceType)(unsafe.Pointer(t)) + return tt.Elem + } + return nil +} + +// StructType returns t cast to a *StructType, or nil if its tag does not match. +func (t *Type) StructType() *StructType { + if t.Kind() != Struct { + return nil + } + return (*StructType)(unsafe.Pointer(t)) +} + +// MapType returns t cast to a *MapType, or nil if its tag does not match. +func (t *Type) MapType() *MapType { + if t.Kind() != Map { + return nil + } + return (*MapType)(unsafe.Pointer(t)) +} + +// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match. +func (t *Type) ArrayType() *ArrayType { + if t.Kind() != Array { + return nil + } + return (*ArrayType)(unsafe.Pointer(t)) +} + +// FuncType returns t cast to a *FuncType, or nil if its tag does not match. +func (t *Type) FuncType() *FuncType { + if t.Kind() != Func { + return nil + } + return (*FuncType)(unsafe.Pointer(t)) +} + +// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match. +func (t *Type) InterfaceType() *InterfaceType { + if t.Kind() != Interface { + return nil + } + return (*InterfaceType)(unsafe.Pointer(t)) +} + +// Size returns the size of data with type t. +func (t *Type) Size() uintptr { return t.Size_ } + +// Align returns the alignment of data with type t. +func (t *Type) Align() int { return int(t.Align_) } + +func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } + +type InterfaceType struct { + Type + PkgPath Name // import path + Methods []Imethod // sorted by hash +} + +func (t *Type) ExportedMethods() []Method { + ut := t.Uncommon() + if ut == nil { + return nil + } + return ut.ExportedMethods() +} + +func (t *Type) NumMethod() int { + if t.Kind() == Interface { + tt := (*InterfaceType)(unsafe.Pointer(t)) + return tt.NumMethod() + } + return len(t.ExportedMethods()) +} + +// NumMethod returns the number of interface methods in the type's method set. +func (t *InterfaceType) NumMethod() int { return len(t.Methods) } + +func (t *Type) Key() *Type { + if t.Kind() == Map { + return (*MapType)(unsafe.Pointer(t)).Key + } + return nil +} + +type SliceType struct { + Type + Elem *Type // slice element type +} + +// FuncType represents a function type. +// +// A *Type for each in and out parameter is stored in an array that +// directly follows the funcType (and possibly its uncommonType). So +// a function type with one method, one input, and one output is: +// +// struct { +// funcType +// uncommonType +// [2]*rtype // [0] is in, [1] is out +// } +type FuncType struct { + Type + InCount uint16 + OutCount uint16 // top bit is set if last input parameter is ... +} + +func (t *FuncType) In(i int) *Type { + return t.InSlice()[i] +} + +func (t *FuncType) NumIn() int { + return int(t.InCount) +} + +func (t *FuncType) NumOut() int { + return int(t.OutCount & (1<<15 - 1)) +} + +func (t *FuncType) Out(i int) *Type { + return (t.OutSlice()[i]) +} + +func (t *FuncType) InSlice() []*Type { + uadd := unsafe.Sizeof(*t) + if t.TFlag&TFlagUncommon != 0 { + uadd += unsafe.Sizeof(UncommonType{}) + } + if t.InCount == 0 { + return nil + } + return (*[1 << 16]*Type)(addChecked(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.InCount:t.InCount] +} +func (t *FuncType) OutSlice() []*Type { + outCount := uint16(t.NumOut()) + if outCount == 0 { + return nil + } + uadd := unsafe.Sizeof(*t) + if t.TFlag&TFlagUncommon != 0 { + uadd += unsafe.Sizeof(UncommonType{}) + } + return (*[1 << 17]*Type)(addChecked(unsafe.Pointer(t), uadd, "outCount > 0"))[t.InCount : t.InCount+outCount : t.InCount+outCount] +} + +func (t *FuncType) IsVariadic() bool { + return t.OutCount&(1<<15) != 0 +} + +type PtrType struct { + Type + Elem *Type // pointer element (pointed at) type +} + +type StructField struct { + Name Name // name is always non-empty + Typ *Type // type of field + Offset uintptr // byte offset of field +} + +func (f *StructField) Embedded() bool { + return f.Name.IsEmbedded() +} + +type StructType struct { + Type + PkgPath Name + Fields []StructField +} + +// Name is an encoded type Name with optional extra data. +// +// The first byte is a bit field containing: +// +// 1<<0 the name is exported +// 1<<1 tag data follows the name +// 1<<2 pkgPath nameOff follows the name and tag +// 1<<3 the name is of an embedded (a.k.a. anonymous) field +// +// Following that, there is a varint-encoded length of the name, +// followed by the name itself. +// +// If tag data is present, it also has a varint-encoded length +// followed by the tag itself. +// +// If the import path follows, then 4 bytes at the end of +// the data form a nameOff. The import path is only set for concrete +// methods that are defined in a different package than their type. +// +// If a name starts with "*", then the exported bit represents +// whether the pointed to type is exported. +// +// Note: this encoding must match here and in: +// cmd/compile/internal/reflectdata/reflect.go +// cmd/link/internal/ld/decodesym.go + +type Name struct { + Bytes *byte +} + +// DataChecked does pointer arithmetic on n's Bytes, and that arithmetic is asserted to +// be safe for the reason in whySafe (which can appear in a backtrace, etc.) +func (n Name) DataChecked(off int, whySafe string) *byte { + return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), whySafe)) +} + +// Data does pointer arithmetic on n's Bytes, and that arithmetic is asserted to +// be safe because the runtime made the call (other packages use DataChecked) +func (n Name) Data(off int) *byte { + return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), "the runtime doesn't need to give you a reason")) +} + +// IsExported returns "is n exported?" +func (n Name) IsExported() bool { + return (*n.Bytes)&(1<<0) != 0 +} + +// HasTag returns true iff there is tag data following this name +func (n Name) HasTag() bool { + return (*n.Bytes)&(1<<1) != 0 +} + +// IsEmbedded returns true iff n is embedded (an anonymous field). +func (n Name) IsEmbedded() bool { + return (*n.Bytes)&(1<<3) != 0 +} + +// ReadVarint parses a varint as encoded by encoding/binary. +// It returns the number of encoded bytes and the encoded value. +func (n Name) ReadVarint(off int) (int, int) { + v := 0 + for i := 0; ; i++ { + x := *n.DataChecked(off+i, "read varint") + v += int(x&0x7f) << (7 * i) + if x&0x80 == 0 { + return i + 1, v + } + } +} + +// IsBlank indicates whether n is "_". +func (n Name) IsBlank() bool { + if n.Bytes == nil { + return false + } + _, l := n.ReadVarint(1) + return l == 1 && *n.Data(2) == '_' +} + +// writeVarint writes n to buf in varint form. Returns the +// number of bytes written. n must be nonnegative. +// Writes at most 10 bytes. +func writeVarint(buf []byte, n int) int { + for i := 0; ; i++ { + b := byte(n & 0x7f) + n >>= 7 + if n == 0 { + buf[i] = b + return i + 1 + } + buf[i] = b | 0x80 + } +} + +// Name returns the name of n, or empty if it does not actually have a name. +func (n Name) Name() string { + if n.Bytes == nil { + return "" + } + i, l := n.ReadVarint(1) + return unsafe.String(n.DataChecked(1+i, "non-empty string"), l) +} + +// Tag returns the tag string for n, or empty if there is none. +func (n Name) Tag() string { + if !n.HasTag() { + return "" + } + i, l := n.ReadVarint(1) + i2, l2 := n.ReadVarint(1 + i + l) + return unsafe.String(n.DataChecked(1+i+l+i2, "non-empty string"), l2) +} + +func NewName(n, tag string, exported, embedded bool) Name { + if len(n) >= 1<<29 { + panic("abi.NewName: name too long: " + n[:1024] + "...") + } + if len(tag) >= 1<<29 { + panic("abi.NewName: tag too long: " + tag[:1024] + "...") + } + var nameLen [10]byte + var tagLen [10]byte + nameLenLen := writeVarint(nameLen[:], len(n)) + tagLenLen := writeVarint(tagLen[:], len(tag)) + + var bits byte + l := 1 + nameLenLen + len(n) + if exported { + bits |= 1 << 0 + } + if len(tag) > 0 { + l += tagLenLen + len(tag) + bits |= 1 << 1 + } + if embedded { + bits |= 1 << 3 + } + + b := make([]byte, l) + b[0] = bits + copy(b[1:], nameLen[:nameLenLen]) + copy(b[1+nameLenLen:], n) + if len(tag) > 0 { + tb := b[1+nameLenLen+len(n):] + copy(tb, tagLen[:tagLenLen]) + copy(tb[tagLenLen:], tag) + } + + return Name{Bytes: &b[0]} +} + +const ( + TraceArgsLimit = 10 // print no more than 10 args/components + TraceArgsMaxDepth = 5 // no more than 5 layers of nesting + + // maxLen is a (conservative) upper bound of the byte stream length. For + // each arg/component, it has no more than 2 bytes of data (size, offset), + // and no more than one {, }, ... at each level (it cannot have both the + // data and ... unless it is the last one, just be conservative). Plus 1 + // for _endSeq. + TraceArgsMaxLen = (TraceArgsMaxDepth*3+2)*TraceArgsLimit + 1 +) + +// Populate the data. +// The data is a stream of bytes, which contains the offsets and sizes of the +// non-aggregate arguments or non-aggregate fields/elements of aggregate-typed +// arguments, along with special "operators". Specifically, +// - for each non-aggregate arg/field/element, its offset from FP (1 byte) and +// size (1 byte) +// - special operators: +// - 0xff - end of sequence +// - 0xfe - print { (at the start of an aggregate-typed argument) +// - 0xfd - print } (at the end of an aggregate-typed argument) +// - 0xfc - print ... (more args/fields/elements) +// - 0xfb - print _ (offset too large) +const ( + TraceArgsEndSeq = 0xff + TraceArgsStartAgg = 0xfe + TraceArgsEndAgg = 0xfd + TraceArgsDotdotdot = 0xfc + TraceArgsOffsetTooLarge = 0xfb + TraceArgsSpecial = 0xf0 // above this are operators, below this are ordinary offsets +) + +// MaxPtrmaskBytes is the maximum length of a GC ptrmask bitmap, +// which holds 1-bit entries describing where pointers are in a given type. +// Above this length, the GC information is recorded as a GC program, +// which can express repetition compactly. In either form, the +// information is used by the runtime to initialize the heap bitmap, +// and for large types (like 128 or more words), they are roughly the +// same speed. GC programs are never much larger and often more +// compact. (If large arrays are involved, they can be arbitrarily +// more compact.) +// +// The cutoff must be large enough that any allocation large enough to +// use a GC program is large enough that it does not share heap bitmap +// bytes with any other objects, allowing the GC program execution to +// assume an aligned start and not use atomic operations. In the current +// runtime, this means all malloc size classes larger than the cutoff must +// be multiples of four words. On 32-bit systems that's 16 bytes, and +// all size classes >= 16 bytes are 16-byte aligned, so no real constraint. +// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed +// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated +// is 32 pointers, the bits for which fit in 4 bytes. So MaxPtrmaskBytes +// must be >= 4. +// +// We used to use 16 because the GC programs do have some constant overhead +// to get started, and processing 128 pointers seems to be enough to +// amortize that overhead well. +// +// To make sure that the runtime's chansend can call typeBitsBulkBarrier, +// we raised the limit to 2048, so that even 32-bit systems are guaranteed to +// use bitmaps for objects up to 64 kB in size. +const MaxPtrmaskBytes = 2048 diff --git a/internal/go/src/archive/archive.go b/internal/go/src/archive/archive.go new file mode 100755 index 0000000..8623acb --- /dev/null +++ b/internal/go/src/archive/archive.go @@ -0,0 +1,503 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package archive implements reading of archive files generated by the Go +// toolchain. +package archive + +import ( + "bufio" + "bytes" + "loov.dev/lensm/internal/go/src/bio" + "loov.dev/lensm/internal/go/src/goobj" + "errors" + "fmt" + "io" + "log" + "os" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +/* +The archive format is: + +First, on a line by itself + ! + +Then zero or more file records. Each file record has a fixed-size one-line header +followed by data bytes followed by an optional padding byte. The header is: + + %-16s%-12d%-6d%-6d%-8o%-10d` + name mtime uid gid mode size + +(note the trailing backquote). The %-16s here means at most 16 *bytes* of +the name, and if shorter, space padded on the right. +*/ + +// A Data is a reference to data stored in an object file. +// It records the offset and size of the data, so that a client can +// read the data only if necessary. +type Data struct { + Offset int64 + Size int64 +} + +type Archive struct { + f *os.File + Entries []Entry +} + +func (a *Archive) File() *os.File { return a.f } + +type Entry struct { + Name string + Type EntryType + Mtime int64 + Uid int + Gid int + Mode os.FileMode + Data + Obj *GoObj // nil if this entry is not a Go object file +} + +type EntryType int + +const ( + EntryPkgDef EntryType = iota + EntryGoObj + EntryNativeObj + EntrySentinelNonObj +) + +func (e *Entry) String() string { + return fmt.Sprintf("%s %6d/%-6d %12d %s %s", + (e.Mode & 0777).String(), + e.Uid, + e.Gid, + e.Size, + time.Unix(e.Mtime, 0).Format(timeFormat), + e.Name) +} + +type GoObj struct { + TextHeader []byte + Arch string + Data +} + +const ( + entryHeader = "%s%-12d%-6d%-6d%-8o%-10d`\n" + // In entryHeader the first entry, the name, is always printed as 16 bytes right-padded. + entryLen = 16 + 12 + 6 + 6 + 8 + 10 + 1 + 1 + timeFormat = "Jan _2 15:04 2006" +) + +var ( + archiveHeader = []byte("!\n") + archiveMagic = []byte("`\n") + goobjHeader = []byte("go objec") // truncated to size of archiveHeader + + errCorruptArchive = errors.New("corrupt archive") + errTruncatedArchive = errors.New("truncated archive") + errCorruptObject = errors.New("corrupt object file") + errNotObject = errors.New("unrecognized object file format") +) + +type ErrGoObjOtherVersion struct{ magic []byte } + +func (e ErrGoObjOtherVersion) Error() string { + return fmt.Sprintf("go object of a different version: %q", e.magic) +} + +// An objReader is an object file reader. +type objReader struct { + a *Archive + b *bio.Reader + err error + offset int64 + limit int64 + tmp [256]byte +} + +func (r *objReader) init(f *os.File) { + r.a = &Archive{f, nil} + r.offset, _ = f.Seek(0, io.SeekCurrent) + r.limit, _ = f.Seek(0, io.SeekEnd) + f.Seek(r.offset, io.SeekStart) + r.b = bio.NewReader(f) +} + +// error records that an error occurred. +// It returns only the first error, so that an error +// caused by an earlier error does not discard information +// about the earlier error. +func (r *objReader) error(err error) error { + if r.err == nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + r.err = err + } + // panic("corrupt") // useful for debugging + return r.err +} + +// peek returns the next n bytes without advancing the reader. +func (r *objReader) peek(n int) ([]byte, error) { + if r.err != nil { + return nil, r.err + } + if r.offset >= r.limit { + r.error(io.ErrUnexpectedEOF) + return nil, r.err + } + b, err := r.b.Peek(n) + if err != nil { + if err != bufio.ErrBufferFull { + r.error(err) + } + } + return b, err +} + +// readByte reads and returns a byte from the input file. +// On I/O error or EOF, it records the error but returns byte 0. +// A sequence of 0 bytes will eventually terminate any +// parsing state in the object file. In particular, it ends the +// reading of a varint. +func (r *objReader) readByte() byte { + if r.err != nil { + return 0 + } + if r.offset >= r.limit { + r.error(io.ErrUnexpectedEOF) + return 0 + } + b, err := r.b.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + r.error(err) + b = 0 + } else { + r.offset++ + } + return b +} + +// readFull reads exactly len(b) bytes from the input file. +// If an error occurs, read returns the error but also +// records it, so it is safe for callers to ignore the result +// as long as delaying the report is not a problem. +func (r *objReader) readFull(b []byte) error { + if r.err != nil { + return r.err + } + if r.offset+int64(len(b)) > r.limit { + return r.error(io.ErrUnexpectedEOF) + } + n, err := io.ReadFull(r.b, b) + r.offset += int64(n) + if err != nil { + return r.error(err) + } + return nil +} + +// skip skips n bytes in the input. +func (r *objReader) skip(n int64) { + if n < 0 { + r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip")) + } + if n < int64(len(r.tmp)) { + // Since the data is so small, a just reading from the buffered + // reader is better than flushing the buffer and seeking. + r.readFull(r.tmp[:n]) + } else if n <= int64(r.b.Buffered()) { + // Even though the data is not small, it has already been read. + // Advance the buffer instead of seeking. + for n > int64(len(r.tmp)) { + r.readFull(r.tmp[:]) + n -= int64(len(r.tmp)) + } + r.readFull(r.tmp[:n]) + } else { + // Seek, giving up buffered data. + r.b.MustSeek(r.offset+n, io.SeekStart) + r.offset += n + } +} + +// New writes to f to make a new archive. +func New(f *os.File) (*Archive, error) { + _, err := f.Write(archiveHeader) + if err != nil { + return nil, err + } + return &Archive{f: f}, nil +} + +// Parse parses an object file or archive from f. +func Parse(f *os.File, verbose bool) (*Archive, error) { + var r objReader + r.init(f) + t, err := r.peek(8) + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return nil, err + } + + switch { + default: + return nil, errNotObject + + case bytes.Equal(t, archiveHeader): + if err := r.parseArchive(verbose); err != nil { + return nil, err + } + case bytes.Equal(t, goobjHeader): + off := r.offset + o := &GoObj{} + if err := r.parseObject(o, r.limit-off); err != nil { + return nil, err + } + r.a.Entries = []Entry{{ + Name: f.Name(), + Type: EntryGoObj, + Data: Data{off, r.limit - off}, + Obj: o, + }} + } + + return r.a, nil +} + +// trimSpace removes trailing spaces from b and returns the corresponding string. +// This effectively parses the form used in archive headers. +func trimSpace(b []byte) string { + return string(bytes.TrimRight(b, " ")) +} + +// parseArchive parses a Unix archive of Go object files. +func (r *objReader) parseArchive(verbose bool) error { + r.readFull(r.tmp[:8]) // consume header (already checked) + for r.offset < r.limit { + if err := r.readFull(r.tmp[:60]); err != nil { + return err + } + data := r.tmp[:60] + + // Each file is preceded by this text header (slice indices in first column): + // 0:16 name + // 16:28 date + // 28:34 uid + // 34:40 gid + // 40:48 mode + // 48:58 size + // 58:60 magic - `\n + // We only care about name, size, and magic, unless in verbose mode. + // The fields are space-padded on the right. + // The size is in decimal. + // The file data - size bytes - follows the header. + // Headers are 2-byte aligned, so if size is odd, an extra padding + // byte sits between the file data and the next header. + // The file data that follows is padded to an even number of bytes: + // if size is odd, an extra padding byte is inserted betw the next header. + if len(data) < 60 { + return errTruncatedArchive + } + if !bytes.Equal(data[58:60], archiveMagic) { + return errCorruptArchive + } + name := trimSpace(data[0:16]) + var err error + get := func(start, end, base, bitsize int) int64 { + if err != nil { + return 0 + } + var v int64 + v, err = strconv.ParseInt(trimSpace(data[start:end]), base, bitsize) + return v + } + size := get(48, 58, 10, 64) + var ( + mtime int64 + uid, gid int + mode os.FileMode + ) + if verbose { + mtime = get(16, 28, 10, 64) + uid = int(get(28, 34, 10, 32)) + gid = int(get(34, 40, 10, 32)) + mode = os.FileMode(get(40, 48, 8, 32)) + } + if err != nil { + return errCorruptArchive + } + data = data[60:] + fsize := size + size&1 + if fsize < 0 || fsize < size { + return errCorruptArchive + } + switch name { + case "__.PKGDEF": + r.a.Entries = append(r.a.Entries, Entry{ + Name: name, + Type: EntryPkgDef, + Mtime: mtime, + Uid: uid, + Gid: gid, + Mode: mode, + Data: Data{r.offset, size}, + }) + r.skip(size) + case "preferlinkext", "dynimportfail": + if size == 0 { + // These are not actual objects, but rather sentinel + // entries put into the archive by the Go command to + // be read by the linker. See #62036. + r.a.Entries = append(r.a.Entries, Entry{ + Name: name, + Type: EntrySentinelNonObj, + Mtime: mtime, + Uid: uid, + Gid: gid, + Mode: mode, + Data: Data{r.offset, size}, + }) + break + } + fallthrough + default: + var typ EntryType + var o *GoObj + offset := r.offset + p, err := r.peek(8) + if err != nil { + return err + } + if bytes.Equal(p, goobjHeader) { + typ = EntryGoObj + o = &GoObj{} + err := r.parseObject(o, size) + if err != nil { + return err + } + } else { + typ = EntryNativeObj + r.skip(size) + } + r.a.Entries = append(r.a.Entries, Entry{ + Name: name, + Type: typ, + Mtime: mtime, + Uid: uid, + Gid: gid, + Mode: mode, + Data: Data{offset, size}, + Obj: o, + }) + } + if size&1 != 0 { + r.skip(1) + } + } + return nil +} + +// parseObject parses a single Go object file. +// The object file consists of a textual header ending in "\n!\n" +// and then the part we want to parse begins. +// The format of that part is defined in a comment at the top +// of cmd/internal/goobj/objfile.go. +func (r *objReader) parseObject(o *GoObj, size int64) error { + h := make([]byte, 0, 256) + var c1, c2, c3 byte + for { + c1, c2, c3 = c2, c3, r.readByte() + h = append(h, c3) + // The new export format can contain 0 bytes. + // Don't consider them errors, only look for r.err != nil. + if r.err != nil { + return errCorruptObject + } + if c1 == '\n' && c2 == '!' && c3 == '\n' { + break + } + } + o.TextHeader = h + hs := strings.Fields(string(h)) + if len(hs) >= 4 { + o.Arch = hs[3] + } + o.Offset = r.offset + o.Size = size - int64(len(h)) + + p, err := r.peek(8) + if err != nil { + return err + } + if !bytes.Equal(p, []byte(goobj.Magic)) { + if bytes.HasPrefix(p, []byte("\x00go1")) && bytes.HasSuffix(p, []byte("ld")) { + return r.error(ErrGoObjOtherVersion{p[1:]}) // strip the \x00 byte + } + return r.error(errCorruptObject) + } + r.skip(o.Size) + return nil +} + +// AddEntry adds an entry to the end of a, with the content from r. +func (a *Archive) AddEntry(typ EntryType, name string, mtime int64, uid, gid int, mode os.FileMode, size int64, r io.Reader) { + off, err := a.f.Seek(0, io.SeekEnd) + if err != nil { + log.Fatal(err) + } + n, err := fmt.Fprintf(a.f, entryHeader, exactly16Bytes(name), mtime, uid, gid, mode, size) + if err != nil || n != entryLen { + log.Fatal("writing entry header: ", err) + } + n1, _ := io.CopyN(a.f, r, size) + if n1 != size { + log.Fatal(err) + } + if (off+size)&1 != 0 { + a.f.Write([]byte{0}) // pad to even byte + } + a.Entries = append(a.Entries, Entry{ + Name: name, + Type: typ, + Mtime: mtime, + Uid: uid, + Gid: gid, + Mode: mode, + Data: Data{off + entryLen, size}, + }) +} + +// exactly16Bytes truncates the string if necessary so it is at most 16 bytes long, +// then pads the result with spaces to be exactly 16 bytes. +// Fmt uses runes for its width calculation, but we need bytes in the entry header. +func exactly16Bytes(s string) string { + for len(s) > 16 { + _, wid := utf8.DecodeLastRuneInString(s) + s = s[:len(s)-wid] + } + const sixteenSpaces = " " + s += sixteenSpaces[:16-len(s)] + return s +} + +// architecture-independent object file output +const HeaderSize = 60 + +func FormatHeader(arhdr []byte, name string, size int64) { + copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size)) +} diff --git a/internal/go/src/bio/buf.go b/internal/go/src/bio/buf.go new file mode 100755 index 0000000..c4c2514 --- /dev/null +++ b/internal/go/src/bio/buf.go @@ -0,0 +1,148 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bio implements common I/O abstractions used within the Go toolchain. +package bio + +import ( + "bufio" + "io" + "log" + "os" +) + +// Reader implements a seekable buffered io.Reader. +type Reader struct { + f *os.File + *bufio.Reader +} + +// Writer implements a seekable buffered io.Writer. +type Writer struct { + f *os.File + *bufio.Writer +} + +// Create creates the file named name and returns a Writer +// for that file. +func Create(name string) (*Writer, error) { + f, err := os.Create(name) + if err != nil { + return nil, err + } + return &Writer{f: f, Writer: bufio.NewWriter(f)}, nil +} + +// Open returns a Reader for the file named name. +func Open(name string) (*Reader, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + return NewReader(f), nil +} + +// NewReader returns a Reader from an open file. +func NewReader(f *os.File) *Reader { + return &Reader{f: f, Reader: bufio.NewReader(f)} +} + +func (r *Reader) MustSeek(offset int64, whence int) int64 { + if whence == 1 { + offset -= int64(r.Buffered()) + } + off, err := r.f.Seek(offset, whence) + if err != nil { + log.Fatalf("seeking in output: %v", err) + } + r.Reset(r.f) + return off +} + +func (w *Writer) MustSeek(offset int64, whence int) int64 { + if err := w.Flush(); err != nil { + log.Fatalf("writing output: %v", err) + } + off, err := w.f.Seek(offset, whence) + if err != nil { + log.Fatalf("seeking in output: %v", err) + } + return off +} + +func (r *Reader) Offset() int64 { + off, err := r.f.Seek(0, 1) + if err != nil { + log.Fatalf("seeking in output [0, 1]: %v", err) + } + off -= int64(r.Buffered()) + return off +} + +func (w *Writer) Offset() int64 { + if err := w.Flush(); err != nil { + log.Fatalf("writing output: %v", err) + } + off, err := w.f.Seek(0, 1) + if err != nil { + log.Fatalf("seeking in output [0, 1]: %v", err) + } + return off +} + +func (r *Reader) Close() error { + return r.f.Close() +} + +func (w *Writer) Close() error { + err := w.Flush() + err1 := w.f.Close() + if err == nil { + err = err1 + } + return err +} + +func (r *Reader) File() *os.File { + return r.f +} + +func (w *Writer) File() *os.File { + return w.f +} + +// Slice reads the next length bytes of r into a slice. +// +// This slice may be backed by mmap'ed memory. Currently, this memory +// will never be unmapped. The second result reports whether the +// backing memory is read-only. +func (r *Reader) Slice(length uint64) ([]byte, bool, error) { + if length == 0 { + return []byte{}, false, nil + } + + data, ok := r.sliceOS(length) + if ok { + return data, true, nil + } + + data = make([]byte, length) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, false, err + } + return data, false, nil +} + +// SliceRO returns a slice containing the next length bytes of r +// backed by a read-only mmap'd data. If the mmap cannot be +// established (limit exceeded, region too small, etc) a nil slice +// will be returned. If mmap succeeds, it will never be unmapped. +func (r *Reader) SliceRO(length uint64) []byte { + data, ok := r.sliceOS(length) + if ok { + return data + } + return nil +} diff --git a/internal/go/src/bio/buf_mmap.go b/internal/go/src/bio/buf_mmap.go new file mode 100755 index 0000000..9ce2b4d --- /dev/null +++ b/internal/go/src/bio/buf_mmap.go @@ -0,0 +1,62 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix + +package bio + +import ( + "runtime" + "sync/atomic" + "syscall" +) + +// mmapLimit is the maximum number of mmaped regions to create before +// falling back to reading into a heap-allocated slice. This exists +// because some operating systems place a limit on the number of +// distinct mapped regions per process. As of this writing: +// +// Darwin unlimited +// DragonFly 1000000 (vm.max_proc_mmap) +// FreeBSD unlimited +// Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count? +// NetBSD unlimited +// OpenBSD unlimited +var mmapLimit int32 = 1<<31 - 1 + +func init() { + // Linux is the only practically concerning OS. + if runtime.GOOS == "linux" { + mmapLimit = 30000 + } +} + +func (r *Reader) sliceOS(length uint64) ([]byte, bool) { + // For small slices, don't bother with the overhead of a + // mapping, especially since we have no way to unmap it. + const threshold = 16 << 10 + if length < threshold { + return nil, false + } + + // Have we reached the mmap limit? + if atomic.AddInt32(&mmapLimit, -1) < 0 { + atomic.AddInt32(&mmapLimit, 1) + return nil, false + } + + // Page-align the offset. + off := r.Offset() + align := syscall.Getpagesize() + aoff := off &^ int64(align-1) + + data, err := syscall.Mmap(int(r.f.Fd()), aoff, int(length+uint64(off-aoff)), syscall.PROT_READ, syscall.MAP_SHARED|syscall.MAP_FILE) + if err != nil { + return nil, false + } + + data = data[off-aoff:] + r.MustSeek(int64(length), 1) + return data, true +} diff --git a/internal/go/src/bio/buf_nommap.go b/internal/go/src/bio/buf_nommap.go new file mode 100755 index 0000000..1d78a01 --- /dev/null +++ b/internal/go/src/bio/buf_nommap.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !unix + +package bio + +func (r *Reader) sliceOS(length uint64) ([]byte, bool) { + return nil, false +} diff --git a/internal/go/src/bisect/bisect.go b/internal/go/src/bisect/bisect.go new file mode 100755 index 0000000..a79bb80 --- /dev/null +++ b/internal/go/src/bisect/bisect.go @@ -0,0 +1,778 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bisect can be used by compilers and other programs +// to serve as a target for the bisect debugging tool. +// See [golang.org/x/tools/cmd/bisect] for details about using the tool. +// +// To be a bisect target, allowing bisect to help determine which of a set of independent +// changes provokes a failure, a program needs to: +// +// 1. Define a way to accept a change pattern on its command line or in its environment. +// The most common mechanism is a command-line flag. +// The pattern can be passed to [New] to create a [Matcher], the compiled form of a pattern. +// +// 2. Assign each change a unique ID. One possibility is to use a sequence number, +// but the most common mechanism is to hash some kind of identifying information +// like the file and line number where the change might be applied. +// [Hash] hashes its arguments to compute an ID. +// +// 3. Enable each change that the pattern says should be enabled. +// The [Matcher.ShouldEnable] method answers this question for a given change ID. +// +// 4. Print a report identifying each change that the pattern says should be printed. +// The [Matcher.ShouldPrint] method answers this question for a given change ID. +// The report consists of one more lines on standard error or standard output +// that contain a “match marker”. [Marker] returns the match marker for a given ID. +// When bisect reports a change as causing the failure, it identifies the change +// by printing the report lines with the match marker removed. +// +// # Example Usage +// +// A program starts by defining how it receives the pattern. In this example, we will assume a flag. +// The next step is to compile the pattern: +// +// m, err := bisect.New(patternFlag) +// if err != nil { +// log.Fatal(err) +// } +// +// Then, each time a potential change is considered, the program computes +// a change ID by hashing identifying information (source file and line, in this case) +// and then calls m.ShouldPrint and m.ShouldEnable to decide whether to +// print and enable the change, respectively. The two can return different values +// depending on whether bisect is trying to find a minimal set of changes to +// disable or to enable to provoke the failure. +// +// It is usually helpful to write a helper function that accepts the identifying information +// and then takes care of hashing, printing, and reporting whether the identified change +// should be enabled. For example, a helper for changes identified by a file and line number +// would be: +// +// func ShouldEnable(file string, line int) { +// h := bisect.Hash(file, line) +// if m.ShouldPrint(h) { +// fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line) +// } +// return m.ShouldEnable(h) +// } +// +// Finally, note that New returns a nil Matcher when there is no pattern, +// meaning that the target is not running under bisect at all, +// so all changes should be enabled and none should be printed. +// In that common case, the computation of the hash can be avoided entirely +// by checking for m == nil first: +// +// func ShouldEnable(file string, line int) bool { +// if m == nil { +// return true +// } +// h := bisect.Hash(file, line) +// if m.ShouldPrint(h) { +// fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line) +// } +// return m.ShouldEnable(h) +// } +// +// When the identifying information is expensive to format, this code can call +// [Matcher.MarkerOnly] to find out whether short report lines containing only the +// marker are permitted for a given run. (Bisect permits such lines when it is +// still exploring the space of possible changes and will not be showing the +// output to the user.) If so, the client can choose to print only the marker: +// +// func ShouldEnable(file string, line int) bool { +// if m == nil { +// return true +// } +// h := bisect.Hash(file, line) +// if m.ShouldPrint(h) { +// if m.MarkerOnly() { +// bisect.PrintMarker(os.Stderr, h) +// } else { +// fmt.Fprintf(os.Stderr, "%v %s:%d\n", bisect.Marker(h), file, line) +// } +// } +// return m.ShouldEnable(h) +// } +// +// This specific helper – deciding whether to enable a change identified by +// file and line number and printing about the change when necessary – is +// provided by the [Matcher.FileLine] method. +// +// Another common usage is deciding whether to make a change in a function +// based on the caller's stack, to identify the specific calling contexts that the +// change breaks. The [Matcher.Stack] method takes care of obtaining the stack, +// printing it when necessary, and reporting whether to enable the change +// based on that stack. +// +// # Pattern Syntax +// +// Patterns are generated by the bisect tool and interpreted by [New]. +// Users should not have to understand the patterns except when +// debugging a target's bisect support or debugging the bisect tool itself. +// +// The pattern syntax selecting a change is a sequence of bit strings +// separated by + and - operators. Each bit string denotes the set of +// changes with IDs ending in those bits, + is set addition, - is set subtraction, +// and the expression is evaluated in the usual left-to-right order. +// The special binary number “y” denotes the set of all changes, +// standing in for the empty bit string. +// In the expression, all the + operators must appear before all the - operators. +// A leading + adds to an empty set. A leading - subtracts from the set of all +// possible suffixes. +// +// For example: +// +// - “01+10” and “+01+10” both denote the set of changes +// with IDs ending with the bits 01 or 10. +// +// - “01+10-1001” denotes the set of changes with IDs +// ending with the bits 01 or 10, but excluding those ending in 1001. +// +// - “-01-1000” and “y-01-1000 both denote the set of all changes +// with IDs not ending in 01 nor 1000. +// +// - “0+1-01+001” is not a valid pattern, because all the + operators do not +// appear before all the - operators. +// +// In the syntaxes described so far, the pattern specifies the changes to +// enable and report. If a pattern is prefixed by a “!”, the meaning +// changes: the pattern specifies the changes to DISABLE and report. This +// mode of operation is needed when a program passes with all changes +// enabled but fails with no changes enabled. In this case, bisect +// searches for minimal sets of changes to disable. +// Put another way, the leading “!” inverts the result from [Matcher.ShouldEnable] +// but does not invert the result from [Matcher.ShouldPrint]. +// +// As a convenience for manual debugging, “n” is an alias for “!y”, +// meaning to disable and report all changes. +// +// Finally, a leading “v” in the pattern indicates that the reports will be shown +// to the user of bisect to describe the changes involved in a failure. +// At the API level, the leading “v” causes [Matcher.Visible] to return true. +// See the next section for details. +// +// # Match Reports +// +// The target program must enable only those changed matched +// by the pattern, and it must print a match report for each such change. +// A match report consists of one or more lines of text that will be +// printed by the bisect tool to describe a change implicated in causing +// a failure. Each line in the report for a given change must contain a +// match marker with that change ID, as returned by [Marker]. +// The markers are elided when displaying the lines to the user. +// +// A match marker has the form “[bisect-match 0x1234]” where +// 0x1234 is the change ID in hexadecimal. +// An alternate form is “[bisect-match 010101]”, giving the change ID in binary. +// +// When [Matcher.Visible] returns false, the match reports are only +// being processed by bisect to learn the set of enabled changes, +// not shown to the user, meaning that each report can be a match +// marker on a line by itself, eliding the usual textual description. +// When the textual description is expensive to compute, +// checking [Matcher.Visible] can help the avoid that expense +// in most runs. +package bisect + +import ( + "runtime" + "sync" + "sync/atomic" +) + +// New creates and returns a new Matcher implementing the given pattern. +// The pattern syntax is defined in the package doc comment. +// +// In addition to the pattern syntax syntax, New("") returns nil, nil. +// The nil *Matcher is valid for use: it returns true from ShouldEnable +// and false from ShouldPrint for all changes. Callers can avoid calling +// [Hash], [Matcher.ShouldEnable], and [Matcher.ShouldPrint] entirely +// when they recognize the nil Matcher. +func New(pattern string) (*Matcher, error) { + if pattern == "" { + return nil, nil + } + + m := new(Matcher) + + p := pattern + // Special case for leading 'q' so that 'qn' quietly disables, e.g. fmahash=qn to disable fma + // Any instance of 'v' disables 'q'. + if len(p) > 0 && p[0] == 'q' { + m.quiet = true + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + // Allow multiple v, so that “bisect cmd vPATTERN” can force verbose all the time. + for len(p) > 0 && p[0] == 'v' { + m.verbose = true + m.quiet = false + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + + // Allow multiple !, each negating the last, so that “bisect cmd !PATTERN” works + // even when bisect chooses to add its own !. + m.enable = true + for len(p) > 0 && p[0] == '!' { + m.enable = !m.enable + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + + if p == "n" { + // n is an alias for !y. + m.enable = !m.enable + p = "y" + } + + // Parse actual pattern syntax. + result := true + bits := uint64(0) + start := 0 + wid := 1 // 1-bit (binary); sometimes 4-bit (hex) + for i := 0; i <= len(p); i++ { + // Imagine a trailing - at the end of the pattern to flush final suffix + c := byte('-') + if i < len(p) { + c = p[i] + } + if i == start && wid == 1 && c == 'x' { // leading x for hex + start = i + 1 + wid = 4 + continue + } + switch c { + default: + return nil, &parseError{"invalid pattern syntax: " + pattern} + case '2', '3', '4', '5', '6', '7', '8', '9': + if wid != 4 { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + fallthrough + case '0', '1': + bits <<= wid + bits |= uint64(c - '0') + case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F': + if wid != 4 { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + bits <<= 4 + bits |= uint64(c&^0x20 - 'A' + 10) + case 'y': + if i+1 < len(p) && (p[i+1] == '0' || p[i+1] == '1') { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + bits = 0 + case '+', '-': + if c == '+' && result == false { + // Have already seen a -. Should be - from here on. + return nil, &parseError{"invalid pattern syntax (+ after -): " + pattern} + } + if i > 0 { + n := (i - start) * wid + if n > 64 { + return nil, &parseError{"pattern bits too long: " + pattern} + } + if n <= 0 { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + if p[start] == 'y' { + n = 0 + } + mask := uint64(1)<= 0; i-- { + c := &m.list[i] + if id&c.mask == c.bits { + return c.result + } + } + return false +} + +// FileLine reports whether the change identified by file and line should be enabled. +// If the change should be printed, FileLine prints a one-line report to w. +func (m *Matcher) FileLine(w Writer, file string, line int) bool { + if m == nil { + return true + } + return m.fileLine(w, file, line) +} + +// fileLine does the real work for FileLine. +// This lets FileLine's body handle m == nil and potentially be inlined. +func (m *Matcher) fileLine(w Writer, file string, line int) bool { + h := Hash(file, line) + if m.ShouldPrint(h) { + if m.MarkerOnly() { + PrintMarker(w, h) + } else { + printFileLine(w, h, file, line) + } + } + return m.ShouldEnable(h) +} + +// printFileLine prints a non-marker-only report for file:line to w. +func printFileLine(w Writer, h uint64, file string, line int) error { + const markerLen = 40 // overestimate + b := make([]byte, 0, markerLen+len(file)+24) + b = AppendMarker(b, h) + b = appendFileLine(b, file, line) + b = append(b, '\n') + _, err := w.Write(b) + return err +} + +// appendFileLine appends file:line to dst, returning the extended slice. +func appendFileLine(dst []byte, file string, line int) []byte { + dst = append(dst, file...) + dst = append(dst, ':') + u := uint(line) + if line < 0 { + dst = append(dst, '-') + u = -u + } + var buf [24]byte + i := len(buf) + for i == len(buf) || u > 0 { + i-- + buf[i] = '0' + byte(u%10) + u /= 10 + } + dst = append(dst, buf[i:]...) + return dst +} + +// MatchStack assigns the current call stack a change ID. +// If the stack should be printed, MatchStack prints it. +// Then MatchStack reports whether a change at the current call stack should be enabled. +func (m *Matcher) Stack(w Writer) bool { + if m == nil { + return true + } + return m.stack(w) +} + +// stack does the real work for Stack. +// This lets stack's body handle m == nil and potentially be inlined. +func (m *Matcher) stack(w Writer) bool { + const maxStack = 16 + var stk [maxStack]uintptr + n := runtime.Callers(2, stk[:]) + // caller #2 is not for printing; need it to normalize PCs if ASLR. + if n <= 1 { + return false + } + + base := stk[0] + // normalize PCs + for i := range stk[:n] { + stk[i] -= base + } + + h := Hash(stk[:n]) + if m.ShouldPrint(h) { + var d *dedup + for { + d = m.dedup.Load() + if d != nil { + break + } + d = new(dedup) + if m.dedup.CompareAndSwap(nil, d) { + break + } + } + + if m.MarkerOnly() { + if !d.seenLossy(h) { + PrintMarker(w, h) + } + } else { + if !d.seen(h) { + // Restore PCs in stack for printing + for i := range stk[:n] { + stk[i] += base + } + printStack(w, h, stk[1:n]) + } + } + } + return m.ShouldEnable(h) +} + +// Writer is the same interface as io.Writer. +// It is duplicated here to avoid importing io. +type Writer interface { + Write([]byte) (int, error) +} + +// PrintMarker prints to w a one-line report containing only the marker for h. +// It is appropriate to use when [Matcher.ShouldPrint] and [Matcher.MarkerOnly] both return true. +func PrintMarker(w Writer, h uint64) error { + var buf [50]byte + b := AppendMarker(buf[:0], h) + b = append(b, '\n') + _, err := w.Write(b) + return err +} + +// printStack prints to w a multi-line report containing a formatting of the call stack stk, +// with each line preceded by the marker for h. +func printStack(w Writer, h uint64, stk []uintptr) error { + buf := make([]byte, 0, 2048) + + var prefixBuf [100]byte + prefix := AppendMarker(prefixBuf[:0], h) + + frames := runtime.CallersFrames(stk) + for { + f, more := frames.Next() + buf = append(buf, prefix...) + buf = append(buf, f.Function...) + buf = append(buf, "()\n"...) + buf = append(buf, prefix...) + buf = append(buf, '\t') + buf = appendFileLine(buf, f.File, f.Line) + buf = append(buf, '\n') + if !more { + break + } + } + buf = append(buf, prefix...) + buf = append(buf, '\n') + _, err := w.Write(buf) + return err +} + +// Marker returns the match marker text to use on any line reporting details +// about a match of the given ID. +// It always returns the hexadecimal format. +func Marker(id uint64) string { + return string(AppendMarker(nil, id)) +} + +// AppendMarker is like [Marker] but appends the marker to dst. +func AppendMarker(dst []byte, id uint64) []byte { + const prefix = "[bisect-match 0x" + var buf [len(prefix) + 16 + 1]byte + copy(buf[:], prefix) + for i := 0; i < 16; i++ { + buf[len(prefix)+i] = "0123456789abcdef"[id>>60] + id <<= 4 + } + buf[len(prefix)+16] = ']' + return append(dst, buf[:]...) +} + +// CutMarker finds the first match marker in line and removes it, +// returning the shortened line (with the marker removed), +// the ID from the match marker, +// and whether a marker was found at all. +// If there is no marker, CutMarker returns line, 0, false. +func CutMarker(line string) (short string, id uint64, ok bool) { + // Find first instance of prefix. + prefix := "[bisect-match " + i := 0 + for ; ; i++ { + if i >= len(line)-len(prefix) { + return line, 0, false + } + if line[i] == '[' && line[i:i+len(prefix)] == prefix { + break + } + } + + // Scan to ]. + j := i + len(prefix) + for j < len(line) && line[j] != ']' { + j++ + } + if j >= len(line) { + return line, 0, false + } + + // Parse id. + idstr := line[i+len(prefix) : j] + if len(idstr) >= 3 && idstr[:2] == "0x" { + // parse hex + if len(idstr) > 2+16 { // max 0x + 16 digits + return line, 0, false + } + for i := 2; i < len(idstr); i++ { + id <<= 4 + switch c := idstr[i]; { + case '0' <= c && c <= '9': + id |= uint64(c - '0') + case 'a' <= c && c <= 'f': + id |= uint64(c - 'a' + 10) + case 'A' <= c && c <= 'F': + id |= uint64(c - 'A' + 10) + } + } + } else { + if idstr == "" || len(idstr) > 64 { // min 1 digit, max 64 digits + return line, 0, false + } + // parse binary + for i := 0; i < len(idstr); i++ { + id <<= 1 + switch c := idstr[i]; c { + default: + return line, 0, false + case '0', '1': + id |= uint64(c - '0') + } + } + } + + // Construct shortened line. + // Remove at most one space from around the marker, + // so that "foo [marker] bar" shortens to "foo bar". + j++ // skip ] + if i > 0 && line[i-1] == ' ' { + i-- + } else if j < len(line) && line[j] == ' ' { + j++ + } + short = line[:i] + line[j:] + return short, id, true +} + +// Hash computes a hash of the data arguments, +// each of which must be of type string, byte, int, uint, int32, uint32, int64, uint64, uintptr, or a slice of one of those types. +func Hash(data ...any) uint64 { + h := offset64 + for _, v := range data { + switch v := v.(type) { + default: + // Note: Not printing the type, because reflect.ValueOf(v) + // would make the interfaces prepared by the caller escape + // and therefore allocate. This way, Hash(file, line) runs + // without any allocation. It should be clear from the + // source code calling Hash what the bad argument was. + panic("bisect.Hash: unexpected argument type") + case string: + h = fnvString(h, v) + case byte: + h = fnv(h, v) + case int: + h = fnvUint64(h, uint64(v)) + case uint: + h = fnvUint64(h, uint64(v)) + case int32: + h = fnvUint32(h, uint32(v)) + case uint32: + h = fnvUint32(h, v) + case int64: + h = fnvUint64(h, uint64(v)) + case uint64: + h = fnvUint64(h, v) + case uintptr: + h = fnvUint64(h, uint64(v)) + case []string: + for _, x := range v { + h = fnvString(h, x) + } + case []byte: + for _, x := range v { + h = fnv(h, x) + } + case []int: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + case []uint: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + case []int32: + for _, x := range v { + h = fnvUint32(h, uint32(x)) + } + case []uint32: + for _, x := range v { + h = fnvUint32(h, x) + } + case []int64: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + case []uint64: + for _, x := range v { + h = fnvUint64(h, x) + } + case []uintptr: + for _, x := range v { + h = fnvUint64(h, uint64(x)) + } + } + } + return h +} + +// Trivial error implementation, here to avoid importing errors. + +// parseError is a trivial error implementation, +// defined here to avoid importing errors. +type parseError struct{ text string } + +func (e *parseError) Error() string { return e.text } + +// FNV-1a implementation. See Go's hash/fnv/fnv.go. +// Copied here for simplicity (can handle integers more directly) +// and to avoid importing hash/fnv. + +const ( + offset64 uint64 = 14695981039346656037 + prime64 uint64 = 1099511628211 +) + +func fnv(h uint64, x byte) uint64 { + h ^= uint64(x) + h *= prime64 + return h +} + +func fnvString(h uint64, x string) uint64 { + for i := 0; i < len(x); i++ { + h ^= uint64(x[i]) + h *= prime64 + } + return h +} + +func fnvUint64(h uint64, x uint64) uint64 { + for i := 0; i < 8; i++ { + h ^= x & 0xFF + x >>= 8 + h *= prime64 + } + return h +} + +func fnvUint32(h uint64, x uint32) uint64 { + for i := 0; i < 4; i++ { + h ^= uint64(x & 0xFF) + x >>= 8 + h *= prime64 + } + return h +} + +// A dedup is a deduplicator for call stacks, so that we only print +// a report for new call stacks, not for call stacks we've already +// reported. +// +// It has two modes: an approximate but lock-free mode that +// may still emit some duplicates, and a precise mode that uses +// a lock and never emits duplicates. +type dedup struct { + // 128-entry 4-way, lossy cache for seenLossy + recent [128][4]uint64 + + // complete history for seen + mu sync.Mutex + m map[uint64]bool +} + +// seen records that h has now been seen and reports whether it was seen before. +// When seen returns false, the caller is expected to print a report for h. +func (d *dedup) seen(h uint64) bool { + d.mu.Lock() + if d.m == nil { + d.m = make(map[uint64]bool) + } + seen := d.m[h] + d.m[h] = true + d.mu.Unlock() + return seen +} + +// seenLossy is a variant of seen that avoids a lock by using a cache of recently seen hashes. +// Each cache entry is N-way set-associative: h can appear in any of the slots. +// If h does not appear in any of them, then it is inserted into a random slot, +// overwriting whatever was there before. +func (d *dedup) seenLossy(h uint64) bool { + cache := &d.recent[uint(h)%uint(len(d.recent))] + for i := 0; i < len(cache); i++ { + if atomic.LoadUint64(&cache[i]) == h { + return true + } + } + + // Compute index in set to evict as hash of current set. + ch := offset64 + for _, x := range cache { + ch = fnvUint64(ch, x) + } + atomic.StoreUint64(&cache[uint(ch)%uint(len(cache))], h) + return false +} diff --git a/internal/go/src/buildcfg/cfg.go b/internal/go/src/buildcfg/cfg.go new file mode 100755 index 0000000..89fd74e --- /dev/null +++ b/internal/go/src/buildcfg/cfg.go @@ -0,0 +1,467 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package buildcfg provides access to the build configuration +// described by the current environment. It is for use by build tools +// such as cmd/go or cmd/compile and for setting up go/build's Default context. +// +// Note that it does NOT provide access to the build configuration used to +// build the currently-running binary. For that, use runtime.GOOS etc +// as well as internal/goexperiment. +package buildcfg + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" +) + +var ( + GOROOT = os.Getenv("GOROOT") // cached for efficiency + GOARCH = envOr("GOARCH", defaultGOARCH) + GOOS = envOr("GOOS", defaultGOOS) + GO386 = envOr("GO386", DefaultGO386) + GOAMD64 = goamd64() + GOARM = goarm() + GOARM64 = goarm64() + GOMIPS = gomips() + GOMIPS64 = gomips64() + GOPPC64 = goppc64() + GORISCV64 = goriscv64() + GOWASM = gowasm() + ToolTags = toolTags() + GO_LDSO = defaultGO_LDSO + GOFIPS140 = gofips140() + Version = version +) + +// Error is one of the errors found (if any) in the build configuration. +var Error error + +// Check exits the program with a fatal error if Error is non-nil. +func Check() { + if Error != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), Error) + os.Exit(2) + } +} + +func envOr(key, value string) string { + if x := os.Getenv(key); x != "" { + return x + } + return value +} + +func goamd64() int { + switch v := envOr("GOAMD64", DefaultGOAMD64); v { + case "v1": + return 1 + case "v2": + return 2 + case "v3": + return 3 + case "v4": + return 4 + } + Error = fmt.Errorf("invalid GOAMD64: must be v1, v2, v3, v4") + return int(DefaultGOAMD64[len("v")] - '0') +} + +func gofips140() string { + v := envOr("GOFIPS140", DefaultGOFIPS140) + switch v { + case "off", "latest", "inprocess", "certified": + return v + } + if isFIPSVersion(v) { + return v + } + Error = fmt.Errorf("invalid GOFIPS140: must be off, latest, inprocess, certified, or v1.Y.Z") + return DefaultGOFIPS140 +} + +// isFIPSVersion reports whether v is a valid FIPS version, +// of the form v1.Y.Z or v1.Y.Z-hhhhhhhh or v1.Y.Z-rcN. +func isFIPSVersion(v string) bool { + v, ok := strings.CutPrefix(v, "v1.") + if !ok { + return false + } + if v, ok = cutNum(v); !ok { + return false + } + if v, ok = strings.CutPrefix(v, "."); !ok { + return false + } + if v, ok = cutNum(v); !ok { + return false + } + if v == "" { + return true + } + if v, ok = strings.CutPrefix(v, "-rc"); ok { + v, ok = cutNum(v) + return ok && v == "" + } + if v, ok = strings.CutPrefix(v, "-"); ok { + return len(v) == 8 + } + return false +} + +// cutNum skips the leading text matching [0-9]+ +// in s, returning the rest and whether such text was found. +func cutNum(s string) (rest string, ok bool) { + i := 0 + for i < len(s) && '0' <= s[i] && s[i] <= '9' { + i++ + } + return s[i:], i > 0 +} + +type GoarmFeatures struct { + Version int + SoftFloat bool +} + +func (g GoarmFeatures) String() string { + armStr := strconv.Itoa(g.Version) + if g.SoftFloat { + armStr += ",softfloat" + } else { + armStr += ",hardfloat" + } + return armStr +} + +func goarm() (g GoarmFeatures) { + const ( + softFloatOpt = ",softfloat" + hardFloatOpt = ",hardfloat" + ) + def := DefaultGOARM + if GOOS == "android" && GOARCH == "arm" { + // Android arm devices always support GOARM=7. + def = "7" + } + v := envOr("GOARM", def) + + floatSpecified := false + if strings.HasSuffix(v, softFloatOpt) { + g.SoftFloat = true + floatSpecified = true + v = v[:len(v)-len(softFloatOpt)] + } + if strings.HasSuffix(v, hardFloatOpt) { + floatSpecified = true + v = v[:len(v)-len(hardFloatOpt)] + } + + switch v { + case "5": + g.Version = 5 + case "6": + g.Version = 6 + case "7": + g.Version = 7 + default: + Error = fmt.Errorf("invalid GOARM: must start with 5, 6, or 7, and may optionally end in either %q or %q", hardFloatOpt, softFloatOpt) + g.Version = int(def[0] - '0') + } + + // 5 defaults to softfloat. 6 and 7 default to hardfloat. + if !floatSpecified && g.Version == 5 { + g.SoftFloat = true + } + return +} + +type Goarm64Features struct { + Version string + // Large Systems Extension + LSE bool + // ARM v8.0 Cryptographic Extension. It includes the following features: + // * FEAT_AES, which includes the AESD and AESE instructions. + // * FEAT_PMULL, which includes the PMULL, PMULL2 instructions. + // * FEAT_SHA1, which includes the SHA1* instructions. + // * FEAT_SHA256, which includes the SHA256* instructions. + Crypto bool +} + +func (g Goarm64Features) String() string { + arm64Str := g.Version + if g.LSE { + arm64Str += ",lse" + } + if g.Crypto { + arm64Str += ",crypto" + } + return arm64Str +} + +func ParseGoarm64(v string) (g Goarm64Features, e error) { + const ( + lseOpt = ",lse" + cryptoOpt = ",crypto" + ) + + g.LSE = false + g.Crypto = false + // We allow any combination of suffixes, in any order + for { + if strings.HasSuffix(v, lseOpt) { + g.LSE = true + v = v[:len(v)-len(lseOpt)] + continue + } + + if strings.HasSuffix(v, cryptoOpt) { + g.Crypto = true + v = v[:len(v)-len(cryptoOpt)] + continue + } + + break + } + + switch v { + case "v8.0": + g.Version = v + case "v8.1", "v8.2", "v8.3", "v8.4", "v8.5", "v8.6", "v8.7", "v8.8", "v8.9", + "v9.0", "v9.1", "v9.2", "v9.3", "v9.4", "v9.5": + g.Version = v + // LSE extension is mandatory starting from 8.1 + g.LSE = true + default: + e = fmt.Errorf("invalid GOARM64: must start with v8.{0-9} or v9.{0-5} and may optionally end in %q and/or %q", + lseOpt, cryptoOpt) + g.Version = DefaultGOARM64 + } + + return +} + +func goarm64() (g Goarm64Features) { + g, Error = ParseGoarm64(envOr("GOARM64", DefaultGOARM64)) + return +} + +// Returns true if g supports giving ARM64 ISA +// Note that this function doesn't accept / test suffixes (like ",lse" or ",crypto") +func (g Goarm64Features) Supports(s string) bool { + // We only accept "v{8-9}.{0-9}. Everything else is malformed. + if len(s) != 4 { + return false + } + + major := s[1] + minor := s[3] + + // We only accept "v{8-9}.{0-9}. Everything else is malformed. + if major < '8' || major > '9' || + minor < '0' || minor > '9' || + s[0] != 'v' || s[2] != '.' { + return false + } + + g_major := g.Version[1] + g_minor := g.Version[3] + + if major == g_major { + return minor <= g_minor + } else if g_major == '9' { + // v9.0 diverged from v8.5. This means we should compare with g_minor increased by five. + return minor <= g_minor+5 + } else { + return false + } +} + +func gomips() string { + switch v := envOr("GOMIPS", DefaultGOMIPS); v { + case "hardfloat", "softfloat": + return v + } + Error = fmt.Errorf("invalid GOMIPS: must be hardfloat, softfloat") + return DefaultGOMIPS +} + +func gomips64() string { + switch v := envOr("GOMIPS64", DefaultGOMIPS64); v { + case "hardfloat", "softfloat": + return v + } + Error = fmt.Errorf("invalid GOMIPS64: must be hardfloat, softfloat") + return DefaultGOMIPS64 +} + +func goppc64() int { + switch v := envOr("GOPPC64", DefaultGOPPC64); v { + case "power8": + return 8 + case "power9": + return 9 + case "power10": + return 10 + } + Error = fmt.Errorf("invalid GOPPC64: must be power8, power9, power10") + return int(DefaultGOPPC64[len("power")] - '0') +} + +func goriscv64() int { + switch v := envOr("GORISCV64", DefaultGORISCV64); v { + case "rva20u64": + return 20 + case "rva22u64": + return 22 + case "rva23u64": + return 23 + } + Error = fmt.Errorf("invalid GORISCV64: must be rva20u64, rva22u64, rva23u64") + v := DefaultGORISCV64[len("rva"):] + i := strings.IndexFunc(v, func(r rune) bool { + return r < '0' || r > '9' + }) + year, _ := strconv.Atoi(v[:i]) + return year +} + +type gowasmFeatures struct { + // Legacy features, now always enabled + //SatConv bool + //SignExt bool +} + +func (f gowasmFeatures) String() string { + var flags []string + return strings.Join(flags, ",") +} + +func gowasm() (f gowasmFeatures) { + for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") { + switch opt { + case "satconv": + // ignore, always enabled + case "signext": + // ignore, always enabled + case "": + // ignore + default: + Error = fmt.Errorf("invalid GOWASM: no such feature %q", opt) + } + } + return +} + +func Getgoextlinkenabled() string { + return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED) +} + +func toolTags() []string { + tags := experimentTags() + tags = append(tags, gogoarchTags()...) + return tags +} + +func experimentTags() []string { + var list []string + // For each experiment that has been enabled in the toolchain, define a + // build tag with the same name but prefixed by "goexperiment." which can be + // used for compiling alternative files for the experiment. This allows + // changes for the experiment, like extra struct fields in the runtime, + // without affecting the base non-experiment code at all. + for _, exp := range Experiment.Enabled() { + list = append(list, "goexperiment."+exp) + } + return list +} + +// GOGOARCH returns the name and value of the GO$GOARCH setting. +// For example, if GOARCH is "amd64" it might return "GOAMD64", "v2". +func GOGOARCH() (name, value string) { + switch GOARCH { + case "386": + return "GO386", GO386 + case "amd64": + return "GOAMD64", fmt.Sprintf("v%d", GOAMD64) + case "arm": + return "GOARM", GOARM.String() + case "arm64": + return "GOARM64", GOARM64.String() + case "mips", "mipsle": + return "GOMIPS", GOMIPS + case "mips64", "mips64le": + return "GOMIPS64", GOMIPS64 + case "ppc64", "ppc64le": + return "GOPPC64", fmt.Sprintf("power%d", GOPPC64) + case "riscv64": + return "GORISCV64", fmt.Sprintf("rva%du64", GORISCV64) + case "wasm": + return "GOWASM", GOWASM.String() + } + return "", "" +} + +func gogoarchTags() []string { + switch GOARCH { + case "386": + return []string{GOARCH + "." + GO386} + case "amd64": + var list []string + for i := 1; i <= GOAMD64; i++ { + list = append(list, fmt.Sprintf("%s.v%d", GOARCH, i)) + } + return list + case "arm": + var list []string + for i := 5; i <= GOARM.Version; i++ { + list = append(list, fmt.Sprintf("%s.%d", GOARCH, i)) + } + return list + case "arm64": + var list []string + major := int(GOARM64.Version[1] - '0') + minor := int(GOARM64.Version[3] - '0') + for i := 0; i <= minor; i++ { + list = append(list, fmt.Sprintf("%s.v%d.%d", GOARCH, major, i)) + } + // ARM64 v9.x also includes support of v8.x+5 (i.e. v9.1 includes v8.(1+5) = v8.6). + if major == 9 { + for i := 0; i <= minor+5 && i <= 9; i++ { + list = append(list, fmt.Sprintf("%s.v%d.%d", GOARCH, 8, i)) + } + } + return list + case "mips", "mipsle": + return []string{GOARCH + "." + GOMIPS} + case "mips64", "mips64le": + return []string{GOARCH + "." + GOMIPS64} + case "ppc64", "ppc64le": + var list []string + for i := 8; i <= GOPPC64; i++ { + list = append(list, fmt.Sprintf("%s.power%d", GOARCH, i)) + } + return list + case "riscv64": + list := []string{GOARCH + "." + "rva20u64"} + if GORISCV64 >= 22 { + list = append(list, GOARCH+"."+"rva22u64") + } + if GORISCV64 >= 23 { + list = append(list, GOARCH+"."+"rva23u64") + } + return list + case "wasm": + var list []string + // SatConv is always enabled + list = append(list, GOARCH+".satconv") + // SignExt is always enabled + list = append(list, GOARCH+".signext") + return list + } + return nil +} diff --git a/internal/go/src/buildcfg/exp.go b/internal/go/src/buildcfg/exp.go new file mode 100755 index 0000000..5921962 --- /dev/null +++ b/internal/go/src/buildcfg/exp.go @@ -0,0 +1,203 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package buildcfg + +import ( + "fmt" + "reflect" + "strings" + + "loov.dev/lensm/internal/go/src/goexperiment" +) + +// ExperimentFlags represents a set of GOEXPERIMENT flags relative to a baseline +// (platform-default) experiment configuration. +type ExperimentFlags struct { + goexperiment.Flags + baseline goexperiment.Flags +} + +// Experiment contains the toolchain experiments enabled for the +// current build. +// +// (This is not necessarily the set of experiments the compiler itself +// was built with.) +// +// Experiment.baseline specifies the experiment flags that are enabled by +// default in the current toolchain. This is, in effect, the "control" +// configuration and any variation from this is an experiment. +var Experiment ExperimentFlags = func() ExperimentFlags { + flags, err := ParseGOEXPERIMENT(GOOS, GOARCH, envOr("GOEXPERIMENT", defaultGOEXPERIMENT)) + if err != nil { + Error = err + return ExperimentFlags{} + } + return *flags +}() + +// DefaultGOEXPERIMENT is the embedded default GOEXPERIMENT string. +// It is not guaranteed to be canonical. +const DefaultGOEXPERIMENT = defaultGOEXPERIMENT + +// FramePointerEnabled enables the use of platform conventions for +// saving frame pointers. +// +// This used to be an experiment, but now it's always enabled on +// platforms that support it. +// +// Note: must agree with runtime.framepointer_enabled. +var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64" + +// ParseGOEXPERIMENT parses a (GOOS, GOARCH, GOEXPERIMENT) +// configuration tuple and returns the enabled and baseline experiment +// flag sets. +// +// TODO(mdempsky): Move to [internal/goexperiment]. +func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { + // regabiSupported is set to true on platforms where register ABI is + // supported and enabled by default. + // regabiAlwaysOn is set to true on platforms where register ABI is + // always on. + var regabiSupported, regabiAlwaysOn bool + switch goarch { + case "amd64", "arm64", "loong64", "ppc64le", "ppc64", "riscv64": + regabiAlwaysOn = true + regabiSupported = true + case "s390x": + regabiSupported = true + } + + // Older versions (anything before V16) of dsymutil don't handle + // the .debug_rnglists section in DWARF5. See + // https://github.com/golang/go/issues/26379#issuecomment-2677068742 + // for more context. This disables all DWARF5 on mac, which is not + // ideal (would be better to disable just for cases where we know + // the build will use external linking). In the GOOS=aix case, the + // XCOFF format (as far as can be determined) doesn't seem to + // support the necessary section subtypes for DWARF-specific + // things like .debug_addr (needed for DWARF 5). + dwarf5Supported := (goos != "darwin" && goos != "ios" && goos != "aix") + + baseline := goexperiment.Flags{ + RegabiWrappers: regabiSupported, + RegabiArgs: regabiSupported, + Dwarf5: dwarf5Supported, + RandomizedHeapBase64: true, + GreenTeaGC: true, + } + flags := &ExperimentFlags{ + Flags: baseline, + baseline: baseline, + } + + // Pick up any changes to the baseline configuration from the + // GOEXPERIMENT environment. This can be set at make.bash time + // and overridden at build time. + if goexp != "" { + // Create a map of known experiment names. + names := make(map[string]func(bool)) + rv := reflect.ValueOf(&flags.Flags).Elem() + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + field := rv.Field(i) + names[strings.ToLower(rt.Field(i).Name)] = field.SetBool + } + + // "regabi" is an alias for all working regabi + // subexperiments, and not an experiment itself. Doing + // this as an alias make both "regabi" and "noregabi" + // do the right thing. + names["regabi"] = func(v bool) { + flags.RegabiWrappers = v + flags.RegabiArgs = v + } + + // Parse names. + for f := range strings.SplitSeq(goexp, ",") { + if f == "" { + continue + } + if f == "none" { + // GOEXPERIMENT=none disables all experiment flags. + // This is used by cmd/dist, which doesn't know how + // to build with any experiment flags. + flags.Flags = goexperiment.Flags{} + continue + } + val := true + if strings.HasPrefix(f, "no") { + f, val = f[2:], false + } + set, ok := names[f] + if !ok { + return nil, fmt.Errorf("unknown GOEXPERIMENT %s", f) + } + set(val) + } + } + + if regabiAlwaysOn { + flags.RegabiWrappers = true + flags.RegabiArgs = true + } + // regabi is only supported on amd64, arm64, loong64, riscv64, s390x, ppc64 and ppc64le. + if !regabiSupported { + flags.RegabiWrappers = false + flags.RegabiArgs = false + } + // Check regabi dependencies. + if flags.RegabiArgs && !flags.RegabiWrappers { + return nil, fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers") + } + return flags, nil +} + +// String returns the canonical GOEXPERIMENT string to enable this experiment +// configuration. (Experiments in the same state as in the baseline are elided.) +func (exp *ExperimentFlags) String() string { + return strings.Join(expList(&exp.Flags, &exp.baseline, false), ",") +} + +// expList returns the list of lower-cased experiment names for +// experiments that differ from base. base may be nil to indicate no +// experiments. If all is true, then include all experiment flags, +// regardless of base. +func expList(exp, base *goexperiment.Flags, all bool) []string { + var list []string + rv := reflect.ValueOf(exp).Elem() + var rBase reflect.Value + if base != nil { + rBase = reflect.ValueOf(base).Elem() + } + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + name := strings.ToLower(rt.Field(i).Name) + val := rv.Field(i).Bool() + baseVal := false + if base != nil { + baseVal = rBase.Field(i).Bool() + } + if all || val != baseVal { + if val { + list = append(list, name) + } else { + list = append(list, "no"+name) + } + } + } + return list +} + +// Enabled returns a list of enabled experiments, as +// lower-cased experiment names. +func (exp *ExperimentFlags) Enabled() []string { + return expList(&exp.Flags, nil, false) +} + +// All returns a list of all experiment settings. +// Disabled experiments appear in the list prefixed by "no". +func (exp *ExperimentFlags) All() []string { + return expList(&exp.Flags, nil, true) +} diff --git a/internal/go/src/buildcfg/zbootstrap.go b/internal/go/src/buildcfg/zbootstrap.go new file mode 100755 index 0000000..95cda6e --- /dev/null +++ b/internal/go/src/buildcfg/zbootstrap.go @@ -0,0 +1,22 @@ +// Code generated by go tool dist; DO NOT EDIT. + +package buildcfg + +import "runtime" + +const DefaultGO386 = `sse2` +const DefaultGOAMD64 = `v1` +const DefaultGOARM = `7` +const DefaultGOARM64 = `v8.0` +const DefaultGOMIPS = `hardfloat` +const DefaultGOMIPS64 = `hardfloat` +const DefaultGOPPC64 = `power8` +const DefaultGORISCV64 = `rva20u64` +const defaultGOEXPERIMENT = `` +const defaultGO_EXTLINK_ENABLED = `` +const defaultGO_LDSO = `` +const version = `go1.26.4` +const defaultGOOS = runtime.GOOS +const defaultGOARCH = runtime.GOARCH +const DefaultGOFIPS140 = `off` +const DefaultCGO_ENABLED = "" diff --git a/internal/go/src/disasm/disasm.go b/internal/go/src/disasm/disasm.go new file mode 100755 index 0000000..f6e6852 --- /dev/null +++ b/internal/go/src/disasm/disasm.go @@ -0,0 +1,470 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package disasm provides disassembly routines. +// +// It is broken out from cmd/internal/objfile so tools that don't need +// disassembling don't need to depend on x/arch disassembler code. +package disasm + +import ( + "bufio" + "bytes" + "container/list" + "encoding/binary" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "sort" + "strings" + "text/tabwriter" + + "loov.dev/lensm/internal/go/src/objfile" + "loov.dev/lensm/internal/go/src/src" + + "golang.org/x/arch/arm/armasm" + "golang.org/x/arch/arm64/arm64asm" + "golang.org/x/arch/loong64/loong64asm" + "golang.org/x/arch/ppc64/ppc64asm" + "golang.org/x/arch/riscv64/riscv64asm" + "golang.org/x/arch/s390x/s390xasm" + "golang.org/x/arch/x86/x86asm" +) + +// Disasm is a disassembler for a given File. +type Disasm struct { + syms []objfile.Sym // symbols in file, sorted by address + pcln objfile.Liner // pcln table + text []byte // bytes of text segment (actual instructions) + textStart uint64 // start PC of text + textEnd uint64 // end PC of text + goarch string // GOARCH string + disasm disasmFunc // disassembler function for goarch + byteOrder binary.ByteOrder // byte order for goarch +} + +// DisasmForFile returns a disassembler for the file f. +func DisasmForFile(f *objfile.File) (*Disasm, error) { + return disasmForEntry(f.Entries()[0]) +} + +func disasmForEntry(e *objfile.Entry) (*Disasm, error) { + syms, err := e.Symbols() + if err != nil { + return nil, err + } + + pcln, err := e.PCLineTable() + if err != nil { + return nil, err + } + + textStart, textBytes, err := e.Text() + if err != nil { + return nil, err + } + + goarch := e.GOARCH() + disasm := disasms[goarch] + byteOrder := byteOrders[goarch] + if disasm == nil || byteOrder == nil { + return nil, fmt.Errorf("unsupported architecture %q", goarch) + } + + // Filter out section symbols, overwriting syms in place. + keep := syms[:0] + for _, sym := range syms { + switch sym.Name { + case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext": + // drop + default: + keep = append(keep, sym) + } + } + syms = keep + d := &Disasm{ + syms: syms, + pcln: pcln, + text: textBytes, + textStart: textStart, + textEnd: textStart + uint64(len(textBytes)), + goarch: goarch, + disasm: disasm, + byteOrder: byteOrder, + } + + return d, nil +} + +// lookup finds the symbol name containing addr. +func (d *Disasm) lookup(addr uint64) (name string, base uint64) { + i := sort.Search(len(d.syms), func(i int) bool { return addr < d.syms[i].Addr }) + if i > 0 { + s := d.syms[i-1] + if s.Addr != 0 && s.Addr <= addr && addr < s.Addr+uint64(s.Size) { + return s.Name, s.Addr + } + } + return "", 0 +} + +// base returns the final element in the path. +// It works on both Windows and Unix paths, +// regardless of host operating system. +func base(path string) string { + path = path[strings.LastIndex(path, "/")+1:] + path = path[strings.LastIndex(path, `\`)+1:] + return path +} + +// CachedFile contains the content of a file split into lines. +type CachedFile struct { + FileName string + Lines [][]byte +} + +// FileCache is a simple LRU cache of file contents. +type FileCache struct { + files *list.List + maxLen int +} + +// NewFileCache returns a FileCache which can contain up to maxLen cached file contents. +func NewFileCache(maxLen int) *FileCache { + return &FileCache{ + files: list.New(), + maxLen: maxLen, + } +} + +// Line returns the source code line for the given file and line number. +// If the file is not already cached, reads it, inserts it into the cache, +// and removes the least recently used file if necessary. +// If the file is in cache, it is moved to the front of the list. +func (fc *FileCache) Line(filename string, line int) ([]byte, error) { + if filepath.Ext(filename) != ".go" { + return nil, nil + } + + // Clean filenames returned by src.Pos.SymFilename() + // or src.PosBase.SymFilename() removing + // the leading src.FileSymPrefix. + filename = strings.TrimPrefix(filename, src.FileSymPrefix) + + // Expand literal "$GOROOT" rewritten by obj.AbsFile() + filename = filepath.Clean(os.ExpandEnv(filename)) + + var cf *CachedFile + var e *list.Element + + for e = fc.files.Front(); e != nil; e = e.Next() { + cf = e.Value.(*CachedFile) + if cf.FileName == filename { + break + } + } + + if e == nil { + content, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + + cf = &CachedFile{ + FileName: filename, + Lines: bytes.Split(content, []byte{'\n'}), + } + fc.files.PushFront(cf) + + if fc.files.Len() >= fc.maxLen { + fc.files.Remove(fc.files.Back()) + } + } else { + fc.files.MoveToFront(e) + } + + // because //line directives can be out-of-range. (#36683) + if line-1 >= len(cf.Lines) || line-1 < 0 { + return nil, nil + } + + return cf.Lines[line-1], nil +} + +// Print prints a disassembly of the file to w. +// If filter is non-nil, the disassembly only includes functions with names matching filter. +// If printCode is true, the disassembly includes corresponding source lines. +// The disassembly only includes functions that overlap the range [start, end). +func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, printCode bool, gnuAsm bool) { + if start < d.textStart { + start = d.textStart + } + if end > d.textEnd { + end = d.textEnd + } + printed := false + bw := bufio.NewWriter(w) + + var fc *FileCache + if printCode { + fc = NewFileCache(8) + } + + tw := tabwriter.NewWriter(bw, 18, 8, 1, '\t', tabwriter.StripEscape) + for _, sym := range d.syms { + symStart := sym.Addr + symEnd := sym.Addr + uint64(sym.Size) + relocs := sym.Relocs + if sym.Code != 'T' && sym.Code != 't' || + symStart < d.textStart || + symEnd <= start || end <= symStart || + filter != nil && !filter.MatchString(sym.Name) { + continue + } + if printed { + fmt.Fprintf(bw, "\n") + } + printed = true + + file, _, _ := d.pcln.PCToLine(sym.Addr) + fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file) + + if symEnd > end { + symEnd = end + } + code := d.text[:end-d.textStart] + + var lastFile string + var lastLine int + + d.Decode(symStart, symEnd, relocs, gnuAsm, func(pc, size uint64, file string, line int, text string) { + i := pc - d.textStart + + if printCode { + if file != lastFile || line != lastLine { + if srcLine, err := fc.Line(file, line); err == nil { + fmt.Fprintf(tw, "%s%s%s\n", []byte{tabwriter.Escape}, srcLine, []byte{tabwriter.Escape}) + } + + lastFile, lastLine = file, line + } + + fmt.Fprintf(tw, " %#x\t", pc) + } else { + fmt.Fprintf(tw, " %s:%d\t%#x\t", base(file), line, pc) + } + + if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" { + // Print instruction as bytes. + fmt.Fprintf(tw, "%x", code[i:i+size]) + } else { + // Print instruction as 32-bit words. + for j := uint64(0); j < size; j += 4 { + if j > 0 { + fmt.Fprintf(tw, " ") + } + fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:])) + } + } + fmt.Fprintf(tw, "\t%s\t\n", text) + }) + tw.Flush() + } + bw.Flush() +} + +// Decode disassembles the text segment range [start, end), calling f for each instruction. +func (d *Disasm) Decode(start, end uint64, relocs []objfile.Reloc, gnuAsm bool, f func(pc, size uint64, file string, line int, text string)) { + if start < d.textStart { + start = d.textStart + } + if end > d.textEnd { + end = d.textEnd + } + code := d.text[:end-d.textStart] + lookup := d.lookup + for pc := start; pc < end; { + i := pc - d.textStart + text, _, size := d.disasm(code[i:], pc, lookup, d.byteOrder, gnuAsm) + file, line, _ := d.pcln.PCToLine(pc) + sep := "\t" + for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) { + text += sep + relocs[0].Stringer.String(pc-start) + sep = " " + relocs = relocs[1:] + } + f(pc, uint64(size), file, line, text) + pc += uint64(size) + } +} + +type lookupFunc = func(addr uint64) (sym string, base uint64) + +// A disasmFunc returns the formatted text, the canonical (decoder) mnemonic — +// e.g. "LD1" where the Go syntax spells it "VLD1" — and the instruction size. +type disasmFunc func(code []byte, pc uint64, lookup lookupFunc, ord binary.ByteOrder, _ bool) (text, mnemonic string, size int) + +func disasm_386(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, string, int) { + return disasm_x86(code, pc, lookup, 32, gnuAsm) +} + +func disasm_amd64(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, string, int) { + return disasm_x86(code, pc, lookup, 64, gnuAsm) +} + +func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int, gnuAsm bool) (string, string, int) { + inst, err := x86asm.Decode(code, arch) + var text, mnemonic string + size := inst.Len + if err != nil || size == 0 || inst.Op == 0 { + size = 1 + text = "?" + } else { + mnemonic = inst.Op.String() + if gnuAsm { + text = fmt.Sprintf("%-36s // %s", x86asm.GoSyntax(inst, pc, lookup), x86asm.GNUSyntax(inst, pc, nil)) + } else { + text = x86asm.GoSyntax(inst, pc, lookup) + } + } + return text, mnemonic, size +} + +type textReader struct { + code []byte + pc uint64 +} + +func (r textReader) ReadAt(data []byte, off int64) (n int, err error) { + if off < 0 || uint64(off) < r.pc { + return 0, io.EOF + } + d := uint64(off) - r.pc + if d >= uint64(len(r.code)) { + return 0, io.EOF + } + n = copy(data, r.code[d:]) + if n < len(data) { + err = io.ErrUnexpectedEOF + } + return +} + +func disasm_arm(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, string, int) { + inst, err := armasm.Decode(code, armasm.ModeARM) + var text, mnemonic string + size := inst.Len + if err != nil || size == 0 || inst.Op == 0 { + size = 4 + text = "?" + } else if mnemonic = inst.Op.String(); gnuAsm { + text = fmt.Sprintf("%-36s // %s", armasm.GoSyntax(inst, pc, lookup, textReader{code, pc}), armasm.GNUSyntax(inst)) + } else { + text = armasm.GoSyntax(inst, pc, lookup, textReader{code, pc}) + } + return text, mnemonic, size +} + +func disasm_arm64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, string, int) { + inst, err := arm64asm.Decode(code) + var text, mnemonic string + if err != nil || inst.Op == 0 { + text = "?" + } else if mnemonic = inst.Op.String(); gnuAsm { + text = fmt.Sprintf("%-36s // %s", arm64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}), arm64asm.GNUSyntax(inst)) + } else { + text = arm64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}) + } + return text, mnemonic, 4 +} + +func disasm_loong64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, string, int) { + inst, err := loong64asm.Decode(code) + var text, mnemonic string + if err != nil || inst.Op == 0 { + text = "?" + } else if mnemonic = inst.Op.String(); gnuAsm { + text = fmt.Sprintf("%-36s // %s", loong64asm.GoSyntax(inst, pc, lookup), loong64asm.GNUSyntax(inst)) + } else { + text = loong64asm.GoSyntax(inst, pc, lookup) + } + return text, mnemonic, 4 +} + +func disasm_ppc64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, string, int) { + inst, err := ppc64asm.Decode(code, byteOrder) + var text, mnemonic string + size := inst.Len + if err != nil || size == 0 { + size = 4 + text = "?" + } else { + mnemonic = inst.Op.String() + if gnuAsm { + text = fmt.Sprintf("%-36s // %s", ppc64asm.GoSyntax(inst, pc, lookup), ppc64asm.GNUSyntax(inst, pc)) + } else { + text = ppc64asm.GoSyntax(inst, pc, lookup) + } + } + return text, mnemonic, size +} + +func disasm_riscv64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, string, int) { + inst, err := riscv64asm.Decode(code) + var text, mnemonic string + size := inst.Len + if err != nil || inst.Op == 0 { + size = 2 + text = "?" + } else if mnemonic = inst.Op.String(); gnuAsm { + text = fmt.Sprintf("%-36s // %s", riscv64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}), riscv64asm.GNUSyntax(inst)) + } else { + text = riscv64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}) + } + return text, mnemonic, size +} + +func disasm_s390x(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, string, int) { + inst, err := s390xasm.Decode(code) + var text, mnemonic string + size := inst.Len + if err != nil || size == 0 || inst.Op == 0 { + size = 2 + text = "?" + } else { + mnemonic = inst.Op.String() + if gnuAsm { + text = fmt.Sprintf("%-36s // %s", s390xasm.GoSyntax(inst, pc, lookup), s390xasm.GNUSyntax(inst, pc)) + } else { + text = s390xasm.GoSyntax(inst, pc, lookup) + } + } + return text, mnemonic, size +} + +var disasms = map[string]disasmFunc{ + "386": disasm_386, + "amd64": disasm_amd64, + "arm": disasm_arm, + "arm64": disasm_arm64, + "loong64": disasm_loong64, + "ppc64": disasm_ppc64, + "ppc64le": disasm_ppc64, + "riscv64": disasm_riscv64, + "s390x": disasm_s390x, +} + +var byteOrders = map[string]binary.ByteOrder{ + "386": binary.LittleEndian, + "amd64": binary.LittleEndian, + "arm": binary.LittleEndian, + "arm64": binary.LittleEndian, + "loong64": binary.LittleEndian, + "ppc64": binary.BigEndian, + "ppc64le": binary.LittleEndian, + "riscv64": binary.LittleEndian, + "s390x": binary.BigEndian, +} diff --git a/internal/go/src/disasm/expose.go b/internal/go/src/disasm/expose.go new file mode 100644 index 0000000..3fad616 --- /dev/null +++ b/internal/go/src/disasm/expose.go @@ -0,0 +1,51 @@ +package disasm + +import ( + "strings" + + "loov.dev/lensm/internal/go/src/objfile" +) + +func (d *Disasm) Syms() []objfile.Sym { return d.syms } +func (d *Disasm) TextStart() uint64 { return d.textStart } +func (d *Disasm) TextEnd() uint64 { return d.textEnd } +func (d *Disasm) PCLN() objfile.Liner { return d.pcln } +func (d *Disasm) GOARCH() string { return d.goarch } + +// DecodeSyntax disassembles the text segment range [start, end), calling f for +// each instruction with Go assembler syntax and native (GNU) syntax separately. +// +// This is a lensm addition, re-applied on every `go generate` because upstream +// Decode only yields a single combined syntax. It relies on Decode's gnuAsm=true +// format of "%-36s // %s" (goText // gnuText) to split the two apart. +// mnemonic is the canonical decoder mnemonic (e.g. "LD1" where the Go syntax +// spells it "VLD1"), used for reference lookups; empty for undecodable bytes. +func (d *Disasm) DecodeSyntax(start, end uint64, relocs []objfile.Reloc, f func(pc, size uint64, file string, line int, goText, nativeText, mnemonic string)) { + if start < d.textStart { + start = d.textStart + } + if end > d.textEnd { + end = d.textEnd + } + code := d.text[:end-d.textStart] + lookup := d.lookup + for pc := start; pc < end; { + i := pc - d.textStart + combined, mnemonic, size := d.disasm(code[i:], pc, lookup, d.byteOrder, true) + goText, nativeText := combined, combined + if j := strings.Index(combined, " // "); j >= 0 { + goText = strings.TrimRight(combined[:j], " ") + nativeText = combined[j+len(" // "):] + } + file, line, _ := d.pcln.PCToLine(pc) + reloc := "" + sep := "\t" + for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) { + reloc += sep + relocs[0].Stringer.String(pc-start) + sep = " " + relocs = relocs[1:] + } + f(pc, uint64(size), file, line, goText+reloc, nativeText+reloc, mnemonic) + pc += uint64(size) + } +} diff --git a/internal/go/src/goarch/gengoarch.go b/internal/go/src/goarch/gengoarch.go new file mode 100755 index 0000000..a52936e --- /dev/null +++ b/internal/go/src/goarch/gengoarch.go @@ -0,0 +1,60 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "strings" +) + +var goarches []string + +func main() { + data, err := os.ReadFile("../../internal/syslist/syslist.go") + if err != nil { + log.Fatal(err) + } + const goarchPrefix = `var KnownArch = map[string]bool{` + inGOARCH := false + for _, line := range strings.Split(string(data), "\n") { + if strings.HasPrefix(line, goarchPrefix) { + inGOARCH = true + } else if inGOARCH && strings.HasPrefix(line, "}") { + break + } else if inGOARCH { + goarch := strings.Fields(line)[0] + goarch = strings.TrimPrefix(goarch, `"`) + goarch = strings.TrimSuffix(goarch, `":`) + goarches = append(goarches, goarch) + } + } + + for _, target := range goarches { + if target == "amd64p32" { + continue + } + var buf bytes.Buffer + fmt.Fprintf(&buf, "// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT.\n\n") + fmt.Fprintf(&buf, "//go:build %s\n\n", target) // must explicitly include target for bootstrapping purposes + fmt.Fprintf(&buf, "package goarch\n\n") + fmt.Fprintf(&buf, "const GOARCH = `%s`\n\n", target) + for _, goarch := range goarches { + value := 0 + if goarch == target { + value = 1 + } + fmt.Fprintf(&buf, "const Is%s = %d\n", strings.Title(goarch), value) + } + err := os.WriteFile("zgoarch_"+target+".go", buf.Bytes(), 0666) + if err != nil { + log.Fatal(err) + } + } +} diff --git a/internal/go/src/goarch/goarch.go b/internal/go/src/goarch/goarch.go new file mode 100755 index 0000000..efcf298 --- /dev/null +++ b/internal/go/src/goarch/goarch.go @@ -0,0 +1,65 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// package goarch contains GOARCH-specific constants. +package goarch + +// The next line makes 'go generate' write the zgoarch*.go files with +// per-arch information, including constants named $GOARCH for every +// GOARCH. The constant is 1 on the current system, 0 otherwise; multiplying +// by them is useful for defining GOARCH-specific constants. +// +//go:generate go run gengoarch.go + +// ArchFamilyType represents a family of one or more related architectures. +// For example, ppc64 and ppc64le are both members of the PPC64 family. +type ArchFamilyType int + +const ( + AMD64 ArchFamilyType = iota + ARM + ARM64 + I386 + LOONG64 + MIPS + MIPS64 + PPC64 + RISCV64 + S390X + WASM +) + +// PtrSize is the size of a pointer in bytes - unsafe.Sizeof(uintptr(0)) but as an ideal constant. +// It is also the size of the machine's native word size (that is, 4 on 32-bit systems, 8 on 64-bit). +const PtrSize = 4 << (^uintptr(0) >> 63) + +// PtrBits is bit width of a pointer. +const PtrBits = PtrSize * 8 + +// ArchFamily is the architecture family (AMD64, ARM, ...) +const ArchFamily ArchFamilyType = _ArchFamily + +// BigEndian reports whether the architecture is big-endian. +const BigEndian = IsArmbe|IsArm64be|IsMips|IsMips64|IsPpc|IsPpc64|IsS390|IsS390x|IsSparc|IsSparc64 == 1 + +// DefaultPhysPageSize is the default physical page size. +const DefaultPhysPageSize = _DefaultPhysPageSize + +// PCQuantum is the minimal unit for a program counter (1 on x86, 4 on most other systems). +// The various PC tables record PC deltas pre-divided by PCQuantum. +const PCQuantum = _PCQuantum + +// Int64Align is the required alignment for a 64-bit integer (4 on 32-bit systems, 8 on 64-bit). +const Int64Align = PtrSize + +// MinFrameSize is the size of the system-reserved words at the bottom +// of a frame (just above the architectural stack pointer). +// It is zero on x86 and PtrSize on most non-x86 (LR-based) systems. +// On PowerPC it is larger, to cover three more reserved words: +// the compiler word, the link editor word, and the TOC save word. +const MinFrameSize = _MinFrameSize + +// StackAlign is the required alignment of the SP register. +// The stack must be at least word aligned, but some architectures require more. +const StackAlign = _StackAlign diff --git a/internal/go/src/goarch/goarch_386.go b/internal/go/src/goarch/goarch_386.go new file mode 100755 index 0000000..c621421 --- /dev/null +++ b/internal/go/src/goarch/goarch_386.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = I386 + _DefaultPhysPageSize = 4096 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_amd64.go b/internal/go/src/goarch/goarch_amd64.go new file mode 100755 index 0000000..911e3e7 --- /dev/null +++ b/internal/go/src/goarch/goarch_amd64.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = AMD64 + _DefaultPhysPageSize = 4096 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_arm.go b/internal/go/src/goarch/goarch_arm.go new file mode 100755 index 0000000..a659171 --- /dev/null +++ b/internal/go/src/goarch/goarch_arm.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = ARM + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 4 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_arm64.go b/internal/go/src/goarch/goarch_arm64.go new file mode 100755 index 0000000..85d0b47 --- /dev/null +++ b/internal/go/src/goarch/goarch_arm64.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = ARM64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = 16 +) diff --git a/internal/go/src/goarch/goarch_loong64.go b/internal/go/src/goarch/goarch_loong64.go new file mode 100755 index 0000000..dae1f4d --- /dev/null +++ b/internal/go/src/goarch/goarch_loong64.go @@ -0,0 +1,15 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build loong64 + +package goarch + +const ( + _ArchFamily = LOONG64 + _DefaultPhysPageSize = 16384 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_mips.go b/internal/go/src/goarch/goarch_mips.go new file mode 100755 index 0000000..59f3995 --- /dev/null +++ b/internal/go/src/goarch/goarch_mips.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = MIPS + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 4 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_mips64.go b/internal/go/src/goarch/goarch_mips64.go new file mode 100755 index 0000000..9e4f827 --- /dev/null +++ b/internal/go/src/goarch/goarch_mips64.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = MIPS64 + _DefaultPhysPageSize = 16384 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_mips64le.go b/internal/go/src/goarch/goarch_mips64le.go new file mode 100755 index 0000000..9e4f827 --- /dev/null +++ b/internal/go/src/goarch/goarch_mips64le.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = MIPS64 + _DefaultPhysPageSize = 16384 + _PCQuantum = 4 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_mipsle.go b/internal/go/src/goarch/goarch_mipsle.go new file mode 100755 index 0000000..3e6642b --- /dev/null +++ b/internal/go/src/goarch/goarch_mipsle.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = MIPS + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 4 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_ppc64.go b/internal/go/src/goarch/goarch_ppc64.go new file mode 100755 index 0000000..60cc846 --- /dev/null +++ b/internal/go/src/goarch/goarch_ppc64.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = PPC64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 32 + _StackAlign = 16 +) diff --git a/internal/go/src/goarch/goarch_ppc64le.go b/internal/go/src/goarch/goarch_ppc64le.go new file mode 100755 index 0000000..60cc846 --- /dev/null +++ b/internal/go/src/goarch/goarch_ppc64le.go @@ -0,0 +1,13 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = PPC64 + _DefaultPhysPageSize = 65536 + _PCQuantum = 4 + _MinFrameSize = 32 + _StackAlign = 16 +) diff --git a/internal/go/src/goarch/goarch_riscv64.go b/internal/go/src/goarch/goarch_riscv64.go new file mode 100755 index 0000000..468f9a6 --- /dev/null +++ b/internal/go/src/goarch/goarch_riscv64.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = RISCV64 + _DefaultPhysPageSize = 4096 + _PCQuantum = 2 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_s390x.go b/internal/go/src/goarch/goarch_s390x.go new file mode 100755 index 0000000..20c5705 --- /dev/null +++ b/internal/go/src/goarch/goarch_s390x.go @@ -0,0 +1,13 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = S390X + _DefaultPhysPageSize = 4096 + _PCQuantum = 2 + _MinFrameSize = 8 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/goarch_wasm.go b/internal/go/src/goarch/goarch_wasm.go new file mode 100755 index 0000000..98618d6 --- /dev/null +++ b/internal/go/src/goarch/goarch_wasm.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goarch + +const ( + _ArchFamily = WASM + _DefaultPhysPageSize = 65536 + _PCQuantum = 1 + _MinFrameSize = 0 + _StackAlign = PtrSize +) diff --git a/internal/go/src/goarch/zgoarch_386.go b/internal/go/src/goarch/zgoarch_386.go new file mode 100755 index 0000000..4a9b0e6 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_386.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build 386 + +package goarch + +const GOARCH = `386` + +const Is386 = 1 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_amd64.go b/internal/go/src/goarch/zgoarch_amd64.go new file mode 100755 index 0000000..7926392 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_amd64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build amd64 + +package goarch + +const GOARCH = `amd64` + +const Is386 = 0 +const IsAmd64 = 1 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_arm.go b/internal/go/src/goarch/zgoarch_arm.go new file mode 100755 index 0000000..6c03b8b --- /dev/null +++ b/internal/go/src/goarch/zgoarch_arm.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm + +package goarch + +const GOARCH = `arm` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 1 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_arm64.go b/internal/go/src/goarch/zgoarch_arm64.go new file mode 100755 index 0000000..ad342d7 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_arm64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm64 + +package goarch + +const GOARCH = `arm64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 1 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_arm64be.go b/internal/go/src/goarch/zgoarch_arm64be.go new file mode 100755 index 0000000..0f26003 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_arm64be.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build arm64be + +package goarch + +const GOARCH = `arm64be` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 1 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_armbe.go b/internal/go/src/goarch/zgoarch_armbe.go new file mode 100755 index 0000000..6092fee --- /dev/null +++ b/internal/go/src/goarch/zgoarch_armbe.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build armbe + +package goarch + +const GOARCH = `armbe` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 1 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_loong64.go b/internal/go/src/goarch/zgoarch_loong64.go new file mode 100755 index 0000000..21c67e1 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_loong64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build loong64 + +package goarch + +const GOARCH = `loong64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 1 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_mips.go b/internal/go/src/goarch/zgoarch_mips.go new file mode 100755 index 0000000..0db1974 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_mips.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips + +package goarch + +const GOARCH = `mips` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 1 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_mips64.go b/internal/go/src/goarch/zgoarch_mips64.go new file mode 100755 index 0000000..738806f --- /dev/null +++ b/internal/go/src/goarch/zgoarch_mips64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64 + +package goarch + +const GOARCH = `mips64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 1 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_mips64le.go b/internal/go/src/goarch/zgoarch_mips64le.go new file mode 100755 index 0000000..8de5beb --- /dev/null +++ b/internal/go/src/goarch/zgoarch_mips64le.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64le + +package goarch + +const GOARCH = `mips64le` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 1 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_mips64p32.go b/internal/go/src/goarch/zgoarch_mips64p32.go new file mode 100755 index 0000000..ea461be --- /dev/null +++ b/internal/go/src/goarch/zgoarch_mips64p32.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64p32 + +package goarch + +const GOARCH = `mips64p32` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 1 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_mips64p32le.go b/internal/go/src/goarch/zgoarch_mips64p32le.go new file mode 100755 index 0000000..15473ce --- /dev/null +++ b/internal/go/src/goarch/zgoarch_mips64p32le.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mips64p32le + +package goarch + +const GOARCH = `mips64p32le` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 1 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_mipsle.go b/internal/go/src/goarch/zgoarch_mipsle.go new file mode 100755 index 0000000..4955142 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_mipsle.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build mipsle + +package goarch + +const GOARCH = `mipsle` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 1 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_ppc.go b/internal/go/src/goarch/zgoarch_ppc.go new file mode 100755 index 0000000..ec01763 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_ppc.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build ppc + +package goarch + +const GOARCH = `ppc` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 1 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_ppc64.go b/internal/go/src/goarch/zgoarch_ppc64.go new file mode 100755 index 0000000..39be392 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_ppc64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build ppc64 + +package goarch + +const GOARCH = `ppc64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 1 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_ppc64le.go b/internal/go/src/goarch/zgoarch_ppc64le.go new file mode 100755 index 0000000..5f959e0 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_ppc64le.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build ppc64le + +package goarch + +const GOARCH = `ppc64le` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 1 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_riscv.go b/internal/go/src/goarch/zgoarch_riscv.go new file mode 100755 index 0000000..8d81a14 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_riscv.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build riscv + +package goarch + +const GOARCH = `riscv` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 1 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_riscv64.go b/internal/go/src/goarch/zgoarch_riscv64.go new file mode 100755 index 0000000..1df989c --- /dev/null +++ b/internal/go/src/goarch/zgoarch_riscv64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build riscv64 + +package goarch + +const GOARCH = `riscv64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 1 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_s390.go b/internal/go/src/goarch/zgoarch_s390.go new file mode 100755 index 0000000..56815b9 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_s390.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build s390 + +package goarch + +const GOARCH = `s390` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 1 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_s390x.go b/internal/go/src/goarch/zgoarch_s390x.go new file mode 100755 index 0000000..e61e9bd --- /dev/null +++ b/internal/go/src/goarch/zgoarch_s390x.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build s390x + +package goarch + +const GOARCH = `s390x` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 1 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_sparc.go b/internal/go/src/goarch/zgoarch_sparc.go new file mode 100755 index 0000000..ee5b746 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_sparc.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build sparc + +package goarch + +const GOARCH = `sparc` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 1 +const IsSparc64 = 0 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_sparc64.go b/internal/go/src/goarch/zgoarch_sparc64.go new file mode 100755 index 0000000..519aaa1 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_sparc64.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build sparc64 + +package goarch + +const GOARCH = `sparc64` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 1 +const IsWasm = 0 diff --git a/internal/go/src/goarch/zgoarch_wasm.go b/internal/go/src/goarch/zgoarch_wasm.go new file mode 100755 index 0000000..25567a1 --- /dev/null +++ b/internal/go/src/goarch/zgoarch_wasm.go @@ -0,0 +1,32 @@ +// Code generated by gengoarch.go using 'go generate'. DO NOT EDIT. + +//go:build wasm + +package goarch + +const GOARCH = `wasm` + +const Is386 = 0 +const IsAmd64 = 0 +const IsAmd64p32 = 0 +const IsArm = 0 +const IsArmbe = 0 +const IsArm64 = 0 +const IsArm64be = 0 +const IsLoong64 = 0 +const IsMips = 0 +const IsMipsle = 0 +const IsMips64 = 0 +const IsMips64le = 0 +const IsMips64p32 = 0 +const IsMips64p32le = 0 +const IsPpc = 0 +const IsPpc64 = 0 +const IsPpc64le = 0 +const IsRiscv = 0 +const IsRiscv64 = 0 +const IsS390 = 0 +const IsS390x = 0 +const IsSparc = 0 +const IsSparc64 = 0 +const IsWasm = 1 diff --git a/internal/go/src/goexperiment/exp_arenas_off.go b/internal/go/src/goexperiment/exp_arenas_off.go new file mode 100755 index 0000000..01f5332 --- /dev/null +++ b/internal/go/src/goexperiment/exp_arenas_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.arenas + +package goexperiment + +const Arenas = false +const ArenasInt = 0 diff --git a/internal/go/src/goexperiment/exp_arenas_on.go b/internal/go/src/goexperiment/exp_arenas_on.go new file mode 100755 index 0000000..609dfbc --- /dev/null +++ b/internal/go/src/goexperiment/exp_arenas_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.arenas + +package goexperiment + +const Arenas = true +const ArenasInt = 1 diff --git a/internal/go/src/goexperiment/exp_boringcrypto_off.go b/internal/go/src/goexperiment/exp_boringcrypto_off.go new file mode 100755 index 0000000..de71267 --- /dev/null +++ b/internal/go/src/goexperiment/exp_boringcrypto_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = false +const BoringCryptoInt = 0 diff --git a/internal/go/src/goexperiment/exp_boringcrypto_on.go b/internal/go/src/goexperiment/exp_boringcrypto_on.go new file mode 100755 index 0000000..ce476fa --- /dev/null +++ b/internal/go/src/goexperiment/exp_boringcrypto_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.boringcrypto + +package goexperiment + +const BoringCrypto = true +const BoringCryptoInt = 1 diff --git a/internal/go/src/goexperiment/exp_cgocheck2_off.go b/internal/go/src/goexperiment/exp_cgocheck2_off.go new file mode 100755 index 0000000..e99ad07 --- /dev/null +++ b/internal/go/src/goexperiment/exp_cgocheck2_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.cgocheck2 + +package goexperiment + +const CgoCheck2 = false +const CgoCheck2Int = 0 diff --git a/internal/go/src/goexperiment/exp_cgocheck2_on.go b/internal/go/src/goexperiment/exp_cgocheck2_on.go new file mode 100755 index 0000000..f6d1790 --- /dev/null +++ b/internal/go/src/goexperiment/exp_cgocheck2_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.cgocheck2 + +package goexperiment + +const CgoCheck2 = true +const CgoCheck2Int = 1 diff --git a/internal/go/src/goexperiment/exp_dwarf5_off.go b/internal/go/src/goexperiment/exp_dwarf5_off.go new file mode 100755 index 0000000..b89dd9f --- /dev/null +++ b/internal/go/src/goexperiment/exp_dwarf5_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.dwarf5 + +package goexperiment + +const Dwarf5 = false +const Dwarf5Int = 0 diff --git a/internal/go/src/goexperiment/exp_dwarf5_on.go b/internal/go/src/goexperiment/exp_dwarf5_on.go new file mode 100755 index 0000000..02b28d7 --- /dev/null +++ b/internal/go/src/goexperiment/exp_dwarf5_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.dwarf5 + +package goexperiment + +const Dwarf5 = true +const Dwarf5Int = 1 diff --git a/internal/go/src/goexperiment/exp_fieldtrack_off.go b/internal/go/src/goexperiment/exp_fieldtrack_off.go new file mode 100755 index 0000000..ccced94 --- /dev/null +++ b/internal/go/src/goexperiment/exp_fieldtrack_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.fieldtrack + +package goexperiment + +const FieldTrack = false +const FieldTrackInt = 0 diff --git a/internal/go/src/goexperiment/exp_fieldtrack_on.go b/internal/go/src/goexperiment/exp_fieldtrack_on.go new file mode 100755 index 0000000..a497567 --- /dev/null +++ b/internal/go/src/goexperiment/exp_fieldtrack_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.fieldtrack + +package goexperiment + +const FieldTrack = true +const FieldTrackInt = 1 diff --git a/internal/go/src/goexperiment/exp_goroutineleakprofile_off.go b/internal/go/src/goexperiment/exp_goroutineleakprofile_off.go new file mode 100755 index 0000000..63eafe9 --- /dev/null +++ b/internal/go/src/goexperiment/exp_goroutineleakprofile_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.goroutineleakprofile + +package goexperiment + +const GoroutineLeakProfile = false +const GoroutineLeakProfileInt = 0 diff --git a/internal/go/src/goexperiment/exp_goroutineleakprofile_on.go b/internal/go/src/goexperiment/exp_goroutineleakprofile_on.go new file mode 100755 index 0000000..28a662e --- /dev/null +++ b/internal/go/src/goexperiment/exp_goroutineleakprofile_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.goroutineleakprofile + +package goexperiment + +const GoroutineLeakProfile = true +const GoroutineLeakProfileInt = 1 diff --git a/internal/go/src/goexperiment/exp_greenteagc_off.go b/internal/go/src/goexperiment/exp_greenteagc_off.go new file mode 100755 index 0000000..dce9d8c --- /dev/null +++ b/internal/go/src/goexperiment/exp_greenteagc_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.greenteagc + +package goexperiment + +const GreenTeaGC = false +const GreenTeaGCInt = 0 diff --git a/internal/go/src/goexperiment/exp_greenteagc_on.go b/internal/go/src/goexperiment/exp_greenteagc_on.go new file mode 100755 index 0000000..10a007d --- /dev/null +++ b/internal/go/src/goexperiment/exp_greenteagc_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.greenteagc + +package goexperiment + +const GreenTeaGC = true +const GreenTeaGCInt = 1 diff --git a/internal/go/src/goexperiment/exp_heapminimum512kib_off.go b/internal/go/src/goexperiment/exp_heapminimum512kib_off.go new file mode 100755 index 0000000..d67c5bb --- /dev/null +++ b/internal/go/src/goexperiment/exp_heapminimum512kib_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.heapminimum512kib + +package goexperiment + +const HeapMinimum512KiB = false +const HeapMinimum512KiBInt = 0 diff --git a/internal/go/src/goexperiment/exp_heapminimum512kib_on.go b/internal/go/src/goexperiment/exp_heapminimum512kib_on.go new file mode 100755 index 0000000..2d29c98 --- /dev/null +++ b/internal/go/src/goexperiment/exp_heapminimum512kib_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.heapminimum512kib + +package goexperiment + +const HeapMinimum512KiB = true +const HeapMinimum512KiBInt = 1 diff --git a/internal/go/src/goexperiment/exp_jsonv2_off.go b/internal/go/src/goexperiment/exp_jsonv2_off.go new file mode 100755 index 0000000..a973a35 --- /dev/null +++ b/internal/go/src/goexperiment/exp_jsonv2_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.jsonv2 + +package goexperiment + +const JSONv2 = false +const JSONv2Int = 0 diff --git a/internal/go/src/goexperiment/exp_jsonv2_on.go b/internal/go/src/goexperiment/exp_jsonv2_on.go new file mode 100755 index 0000000..119d086 --- /dev/null +++ b/internal/go/src/goexperiment/exp_jsonv2_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.jsonv2 + +package goexperiment + +const JSONv2 = true +const JSONv2Int = 1 diff --git a/internal/go/src/goexperiment/exp_loopvar_off.go b/internal/go/src/goexperiment/exp_loopvar_off.go new file mode 100755 index 0000000..cfede54 --- /dev/null +++ b/internal/go/src/goexperiment/exp_loopvar_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.loopvar + +package goexperiment + +const LoopVar = false +const LoopVarInt = 0 diff --git a/internal/go/src/goexperiment/exp_loopvar_on.go b/internal/go/src/goexperiment/exp_loopvar_on.go new file mode 100755 index 0000000..e158e0a --- /dev/null +++ b/internal/go/src/goexperiment/exp_loopvar_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.loopvar + +package goexperiment + +const LoopVar = true +const LoopVarInt = 1 diff --git a/internal/go/src/goexperiment/exp_newinliner_off.go b/internal/go/src/goexperiment/exp_newinliner_off.go new file mode 100755 index 0000000..d94e736 --- /dev/null +++ b/internal/go/src/goexperiment/exp_newinliner_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.newinliner + +package goexperiment + +const NewInliner = false +const NewInlinerInt = 0 diff --git a/internal/go/src/goexperiment/exp_newinliner_on.go b/internal/go/src/goexperiment/exp_newinliner_on.go new file mode 100755 index 0000000..6777dbc --- /dev/null +++ b/internal/go/src/goexperiment/exp_newinliner_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.newinliner + +package goexperiment + +const NewInliner = true +const NewInlinerInt = 1 diff --git a/internal/go/src/goexperiment/exp_preemptibleloops_off.go b/internal/go/src/goexperiment/exp_preemptibleloops_off.go new file mode 100755 index 0000000..cddcc1b --- /dev/null +++ b/internal/go/src/goexperiment/exp_preemptibleloops_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.preemptibleloops + +package goexperiment + +const PreemptibleLoops = false +const PreemptibleLoopsInt = 0 diff --git a/internal/go/src/goexperiment/exp_preemptibleloops_on.go b/internal/go/src/goexperiment/exp_preemptibleloops_on.go new file mode 100755 index 0000000..7f474c0 --- /dev/null +++ b/internal/go/src/goexperiment/exp_preemptibleloops_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.preemptibleloops + +package goexperiment + +const PreemptibleLoops = true +const PreemptibleLoopsInt = 1 diff --git a/internal/go/src/goexperiment/exp_randomizedheapbase64_off.go b/internal/go/src/goexperiment/exp_randomizedheapbase64_off.go new file mode 100755 index 0000000..0a57853 --- /dev/null +++ b/internal/go/src/goexperiment/exp_randomizedheapbase64_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.randomizedheapbase64 + +package goexperiment + +const RandomizedHeapBase64 = false +const RandomizedHeapBase64Int = 0 diff --git a/internal/go/src/goexperiment/exp_randomizedheapbase64_on.go b/internal/go/src/goexperiment/exp_randomizedheapbase64_on.go new file mode 100755 index 0000000..10d59c7 --- /dev/null +++ b/internal/go/src/goexperiment/exp_randomizedheapbase64_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.randomizedheapbase64 + +package goexperiment + +const RandomizedHeapBase64 = true +const RandomizedHeapBase64Int = 1 diff --git a/internal/go/src/goexperiment/exp_regabiargs_off.go b/internal/go/src/goexperiment/exp_regabiargs_off.go new file mode 100755 index 0000000..a8c8536 --- /dev/null +++ b/internal/go/src/goexperiment/exp_regabiargs_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.regabiargs + +package goexperiment + +const RegabiArgs = false +const RegabiArgsInt = 0 diff --git a/internal/go/src/goexperiment/exp_regabiargs_on.go b/internal/go/src/goexperiment/exp_regabiargs_on.go new file mode 100755 index 0000000..def3b94 --- /dev/null +++ b/internal/go/src/goexperiment/exp_regabiargs_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.regabiargs + +package goexperiment + +const RegabiArgs = true +const RegabiArgsInt = 1 diff --git a/internal/go/src/goexperiment/exp_regabiwrappers_off.go b/internal/go/src/goexperiment/exp_regabiwrappers_off.go new file mode 100755 index 0000000..a65ed36 --- /dev/null +++ b/internal/go/src/goexperiment/exp_regabiwrappers_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.regabiwrappers + +package goexperiment + +const RegabiWrappers = false +const RegabiWrappersInt = 0 diff --git a/internal/go/src/goexperiment/exp_regabiwrappers_on.go b/internal/go/src/goexperiment/exp_regabiwrappers_on.go new file mode 100755 index 0000000..d525c9a --- /dev/null +++ b/internal/go/src/goexperiment/exp_regabiwrappers_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.regabiwrappers + +package goexperiment + +const RegabiWrappers = true +const RegabiWrappersInt = 1 diff --git a/internal/go/src/goexperiment/exp_runtimefreegc_off.go b/internal/go/src/goexperiment/exp_runtimefreegc_off.go new file mode 100755 index 0000000..195f031 --- /dev/null +++ b/internal/go/src/goexperiment/exp_runtimefreegc_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.runtimefreegc + +package goexperiment + +const RuntimeFreegc = false +const RuntimeFreegcInt = 0 diff --git a/internal/go/src/goexperiment/exp_runtimefreegc_on.go b/internal/go/src/goexperiment/exp_runtimefreegc_on.go new file mode 100755 index 0000000..2afe055 --- /dev/null +++ b/internal/go/src/goexperiment/exp_runtimefreegc_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.runtimefreegc + +package goexperiment + +const RuntimeFreegc = true +const RuntimeFreegcInt = 1 diff --git a/internal/go/src/goexperiment/exp_runtimesecret_off.go b/internal/go/src/goexperiment/exp_runtimesecret_off.go new file mode 100755 index 0000000..d203589 --- /dev/null +++ b/internal/go/src/goexperiment/exp_runtimesecret_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.runtimesecret + +package goexperiment + +const RuntimeSecret = false +const RuntimeSecretInt = 0 diff --git a/internal/go/src/goexperiment/exp_runtimesecret_on.go b/internal/go/src/goexperiment/exp_runtimesecret_on.go new file mode 100755 index 0000000..3788953 --- /dev/null +++ b/internal/go/src/goexperiment/exp_runtimesecret_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.runtimesecret + +package goexperiment + +const RuntimeSecret = true +const RuntimeSecretInt = 1 diff --git a/internal/go/src/goexperiment/exp_simd_off.go b/internal/go/src/goexperiment/exp_simd_off.go new file mode 100755 index 0000000..ebc40b3 --- /dev/null +++ b/internal/go/src/goexperiment/exp_simd_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.simd + +package goexperiment + +const SIMD = false +const SIMDInt = 0 diff --git a/internal/go/src/goexperiment/exp_simd_on.go b/internal/go/src/goexperiment/exp_simd_on.go new file mode 100755 index 0000000..137d1dd --- /dev/null +++ b/internal/go/src/goexperiment/exp_simd_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.simd + +package goexperiment + +const SIMD = true +const SIMDInt = 1 diff --git a/internal/go/src/goexperiment/exp_sizespecializedmalloc_off.go b/internal/go/src/goexperiment/exp_sizespecializedmalloc_off.go new file mode 100755 index 0000000..2642a12 --- /dev/null +++ b/internal/go/src/goexperiment/exp_sizespecializedmalloc_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.sizespecializedmalloc + +package goexperiment + +const SizeSpecializedMalloc = false +const SizeSpecializedMallocInt = 0 diff --git a/internal/go/src/goexperiment/exp_sizespecializedmalloc_on.go b/internal/go/src/goexperiment/exp_sizespecializedmalloc_on.go new file mode 100755 index 0000000..a135cc8 --- /dev/null +++ b/internal/go/src/goexperiment/exp_sizespecializedmalloc_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.sizespecializedmalloc + +package goexperiment + +const SizeSpecializedMalloc = true +const SizeSpecializedMallocInt = 1 diff --git a/internal/go/src/goexperiment/exp_staticlockranking_off.go b/internal/go/src/goexperiment/exp_staticlockranking_off.go new file mode 100755 index 0000000..5fafff2 --- /dev/null +++ b/internal/go/src/goexperiment/exp_staticlockranking_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.staticlockranking + +package goexperiment + +const StaticLockRanking = false +const StaticLockRankingInt = 0 diff --git a/internal/go/src/goexperiment/exp_staticlockranking_on.go b/internal/go/src/goexperiment/exp_staticlockranking_on.go new file mode 100755 index 0000000..dfd32a8 --- /dev/null +++ b/internal/go/src/goexperiment/exp_staticlockranking_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.staticlockranking + +package goexperiment + +const StaticLockRanking = true +const StaticLockRankingInt = 1 diff --git a/internal/go/src/goexperiment/flags.go b/internal/go/src/goexperiment/flags.go new file mode 100755 index 0000000..2cfb715 --- /dev/null +++ b/internal/go/src/goexperiment/flags.go @@ -0,0 +1,131 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package goexperiment implements support for toolchain experiments. +// +// Toolchain experiments are controlled by the GOEXPERIMENT +// environment variable. GOEXPERIMENT is a comma-separated list of +// experiment names. GOEXPERIMENT can be set at make.bash time, which +// sets the default experiments for binaries built with the tool +// chain; or it can be set at build time. GOEXPERIMENT can also be set +// to "none", which disables any experiments that were enabled at +// make.bash time. +// +// Experiments are exposed to the build in the following ways: +// +// - Build tag goexperiment.x is set if experiment x (lower case) is +// enabled. +// +// - For each experiment x (in camel case), this package contains a +// boolean constant x and an integer constant xInt. +// +// - In runtime assembly, the macro GOEXPERIMENT_x is defined if +// experiment x (lower case) is enabled. +// +// In the toolchain, the set of experiments enabled for the current +// build should be accessed via objabi.Experiment. +// +// The set of experiments is included in the output of [runtime.Version]() +// and "go version " if it differs from the default experiments. +// +// For the set of experiments supported by the current toolchain, see +// "go doc goexperiment.Flags". +// +// Note that this package defines the set of experiments (in [Flags]) +// and records the experiments that were enabled when the package +// was compiled (as boolean and integer constants). +// +// Note especially that this package does not itself change behavior +// at run time based on the GOEXPERIMENT variable. +// The code used in builds to interpret the GOEXPERIMENT variable +// is in the separate package [internal/buildcfg]. +package goexperiment + +//go:generate go run mkconsts.go + +// Flags is the set of experiments that can be enabled or disabled in +// the current toolchain. +// +// When specified in the GOEXPERIMENT environment variable or as build +// tags, experiments use the strings.ToLower of their field name. +// +// For the baseline experimental configuration, see +// [internal/buildcfg.Experiment]. +// +// If you change this struct definition, run "go generate". +type Flags struct { + FieldTrack bool + PreemptibleLoops bool + StaticLockRanking bool + BoringCrypto bool + + // Regabi is split into several sub-experiments that can be + // enabled individually. Not all combinations work. + // The "regabi" GOEXPERIMENT is an alias for all "working" + // subexperiments. + + // RegabiWrappers enables ABI wrappers for calling between + // ABI0 and ABIInternal functions. Without this, the ABIs are + // assumed to be identical so cross-ABI calls are direct. + RegabiWrappers bool + // RegabiArgs enables register arguments/results in all + // compiled Go functions. + // + // Requires wrappers (to do ABI translation), and reflect (so + // reflection calls use registers). + RegabiArgs bool + + // HeapMinimum512KiB reduces the minimum heap size to 512 KiB. + // + // This was originally reduced as part of PacerRedesign, but + // has been broken out to its own experiment that is disabled + // by default. + HeapMinimum512KiB bool + + // Arenas causes the "arena" standard library package to be visible + // to the outside world. + Arenas bool + + // CgoCheck2 enables an expensive cgo rule checker. + // When this experiment is enabled, cgo rule checks occur regardless + // of the GODEBUG=cgocheck setting provided at runtime. + CgoCheck2 bool + + // LoopVar changes loop semantics so that each iteration gets its own + // copy of the iteration variable. + LoopVar bool + + // NewInliner enables a new+improved version of the function + // inlining phase within the Go compiler. + NewInliner bool + + // Dwarf5 enables DWARF version 5 debug info generation. + Dwarf5 bool + + // JSONv2 enables the json/v2 package. + JSONv2 bool + + // GreenTeaGC enables the Green Tea GC implementation. + GreenTeaGC bool + + // RandomizedHeapBase enables heap base address randomization on 64-bit + // platforms. + RandomizedHeapBase64 bool + + // RuntimeFreegc enables the runtime to free and reuse memory more eagerly in some circumstances with compiler help. + RuntimeFreegc bool + + // SizeSpecializedMalloc enables malloc implementations that are specialized per size class. + SizeSpecializedMalloc bool + + // GoroutineLeakProfile enables the collection of goroutine leak profiles. + GoroutineLeakProfile bool + + // SIMD enables the simd package and the compiler's handling + // of SIMD intrinsics. + SIMD bool + + // RuntimeSecret enables the runtime/secret package. + RuntimeSecret bool +} diff --git a/internal/go/src/goexperiment/mkconsts.go b/internal/go/src/goexperiment/mkconsts.go new file mode 100755 index 0000000..7f14392 --- /dev/null +++ b/internal/go/src/goexperiment/mkconsts.go @@ -0,0 +1,72 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +// mkconsts generates const definition files for each GOEXPERIMENT. +package main + +import ( + "bytes" + "fmt" + "loov.dev/lensm/internal/go/src/goexperiment" + "log" + "os" + "reflect" + "strings" +) + +func main() { + // Delete existing experiment constant files. + ents, err := os.ReadDir(".") + if err != nil { + log.Fatal(err) + } + for _, ent := range ents { + name := ent.Name() + if !strings.HasPrefix(name, "exp_") { + continue + } + // Check that this is definitely a generated file. + data, err := os.ReadFile(name) + if err != nil { + log.Fatalf("reading %s: %v", name, err) + } + if !bytes.Contains(data, []byte("Code generated by mkconsts")) { + log.Fatalf("%s: expected generated file", name) + } + if err := os.Remove(name); err != nil { + log.Fatal(err) + } + } + + // Generate new experiment constant files. + rt := reflect.TypeOf(&goexperiment.Flags{}).Elem() + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i).Name + buildTag := "goexperiment." + strings.ToLower(f) + for _, val := range []bool{false, true} { + name := fmt.Sprintf("exp_%s_%s.go", strings.ToLower(f), pick(val, "off", "on")) + data := fmt.Sprintf(`// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build %s%s + +package goexperiment + +const %s = %v +const %sInt = %s +`, pick(val, "!", ""), buildTag, f, val, f, pick(val, "0", "1")) + if err := os.WriteFile(name, []byte(data), 0666); err != nil { + log.Fatalf("writing %s: %v", name, err) + } + } + } +} + +func pick(v bool, f, t string) string { + if v { + return t + } + return f +} diff --git a/internal/go/src/goobj/builtin.go b/internal/go/src/goobj/builtin.go new file mode 100755 index 0000000..cc28ef9 --- /dev/null +++ b/internal/go/src/goobj/builtin.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goobj + +import "loov.dev/lensm/internal/go/src/buildcfg" + +// Builtin (compiler-generated) function references appear +// frequently. We assign special indices for them, so they +// don't need to be referenced by name. + +// NBuiltin returns the number of listed builtin +// symbols. +func NBuiltin() int { + return len(builtins) +} + +// BuiltinName returns the name and ABI of the i-th +// builtin symbol. +func BuiltinName(i int) (string, int) { + return builtins[i].name, builtins[i].abi +} + +// BuiltinIdx returns the index of the builtin with the +// given name and abi, or -1 if it is not a builtin. +func BuiltinIdx(name string, abi int) int { + i, ok := builtinMap[name] + if !ok { + return -1 + } + if buildcfg.Experiment.RegabiWrappers && builtins[i].abi != abi { + return -1 + } + return i +} + +//go:generate go run mkbuiltin.go + +var builtinMap map[string]int + +func init() { + builtinMap = make(map[string]int, len(builtins)) + for i, b := range builtins { + builtinMap[b.name] = i + } +} diff --git a/internal/go/src/goobj/builtinlist.go b/internal/go/src/goobj/builtinlist.go new file mode 100755 index 0000000..918ade1 --- /dev/null +++ b/internal/go/src/goobj/builtinlist.go @@ -0,0 +1,275 @@ +// Code generated by mkbuiltin.go. DO NOT EDIT. + +package goobj + +var builtins = [...]struct { + name string + abi int +}{ + {"runtime.newobject", 1}, + {"runtime.mallocgc", 1}, + {"runtime.panicdivide", 1}, + {"runtime.panicshift", 1}, + {"runtime.panicmakeslicelen", 1}, + {"runtime.panicmakeslicecap", 1}, + {"runtime.throwinit", 1}, + {"runtime.panicwrap", 1}, + {"runtime.gopanic", 1}, + {"runtime.gorecover", 1}, + {"runtime.goschedguarded", 1}, + {"runtime.goPanicIndex", 1}, + {"runtime.goPanicIndexU", 1}, + {"runtime.goPanicSliceAlen", 1}, + {"runtime.goPanicSliceAlenU", 1}, + {"runtime.goPanicSliceAcap", 1}, + {"runtime.goPanicSliceAcapU", 1}, + {"runtime.goPanicSliceB", 1}, + {"runtime.goPanicSliceBU", 1}, + {"runtime.goPanicSlice3Alen", 1}, + {"runtime.goPanicSlice3AlenU", 1}, + {"runtime.goPanicSlice3Acap", 1}, + {"runtime.goPanicSlice3AcapU", 1}, + {"runtime.goPanicSlice3B", 1}, + {"runtime.goPanicSlice3BU", 1}, + {"runtime.goPanicSlice3C", 1}, + {"runtime.goPanicSlice3CU", 1}, + {"runtime.goPanicSliceConvert", 1}, + {"runtime.printbool", 1}, + {"runtime.printfloat64", 1}, + {"runtime.printfloat32", 1}, + {"runtime.printint", 1}, + {"runtime.printhex", 1}, + {"runtime.printuint", 1}, + {"runtime.printcomplex128", 1}, + {"runtime.printcomplex64", 1}, + {"runtime.printstring", 1}, + {"runtime.printquoted", 1}, + {"runtime.printpointer", 1}, + {"runtime.printuintptr", 1}, + {"runtime.printiface", 1}, + {"runtime.printeface", 1}, + {"runtime.printslice", 1}, + {"runtime.printnl", 1}, + {"runtime.printsp", 1}, + {"runtime.printlock", 1}, + {"runtime.printunlock", 1}, + {"runtime.concatstring2", 1}, + {"runtime.concatstring3", 1}, + {"runtime.concatstring4", 1}, + {"runtime.concatstring5", 1}, + {"runtime.concatstrings", 1}, + {"runtime.concatbyte2", 1}, + {"runtime.concatbyte3", 1}, + {"runtime.concatbyte4", 1}, + {"runtime.concatbyte5", 1}, + {"runtime.concatbytes", 1}, + {"runtime.cmpstring", 1}, + {"runtime.intstring", 1}, + {"runtime.slicebytetostring", 1}, + {"runtime.slicebytetostringtmp", 1}, + {"runtime.slicerunetostring", 1}, + {"runtime.stringtoslicebyte", 1}, + {"runtime.stringtoslicerune", 1}, + {"runtime.slicecopy", 1}, + {"runtime.decoderune", 1}, + {"runtime.countrunes", 1}, + {"runtime.convT", 1}, + {"runtime.convTnoptr", 1}, + {"runtime.convT16", 1}, + {"runtime.convT32", 1}, + {"runtime.convT64", 1}, + {"runtime.convTstring", 1}, + {"runtime.convTslice", 1}, + {"runtime.assertE2I", 1}, + {"runtime.assertE2I2", 1}, + {"runtime.panicdottypeE", 1}, + {"runtime.panicdottypeI", 1}, + {"runtime.panicnildottype", 1}, + {"runtime.typeAssert", 1}, + {"runtime.interfaceSwitch", 1}, + {"runtime.ifaceeq", 1}, + {"runtime.efaceeq", 1}, + {"runtime.panicrangestate", 1}, + {"runtime.deferrangefunc", 1}, + {"runtime.rand", 1}, + {"runtime.rand32", 1}, + {"runtime.makemap64", 1}, + {"runtime.makemap", 1}, + {"runtime.makemap_small", 1}, + {"runtime.mapaccess1", 1}, + {"runtime.mapaccess1_fast32", 1}, + {"runtime.mapaccess1_fast64", 1}, + {"runtime.mapaccess1_faststr", 1}, + {"runtime.mapaccess1_fat", 1}, + {"runtime.mapaccess2", 1}, + {"runtime.mapaccess2_fast32", 1}, + {"runtime.mapaccess2_fast64", 1}, + {"runtime.mapaccess2_faststr", 1}, + {"runtime.mapaccess2_fat", 1}, + {"runtime.mapassign", 1}, + {"runtime.mapassign_fast32", 1}, + {"runtime.mapassign_fast32ptr", 1}, + {"runtime.mapassign_fast64", 1}, + {"runtime.mapassign_fast64ptr", 1}, + {"runtime.mapassign_faststr", 1}, + {"runtime.mapIterStart", 1}, + {"runtime.mapdelete", 1}, + {"runtime.mapdelete_fast32", 1}, + {"runtime.mapdelete_fast64", 1}, + {"runtime.mapdelete_faststr", 1}, + {"runtime.mapIterNext", 1}, + {"runtime.mapclear", 1}, + {"runtime.makechan64", 1}, + {"runtime.makechan", 1}, + {"runtime.chanrecv1", 1}, + {"runtime.chanrecv2", 1}, + {"runtime.chansend1", 1}, + {"runtime.closechan", 1}, + {"runtime.chanlen", 1}, + {"runtime.chancap", 1}, + {"runtime.writeBarrier", 0}, + {"runtime.typedmemmove", 1}, + {"runtime.typedmemclr", 1}, + {"runtime.typedslicecopy", 1}, + {"runtime.selectnbsend", 1}, + {"runtime.selectnbrecv", 1}, + {"runtime.selectsetpc", 1}, + {"runtime.selectgo", 1}, + {"runtime.block", 1}, + {"runtime.makeslice", 1}, + {"runtime.makeslice64", 1}, + {"runtime.makeslicecopy", 1}, + {"runtime.growslice", 1}, + {"runtime.unsafeslicecheckptr", 1}, + {"runtime.panicunsafeslicelen", 1}, + {"runtime.panicunsafeslicenilptr", 1}, + {"runtime.unsafestringcheckptr", 1}, + {"runtime.panicunsafestringlen", 1}, + {"runtime.panicunsafestringnilptr", 1}, + {"runtime.memmove", 1}, + {"runtime.memclrNoHeapPointers", 1}, + {"runtime.memclrHasPointers", 1}, + {"runtime.memequal", 1}, + {"runtime.memequal0", 1}, + {"runtime.memequal8", 1}, + {"runtime.memequal16", 1}, + {"runtime.memequal32", 1}, + {"runtime.memequal64", 1}, + {"runtime.memequal128", 1}, + {"runtime.f32equal", 1}, + {"runtime.f64equal", 1}, + {"runtime.c64equal", 1}, + {"runtime.c128equal", 1}, + {"runtime.strequal", 1}, + {"runtime.interequal", 1}, + {"runtime.nilinterequal", 1}, + {"runtime.memhash", 1}, + {"runtime.memhash0", 1}, + {"runtime.memhash8", 1}, + {"runtime.memhash16", 1}, + {"runtime.memhash32", 1}, + {"runtime.memhash64", 1}, + {"runtime.memhash128", 1}, + {"runtime.f32hash", 1}, + {"runtime.f64hash", 1}, + {"runtime.c64hash", 1}, + {"runtime.c128hash", 1}, + {"runtime.strhash", 1}, + {"runtime.interhash", 1}, + {"runtime.nilinterhash", 1}, + {"runtime.int64div", 1}, + {"runtime.uint64div", 1}, + {"runtime.int64mod", 1}, + {"runtime.uint64mod", 1}, + {"runtime.float64toint64", 1}, + {"runtime.float64touint64", 1}, + {"runtime.float64touint32", 1}, + {"runtime.int64tofloat64", 1}, + {"runtime.int64tofloat32", 1}, + {"runtime.uint64tofloat64", 1}, + {"runtime.uint64tofloat32", 1}, + {"runtime.uint32tofloat64", 1}, + {"runtime.complex128div", 1}, + {"runtime.racefuncenter", 1}, + {"runtime.racefuncexit", 1}, + {"runtime.raceread", 1}, + {"runtime.racewrite", 1}, + {"runtime.racereadrange", 1}, + {"runtime.racewriterange", 1}, + {"runtime.msanread", 1}, + {"runtime.msanwrite", 1}, + {"runtime.msanmove", 1}, + {"runtime.asanread", 1}, + {"runtime.asanwrite", 1}, + {"runtime.checkptrAlignment", 1}, + {"runtime.checkptrArithmetic", 1}, + {"runtime.libfuzzerTraceCmp1", 1}, + {"runtime.libfuzzerTraceCmp2", 1}, + {"runtime.libfuzzerTraceCmp4", 1}, + {"runtime.libfuzzerTraceCmp8", 1}, + {"runtime.libfuzzerTraceConstCmp1", 1}, + {"runtime.libfuzzerTraceConstCmp2", 1}, + {"runtime.libfuzzerTraceConstCmp4", 1}, + {"runtime.libfuzzerTraceConstCmp8", 1}, + {"runtime.libfuzzerHookStrCmp", 1}, + {"runtime.libfuzzerHookEqualFold", 1}, + {"runtime.addCovMeta", 1}, + {"runtime.x86HasPOPCNT", 0}, + {"runtime.x86HasSSE41", 0}, + {"runtime.x86HasFMA", 0}, + {"runtime.armHasVFPv4", 0}, + {"runtime.arm64HasATOMICS", 0}, + {"runtime.loong64HasLAMCAS", 0}, + {"runtime.loong64HasLAM_BH", 0}, + {"runtime.loong64HasLSX", 0}, + {"runtime.riscv64HasZbb", 0}, + {"runtime.asanregisterglobals", 1}, + {"runtime.deferproc", 1}, + {"runtime.deferprocStack", 1}, + {"runtime.deferreturn", 1}, + {"runtime.newproc", 1}, + {"runtime.panicoverflow", 1}, + {"runtime.sigpanic", 1}, + {"runtime.gcWriteBarrier", 1}, + {"runtime.duffzero", 1}, + {"runtime.duffcopy", 1}, + {"runtime.morestack", 0}, + {"runtime.morestackc", 0}, + {"runtime.morestack_noctxt", 0}, + {"type:int8", 0}, + {"type:*int8", 0}, + {"type:uint8", 0}, + {"type:*uint8", 0}, + {"type:int16", 0}, + {"type:*int16", 0}, + {"type:uint16", 0}, + {"type:*uint16", 0}, + {"type:int32", 0}, + {"type:*int32", 0}, + {"type:uint32", 0}, + {"type:*uint32", 0}, + {"type:int64", 0}, + {"type:*int64", 0}, + {"type:uint64", 0}, + {"type:*uint64", 0}, + {"type:float32", 0}, + {"type:*float32", 0}, + {"type:float64", 0}, + {"type:*float64", 0}, + {"type:complex64", 0}, + {"type:*complex64", 0}, + {"type:complex128", 0}, + {"type:*complex128", 0}, + {"type:unsafe.Pointer", 0}, + {"type:*unsafe.Pointer", 0}, + {"type:uintptr", 0}, + {"type:*uintptr", 0}, + {"type:bool", 0}, + {"type:*bool", 0}, + {"type:string", 0}, + {"type:*string", 0}, + {"type:error", 0}, + {"type:*error", 0}, + {"type:func(error) string", 0}, + {"type:*func(error) string", 0}, +} diff --git a/internal/go/src/goobj/funcinfo.go b/internal/go/src/goobj/funcinfo.go new file mode 100755 index 0000000..800aac5 --- /dev/null +++ b/internal/go/src/goobj/funcinfo.go @@ -0,0 +1,145 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goobj + +import ( + "bytes" + "encoding/binary" + "loov.dev/lensm/internal/go/src/abi" +) + +// CUFileIndex is used to index the filenames that are stored in the +// per-package/per-CU FileList. +type CUFileIndex uint32 + +// FuncInfo is serialized as a symbol (aux symbol). The symbol data is +// the binary encoding of the struct below. +type FuncInfo struct { + Args uint32 + Locals uint32 + FuncID abi.FuncID + FuncFlag abi.FuncFlag + StartLine int32 + File []CUFileIndex + InlTree []InlTreeNode +} + +func (a *FuncInfo) Write(w *bytes.Buffer) { + writeUint8 := func(x uint8) { + w.WriteByte(x) + } + var b [4]byte + writeUint32 := func(x uint32) { + binary.LittleEndian.PutUint32(b[:], x) + w.Write(b[:]) + } + + writeUint32(a.Args) + writeUint32(a.Locals) + writeUint8(uint8(a.FuncID)) + writeUint8(uint8(a.FuncFlag)) + writeUint8(0) // pad to uint32 boundary + writeUint8(0) + writeUint32(uint32(a.StartLine)) + + writeUint32(uint32(len(a.File))) + for _, f := range a.File { + writeUint32(uint32(f)) + } + writeUint32(uint32(len(a.InlTree))) + for i := range a.InlTree { + a.InlTree[i].Write(w) + } +} + +// FuncInfoLengths is a cache containing a roadmap of offsets and +// lengths for things within a serialized FuncInfo. Each length field +// stores the number of items (e.g. files, inltree nodes, etc), and the +// corresponding "off" field stores the byte offset of the start of +// the items in question. +type FuncInfoLengths struct { + NumFile uint32 + FileOff uint32 + NumInlTree uint32 + InlTreeOff uint32 + Initialized bool +} + +func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths { + var result FuncInfoLengths + + // Offset to the number of the file table. This value is determined by counting + // the number of bytes until we write funcdataoff to the file. + const numfileOff = 16 + result.NumFile = binary.LittleEndian.Uint32(b[numfileOff:]) + result.FileOff = numfileOff + 4 + + numinltreeOff := result.FileOff + 4*result.NumFile + result.NumInlTree = binary.LittleEndian.Uint32(b[numinltreeOff:]) + result.InlTreeOff = numinltreeOff + 4 + + result.Initialized = true + + return result +} + +func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) } + +func (*FuncInfo) ReadFuncID(b []byte) abi.FuncID { return abi.FuncID(b[8]) } + +func (*FuncInfo) ReadFuncFlag(b []byte) abi.FuncFlag { return abi.FuncFlag(b[9]) } + +func (*FuncInfo) ReadStartLine(b []byte) int32 { return int32(binary.LittleEndian.Uint32(b[12:])) } + +func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) CUFileIndex { + return CUFileIndex(binary.LittleEndian.Uint32(b[filesoff+4*k:])) +} + +func (*FuncInfo) ReadInlTree(b []byte, inltreeoff uint32, k uint32) InlTreeNode { + const inlTreeNodeSize = 4 * 6 + var result InlTreeNode + result.Read(b[inltreeoff+k*inlTreeNodeSize:]) + return result +} + +// InlTreeNode is the serialized form of FileInfo.InlTree. +type InlTreeNode struct { + Parent int32 + File CUFileIndex + Line int32 + Func SymRef + ParentPC int32 +} + +func (inl *InlTreeNode) Write(w *bytes.Buffer) { + var b [4]byte + writeUint32 := func(x uint32) { + binary.LittleEndian.PutUint32(b[:], x) + w.Write(b[:]) + } + writeUint32(uint32(inl.Parent)) + writeUint32(uint32(inl.File)) + writeUint32(uint32(inl.Line)) + writeUint32(inl.Func.PkgIdx) + writeUint32(inl.Func.SymIdx) + writeUint32(uint32(inl.ParentPC)) +} + +// Read an InlTreeNode from b, return the remaining bytes. +func (inl *InlTreeNode) Read(b []byte) []byte { + readUint32 := func() uint32 { + x := binary.LittleEndian.Uint32(b) + b = b[4:] + return x + } + inl.Parent = int32(readUint32()) + inl.File = CUFileIndex(readUint32()) + inl.Line = int32(readUint32()) + inl.Func = SymRef{readUint32(), readUint32()} + inl.ParentPC = int32(readUint32()) + return b +} diff --git a/internal/go/src/goobj/mkbuiltin.go b/internal/go/src/goobj/mkbuiltin.go new file mode 100755 index 0000000..6d1e7bb --- /dev/null +++ b/internal/go/src/goobj/mkbuiltin.go @@ -0,0 +1,160 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +// Generate builtinlist.go from cmd/compile/internal/typecheck/_builtin/runtime.go. + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io" + "log" + "os" + "path/filepath" + "strings" +) + +var stdout = flag.Bool("stdout", false, "write to stdout instead of builtinlist.go") + +func main() { + flag.Parse() + + var b bytes.Buffer + fmt.Fprintln(&b, "// Code generated by mkbuiltin.go. DO NOT EDIT.") + fmt.Fprintln(&b) + fmt.Fprintln(&b, "package goobj") + + mkbuiltin(&b) + + out, err := format.Source(b.Bytes()) + if err != nil { + log.Fatal(err) + } + if *stdout { + _, err = os.Stdout.Write(out) + } else { + err = os.WriteFile("builtinlist.go", out, 0666) + } + if err != nil { + log.Fatal(err) + } +} + +func mkbuiltin(w io.Writer) { + pkg := "runtime" + fset := token.NewFileSet() + path := filepath.Join("..", "..", "compile", "internal", "typecheck", "_builtin", "runtime.go") + f, err := parser.ParseFile(fset, path, nil, 0) + if err != nil { + log.Fatal(err) + } + + decls := make(map[string]bool) + + fmt.Fprintf(w, "var builtins = [...]struct{ name string; abi int }{\n") + for _, decl := range f.Decls { + switch decl := decl.(type) { + case *ast.FuncDecl: + if decl.Recv != nil { + log.Fatal("methods unsupported") + } + if decl.Body != nil { + log.Fatal("unexpected function body") + } + declName := pkg + "." + decl.Name.Name + decls[declName] = true + fmt.Fprintf(w, "{%q, 1},\n", declName) // functions are ABIInternal (1) + case *ast.GenDecl: + if decl.Tok == token.IMPORT { + continue + } + if decl.Tok != token.VAR { + log.Fatal("unhandled declaration kind: ", decl.Tok) + } + for _, spec := range decl.Specs { + spec := spec.(*ast.ValueSpec) + if len(spec.Values) != 0 { + log.Fatal("unexpected values") + } + for _, name := range spec.Names { + declName := pkg + "." + name.Name + decls[declName] = true + fmt.Fprintf(w, "{%q, 0},\n", declName) // variables are ABI0 + } + } + default: + log.Fatal("unhandled decl type: ", decl) + } + } + + // The list above only contains ones that are used by the frontend. + // The backend may create more references of builtin functions. + // We also want to include predefined types. + // Add them. + extras := append(fextras[:], enumerateBasicTypes()...) + for _, b := range extras { + prefix := "" + if !strings.HasPrefix(b.name, "type:") { + prefix = pkg + "." + } + name := prefix + b.name + if decls[name] { + log.Fatalf("%q already added -- mkbuiltin.go out of sync?", name) + } + fmt.Fprintf(w, "{%q, %d},\n", name, b.abi) + } + fmt.Fprintln(w, "}") +} + +// enumerateBasicTypes returns the symbol names for basic types that are +// defined in the runtime and referenced in other packages. +// Needs to be kept in sync with reflect.go:WriteBasicTypes() and +// reflect.go:writeType() in the compiler. +func enumerateBasicTypes() []extra { + names := [...]string{ + "int8", "uint8", "int16", "uint16", + "int32", "uint32", "int64", "uint64", + "float32", "float64", "complex64", "complex128", + "unsafe.Pointer", "uintptr", "bool", "string", "error", + "func(error) string"} + result := []extra{} + for _, n := range names { + result = append(result, extra{"type:" + n, 0}) + result = append(result, extra{"type:*" + n, 0}) + } + return result +} + +type extra struct { + name string + abi int +} + +var fextras = [...]extra{ + // compiler frontend inserted calls (sysfunc) + {"deferproc", 1}, + {"deferprocStack", 1}, + {"deferreturn", 1}, + {"newproc", 1}, + {"panicoverflow", 1}, + {"sigpanic", 1}, + + // compiler backend inserted calls + {"gcWriteBarrier", 1}, + {"duffzero", 1}, + {"duffcopy", 1}, + + // assembler backend inserted calls + {"morestack", 0}, // asm function, ABI0 + {"morestackc", 0}, // asm function, ABI0 + {"morestack_noctxt", 0}, // asm function, ABI0 +} diff --git a/internal/go/src/goobj/objfile.go b/internal/go/src/goobj/objfile.go new file mode 100755 index 0000000..a92e5c8 --- /dev/null +++ b/internal/go/src/goobj/objfile.go @@ -0,0 +1,874 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This package defines the Go object file format, and provide "low-level" functions +// for reading and writing object files. + +// The object file is understood by the compiler, assembler, linker, and tools. They +// have "high level" code that operates on object files, handling application-specific +// logics, and use this package for the actual reading and writing. Specifically, the +// code below: +// +// - cmd/internal/obj/objfile.go (used by cmd/asm and cmd/compile) +// - cmd/internal/objfile/goobj.go (used cmd/nm, cmd/objdump) +// - cmd/link/internal/loader package (used by cmd/link) +// +// If the object file format changes, they may (or may not) need to change. + +package goobj + +import ( + "loov.dev/lensm/internal/go/src/bio" + "encoding/binary" + "errors" + "fmt" + "unsafe" +) + +// New object file format. +// +// Header struct { +// Magic [...]byte // "\x00go120ld" +// Fingerprint [8]byte +// Flags uint32 +// Offsets [...]uint32 // byte offset of each block below +// } +// +// Strings [...]struct { +// Data [...]byte +// } +// +// Autolib [...]struct { // imported packages (for file loading) +// Pkg string +// Fingerprint [8]byte +// } +// +// PkgIndex [...]string // referenced packages by index +// +// Files [...]string +// +// SymbolDefs [...]struct { +// Name string +// ABI uint16 +// Type uint8 +// Flag uint8 +// Flag2 uint8 +// Size uint32 +// Align uint32 +// } +// Hashed64Defs [...]struct { // short hashed (content-addressable) symbol definitions +// ... // same as SymbolDefs +// } +// HashedDefs [...]struct { // hashed (content-addressable) symbol definitions +// ... // same as SymbolDefs +// } +// NonPkgDefs [...]struct { // non-pkg symbol definitions +// ... // same as SymbolDefs +// } +// NonPkgRefs [...]struct { // non-pkg symbol references +// ... // same as SymbolDefs +// } +// +// RefFlags [...]struct { // referenced symbol flags +// Sym symRef +// Flag uint8 +// Flag2 uint8 +// } +// +// Hash64 [...][8]byte +// Hash [...][N]byte +// +// RelocIndex [...]uint32 // index to Relocs +// AuxIndex [...]uint32 // index to Aux +// DataIndex [...]uint32 // offset to Data +// +// Relocs [...]struct { +// Off int32 +// Size uint8 +// Type uint16 +// Add int64 +// Sym symRef +// } +// +// Aux [...]struct { +// Type uint8 +// Sym symRef +// } +// +// Data [...]byte +// +// // blocks only used by tools (objdump, nm) +// +// RefNames [...]struct { // referenced symbol names +// Sym symRef +// Name string +// // TODO: include ABI version as well? +// } +// +// string is encoded as is a uint32 length followed by a uint32 offset +// that points to the corresponding string bytes. +// +// symRef is struct { PkgIdx, SymIdx uint32 }. +// +// Slice type (e.g. []symRef) is encoded as a length prefix (uint32) +// followed by that number of elements. +// +// The types below correspond to the encoded data structure in the +// object file. + +// Symbol indexing. +// +// Each symbol is referenced with a pair of indices, { PkgIdx, SymIdx }, +// as the symRef struct above. +// +// PkgIdx is either a predeclared index (see PkgIdxNone below) or +// an index of an imported package. For the latter case, PkgIdx is the +// index of the package in the PkgIndex array. 0 is an invalid index. +// +// SymIdx is the index of the symbol in the given package. +// - If PkgIdx is PkgIdxSelf, SymIdx is the index of the symbol in the +// SymbolDefs array. +// - If PkgIdx is PkgIdxHashed64, SymIdx is the index of the symbol in the +// Hashed64Defs array. +// - If PkgIdx is PkgIdxHashed, SymIdx is the index of the symbol in the +// HashedDefs array. +// - If PkgIdx is PkgIdxNone, SymIdx is the index of the symbol in the +// NonPkgDefs array (could naturally overflow to NonPkgRefs array). +// - Otherwise, SymIdx is the index of the symbol in some other package's +// SymbolDefs array. +// +// {0, 0} represents a nil symbol. Otherwise PkgIdx should not be 0. +// +// Hash contains the content hashes of content-addressable symbols, of +// which PkgIdx is PkgIdxHashed, in the same order of HashedDefs array. +// Hash64 is similar, for PkgIdxHashed64 symbols. +// +// RelocIndex, AuxIndex, and DataIndex contains indices/offsets to +// Relocs/Aux/Data blocks, one element per symbol, first for all the +// defined symbols, then all the defined hashed and non-package symbols, +// in the same order of SymbolDefs/Hashed64Defs/HashedDefs/NonPkgDefs +// arrays. For N total defined symbols, the array is of length N+1. The +// last element is the total number of relocations (aux symbols, data +// blocks, etc.). +// +// They can be accessed by index. For the i-th symbol, its relocations +// are the RelocIndex[i]-th (inclusive) to RelocIndex[i+1]-th (exclusive) +// elements in the Relocs array. Aux/Data are likewise. (The index is +// 0-based.) + +// Auxiliary symbols. +// +// Each symbol may (or may not) be associated with a number of auxiliary +// symbols. They are described in the Aux block. See Aux struct below. +// Currently a symbol's Gotype, FuncInfo, and associated DWARF symbols +// are auxiliary symbols. + +const stringRefSize = 8 // two uint32s + +type FingerprintType [8]byte + +func (fp FingerprintType) IsZero() bool { return fp == FingerprintType{} } + +// Package Index. +const ( + PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols + PkgIdxHashed64 // Short hashed (content-addressable) symbols + PkgIdxHashed // Hashed (content-addressable) symbols + PkgIdxBuiltin // Predefined runtime symbols (ex: runtime.newobject) + PkgIdxSelf // Symbols defined in the current package + PkgIdxSpecial = PkgIdxSelf // Indices above it has special meanings + PkgIdxInvalid = 0 + // The index of other referenced packages starts from 1. +) + +// Blocks +const ( + BlkAutolib = iota + BlkPkgIdx + BlkFile + BlkSymdef + BlkHashed64def + BlkHasheddef + BlkNonpkgdef + BlkNonpkgref + BlkRefFlags + BlkHash64 + BlkHash + BlkRelocIdx + BlkAuxIdx + BlkDataIdx + BlkReloc + BlkAux + BlkData + BlkRefName + BlkEnd + NBlk +) + +// File header. +// TODO: probably no need to export this. +type Header struct { + Magic string + Fingerprint FingerprintType + Flags uint32 + Offsets [NBlk]uint32 +} + +const Magic = "\x00go120ld" + +func (h *Header) Write(w *Writer) { + w.RawString(h.Magic) + w.Bytes(h.Fingerprint[:]) + w.Uint32(h.Flags) + for _, x := range h.Offsets { + w.Uint32(x) + } +} + +func (h *Header) Read(r *Reader) error { + b := r.BytesAt(0, len(Magic)) + h.Magic = string(b) + if h.Magic != Magic { + return errors.New("wrong magic, not a Go object file") + } + off := uint32(len(h.Magic)) + copy(h.Fingerprint[:], r.BytesAt(off, len(h.Fingerprint))) + off += 8 + h.Flags = r.uint32At(off) + off += 4 + for i := range h.Offsets { + h.Offsets[i] = r.uint32At(off) + off += 4 + } + return nil +} + +func (h *Header) Size() int { + return len(h.Magic) + len(h.Fingerprint) + 4 + 4*len(h.Offsets) +} + +// Autolib +type ImportedPkg struct { + Pkg string + Fingerprint FingerprintType +} + +const importedPkgSize = stringRefSize + 8 + +func (p *ImportedPkg) Write(w *Writer) { + w.StringRef(p.Pkg) + w.Bytes(p.Fingerprint[:]) +} + +// Symbol definition. +// +// Serialized format: +// +// Sym struct { +// Name string +// ABI uint16 +// Type uint8 +// Flag uint8 +// Flag2 uint8 +// Siz uint32 +// Align uint32 +// } +type Sym [SymSize]byte + +const SymSize = stringRefSize + 2 + 1 + 1 + 1 + 4 + 4 + +const SymABIstatic = ^uint16(0) + +const ( + ObjFlagShared = 1 << iota // this object is built with -shared + _ // was ObjFlagNeedNameExpansion + ObjFlagFromAssembly // object is from asm src, not go + ObjFlagUnlinkable // unlinkable package (linker will emit an error) + ObjFlagStd // standard library package +) + +// Sym.Flag +const ( + SymFlagDupok = 1 << iota + SymFlagLocal + SymFlagTypelink + SymFlagLeaf + SymFlagNoSplit + SymFlagReflectMethod + SymFlagGoType +) + +// Sym.Flag2 +const ( + SymFlagUsedInIface = 1 << iota + SymFlagItab + SymFlagDict + SymFlagPkgInit + SymFlagLinkname + SymFlagABIWrapper + SymFlagWasmExport +) + +// Returns the length of the name of the symbol. +func (s *Sym) NameLen(r *Reader) int { + return int(binary.LittleEndian.Uint32(s[:])) +} + +func (s *Sym) Name(r *Reader) string { + len := binary.LittleEndian.Uint32(s[:]) + off := binary.LittleEndian.Uint32(s[4:]) + return r.StringAt(off, len) +} + +func (s *Sym) ABI() uint16 { return binary.LittleEndian.Uint16(s[8:]) } +func (s *Sym) Type() uint8 { return s[10] } +func (s *Sym) Flag() uint8 { return s[11] } +func (s *Sym) Flag2() uint8 { return s[12] } +func (s *Sym) Siz() uint32 { return binary.LittleEndian.Uint32(s[13:]) } +func (s *Sym) Align() uint32 { return binary.LittleEndian.Uint32(s[17:]) } + +func (s *Sym) Dupok() bool { return s.Flag()&SymFlagDupok != 0 } +func (s *Sym) Local() bool { return s.Flag()&SymFlagLocal != 0 } +func (s *Sym) Typelink() bool { return s.Flag()&SymFlagTypelink != 0 } +func (s *Sym) Leaf() bool { return s.Flag()&SymFlagLeaf != 0 } +func (s *Sym) NoSplit() bool { return s.Flag()&SymFlagNoSplit != 0 } +func (s *Sym) ReflectMethod() bool { return s.Flag()&SymFlagReflectMethod != 0 } +func (s *Sym) IsGoType() bool { return s.Flag()&SymFlagGoType != 0 } +func (s *Sym) UsedInIface() bool { return s.Flag2()&SymFlagUsedInIface != 0 } +func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 0 } +func (s *Sym) IsDict() bool { return s.Flag2()&SymFlagDict != 0 } +func (s *Sym) IsPkgInit() bool { return s.Flag2()&SymFlagPkgInit != 0 } +func (s *Sym) IsLinkname() bool { return s.Flag2()&SymFlagLinkname != 0 } +func (s *Sym) ABIWrapper() bool { return s.Flag2()&SymFlagABIWrapper != 0 } +func (s *Sym) WasmExport() bool { return s.Flag2()&SymFlagWasmExport != 0 } + +func (s *Sym) SetName(x string, w *Writer) { + binary.LittleEndian.PutUint32(s[:], uint32(len(x))) + binary.LittleEndian.PutUint32(s[4:], w.stringOff(x)) +} + +func (s *Sym) SetABI(x uint16) { binary.LittleEndian.PutUint16(s[8:], x) } +func (s *Sym) SetType(x uint8) { s[10] = x } +func (s *Sym) SetFlag(x uint8) { s[11] = x } +func (s *Sym) SetFlag2(x uint8) { s[12] = x } +func (s *Sym) SetSiz(x uint32) { binary.LittleEndian.PutUint32(s[13:], x) } +func (s *Sym) SetAlign(x uint32) { binary.LittleEndian.PutUint32(s[17:], x) } + +func (s *Sym) Write(w *Writer) { w.Bytes(s[:]) } + +// for testing +func (s *Sym) fromBytes(b []byte) { copy(s[:], b) } + +// Symbol reference. +type SymRef struct { + PkgIdx uint32 + SymIdx uint32 +} + +func (s SymRef) IsZero() bool { return s == SymRef{} } + +// Hash64 +type Hash64Type [Hash64Size]byte + +const Hash64Size = 8 + +// Hash +type HashType [HashSize]byte + +const HashSize = 16 // truncated SHA256 + +// Relocation. +// +// Serialized format: +// +// Reloc struct { +// Off int32 +// Siz uint8 +// Type uint16 +// Add int64 +// Sym SymRef +// } +type Reloc [RelocSize]byte + +const RelocSize = 4 + 1 + 2 + 8 + 8 + +func (r *Reloc) Off() int32 { return int32(binary.LittleEndian.Uint32(r[:])) } +func (r *Reloc) Siz() uint8 { return r[4] } +func (r *Reloc) Type() uint16 { return binary.LittleEndian.Uint16(r[5:]) } +func (r *Reloc) Add() int64 { return int64(binary.LittleEndian.Uint64(r[7:])) } +func (r *Reloc) Sym() SymRef { + return SymRef{binary.LittleEndian.Uint32(r[15:]), binary.LittleEndian.Uint32(r[19:])} +} + +func (r *Reloc) SetOff(x int32) { binary.LittleEndian.PutUint32(r[:], uint32(x)) } +func (r *Reloc) SetSiz(x uint8) { r[4] = x } +func (r *Reloc) SetType(x uint16) { binary.LittleEndian.PutUint16(r[5:], x) } +func (r *Reloc) SetAdd(x int64) { binary.LittleEndian.PutUint64(r[7:], uint64(x)) } +func (r *Reloc) SetSym(x SymRef) { + binary.LittleEndian.PutUint32(r[15:], x.PkgIdx) + binary.LittleEndian.PutUint32(r[19:], x.SymIdx) +} + +func (r *Reloc) Set(off int32, size uint8, typ uint16, add int64, sym SymRef) { + r.SetOff(off) + r.SetSiz(size) + r.SetType(typ) + r.SetAdd(add) + r.SetSym(sym) +} + +func (r *Reloc) Write(w *Writer) { w.Bytes(r[:]) } + +// for testing +func (r *Reloc) fromBytes(b []byte) { copy(r[:], b) } + +// Aux symbol info. +// +// Serialized format: +// +// Aux struct { +// Type uint8 +// Sym SymRef +// } +type Aux [AuxSize]byte + +const AuxSize = 1 + 8 + +// Aux Type +const ( + AuxGotype = iota + AuxFuncInfo + AuxFuncdata + AuxDwarfInfo + AuxDwarfLoc + AuxDwarfRanges + AuxDwarfLines + AuxPcsp + AuxPcfile + AuxPcline + AuxPcinline + AuxPcdata + AuxWasmImport + AuxWasmType + AuxSehUnwindInfo +) + +func (a *Aux) Type() uint8 { return a[0] } +func (a *Aux) Sym() SymRef { + return SymRef{binary.LittleEndian.Uint32(a[1:]), binary.LittleEndian.Uint32(a[5:])} +} + +func (a *Aux) SetType(x uint8) { a[0] = x } +func (a *Aux) SetSym(x SymRef) { + binary.LittleEndian.PutUint32(a[1:], x.PkgIdx) + binary.LittleEndian.PutUint32(a[5:], x.SymIdx) +} + +func (a *Aux) Write(w *Writer) { w.Bytes(a[:]) } + +// for testing +func (a *Aux) fromBytes(b []byte) { copy(a[:], b) } + +// Referenced symbol flags. +// +// Serialized format: +// +// RefFlags struct { +// Sym symRef +// Flag uint8 +// Flag2 uint8 +// } +type RefFlags [RefFlagsSize]byte + +const RefFlagsSize = 8 + 1 + 1 + +func (r *RefFlags) Sym() SymRef { + return SymRef{binary.LittleEndian.Uint32(r[:]), binary.LittleEndian.Uint32(r[4:])} +} +func (r *RefFlags) Flag() uint8 { return r[8] } +func (r *RefFlags) Flag2() uint8 { return r[9] } + +func (r *RefFlags) SetSym(x SymRef) { + binary.LittleEndian.PutUint32(r[:], x.PkgIdx) + binary.LittleEndian.PutUint32(r[4:], x.SymIdx) +} +func (r *RefFlags) SetFlag(x uint8) { r[8] = x } +func (r *RefFlags) SetFlag2(x uint8) { r[9] = x } + +func (r *RefFlags) Write(w *Writer) { w.Bytes(r[:]) } + +// Used to construct an artificially large array type when reading an +// item from the object file relocs section or aux sym section (needs +// to work on 32-bit as well as 64-bit). See issue 41621. +const huge = (1<<31 - 1) / RelocSize + +// Referenced symbol name. +// +// Serialized format: +// +// RefName struct { +// Sym symRef +// Name string +// } +type RefName [RefNameSize]byte + +const RefNameSize = 8 + stringRefSize + +func (n *RefName) Sym() SymRef { + return SymRef{binary.LittleEndian.Uint32(n[:]), binary.LittleEndian.Uint32(n[4:])} +} +func (n *RefName) Name(r *Reader) string { + len := binary.LittleEndian.Uint32(n[8:]) + off := binary.LittleEndian.Uint32(n[12:]) + return r.StringAt(off, len) +} + +func (n *RefName) SetSym(x SymRef) { + binary.LittleEndian.PutUint32(n[:], x.PkgIdx) + binary.LittleEndian.PutUint32(n[4:], x.SymIdx) +} +func (n *RefName) SetName(x string, w *Writer) { + binary.LittleEndian.PutUint32(n[8:], uint32(len(x))) + binary.LittleEndian.PutUint32(n[12:], w.stringOff(x)) +} + +func (n *RefName) Write(w *Writer) { w.Bytes(n[:]) } + +type Writer struct { + wr *bio.Writer + stringMap map[string]uint32 + off uint32 // running offset + + b [8]byte // scratch space for writing bytes +} + +func NewWriter(wr *bio.Writer) *Writer { + return &Writer{wr: wr, stringMap: make(map[string]uint32)} +} + +func (w *Writer) AddString(s string) { + if _, ok := w.stringMap[s]; ok { + return + } + w.stringMap[s] = w.off + w.RawString(s) +} + +func (w *Writer) stringOff(s string) uint32 { + off, ok := w.stringMap[s] + if !ok { + panic(fmt.Sprintf("writeStringRef: string not added: %q", s)) + } + return off +} + +func (w *Writer) StringRef(s string) { + w.Uint32(uint32(len(s))) + w.Uint32(w.stringOff(s)) +} + +func (w *Writer) RawString(s string) { + w.wr.WriteString(s) + w.off += uint32(len(s)) +} + +func (w *Writer) Bytes(s []byte) { + w.wr.Write(s) + w.off += uint32(len(s)) +} + +func (w *Writer) Uint64(x uint64) { + binary.LittleEndian.PutUint64(w.b[:], x) + w.wr.Write(w.b[:]) + w.off += 8 +} + +func (w *Writer) Uint32(x uint32) { + binary.LittleEndian.PutUint32(w.b[:4], x) + w.wr.Write(w.b[:4]) + w.off += 4 +} + +func (w *Writer) Uint16(x uint16) { + binary.LittleEndian.PutUint16(w.b[:2], x) + w.wr.Write(w.b[:2]) + w.off += 2 +} + +func (w *Writer) Uint8(x uint8) { + w.wr.WriteByte(x) + w.off++ +} + +func (w *Writer) Offset() uint32 { + return w.off +} + +type Reader struct { + b []byte // mmapped bytes, if not nil + readonly bool // whether b is backed with read-only memory + + start uint32 + h Header // keep block offsets +} + +func NewReaderFromBytes(b []byte, readonly bool) *Reader { + r := &Reader{b: b, readonly: readonly, start: 0} + err := r.h.Read(r) + if err != nil { + return nil + } + return r +} + +func (r *Reader) BytesAt(off uint32, len int) []byte { + if len == 0 { + return nil + } + end := int(off) + len + return r.b[int(off):end:end] +} + +func (r *Reader) uint64At(off uint32) uint64 { + b := r.BytesAt(off, 8) + return binary.LittleEndian.Uint64(b) +} + +func (r *Reader) uint32At(off uint32) uint32 { + b := r.BytesAt(off, 4) + return binary.LittleEndian.Uint32(b) +} + +func (r *Reader) StringAt(off uint32, len uint32) string { + b := r.b[off : off+len] + if r.readonly { + return toString(b) // backed by RO memory, ok to make unsafe string + } + return string(b) +} + +func toString(b []byte) string { + if len(b) == 0 { + return "" + } + return unsafe.String(&b[0], len(b)) +} + +func (r *Reader) StringRef(off uint32) string { + l := r.uint32At(off) + return r.StringAt(r.uint32At(off+4), l) +} + +func (r *Reader) Fingerprint() FingerprintType { + return r.h.Fingerprint +} + +func (r *Reader) Autolib() []ImportedPkg { + n := (r.h.Offsets[BlkAutolib+1] - r.h.Offsets[BlkAutolib]) / importedPkgSize + s := make([]ImportedPkg, n) + off := r.h.Offsets[BlkAutolib] + for i := range s { + s[i].Pkg = r.StringRef(off) + copy(s[i].Fingerprint[:], r.BytesAt(off+stringRefSize, len(s[i].Fingerprint))) + off += importedPkgSize + } + return s +} + +func (r *Reader) Pkglist() []string { + n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / stringRefSize + s := make([]string, n) + off := r.h.Offsets[BlkPkgIdx] + for i := range s { + s[i] = r.StringRef(off) + off += stringRefSize + } + return s +} + +func (r *Reader) NPkg() int { + return int(r.h.Offsets[BlkPkgIdx+1]-r.h.Offsets[BlkPkgIdx]) / stringRefSize +} + +func (r *Reader) Pkg(i int) string { + off := r.h.Offsets[BlkPkgIdx] + uint32(i)*stringRefSize + return r.StringRef(off) +} + +func (r *Reader) NFile() int { + return int(r.h.Offsets[BlkFile+1]-r.h.Offsets[BlkFile]) / stringRefSize +} + +func (r *Reader) File(i int) string { + off := r.h.Offsets[BlkFile] + uint32(i)*stringRefSize + return r.StringRef(off) +} + +func (r *Reader) NSym() int { + return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / SymSize +} + +func (r *Reader) NHashed64def() int { + return int(r.h.Offsets[BlkHashed64def+1]-r.h.Offsets[BlkHashed64def]) / SymSize +} + +func (r *Reader) NHasheddef() int { + return int(r.h.Offsets[BlkHasheddef+1]-r.h.Offsets[BlkHasheddef]) / SymSize +} + +func (r *Reader) NNonpkgdef() int { + return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / SymSize +} + +func (r *Reader) NNonpkgref() int { + return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / SymSize +} + +// SymOff returns the offset of the i-th symbol. +func (r *Reader) SymOff(i uint32) uint32 { + return r.h.Offsets[BlkSymdef] + i*SymSize +} + +// Sym returns a pointer to the i-th symbol. +func (r *Reader) Sym(i uint32) *Sym { + off := r.SymOff(i) + return (*Sym)(unsafe.Pointer(&r.b[off])) +} + +// NRefFlags returns the number of referenced symbol flags. +func (r *Reader) NRefFlags() int { + return int(r.h.Offsets[BlkRefFlags+1]-r.h.Offsets[BlkRefFlags]) / RefFlagsSize +} + +// RefFlags returns a pointer to the i-th referenced symbol flags. +// Note: here i is not a local symbol index, just a counter. +func (r *Reader) RefFlags(i int) *RefFlags { + off := r.h.Offsets[BlkRefFlags] + uint32(i*RefFlagsSize) + return (*RefFlags)(unsafe.Pointer(&r.b[off])) +} + +// Hash64 returns the i-th short hashed symbol's hash. +// Note: here i is the index of short hashed symbols, not all symbols +// (unlike other accessors). +func (r *Reader) Hash64(i uint32) uint64 { + off := r.h.Offsets[BlkHash64] + i*Hash64Size + return r.uint64At(off) +} + +// Hash returns a pointer to the i-th hashed symbol's hash. +// Note: here i is the index of hashed symbols, not all symbols +// (unlike other accessors). +func (r *Reader) Hash(i uint32) *HashType { + off := r.h.Offsets[BlkHash] + i*HashSize + return (*HashType)(unsafe.Pointer(&r.b[off])) +} + +// NReloc returns the number of relocations of the i-th symbol. +func (r *Reader) NReloc(i uint32) int { + relocIdxOff := r.h.Offsets[BlkRelocIdx] + i*4 + return int(r.uint32At(relocIdxOff+4) - r.uint32At(relocIdxOff)) +} + +// RelocOff returns the offset of the j-th relocation of the i-th symbol. +func (r *Reader) RelocOff(i uint32, j int) uint32 { + relocIdxOff := r.h.Offsets[BlkRelocIdx] + i*4 + relocIdx := r.uint32At(relocIdxOff) + return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(RelocSize) +} + +// Reloc returns a pointer to the j-th relocation of the i-th symbol. +func (r *Reader) Reloc(i uint32, j int) *Reloc { + off := r.RelocOff(i, j) + return (*Reloc)(unsafe.Pointer(&r.b[off])) +} + +// Relocs returns a pointer to the relocations of the i-th symbol. +func (r *Reader) Relocs(i uint32) []Reloc { + off := r.RelocOff(i, 0) + n := r.NReloc(i) + return (*[huge]Reloc)(unsafe.Pointer(&r.b[off]))[:n:n] +} + +// NAux returns the number of aux symbols of the i-th symbol. +func (r *Reader) NAux(i uint32) int { + auxIdxOff := r.h.Offsets[BlkAuxIdx] + i*4 + return int(r.uint32At(auxIdxOff+4) - r.uint32At(auxIdxOff)) +} + +// AuxOff returns the offset of the j-th aux symbol of the i-th symbol. +func (r *Reader) AuxOff(i uint32, j int) uint32 { + auxIdxOff := r.h.Offsets[BlkAuxIdx] + i*4 + auxIdx := r.uint32At(auxIdxOff) + return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(AuxSize) +} + +// Aux returns a pointer to the j-th aux symbol of the i-th symbol. +func (r *Reader) Aux(i uint32, j int) *Aux { + off := r.AuxOff(i, j) + return (*Aux)(unsafe.Pointer(&r.b[off])) +} + +// Auxs returns the aux symbols of the i-th symbol. +func (r *Reader) Auxs(i uint32) []Aux { + off := r.AuxOff(i, 0) + n := r.NAux(i) + return (*[huge]Aux)(unsafe.Pointer(&r.b[off]))[:n:n] +} + +// DataOff returns the offset of the i-th symbol's data. +func (r *Reader) DataOff(i uint32) uint32 { + dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4 + return r.h.Offsets[BlkData] + r.uint32At(dataIdxOff) +} + +// DataSize returns the size of the i-th symbol's data. +func (r *Reader) DataSize(i uint32) int { + dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4 + return int(r.uint32At(dataIdxOff+4) - r.uint32At(dataIdxOff)) +} + +// Data returns the i-th symbol's data. +func (r *Reader) Data(i uint32) []byte { + dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4 + base := r.h.Offsets[BlkData] + off := r.uint32At(dataIdxOff) + end := r.uint32At(dataIdxOff + 4) + return r.BytesAt(base+off, int(end-off)) +} + +// DataString returns the i-th symbol's data as a string. +func (r *Reader) DataString(i uint32) string { + dataIdxOff := r.h.Offsets[BlkDataIdx] + i*4 + base := r.h.Offsets[BlkData] + off := r.uint32At(dataIdxOff) + end := r.uint32At(dataIdxOff + 4) + return r.StringAt(base+off, end-off) +} + +// NRefName returns the number of referenced symbol names. +func (r *Reader) NRefName() int { + return int(r.h.Offsets[BlkRefName+1]-r.h.Offsets[BlkRefName]) / RefNameSize +} + +// RefName returns a pointer to the i-th referenced symbol name. +// Note: here i is not a local symbol index, just a counter. +func (r *Reader) RefName(i int) *RefName { + off := r.h.Offsets[BlkRefName] + uint32(i*RefNameSize) + return (*RefName)(unsafe.Pointer(&r.b[off])) +} + +// ReadOnly returns whether r.BytesAt returns read-only bytes. +func (r *Reader) ReadOnly() bool { + return r.readonly +} + +// Flags returns the flag bits read from the object file header. +func (r *Reader) Flags() uint32 { + return r.h.Flags +} + +func (r *Reader) Shared() bool { return r.Flags()&ObjFlagShared != 0 } +func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 } +func (r *Reader) Unlinkable() bool { return r.Flags()&ObjFlagUnlinkable != 0 } +func (r *Reader) Std() bool { return r.Flags()&ObjFlagStd != 0 } diff --git a/internal/go/src/objabi/autotype.go b/internal/go/src/objabi/autotype.go new file mode 100755 index 0000000..f9d17a3 --- /dev/null +++ b/internal/go/src/objabi/autotype.go @@ -0,0 +1,38 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +// Auto.name +const ( + A_AUTO = 1 + iota + A_PARAM + A_DELETED_AUTO +) diff --git a/internal/go/src/objabi/flag.go b/internal/go/src/objabi/flag.go new file mode 100755 index 0000000..ad49061 --- /dev/null +++ b/internal/go/src/objabi/flag.go @@ -0,0 +1,374 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import ( + "flag" + "fmt" + "loov.dev/lensm/internal/go/src/bisect" + "loov.dev/lensm/internal/go/src/buildcfg" + "io" + "log" + "os" + "reflect" + "sort" + "strconv" + "strings" +) + +func Flagcount(name, usage string, val *int) { + flag.Var((*count)(val), name, usage) +} + +func Flagfn1(name, usage string, f func(string)) { + flag.Var(fn1(f), name, usage) +} + +func Flagprint(w io.Writer) { + flag.CommandLine.SetOutput(w) + flag.PrintDefaults() +} + +func Flagparse(usage func()) { + flag.Usage = usage + os.Args = expandArgs(os.Args) + flag.Parse() +} + +// expandArgs expands "response files" arguments in the provided slice. +// +// A "response file" argument starts with '@' and the rest of that +// argument is a filename with CR-or-CRLF-separated arguments. Each +// argument in the named files can also contain response file +// arguments. See Issue 18468. +// +// The returned slice 'out' aliases 'in' iff the input did not contain +// any response file arguments. +// +// TODO: handle relative paths of recursive expansions in different directories? +// Is there a spec for this? Are relative paths allowed? +func expandArgs(in []string) (out []string) { + // out is nil until we see a "@" argument. + for i, s := range in { + if strings.HasPrefix(s, "@") { + if out == nil { + out = make([]string, 0, len(in)*2) + out = append(out, in[:i]...) + } + slurp, err := os.ReadFile(s[1:]) + if err != nil { + log.Fatal(err) + } + args := strings.Split(strings.TrimSpace(strings.ReplaceAll(string(slurp), "\r", "")), "\n") + for i, arg := range args { + args[i] = DecodeArg(arg) + } + out = append(out, expandArgs(args)...) + } else if out != nil { + out = append(out, s) + } + } + if out == nil { + return in + } + return +} + +func AddVersionFlag() { + flag.Var(versionFlag{}, "V", "print version and exit") +} + +var buildID string // filled in by linker + +type versionFlag struct{} + +func (versionFlag) IsBoolFlag() bool { return true } +func (versionFlag) Get() any { return nil } +func (versionFlag) String() string { return "" } +func (versionFlag) Set(s string) error { + name := os.Args[0] + name = name[strings.LastIndex(name, `/`)+1:] + name = name[strings.LastIndex(name, `\`)+1:] + name = strings.TrimSuffix(name, ".exe") + + p := "" + + // If the enabled experiments differ from the baseline, + // include that difference. + if goexperiment := buildcfg.Experiment.String(); goexperiment != "" { + p = " X:" + goexperiment + } + + // The go command invokes -V=full to get a unique identifier + // for this tool. It is assumed that the release version is sufficient + // for releases, but during development we include the full + // build ID of the binary, so that if the compiler is changed and + // rebuilt, we notice and rebuild all packages. + if s == "full" { + if strings.Contains(buildcfg.Version, "devel") { + p += " buildID=" + buildID + } + } + + fmt.Printf("%s version %s%s\n", name, buildcfg.Version, p) + os.Exit(0) + return nil +} + +// count is a flag.Value that is like a flag.Bool and a flag.Int. +// If used as -name, it increments the count, but -name=x sets the count. +// Used for verbose flag -v. +type count int + +func (c *count) String() string { + return fmt.Sprint(int(*c)) +} + +func (c *count) Set(s string) error { + switch s { + case "true": + *c++ + case "false": + *c = 0 + default: + n, err := strconv.Atoi(s) + if err != nil { + return fmt.Errorf("invalid count %q", s) + } + *c = count(n) + } + return nil +} + +func (c *count) Get() any { + return int(*c) +} + +func (c *count) IsBoolFlag() bool { + return true +} + +func (c *count) IsCountFlag() bool { + return true +} + +type fn1 func(string) + +func (f fn1) Set(s string) error { + f(s) + return nil +} + +func (f fn1) String() string { return "" } + +// DecodeArg decodes an argument. +// +// This function is public for testing with the parallel encoder. +func DecodeArg(arg string) string { + // If no encoding, fastpath out. + if !strings.ContainsAny(arg, "\\\n") { + return arg + } + + var b strings.Builder + var wasBS bool + for _, r := range arg { + if wasBS { + switch r { + case '\\': + b.WriteByte('\\') + case 'n': + b.WriteByte('\n') + default: + // This shouldn't happen. The only backslashes that reach here + // should encode '\n' and '\\' exclusively. + panic("badly formatted input") + } + } else if r == '\\' { + wasBS = true + continue + } else { + b.WriteRune(r) + } + wasBS = false + } + return b.String() +} + +type debugField struct { + name string + help string + concurrentOk bool // true if this field/flag is compatible with concurrent compilation + val any // *int or *string +} + +type DebugFlag struct { + tab map[string]debugField + concurrentOk *bool // this is non-nil only for compiler's DebugFlags, but only compiler has concurrent:ok fields + debugSSA DebugSSA // this is non-nil only for compiler's DebugFlags. +} + +// A DebugSSA function is called to set a -d ssa/... option. +// If nil, those options are reported as invalid options. +// If DebugSSA returns a non-empty string, that text is reported as a compiler error. +// If phase is "help", it should print usage information and terminate the process. +type DebugSSA func(phase, flag string, val int, valString string) string + +// NewDebugFlag constructs a DebugFlag for the fields of debug, which +// must be a pointer to a struct. +// +// Each field of *debug is a different value, named for the lower-case of the field name. +// Each field must be an int or string and must have a `help` struct tag. +// There may be an "Any bool" field, which will be set if any debug flags are set. +// +// The returned flag takes a comma-separated list of settings. +// Each setting is name=value; for ints, name is short for name=1. +// +// If debugSSA is non-nil, any debug flags of the form ssa/... will be +// passed to debugSSA for processing. +func NewDebugFlag(debug any, debugSSA DebugSSA) *DebugFlag { + flag := &DebugFlag{ + tab: make(map[string]debugField), + debugSSA: debugSSA, + } + + v := reflect.ValueOf(debug).Elem() + t := v.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + ptr := v.Field(i).Addr().Interface() + if f.Name == "ConcurrentOk" { + switch ptr := ptr.(type) { + default: + panic("debug.ConcurrentOk must have type bool") + case *bool: + flag.concurrentOk = ptr + } + continue + } + name := strings.ToLower(f.Name) + help := f.Tag.Get("help") + if help == "" { + panic(fmt.Sprintf("debug.%s is missing help text", f.Name)) + } + concurrent := f.Tag.Get("concurrent") + + switch ptr.(type) { + default: + panic(fmt.Sprintf("debug.%s has invalid type %v (must be int, string, or *bisect.Matcher)", f.Name, f.Type)) + case *int, *string, **bisect.Matcher: + // ok + } + flag.tab[name] = debugField{name, help, concurrent == "ok", ptr} + } + + return flag +} + +func (f *DebugFlag) Set(debugstr string) error { + if debugstr == "" { + return nil + } + for name := range strings.SplitSeq(debugstr, ",") { + if name == "" { + continue + } + // display help about the debug option itself and quit + if name == "help" { + fmt.Print(debugHelpHeader) + maxLen, names := 0, []string{} + if f.debugSSA != nil { + maxLen = len("ssa/help") + } + for name := range f.tab { + if len(name) > maxLen { + maxLen = len(name) + } + names = append(names, name) + } + sort.Strings(names) + // Indent multi-line help messages. + nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "") + for _, name := range names { + help := f.tab[name].help + fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.ReplaceAll(help, "\n", nl)) + } + if f.debugSSA != nil { + // ssa options have their own help + fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging") + } + os.Exit(0) + } + + val, valstring, haveInt := 1, "", true + if i := strings.IndexAny(name, "=:"); i >= 0 { + var err error + name, valstring = name[:i], name[i+1:] + val, err = strconv.Atoi(valstring) + if err != nil { + val, haveInt = 1, false + } + } + + if t, ok := f.tab[name]; ok { + switch vp := t.val.(type) { + case nil: + // Ignore + case *string: + *vp = valstring + case *int: + if !haveInt { + log.Fatalf("invalid debug value %v", name) + } + *vp = val + case **bisect.Matcher: + var err error + *vp, err = bisect.New(valstring) + if err != nil { + log.Fatalf("debug flag %v: %v", name, err) + } + default: + panic("bad debugtab type") + } + // assembler DebugFlags don't have a ConcurrentOk field to reset, so check against that. + if !t.concurrentOk && f.concurrentOk != nil { + *f.concurrentOk = false + } + } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") { + // expect form ssa/phase/flag + // e.g. -d=ssa/generic_cse/time + // _ in phase name also matches space + phase := name[4:] + flag := "debug" // default flag is debug + if i := strings.Index(phase, "/"); i >= 0 { + flag = phase[i+1:] + phase = phase[:i] + } + err := f.debugSSA(phase, flag, val, valstring) + if err != "" { + log.Fatal(err) + } + // Setting this false for -d=ssa/... preserves old behavior + // of turning off concurrency for any debug flags. + // It's not known for sure if this is necessary, but it is safe. + *f.concurrentOk = false + + } else { + return fmt.Errorf("unknown debug key %s\n", name) + } + } + + return nil +} + +const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=] + + is one of: + +` + +func (f *DebugFlag) String() string { + return "" +} diff --git a/internal/go/src/objabi/funcid.go b/internal/go/src/objabi/funcid.go new file mode 100755 index 0000000..8b8729c --- /dev/null +++ b/internal/go/src/objabi/funcid.go @@ -0,0 +1,52 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import ( + "loov.dev/lensm/internal/go/src/abi" + "strings" +) + +var funcIDs = map[string]abi.FuncID{ + "abort": abi.FuncID_abort, + "asmcgocall": abi.FuncID_asmcgocall, + "asyncPreempt": abi.FuncID_asyncPreempt, + "cgocallback": abi.FuncID_cgocallback, + "corostart": abi.FuncID_corostart, + "debugCallV2": abi.FuncID_debugCallV2, + "gcBgMarkWorker": abi.FuncID_gcBgMarkWorker, + "rt0_go": abi.FuncID_rt0_go, + "goexit": abi.FuncID_goexit, + "gogo": abi.FuncID_gogo, + "gopanic": abi.FuncID_gopanic, + "handleAsyncEvent": abi.FuncID_handleAsyncEvent, + "main": abi.FuncID_runtime_main, + "mcall": abi.FuncID_mcall, + "morestack": abi.FuncID_morestack, + "mstart": abi.FuncID_mstart, + "panicwrap": abi.FuncID_panicwrap, + "runFinalizers": abi.FuncID_runFinalizers, + "runCleanups": abi.FuncID_runCleanups, + "sigpanic": abi.FuncID_sigpanic, + "systemstack_switch": abi.FuncID_systemstack_switch, + "systemstack": abi.FuncID_systemstack, + + // Don't show in call stack but otherwise not special. + "deferreturn": abi.FuncIDWrapper, +} + +// Get the function ID for the named function in the named file. +// The function should be package-qualified. +func GetFuncID(name string, isWrapper bool) abi.FuncID { + if isWrapper { + return abi.FuncIDWrapper + } + if strings.HasPrefix(name, "runtime.") { + if id, ok := funcIDs[name[len("runtime."):]]; ok { + return id + } + } + return abi.FuncIDNormal +} diff --git a/internal/go/src/objabi/head.go b/internal/go/src/objabi/head.go new file mode 100755 index 0000000..3109b5c --- /dev/null +++ b/internal/go/src/objabi/head.go @@ -0,0 +1,114 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +import "fmt" + +// HeadType is the executable header type. +type HeadType uint8 + +const ( + Hunknown HeadType = iota + Hdarwin + Hdragonfly + Hfreebsd + Hjs + Hlinux + Hnetbsd + Hopenbsd + Hplan9 + Hsolaris + Hwasip1 + Hwindows + Haix +) + +func (h *HeadType) Set(s string) error { + switch s { + case "aix": + *h = Haix + case "darwin", "ios": + *h = Hdarwin + case "dragonfly": + *h = Hdragonfly + case "freebsd": + *h = Hfreebsd + case "js": + *h = Hjs + case "linux", "android": + *h = Hlinux + case "netbsd": + *h = Hnetbsd + case "openbsd": + *h = Hopenbsd + case "plan9": + *h = Hplan9 + case "illumos", "solaris": + *h = Hsolaris + case "wasip1": + *h = Hwasip1 + case "windows": + *h = Hwindows + default: + return fmt.Errorf("invalid headtype: %q", s) + } + return nil +} + +func (h HeadType) String() string { + switch h { + case Haix: + return "aix" + case Hdarwin: + return "darwin" + case Hdragonfly: + return "dragonfly" + case Hfreebsd: + return "freebsd" + case Hjs: + return "js" + case Hlinux: + return "linux" + case Hnetbsd: + return "netbsd" + case Hopenbsd: + return "openbsd" + case Hplan9: + return "plan9" + case Hsolaris: + return "solaris" + case Hwasip1: + return "wasip1" + case Hwindows: + return "windows" + } + return fmt.Sprintf("HeadType(%d)", h) +} diff --git a/internal/go/src/objabi/line.go b/internal/go/src/objabi/line.go new file mode 100755 index 0000000..e118ea1 --- /dev/null +++ b/internal/go/src/objabi/line.go @@ -0,0 +1,134 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import ( + "loov.dev/lensm/internal/go/src/buildcfg" + "os" + "path/filepath" + "runtime" + "strings" +) + +// WorkingDir returns the current working directory +// (or "/???" if the directory cannot be identified), +// with "/" as separator. +func WorkingDir() string { + var path string + path, _ = os.Getwd() + if path == "" { + path = "/???" + } + return filepath.ToSlash(path) +} + +// AbsFile returns the absolute filename for file in the given directory, +// as rewritten by the rewrites argument. +// For unrewritten paths, AbsFile rewrites a leading $GOROOT prefix to the literal "$GOROOT". +// If the resulting path is the empty string, the result is "??". +// +// The rewrites argument is a ;-separated list of rewrites. +// Each rewrite is of the form "prefix" or "prefix=>replace", +// where prefix must match a leading sequence of path elements +// and is either removed entirely or replaced by the replacement. +func AbsFile(dir, file, rewrites string) string { + abs := file + if dir != "" && !filepath.IsAbs(file) { + abs = filepath.Join(dir, file) + } + + abs, rewritten := ApplyRewrites(abs, rewrites) + if !rewritten && buildcfg.GOROOT != "" && hasPathPrefix(abs, buildcfg.GOROOT) { + abs = "$GOROOT" + abs[len(buildcfg.GOROOT):] + } + + // Rewrite paths to match the slash convention of the target. + // This helps ensure that cross-compiled distributions remain + // bit-for-bit identical to natively compiled distributions. + if runtime.GOOS == "windows" { + abs = strings.ReplaceAll(abs, `\`, "/") + } + + if abs == "" { + abs = "??" + } + return abs +} + +// ApplyRewrites returns the filename for file in the given directory, +// as rewritten by the rewrites argument. +// +// The rewrites argument is a ;-separated list of rewrites. +// Each rewrite is of the form "prefix" or "prefix=>replace", +// where prefix must match a leading sequence of path elements +// and is either removed entirely or replaced by the replacement. +func ApplyRewrites(file, rewrites string) (string, bool) { + start := 0 + for i := 0; i <= len(rewrites); i++ { + if i == len(rewrites) || rewrites[i] == ';' { + if new, ok := applyRewrite(file, rewrites[start:i]); ok { + return new, true + } + start = i + 1 + } + } + + return file, false +} + +// applyRewrite applies the rewrite to the path, +// returning the rewritten path and a boolean +// indicating whether the rewrite applied at all. +func applyRewrite(path, rewrite string) (string, bool) { + prefix, replace := rewrite, "" + if j := strings.LastIndex(rewrite, "=>"); j >= 0 { + prefix, replace = rewrite[:j], rewrite[j+len("=>"):] + } + + if prefix == "" || !hasPathPrefix(path, prefix) { + return path, false + } + if len(path) == len(prefix) { + return replace, true + } + if replace == "" { + return path[len(prefix)+1:], true + } + return replace + path[len(prefix):], true +} + +// Does s have t as a path prefix? +// That is, does s == t or does s begin with t followed by a slash? +// For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true. +// Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true. +// We do not allow full Unicode case folding, for fear of causing more confusion +// or harm than good. (For an example of the kinds of things that can go wrong, +// see http://article.gmane.org/gmane.linux.kernel/1853266.) +func hasPathPrefix(s string, t string) bool { + if len(t) > len(s) { + return false + } + var i int + for i = 0; i < len(t); i++ { + cs := int(s[i]) + ct := int(t[i]) + if 'A' <= cs && cs <= 'Z' { + cs += 'a' - 'A' + } + if 'A' <= ct && ct <= 'Z' { + ct += 'a' - 'A' + } + if cs == '\\' { + cs = '/' + } + if ct == '\\' { + ct = '/' + } + if cs != ct { + return false + } + } + return i >= len(s) || s[i] == '/' || s[i] == '\\' +} diff --git a/internal/go/src/objabi/path.go b/internal/go/src/objabi/path.go new file mode 100755 index 0000000..30301b1 --- /dev/null +++ b/internal/go/src/objabi/path.go @@ -0,0 +1,78 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import ( + "fmt" + "strconv" + "strings" +) + +// PathToPrefix converts raw string to the prefix that will be used in the +// symbol table. All control characters, space, '%' and '"', as well as +// non-7-bit clean bytes turn into %xx. The period needs escaping only in the +// last segment of the path, and it makes for happier users if we escape that as +// little as possible. +func PathToPrefix(s string) string { + slash := strings.LastIndex(s, "/") + // check for chars that need escaping + n := 0 + for r := 0; r < len(s); r++ { + if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F { + n++ + } + } + + // quick exit + if n == 0 { + return s + } + + // escape + const hex = "0123456789abcdef" + p := make([]byte, 0, len(s)+2*n) + for r := 0; r < len(s); r++ { + if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F { + p = append(p, '%', hex[c>>4], hex[c&0xF]) + } else { + p = append(p, c) + } + } + + return string(p) +} + +// PrefixToPath is the inverse of PathToPrefix, replacing escape sequences with +// the original character. +func PrefixToPath(s string) (string, error) { + percent := strings.IndexByte(s, '%') + if percent == -1 { + return s, nil + } + + p := make([]byte, 0, len(s)) + for i := 0; i < len(s); { + if s[i] != '%' { + p = append(p, s[i]) + i++ + continue + } + if i+2 >= len(s) { + // Not enough characters remaining to be a valid escape + // sequence. + return "", fmt.Errorf("malformed prefix %q: escape sequence must contain two hex digits", s) + } + + b, err := strconv.ParseUint(s[i+1:i+3], 16, 8) + if err != nil { + // Not a valid escape sequence. + return "", fmt.Errorf("malformed prefix %q: escape sequence %q must contain two hex digits", s, s[i:i+3]) + } + + p = append(p, byte(b)) + i += 3 + } + return string(p), nil +} diff --git a/internal/go/src/objabi/pkgspecial.go b/internal/go/src/objabi/pkgspecial.go new file mode 100755 index 0000000..7e49962 --- /dev/null +++ b/internal/go/src/objabi/pkgspecial.go @@ -0,0 +1,136 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import "sync" + +// PkgSpecial indicates special build properties of a given runtime-related +// package. +type PkgSpecial struct { + // Runtime indicates that this package is "runtime" or imported by + // "runtime". This has several effects (which maybe should be split out): + // + // - Implicit allocation is disallowed. + // + // - Various runtime pragmas are enabled. + // + // - Optimizations are always enabled. + // + // - Checkptr is always disabled. + // + // This should be set for runtime and all packages it imports, and may be + // set for additional packages. + Runtime bool + + // NoInstrument indicates this package should not receive sanitizer + // instrumentation. In many of these, instrumentation could cause infinite + // recursion. This is all runtime packages, plus those that support the + // sanitizers. + NoInstrument bool + + // NoRaceFunc indicates functions in this package should not get + // racefuncenter/racefuncexit instrumentation Memory accesses in these + // packages are either uninteresting or will cause false positives. + NoRaceFunc bool + + // AllowAsmABI indicates that assembly in this package is allowed to use ABI + // selectors in symbol names. Generally this is needed for packages that + // interact closely with the runtime package or have performance-critical + // assembly. + AllowAsmABI bool +} + +var runtimePkgs = []string{ + // TODO(panjf2000): consider syncing the list inside the + // isAsyncSafePoint in preempt.go based on this list? + + "runtime", + + "loov.dev/lensm/internal/go/src/runtime/atomic", + "loov.dev/lensm/internal/go/src/runtime/cgroup", + "loov.dev/lensm/internal/go/src/runtime/exithook", + "loov.dev/lensm/internal/go/src/runtime/gc", + "loov.dev/lensm/internal/go/src/runtime/gc/scan", + "loov.dev/lensm/internal/go/src/runtime/maps", + "loov.dev/lensm/internal/go/src/runtime/math", + "loov.dev/lensm/internal/go/src/runtime/sys", + "loov.dev/lensm/internal/go/src/runtime/syscall/linux", + "loov.dev/lensm/internal/go/src/runtime/syscall/windows", + + "loov.dev/lensm/internal/go/src/abi", + "loov.dev/lensm/internal/go/src/bytealg", + "loov.dev/lensm/internal/go/src/byteorder", + "loov.dev/lensm/internal/go/src/chacha8rand", + "loov.dev/lensm/internal/go/src/coverage/rtcov", + "loov.dev/lensm/internal/go/src/cpu", + "loov.dev/lensm/internal/go/src/goarch", + "loov.dev/lensm/internal/go/src/godebugs", + "loov.dev/lensm/internal/go/src/goexperiment", + "loov.dev/lensm/internal/go/src/goos", + "loov.dev/lensm/internal/go/src/profilerecord", + "loov.dev/lensm/internal/go/src/strconv", + "loov.dev/lensm/internal/go/src/stringslite", +} + +// extraNoInstrumentPkgs is the set of packages in addition to runtimePkgs that +// should have NoInstrument set. +var extraNoInstrumentPkgs = []string{ + "runtime/race", + "runtime/msan", + "runtime/asan", + // We omit bytealg even though it's imported by runtime because it also + // backs a lot of package bytes. Currently we don't have a way to omit race + // instrumentation when used from the runtime while keeping race + // instrumentation when used from user code. Somehow this doesn't seem to + // cause problems, though we may be skating on thin ice. See #61204. + "-internal/bytealg", +} + +var noRaceFuncPkgs = []string{"sync", "sync/atomic", "internal/sync", "internal/runtime/atomic"} + +var allowAsmABIPkgs = []string{ + "runtime", + "reflect", + "syscall", + "loov.dev/lensm/internal/go/src/bytealg", + "loov.dev/lensm/internal/go/src/chacha8rand", + "loov.dev/lensm/internal/go/src/runtime/syscall/linux", + "loov.dev/lensm/internal/go/src/runtime/syscall/windows", + "loov.dev/lensm/internal/go/src/runtime/startlinetest", +} + +// LookupPkgSpecial returns special build properties for the given package path. +func LookupPkgSpecial(pkgPath string) PkgSpecial { + return pkgSpecialsOnce()[pkgPath] +} + +var pkgSpecialsOnce = sync.OnceValue(func() map[string]PkgSpecial { + // Construct pkgSpecials from various package lists. This lets us use + // more flexible logic, while keeping the final map simple, and avoids + // the init-time cost of a map. + pkgSpecials := make(map[string]PkgSpecial) + set := func(elt string, f func(*PkgSpecial)) { + s := pkgSpecials[elt] + f(&s) + pkgSpecials[elt] = s + } + for _, pkg := range runtimePkgs { + set(pkg, func(ps *PkgSpecial) { ps.Runtime = true; ps.NoInstrument = true }) + } + for _, pkg := range extraNoInstrumentPkgs { + if pkg[0] == '-' { + set(pkg[1:], func(ps *PkgSpecial) { ps.NoInstrument = false }) + } else { + set(pkg, func(ps *PkgSpecial) { ps.NoInstrument = true }) + } + } + for _, pkg := range noRaceFuncPkgs { + set(pkg, func(ps *PkgSpecial) { ps.NoRaceFunc = true }) + } + for _, pkg := range allowAsmABIPkgs { + set(pkg, func(ps *PkgSpecial) { ps.AllowAsmABI = true }) + } + return pkgSpecials +}) diff --git a/internal/go/src/objabi/reloctype.go b/internal/go/src/objabi/reloctype.go new file mode 100755 index 0000000..aef8900 --- /dev/null +++ b/internal/go/src/objabi/reloctype.go @@ -0,0 +1,544 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +type RelocType int16 + +//go:generate stringer -type=RelocType +const ( + R_ADDR RelocType = 1 + iota + // R_ADDRPOWER relocates a pair of "D-form" instructions (instructions with 16-bit + // immediates in the low half of the instruction word), usually addis followed by + // another add or a load, inserting the "high adjusted" 16 bits of the address of + // the referenced symbol into the immediate field of the first instruction and the + // low 16 bits into that of the second instruction. + R_ADDRPOWER + // R_ADDRARM64 relocates an adrp, add pair to compute the address of the + // referenced symbol. + R_ADDRARM64 + // R_ADDRMIPS (only used on mips/mips64) resolves to the low 16 bits of an external + // address, by encoding it into the instruction. + R_ADDRMIPS + // R_ADDROFF resolves to a 32-bit offset from the beginning of the section + // holding the data being relocated to the referenced symbol. + R_ADDROFF + R_SIZE + R_CALL + R_CALLARM + R_CALLARM64 + R_CALLIND + R_CALLPOWER + // R_CALLMIPS (only used on mips64) resolves to non-PC-relative target address + // of a CALL (JAL) instruction, by encoding the address into the instruction. + R_CALLMIPS + R_CONST + R_PCREL + // R_TLS_LE, used on 386, amd64, and ARM, resolves to the offset of the + // thread-local symbol from the thread local base and is used to implement the + // "local exec" model for tls access (r.Sym is not set on intel platforms but is + // set to a TLS symbol -- runtime.tlsg -- in the linker when externally linking). + R_TLS_LE + // R_TLS_IE, used 386, amd64, and ARM resolves to the PC-relative offset to a GOT + // slot containing the offset from the thread-local symbol from the thread local + // base and is used to implemented the "initial exec" model for tls access (r.Sym + // is not set on intel platforms but is set to a TLS symbol -- runtime.tlsg -- in + // the linker when externally linking). + R_TLS_IE + R_GOTOFF + R_PLT0 + R_PLT1 + R_PLT2 + R_USEFIELD + // R_USETYPE resolves to an *rtype, but no relocation is created. The + // linker uses this as a signal that the pointed-to type information + // should be linked into the final binary, even if there are no other + // direct references. (This is used for types reachable by reflection.) + R_USETYPE + // R_USEIFACE marks a type is converted to an interface in the function this + // relocation is applied to. The target is a type descriptor or an itab + // (in the latter case it refers to the concrete type contained in the itab). + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACE + // R_USEIFACEMETHOD marks an interface method that is used in the function + // this relocation is applied to. The target is an interface type descriptor. + // The addend is the offset of the method in the type descriptor. + // This is a marker relocation (0-sized), for the linker's reachabililty + // analysis. + R_USEIFACEMETHOD + // R_USENAMEDMETHOD marks that methods with a specific name must not be eliminated. + // The target is a symbol containing the name of a method called via a generic + // interface or looked up via MethodByName("F"). + R_USENAMEDMETHOD + // R_METHODOFF resolves to a 32-bit offset from the beginning of the section + // holding the data being relocated to the referenced symbol. + // It is a variant of R_ADDROFF used when linking from the uncommonType of a + // *rtype, and may be set to zero by the linker if it determines the method + // text is unreachable by the linked program. + R_METHODOFF + // R_KEEP tells the linker to keep the referred-to symbol in the final binary + // if the symbol containing the R_KEEP relocation is in the final binary. + R_KEEP + R_POWER_TOC + R_GOTPCREL + // R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address + // of a JMP instruction, by encoding the address into the instruction. + // The stack nosplit check ignores this since it is not a function call. + R_JMPMIPS + + // R_DWARFSECREF resolves to the offset of the symbol from its section. + // Target of relocation must be size 4 (in current implementation). + R_DWARFSECREF + + // Platform dependent relocations. Architectures with fixed width instructions + // have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be + // stuffed into a 32-bit instruction, so an address needs to be spread across + // several instructions, and in turn this requires a sequence of relocations, each + // updating a part of an instruction. This leads to relocation codes that are + // inherently processor specific. + + // Arm64. + + // Set a MOV[NZ] immediate field to bits [15:0] of the offset from the thread + // local base to the thread local variable defined by the referenced (thread + // local) symbol. Error if the offset does not fit into 16 bits. + R_ARM64_TLS_LE + + // Relocates an ADRP; LD64 instruction sequence to load the offset between + // the thread local base and the thread local variable defined by the + // referenced (thread local) symbol from the GOT. + R_ARM64_TLS_IE + + // R_ARM64_GOTPCREL relocates an adrp, ld64 pair to compute the address of the GOT + // slot of the referenced symbol. + R_ARM64_GOTPCREL + + // R_ARM64_GOT resolves a GOT-relative instruction sequence, usually an adrp + // followed by another ld instruction. + R_ARM64_GOT + + // R_ARM64_PCREL resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by another add instruction. + R_ARM64_PCREL + + // R_ARM64_PCREL_LDST8 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD8 or ST8 instruction. + R_ARM64_PCREL_LDST8 + + // R_ARM64_PCREL_LDST16 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD16 or ST16 instruction. + R_ARM64_PCREL_LDST16 + + // R_ARM64_PCREL_LDST32 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD32 or ST32 instruction. + R_ARM64_PCREL_LDST32 + + // R_ARM64_PCREL_LDST64 resolves a PC-relative addresses instruction sequence, usually an + // adrp followed by a LD64 or ST64 instruction. + R_ARM64_PCREL_LDST64 + + // R_ARM64_LDST8 sets a LD/ST immediate value to bits [11:0] of a local address. + R_ARM64_LDST8 + + // R_ARM64_LDST16 sets a LD/ST immediate value to bits [11:1] of a local address. + R_ARM64_LDST16 + + // R_ARM64_LDST32 sets a LD/ST immediate value to bits [11:2] of a local address. + R_ARM64_LDST32 + + // R_ARM64_LDST64 sets a LD/ST immediate value to bits [11:3] of a local address. + R_ARM64_LDST64 + + // R_ARM64_LDST128 sets a LD/ST immediate value to bits [11:4] of a local address. + R_ARM64_LDST128 + + // PPC64. + + // R_POWER_TLS_LE is used to implement the "local exec" model for tls + // access. It resolves to the offset of the thread-local symbol from the + // thread pointer (R13) and is split against a pair of instructions to + // support a 32 bit displacement. + R_POWER_TLS_LE + + // R_POWER_TLS_IE is used to implement the "initial exec" model for tls access. It + // relocates a D-form, DS-form instruction sequence like R_ADDRPOWER_DS. It + // inserts to the offset of GOT slot for the thread-local symbol from the TOC (the + // GOT slot is filled by the dynamic linker with the offset of the thread-local + // symbol from the thread pointer (R13)). + R_POWER_TLS_IE + + // R_POWER_TLS marks an X-form instruction such as "ADD R3,R13,R4" as completing + // a sequence of GOT-relative relocations to compute a TLS address. This can be + // used by the system linker to rewrite the GOT-relative TLS relocation into a + // simpler thread-pointer relative relocation. See table 3.26 and 3.28 in the + // ppc64 elfv2 1.4 ABI on this transformation. Likewise, the second argument + // (usually called RB in X-form instructions) is assumed to be R13. + R_POWER_TLS + + // R_POWER_TLS_IE_PCREL34 is similar to R_POWER_TLS_IE, but marks a single MOVD + // which has been assembled as a single prefixed load doubleword without using the + // TOC. + R_POWER_TLS_IE_PCREL34 + + // R_POWER_TLS_LE_TPREL34 is similar to R_POWER_TLS_LE, but computes an offset from + // the thread pointer in one prefixed instruction. + R_POWER_TLS_LE_TPREL34 + + // R_ADDRPOWER_DS is similar to R_ADDRPOWER above, but assumes the second + // instruction is a "DS-form" instruction, which has an immediate field occupying + // bits [15:2] of the instruction word. Bits [15:2] of the address of the + // relocated symbol are inserted into this field; it is an error if the last two + // bits of the address are not 0. + R_ADDRPOWER_DS + + // R_ADDRPOWER_GOT relocates a D-form + DS-form instruction sequence by inserting + // a relative displacement of referenced symbol's GOT entry to the TOC pointer. + R_ADDRPOWER_GOT + + // R_ADDRPOWER_GOT_PCREL34 is identical to R_ADDRPOWER_GOT, but uses a PC relative + // sequence to generate a GOT symbol addresses. + R_ADDRPOWER_GOT_PCREL34 + + // R_ADDRPOWER_PCREL relocates two D-form instructions like R_ADDRPOWER, but + // inserts the displacement from the place being relocated to the address of the + // relocated symbol instead of just its address. + R_ADDRPOWER_PCREL + + // R_ADDRPOWER_TOCREL relocates two D-form instructions like R_ADDRPOWER, but + // inserts the offset from the TOC to the address of the relocated symbol + // rather than the symbol's address. + R_ADDRPOWER_TOCREL + + // R_ADDRPOWER_TOCREL_DS relocates a D-form, DS-form instruction sequence like + // R_ADDRPOWER_DS but inserts the offset from the TOC to the address of the + // relocated symbol rather than the symbol's address. + R_ADDRPOWER_TOCREL_DS + + // R_ADDRPOWER_D34 relocates a single prefixed D-form load/store operation. All + // prefixed forms are D form. The high 18 bits are stored in the prefix, + // and the low 16 are stored in the suffix. The address is absolute. + R_ADDRPOWER_D34 + + // R_ADDRPOWER_PCREL34 relates a single prefixed D-form load/store/add operation. + // All prefixed forms are D form. The resulting address is relative to the + // PC. It is a signed 34 bit offset. + R_ADDRPOWER_PCREL34 + + // RISC-V. + + // R_RISCV_JAL resolves a 20 bit offset for a J-type instruction. + R_RISCV_JAL + + // R_RISCV_JAL_TRAMP is the same as R_RISCV_JAL but denotes the use of a + // trampoline, which we may be able to avoid during relocation. These are + // only used by the linker and are not emitted by the compiler or assembler. + R_RISCV_JAL_TRAMP + + // R_RISCV_CALL resolves a 32 bit PC-relative address for an AUIPC + JALR + // instruction pair. + R_RISCV_CALL + + // R_RISCV_PCREL_ITYPE resolves a 32 bit PC-relative address for an + // AUIPC + I-type instruction pair. + R_RISCV_PCREL_ITYPE + + // R_RISCV_PCREL_STYPE resolves a 32 bit PC-relative address for an + // AUIPC + S-type instruction pair. + R_RISCV_PCREL_STYPE + + // R_RISCV_TLS_IE resolves a 32 bit TLS initial-exec address for an + // AUIPC + I-type instruction pair. + R_RISCV_TLS_IE + + // R_RISCV_TLS_LE resolves a 32 bit TLS local-exec address for a + // LUI + I-type instruction sequence. + R_RISCV_TLS_LE + + // R_RISCV_GOT_HI20 resolves the high 20 bits of a 32-bit PC-relative GOT + // address. + R_RISCV_GOT_HI20 + + // R_RISCV_GOT_PCREL_ITYPE resolves a 32-bit PC-relative GOT entry + // address for an AUIPC + I-type instruction pair. + R_RISCV_GOT_PCREL_ITYPE + + // R_RISCV_PCREL_HI20 resolves the high 20 bits of a 32-bit PC-relative + // address. + R_RISCV_PCREL_HI20 + + // R_RISCV_PCREL_LO12_I resolves the low 12 bits of a 32-bit PC-relative + // address using an I-type instruction. + R_RISCV_PCREL_LO12_I + + // R_RISCV_PCREL_LO12_S resolves the low 12 bits of a 32-bit PC-relative + // address using an S-type instruction. + R_RISCV_PCREL_LO12_S + + // R_RISCV_BRANCH resolves a 12-bit PC-relative branch offset. + R_RISCV_BRANCH + + // R_RISCV_ADD32 resolves a 32-bit label addition, being the stored value, + // plus the symbol address plus the addend (V + S + A). + R_RISCV_ADD32 + + // R_RISCV_SUB32 resolves a 32-bit label subtraction, being the stored value, + // minus the symbol address minus the addend (V - S - A). + R_RISCV_SUB32 + + // R_RISCV_RVC_BRANCH resolves an 8-bit PC-relative offset for a CB-type + // instruction. + R_RISCV_RVC_BRANCH + + // R_RISCV_RVC_JUMP resolves an 11-bit PC-relative offset for a CJ-type + // instruction. + R_RISCV_RVC_JUMP + + // R_PCRELDBL relocates s390x 2-byte aligned PC-relative addresses. + // TODO(mundaym): remove once variants can be serialized - see issue 14218. + R_PCRELDBL + + // Loong64. + + // R_LOONG64_ADDR_HI resolves to the sign-adjusted "upper" 20 bits (bit 5-24) of an + // external address, by encoding it into the instruction. + // R_LOONG64_ADDR_LO resolves to the low 12 bits of an external address, by encoding + // it into the instruction. + R_LOONG64_ADDR_HI + R_LOONG64_ADDR_LO + + // R_LOONG64_ADDR_PCREL20_S2 resolves to the 22-bit, 4-byte aligned offset of an + // external address, by encoding it into a PCADDI instruction. + R_LOONG64_ADDR_PCREL20_S2 + + // R_LOONG64_TLS_LE_HI resolves to the high 20 bits of a TLS address (offset from + // thread pointer), by encoding it into the instruction. + // R_LOONG64_TLS_LE_LO resolves to the low 12 bits of a TLS address (offset from + // thread pointer), by encoding it into the instruction. + R_LOONG64_TLS_LE_HI + R_LOONG64_TLS_LE_LO + + // R_CALLLOONG64 resolves to the 28-bit 4-byte aligned PC-relative target + // address of a BL instruction, by encoding it into the instruction. + R_CALLLOONG64 + + // R_LOONG64_CALL36 resolves to the 38-bit 4-byte aligned PC-relative target + // address of a PCADDU18I + JIRL pair, by encoding it into the instructions. + R_LOONG64_CALL36 + + // R_LOONG64_TLS_IE_HI and R_LOONG64_TLS_IE_LO relocates a pcalau12i, ld.d + // pair to compute the address of the GOT slot of the tls symbol. + R_LOONG64_TLS_IE_HI + R_LOONG64_TLS_IE_LO + + // R_LOONG64_GOT_HI and R_LOONG64_GOT_LO resolves a GOT-relative instruction sequence, + // usually an pcalau12i followed by another ld or addi instruction. + R_LOONG64_GOT_HI + R_LOONG64_GOT_LO + + // 64-bit in-place addition. + R_LOONG64_ADD64 + // 64-bit in-place subtraction. + R_LOONG64_SUB64 + + // R_JMP16LOONG64 resolves to the 18-bit 4-byte aligned PC-relative target + // address of a BEQ/BNE/BLT/BGE/BLTU/BGEU instruction, by encoding it into + // the instruction. + R_JMP16LOONG64 + + // R_JMP21LOONG64 resolves to the 23-bit 4-byte aligned PC-relative target + // address of a BEQZ/BNEZ instruction, by encoding it into the instruction. + R_JMP21LOONG64 + + // R_JMPLOONG64 resolves to the 28-bit 4-byte aligned PC-relative target + // address of a B instruction, by encoding it into the instruction. + R_JMPLOONG64 + + // R_ADDRMIPSU (only used on mips/mips64) resolves to the sign-adjusted "upper" 16 + // bits (bit 16-31) of an external address, by encoding it into the instruction. + R_ADDRMIPSU + // R_ADDRMIPSTLS (only used on mips64) resolves to the low 16 bits of a TLS + // address (offset from thread pointer), by encoding it into the instruction. + R_ADDRMIPSTLS + + // R_ADDRCUOFF resolves to a pointer-sized offset from the start of the + // symbol's DWARF compile unit. + R_ADDRCUOFF + + // R_WASMIMPORT resolves to the index of the WebAssembly function import. + R_WASMIMPORT + + // R_XCOFFREF (only used on aix/ppc64) prevents garbage collection by ld + // of a symbol. This isn't a real relocation, it can be placed in anywhere + // in a symbol and target any symbols. + R_XCOFFREF + + // R_PEIMAGEOFF resolves to a 32-bit offset from the start address of where + // the executable file is mapped in memory. + R_PEIMAGEOFF + + // R_INITORDER specifies an ordering edge between two inittask records. + // (From one p..inittask record to another one.) + // This relocation does not apply any changes to the actual data, it is + // just used in the linker to order the inittask records appropriately. + R_INITORDER + + // The R_DWTXTADDR_* family of relocations are effectively + // references to the .debug_addr entry for a given TEXT symbol + // corresponding to a Go function. Given a R_DWTXTADDR_* reloc + // applied to dwarf section S at offset O against sym F, the linker + // locates the .debug_addr entry for F (within its package) and + // writes the index of that entry to section S at offset O, using + // ULEB encoding, writing a number of bytes controlled by the + // suffix (e.g. for R_DWTXTADDR_U2 we write two bytes). Note + // also that .debug_addr indices are not finalized until link time; + // when the compiler creates a R_DWTXTADDR_* relocation the + // index payload will be left as zero (to be filled in later). + R_DWTXTADDR_U1 + R_DWTXTADDR_U2 + R_DWTXTADDR_U3 + R_DWTXTADDR_U4 + + // R_WEAK marks the relocation as a weak reference. + // A weak relocation does not make the symbol it refers to reachable, + // and is only honored by the linker if the symbol is in some other way + // reachable. + R_WEAK = -1 << 15 + + R_WEAKADDR = R_WEAK | R_ADDR + R_WEAKADDROFF = R_WEAK | R_ADDROFF +) + +// IsDirectCall reports whether r is a relocation for a direct call. +// A direct call is a CALL instruction that takes the target address +// as an immediate. The address is embedded into the instruction(s), possibly +// with limited width. An indirect call is a CALL instruction that takes +// the target address in register or memory. +func (r RelocType) IsDirectCall() bool { + switch r { + case R_CALL, R_CALLARM, R_CALLARM64, R_CALLLOONG64, R_CALLMIPS, R_CALLPOWER, + R_RISCV_CALL, R_RISCV_JAL, R_RISCV_JAL_TRAMP: + return true + } + return false +} + +// IsDirectJump reports whether r is a relocation for a direct jump. +// A direct jump is a JMP instruction that takes the target address +// as an immediate. The address is embedded into the instruction, possibly +// with limited width. An indirect jump is a JMP instruction that takes +// the target address in register or memory. +func (r RelocType) IsDirectJump() bool { + switch r { + case R_JMPMIPS: + return true + case R_JMPLOONG64: + return true + } + return false +} + +// IsDirectCallOrJump reports whether r is a relocation for a direct +// call or a direct jump. +func (r RelocType) IsDirectCallOrJump() bool { + return r.IsDirectCall() || r.IsDirectJump() +} + +// IsDwTxtAddr reports whether r is one of the several DWARF +// .debug_addr section indirect relocations. +func (r RelocType) IsDwTxtAddr() bool { + switch r { + case R_DWTXTADDR_U1, R_DWTXTADDR_U2, R_DWTXTADDR_U3, R_DWTXTADDR_U4: + return true + default: + return false + } +} + +// FuncCountToDwTxtAddrFlavor returns the correct DWARF .debug_addr +// section relocation to use when compiling a package with a total of +// fncount functions, along with the size of the ULEB128-encoded blob +// needed to store the eventual .debug_addr index. +func FuncCountToDwTxtAddrFlavor(fncount int) (RelocType, int) { + switch { + case fncount <= 127: + return R_DWTXTADDR_U1, 1 + case fncount <= 16383: + return R_DWTXTADDR_U2, 2 + case fncount <= 2097151: + return R_DWTXTADDR_U3, 3 + case fncount <= 268435455: + return R_DWTXTADDR_U4, 4 + default: + panic("package has more than 268435455 functions") + } +} + +// DummyDwarfFunctionCountForAssembler returns a dummy value to be +// used for "total number of functions in the package" for use in the +// assembler (compiler does not call this function). +// +// Background/motivation: let's say we have a package P with some +// assembly functions (in "a.s") and some Go functions (in +// "b.go"). The compilation sequence used by the Go commmand will be: +// +// 1. run the assembler on a.s to generate a "symabis" file +// 2. run the compiler on b.go passing it the symabis file and generating a "go_defs.h" asm header +// 3. run the assembler on a.s passing it an include dir with the generated "go_defs.h" file +// +// When the compiler runs, it can easily determine the total function +// count for the package (for use with FuncCountToDwTxtAddrFlavor +// above) by counting defined Go funcs and looking at the symabis +// file. With the assembler however there is no easy way for it to +// figure out the total number of Go source funcs. To keep things +// simple, we instead just use a dummy total function count while +// running the assembler that will guarantee we pick a relocation +// flavor that will work for any package size. +func DummyDwarfFunctionCountForAssembler() int { + return 9999999 +} + +// DwTxtAddrRelocParams returns the maximum number of functions per +// package supported for the DWARF .debug_addr relocation variant r, +// along with the number of bytes it takes up in encoded form. +func (r RelocType) DwTxtAddrRelocParams() (int, int) { + switch r { + case R_DWTXTADDR_U1: + return 0x7f, 1 + case R_DWTXTADDR_U2: + return 0x3fff, 2 + case R_DWTXTADDR_U3: + return 0x1fffff, 3 + case R_DWTXTADDR_U4: + return 0xfffffff, 4 + default: + panic("not a dwtxtaddr relocation") + } +} diff --git a/internal/go/src/objabi/reloctype_string.go b/internal/go/src/objabi/reloctype_string.go new file mode 100755 index 0000000..9137bae --- /dev/null +++ b/internal/go/src/objabi/reloctype_string.go @@ -0,0 +1,126 @@ +// Code generated by "stringer -type=RelocType"; DO NOT EDIT. + +package objabi + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[R_ADDR-1] + _ = x[R_ADDRPOWER-2] + _ = x[R_ADDRARM64-3] + _ = x[R_ADDRMIPS-4] + _ = x[R_ADDROFF-5] + _ = x[R_SIZE-6] + _ = x[R_CALL-7] + _ = x[R_CALLARM-8] + _ = x[R_CALLARM64-9] + _ = x[R_CALLIND-10] + _ = x[R_CALLPOWER-11] + _ = x[R_CALLMIPS-12] + _ = x[R_CONST-13] + _ = x[R_PCREL-14] + _ = x[R_TLS_LE-15] + _ = x[R_TLS_IE-16] + _ = x[R_GOTOFF-17] + _ = x[R_PLT0-18] + _ = x[R_PLT1-19] + _ = x[R_PLT2-20] + _ = x[R_USEFIELD-21] + _ = x[R_USETYPE-22] + _ = x[R_USEIFACE-23] + _ = x[R_USEIFACEMETHOD-24] + _ = x[R_USENAMEDMETHOD-25] + _ = x[R_METHODOFF-26] + _ = x[R_KEEP-27] + _ = x[R_POWER_TOC-28] + _ = x[R_GOTPCREL-29] + _ = x[R_JMPMIPS-30] + _ = x[R_DWARFSECREF-31] + _ = x[R_ARM64_TLS_LE-32] + _ = x[R_ARM64_TLS_IE-33] + _ = x[R_ARM64_GOTPCREL-34] + _ = x[R_ARM64_GOT-35] + _ = x[R_ARM64_PCREL-36] + _ = x[R_ARM64_PCREL_LDST8-37] + _ = x[R_ARM64_PCREL_LDST16-38] + _ = x[R_ARM64_PCREL_LDST32-39] + _ = x[R_ARM64_PCREL_LDST64-40] + _ = x[R_ARM64_LDST8-41] + _ = x[R_ARM64_LDST16-42] + _ = x[R_ARM64_LDST32-43] + _ = x[R_ARM64_LDST64-44] + _ = x[R_ARM64_LDST128-45] + _ = x[R_POWER_TLS_LE-46] + _ = x[R_POWER_TLS_IE-47] + _ = x[R_POWER_TLS-48] + _ = x[R_POWER_TLS_IE_PCREL34-49] + _ = x[R_POWER_TLS_LE_TPREL34-50] + _ = x[R_ADDRPOWER_DS-51] + _ = x[R_ADDRPOWER_GOT-52] + _ = x[R_ADDRPOWER_GOT_PCREL34-53] + _ = x[R_ADDRPOWER_PCREL-54] + _ = x[R_ADDRPOWER_TOCREL-55] + _ = x[R_ADDRPOWER_TOCREL_DS-56] + _ = x[R_ADDRPOWER_D34-57] + _ = x[R_ADDRPOWER_PCREL34-58] + _ = x[R_RISCV_JAL-59] + _ = x[R_RISCV_JAL_TRAMP-60] + _ = x[R_RISCV_CALL-61] + _ = x[R_RISCV_PCREL_ITYPE-62] + _ = x[R_RISCV_PCREL_STYPE-63] + _ = x[R_RISCV_TLS_IE-64] + _ = x[R_RISCV_TLS_LE-65] + _ = x[R_RISCV_GOT_HI20-66] + _ = x[R_RISCV_GOT_PCREL_ITYPE-67] + _ = x[R_RISCV_PCREL_HI20-68] + _ = x[R_RISCV_PCREL_LO12_I-69] + _ = x[R_RISCV_PCREL_LO12_S-70] + _ = x[R_RISCV_BRANCH-71] + _ = x[R_RISCV_ADD32-72] + _ = x[R_RISCV_SUB32-73] + _ = x[R_RISCV_RVC_BRANCH-74] + _ = x[R_RISCV_RVC_JUMP-75] + _ = x[R_PCRELDBL-76] + _ = x[R_LOONG64_ADDR_HI-77] + _ = x[R_LOONG64_ADDR_LO-78] + _ = x[R_LOONG64_ADDR_PCREL20_S2-79] + _ = x[R_LOONG64_TLS_LE_HI-80] + _ = x[R_LOONG64_TLS_LE_LO-81] + _ = x[R_CALLLOONG64-82] + _ = x[R_LOONG64_CALL36-83] + _ = x[R_LOONG64_TLS_IE_HI-84] + _ = x[R_LOONG64_TLS_IE_LO-85] + _ = x[R_LOONG64_GOT_HI-86] + _ = x[R_LOONG64_GOT_LO-87] + _ = x[R_LOONG64_ADD64-88] + _ = x[R_LOONG64_SUB64-89] + _ = x[R_JMP16LOONG64-90] + _ = x[R_JMP21LOONG64-91] + _ = x[R_JMPLOONG64-92] + _ = x[R_ADDRMIPSU-93] + _ = x[R_ADDRMIPSTLS-94] + _ = x[R_ADDRCUOFF-95] + _ = x[R_WASMIMPORT-96] + _ = x[R_XCOFFREF-97] + _ = x[R_PEIMAGEOFF-98] + _ = x[R_INITORDER-99] + _ = x[R_DWTXTADDR_U1-100] + _ = x[R_DWTXTADDR_U2-101] + _ = x[R_DWTXTADDR_U3-102] + _ = x[R_DWTXTADDR_U4-103] +} + +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_GOT_PCREL_ITYPER_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_ADD32R_RISCV_SUB32R_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_LOONG64_ADDR_HIR_LOONG64_ADDR_LOR_LOONG64_ADDR_PCREL20_S2R_LOONG64_TLS_LE_HIR_LOONG64_TLS_LE_LOR_CALLLOONG64R_LOONG64_CALL36R_LOONG64_TLS_IE_HIR_LOONG64_TLS_IE_LOR_LOONG64_GOT_HIR_LOONG64_GOT_LOR_LOONG64_ADD64R_LOONG64_SUB64R_JMP16LOONG64R_JMP21LOONG64R_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDERR_DWTXTADDR_U1R_DWTXTADDR_U2R_DWTXTADDR_U3R_DWTXTADDR_U4" + +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 330, 341, 354, 373, 393, 413, 433, 446, 460, 474, 488, 503, 517, 531, 542, 564, 586, 600, 615, 638, 655, 673, 694, 709, 728, 739, 756, 768, 787, 806, 820, 834, 850, 873, 891, 911, 931, 945, 958, 971, 989, 1005, 1015, 1032, 1049, 1074, 1093, 1112, 1125, 1141, 1160, 1179, 1195, 1211, 1226, 1241, 1255, 1269, 1281, 1292, 1305, 1316, 1328, 1338, 1350, 1361, 1375, 1389, 1403, 1417} + +func (i RelocType) String() string { + i -= 1 + if i < 0 || i >= RelocType(len(_RelocType_index)-1) { + return "RelocType(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]] +} diff --git a/internal/go/src/objabi/stack.go b/internal/go/src/objabi/stack.go new file mode 100755 index 0000000..c1b4464 --- /dev/null +++ b/internal/go/src/objabi/stack.go @@ -0,0 +1,32 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import ( + "loov.dev/lensm/internal/go/src/abi" + "loov.dev/lensm/internal/go/src/buildcfg" +) + +func StackNosplit(race bool) int { + // This arithmetic must match that in runtime/stack.go:stackNosplit. + return abi.StackNosplitBase * stackGuardMultiplier(race) +} + +// stackGuardMultiplier returns a multiplier to apply to the default +// stack guard size. Larger multipliers are used for non-optimized +// builds that have larger stack frames or for specific targets. +func stackGuardMultiplier(race bool) int { + // This arithmetic must match that in internal/runtime/sys/consts.go:StackGuardMultiplier. + n := 1 + // On AIX and OpenBSD, a larger stack is needed for syscalls. + if buildcfg.GOOS == "aix" || buildcfg.GOOS == "openbsd" { + n += 1 + } + // The race build also needs more stack. + if race { + n += 1 + } + return n +} diff --git a/internal/go/src/objabi/symkind.go b/internal/go/src/objabi/symkind.go new file mode 100755 index 0000000..155bdb9 --- /dev/null +++ b/internal/go/src/objabi/symkind.go @@ -0,0 +1,98 @@ +// Derived from Inferno utils/6l/l.h and related files. +// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package objabi + +// A SymKind describes the kind of memory represented by a symbol. +type SymKind uint8 + +// Defined SymKind values. +// These are used to index into cmd/link/internal/sym/AbiSymKindToSymKind +// +// TODO(rsc): Give idiomatic Go names. +// +//go:generate stringer -type=SymKind +const ( + // An otherwise invalid zero value for the type + Sxxx SymKind = iota + // Executable instructions + STEXT + STEXTFIPS + // Read only static data + SRODATA + SRODATAFIPS + // Static data that does not contain any pointers + SNOPTRDATA + SNOPTRDATAFIPS + // Static data + SDATA + SDATAFIPS + // Statically data that is initially all 0s + SBSS + // Statically data that is initially all 0s and does not contain pointers + SNOPTRBSS + // Thread-local data that is initially all 0s + STLSBSS + // Debugging data + SDWARFCUINFO + SDWARFCONST + SDWARFFCN + SDWARFABSFCN + SDWARFTYPE + SDWARFVAR + SDWARFRANGE + SDWARFLOC + SDWARFLINES + SDWARFADDR + // Coverage instrumentation counter for libfuzzer. + SLIBFUZZER_8BIT_COUNTER + // Coverage instrumentation counter, aux variable for cmd/cover + SCOVERAGE_COUNTER + SCOVERAGE_AUXVAR + + SSEHUNWINDINFO + // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values. +) + +// IsText reports whether t is one of the text kinds. +func (t SymKind) IsText() bool { + return t == STEXT || t == STEXTFIPS +} + +// IsDATA reports whether t is one of the DATA kinds (SDATA or SDATAFIPS, +// excluding NOPTRDATA, RODATA, BSS, and so on). +func (t SymKind) IsDATA() bool { + return t == SDATA || t == SDATAFIPS +} + +// IsFIPS reports whether t is one fo the FIPS kinds. +func (t SymKind) IsFIPS() bool { + return t == STEXTFIPS || t == SRODATAFIPS || t == SNOPTRDATAFIPS || t == SDATAFIPS +} diff --git a/internal/go/src/objabi/symkind_string.go b/internal/go/src/objabi/symkind_string.go new file mode 100755 index 0000000..fbf6a82 --- /dev/null +++ b/internal/go/src/objabi/symkind_string.go @@ -0,0 +1,48 @@ +// Code generated by "stringer -type=SymKind"; DO NOT EDIT. + +package objabi + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Sxxx-0] + _ = x[STEXT-1] + _ = x[STEXTFIPS-2] + _ = x[SRODATA-3] + _ = x[SRODATAFIPS-4] + _ = x[SNOPTRDATA-5] + _ = x[SNOPTRDATAFIPS-6] + _ = x[SDATA-7] + _ = x[SDATAFIPS-8] + _ = x[SBSS-9] + _ = x[SNOPTRBSS-10] + _ = x[STLSBSS-11] + _ = x[SDWARFCUINFO-12] + _ = x[SDWARFCONST-13] + _ = x[SDWARFFCN-14] + _ = x[SDWARFABSFCN-15] + _ = x[SDWARFTYPE-16] + _ = x[SDWARFVAR-17] + _ = x[SDWARFRANGE-18] + _ = x[SDWARFLOC-19] + _ = x[SDWARFLINES-20] + _ = x[SDWARFADDR-21] + _ = x[SLIBFUZZER_8BIT_COUNTER-22] + _ = x[SCOVERAGE_COUNTER-23] + _ = x[SCOVERAGE_AUXVAR-24] + _ = x[SSEHUNWINDINFO-25] +} + +const _SymKind_name = "SxxxSTEXTSTEXTFIPSSRODATASRODATAFIPSSNOPTRDATASNOPTRDATAFIPSSDATASDATAFIPSSBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSDWARFADDRSLIBFUZZER_8BIT_COUNTERSCOVERAGE_COUNTERSCOVERAGE_AUXVARSSEHUNWINDINFO" + +var _SymKind_index = [...]uint16{0, 4, 9, 18, 25, 36, 46, 60, 65, 74, 78, 87, 94, 106, 117, 126, 138, 148, 157, 168, 177, 188, 198, 221, 238, 254, 268} + +func (i SymKind) String() string { + if i >= SymKind(len(_SymKind_index)-1) { + return "SymKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SymKind_name[_SymKind_index[i]:_SymKind_index[i+1]] +} diff --git a/internal/go/src/objabi/util.go b/internal/go/src/objabi/util.go new file mode 100755 index 0000000..9995020 --- /dev/null +++ b/internal/go/src/objabi/util.go @@ -0,0 +1,33 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package objabi + +import ( + "fmt" + "strings" + + "loov.dev/lensm/internal/go/src/buildcfg" +) + +const ( + ElfRelocOffset = 256 + MachoRelocOffset = 2048 // reserve enough space for ELF relocations + GlobalDictPrefix = ".dict" // prefix for names of global dictionaries +) + +// HeaderString returns the toolchain configuration string written in +// Go object headers. This string ensures we don't attempt to import +// or link object files that are incompatible with each other. This +// string always starts with "go object ". +func HeaderString() string { + archExtra := "" + if k, v := buildcfg.GOGOARCH(); k != "" && v != "" { + archExtra = " " + k + "=" + v + } + return fmt.Sprintf("go object %s %s %s%s X:%s\n", + buildcfg.GOOS, buildcfg.GOARCH, + buildcfg.Version, archExtra, + strings.Join(buildcfg.Experiment.Enabled(), ",")) +} diff --git a/internal/go/src/objabi/zbootstrap.go b/internal/go/src/objabi/zbootstrap.go new file mode 100755 index 0000000..6cfb5a6 --- /dev/null +++ b/internal/go/src/objabi/zbootstrap.go @@ -0,0 +1,3 @@ +// Code generated by go tool dist; DO NOT EDIT. + +package objabi diff --git a/internal/go/src/objfile/elf.go b/internal/go/src/objfile/elf.go new file mode 100755 index 0000000..6988cea --- /dev/null +++ b/internal/go/src/objfile/elf.go @@ -0,0 +1,174 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of ELF executables (Linux, FreeBSD, and so on). + +package objfile + +import ( + "debug/dwarf" + "debug/elf" + "encoding/binary" + "fmt" + "io" +) + +type elfFile struct { + elf *elf.File +} + +func openElf(r io.ReaderAt) (rawFile, error) { + f, err := elf.NewFile(r) + if err != nil { + return nil, err + } + return &elfFile{f}, nil +} + +func (f *elfFile) symbols() ([]Sym, error) { + elfSyms, err := f.elf.Symbols() + if err != nil { + return nil, err + } + + var syms []Sym + for _, s := range elfSyms { + sym := Sym{Addr: s.Value, Name: s.Name, Size: int64(s.Size), Code: '?'} + switch s.Section { + case elf.SHN_UNDEF: + sym.Code = 'U' + case elf.SHN_COMMON: + sym.Code = 'B' + default: + i := int(s.Section) + if i < 0 || i >= len(f.elf.Sections) { + break + } + sect := f.elf.Sections[i] + switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) { + case elf.SHF_ALLOC | elf.SHF_EXECINSTR: + sym.Code = 'T' + case elf.SHF_ALLOC: + sym.Code = 'R' + case elf.SHF_ALLOC | elf.SHF_WRITE: + sym.Code = 'D' + } + } + if elf.ST_BIND(s.Info) == elf.STB_LOCAL { + sym.Code += 'a' - 'A' + } + syms = append(syms, sym) + } + + return syms, nil +} + +func (f *elfFile) pcln() (textStart uint64, pclntab []byte, err error) { + if sect := f.elf.Section(".text"); sect != nil { + textStart = sect.Addr + } + + sect := f.elf.Section(".gopclntab") + if sect == nil { + // try .data.rel.ro.gopclntab, for PIE binaries + sect = f.elf.Section(".data.rel.ro.gopclntab") + } + if sect != nil { + if pclntab, err = sect.Data(); err != nil { + return 0, nil, err + } + } else { + // if both sections failed, try the symbol + pclntab = f.symbolData("runtime.pclntab", "runtime.epclntab") + } + + return textStart, pclntab, nil +} + +func (f *elfFile) text() (textStart uint64, text []byte, err error) { + sect := f.elf.Section(".text") + if sect == nil { + return 0, nil, fmt.Errorf("text section not found") + } + textStart = sect.Addr + text, err = sect.Data() + return +} + +func (f *elfFile) goarch() string { + switch f.elf.Machine { + case elf.EM_386: + return "386" + case elf.EM_X86_64: + return "amd64" + case elf.EM_ARM: + return "arm" + case elf.EM_AARCH64: + return "arm64" + case elf.EM_LOONGARCH: + return "loong64" + case elf.EM_PPC64: + if f.elf.ByteOrder == binary.LittleEndian { + return "ppc64le" + } + return "ppc64" + case elf.EM_RISCV: + if f.elf.Class == elf.ELFCLASS64 { + return "riscv64" + } + case elf.EM_S390: + return "s390x" + } + return "" +} + +func (f *elfFile) loadAddress() (uint64, error) { + for _, p := range f.elf.Progs { + if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 { + // The memory mapping that contains the segment + // starts at an aligned address. Apparently this + // is what pprof expects, as it uses this and the + // start address of the mapping to compute PC + // delta. + return p.Vaddr - p.Vaddr%p.Align, nil + } + } + return 0, fmt.Errorf("unknown load address") +} + +func (f *elfFile) dwarf() (*dwarf.Data, error) { + return f.elf.DWARF() +} + +func (f *elfFile) symbolData(start, end string) []byte { + elfSyms, err := f.elf.Symbols() + if err != nil { + return nil + } + var addr, eaddr uint64 + for _, s := range elfSyms { + if s.Name == start { + addr = s.Value + } else if s.Name == end { + eaddr = s.Value + } + if addr != 0 && eaddr != 0 { + break + } + } + if addr == 0 || eaddr < addr { + return nil + } + size := eaddr - addr + data := make([]byte, size) + for _, prog := range f.elf.Progs { + if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 { + if _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)); err != nil { + return nil + } + return data + } + } + return nil +} diff --git a/internal/go/src/objfile/goobj.go b/internal/go/src/objfile/goobj.go new file mode 100755 index 0000000..9327189 --- /dev/null +++ b/internal/go/src/objfile/goobj.go @@ -0,0 +1,342 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of Go intermediate object files and archives. + +package objfile + +import ( + "loov.dev/lensm/internal/go/src/archive" + "loov.dev/lensm/internal/go/src/goobj" + "loov.dev/lensm/internal/go/src/objabi" + "loov.dev/lensm/internal/go/src/sys" + "debug/dwarf" + "debug/gosym" + "errors" + "fmt" + "io" + "os" +) + +type goobjFile struct { + goobj *archive.GoObj + r *goobj.Reader + f *os.File + arch *sys.Arch +} + +func openGoFile(f *os.File) (*File, error) { + a, err := archive.Parse(f, false) + if err != nil { + return nil, err + } + entries := make([]*Entry, 0, len(a.Entries)) +L: + for _, e := range a.Entries { + switch e.Type { + case archive.EntryPkgDef, archive.EntrySentinelNonObj: + continue + case archive.EntryGoObj: + o := e.Obj + b := make([]byte, o.Size) + _, err := f.ReadAt(b, o.Offset) + if err != nil { + return nil, err + } + r := goobj.NewReaderFromBytes(b, false) + var arch *sys.Arch + for _, a := range sys.Archs { + if a.Name == e.Obj.Arch { + arch = a + break + } + } + entries = append(entries, &Entry{ + name: e.Name, + raw: &goobjFile{e.Obj, r, f, arch}, + }) + continue + case archive.EntryNativeObj: + nr := io.NewSectionReader(f, e.Offset, e.Size) + for _, try := range openers { + if raw, err := try(nr); err == nil { + entries = append(entries, &Entry{ + name: e.Name, + raw: raw, + }) + continue L + } + } + } + return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name) + } + return &File{f, entries}, nil +} + +func goobjName(name string, ver int) string { + if ver == 0 { + return name + } + return fmt.Sprintf("%s<%d>", name, ver) +} + +type goobjReloc struct { + Off int32 + Size uint8 + Type objabi.RelocType + Add int64 + Sym string +} + +func (r goobjReloc) String(insnOffset uint64) string { + delta := int64(r.Off) - int64(insnOffset) + s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type) + if r.Sym != "" { + if r.Add != 0 { + return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add) + } + return fmt.Sprintf("%s:%s", s, r.Sym) + } + if r.Add != 0 { + return fmt.Sprintf("%s:%d", s, r.Add) + } + return s +} + +func (f *goobjFile) symbols() ([]Sym, error) { + r := f.r + var syms []Sym + + // Name of referenced indexed symbols. + nrefName := r.NRefName() + refNames := make(map[goobj.SymRef]string, nrefName) + for i := 0; i < nrefName; i++ { + rn := r.RefName(i) + refNames[rn.Sym()] = rn.Name(r) + } + + abiToVer := func(abi uint16) int { + var ver int + if abi == goobj.SymABIstatic { + // Static symbol + ver = 1 + } + return ver + } + + resolveSymRef := func(s goobj.SymRef) string { + var i uint32 + switch p := s.PkgIdx; p { + case goobj.PkgIdxInvalid: + if s.SymIdx != 0 { + panic("bad sym ref") + } + return "" + case goobj.PkgIdxHashed64: + i = s.SymIdx + uint32(r.NSym()) + case goobj.PkgIdxHashed: + i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()) + case goobj.PkgIdxNone: + i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef()) + case goobj.PkgIdxBuiltin: + name, abi := goobj.BuiltinName(int(s.SymIdx)) + return goobjName(name, abi) + case goobj.PkgIdxSelf: + i = s.SymIdx + default: + return refNames[s] + } + sym := r.Sym(i) + return goobjName(sym.Name(r), abiToVer(sym.ABI())) + } + + // Defined symbols + ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) + for i := uint32(0); i < ndef; i++ { + osym := r.Sym(i) + if osym.Name(r) == "" { + continue // not a real symbol + } + name := osym.Name(r) + ver := osym.ABI() + name = goobjName(name, abiToVer(ver)) + typ := objabi.SymKind(osym.Type()) + var code rune = '?' + switch typ { + case objabi.STEXT, objabi.STEXTFIPS: + code = 'T' + case objabi.SRODATA, objabi.SRODATAFIPS: + code = 'R' + case objabi.SNOPTRDATA, objabi.SNOPTRDATAFIPS, + objabi.SDATA, objabi.SDATAFIPS: + code = 'D' + case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS: + code = 'B' + } + if ver >= goobj.SymABIstatic { + code += 'a' - 'A' + } + + sym := Sym{ + Name: name, + Addr: uint64(r.DataOff(i)), + Size: int64(osym.Siz()), + Code: code, + } + + relocs := r.Relocs(i) + sym.Relocs = make([]Reloc, len(relocs)) + for j := range relocs { + rel := &relocs[j] + sym.Relocs[j] = Reloc{ + Addr: uint64(r.DataOff(i)) + uint64(rel.Off()), + Size: uint64(rel.Siz()), + Stringer: goobjReloc{ + Off: rel.Off(), + Size: rel.Siz(), + Type: objabi.RelocType(rel.Type()), + Add: rel.Add(), + Sym: resolveSymRef(rel.Sym()), + }, + } + } + + syms = append(syms, sym) + } + + // Referenced symbols + n := ndef + uint32(r.NNonpkgref()) + for i := ndef; i < n; i++ { + osym := r.Sym(i) + sym := Sym{Name: osym.Name(r), Code: 'U'} + syms = append(syms, sym) + } + for i := 0; i < nrefName; i++ { + rn := r.RefName(i) + sym := Sym{Name: rn.Name(r), Code: 'U'} + syms = append(syms, sym) + } + + return syms, nil +} + +func (f *goobjFile) pcln() (textStart uint64, pclntab []byte, err error) { + // Should never be called. We implement Liner below, callers + // should use that instead. + return 0, nil, fmt.Errorf("pcln not available in go object file") +} + +// PCToLine returns the file name, line, and function data for the given pc. +// Returns "",0,nil if unknown. +// This function implements the Liner interface in preference to pcln() above. +func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { + r := f.r + if f.arch == nil { + return "", 0, nil + } + getSymData := func(s goobj.SymRef) []byte { + if s.PkgIdx != goobj.PkgIdxHashed { + // We don't need the data for non-hashed symbols, yet. + panic("not supported") + } + i := s.SymIdx + uint32(r.NSym()+r.NHashed64def()) + return r.BytesAt(r.DataOff(i), r.DataSize(i)) + } + + ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) + for i := uint32(0); i < ndef; i++ { + osym := r.Sym(i) + addr := uint64(r.DataOff(i)) + if pc < addr || pc >= addr+uint64(osym.Siz()) { + continue + } + var pcfileSym, pclineSym goobj.SymRef + for _, a := range r.Auxs(i) { + switch a.Type() { + case goobj.AuxPcfile: + pcfileSym = a.Sym() + case goobj.AuxPcline: + pclineSym = a.Sym() + } + } + if pcfileSym.IsZero() || pclineSym.IsZero() { + continue + } + pcline := getSymData(pclineSym) + line := int(pcValue(pcline, pc-addr, f.arch)) + pcfile := getSymData(pcfileSym) + fileID := pcValue(pcfile, pc-addr, f.arch) + fileName := r.File(int(fileID)) + // Note: we provide only the name in the Func structure. + // We could provide more if needed. + return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}} + } + return "", 0, nil +} + +// pcValue looks up the given PC in a pc value table. target is the +// offset of the pc from the entry point. +func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 { + val := int32(-1) + var pc uint64 + for step(&tab, &pc, &val, pc == 0, arch) { + if target < pc { + return val + } + } + return -1 +} + +// step advances to the next pc, value pair in the encoded table. +func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool { + uvdelta := readvarint(p) + if uvdelta == 0 && !first { + return false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + pcdelta := readvarint(p) * uint32(arch.MinLC) + *pc += uint64(pcdelta) + *val += vdelta + return true +} + +// readvarint reads, removes, and returns a varint from *p. +func readvarint(p *[]byte) uint32 { + var v, shift uint32 + s := *p + for shift = 0; ; shift += 7 { + b := s[0] + s = s[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + } + *p = s + return v +} + +// We treat the whole object file as the text section. +func (f *goobjFile) text() (textStart uint64, text []byte, err error) { + text = make([]byte, f.goobj.Size) + _, err = f.f.ReadAt(text, f.goobj.Offset) + return +} + +func (f *goobjFile) goarch() string { + return f.goobj.Arch +} + +func (f *goobjFile) loadAddress() (uint64, error) { + return 0, fmt.Errorf("unknown load address") +} + +func (f *goobjFile) dwarf() (*dwarf.Data, error) { + return nil, errors.New("no DWARF data in go object file") +} diff --git a/internal/go/src/objfile/macho.go b/internal/go/src/objfile/macho.go new file mode 100755 index 0000000..eaf665f --- /dev/null +++ b/internal/go/src/objfile/macho.go @@ -0,0 +1,129 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of Mach-O executables (OS X). + +package objfile + +import ( + "debug/dwarf" + "debug/macho" + "fmt" + "io" + "slices" + "sort" +) + +const stabTypeMask = 0xe0 + +type machoFile struct { + macho *macho.File +} + +func openMacho(r io.ReaderAt) (rawFile, error) { + f, err := macho.NewFile(r) + if err != nil { + return nil, err + } + return &machoFile{f}, nil +} + +func (f *machoFile) symbols() ([]Sym, error) { + if f.macho.Symtab == nil { + return nil, nil + } + + // Build sorted list of addresses of all symbols. + // We infer the size of a symbol by looking at where the next symbol begins. + var addrs []uint64 + for _, s := range f.macho.Symtab.Syms { + // Skip stab debug info. + if s.Type&stabTypeMask == 0 { + addrs = append(addrs, s.Value) + } + } + slices.Sort(addrs) + + var syms []Sym + for _, s := range f.macho.Symtab.Syms { + if s.Type&stabTypeMask != 0 { + // Skip stab debug info. + continue + } + sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'} + i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) + if i < len(addrs) { + sym.Size = int64(addrs[i] - s.Value) + } + if s.Sect == 0 { + sym.Code = 'U' + } else if int(s.Sect) <= len(f.macho.Sections) { + sect := f.macho.Sections[s.Sect-1] + switch sect.Seg { + case "__TEXT", "__DATA_CONST": + sym.Code = 'R' + case "__DATA": + sym.Code = 'D' + } + switch sect.Seg + " " + sect.Name { + case "__TEXT __text": + sym.Code = 'T' + case "__DATA __bss", "__DATA __noptrbss": + sym.Code = 'B' + } + } + syms = append(syms, sym) + } + + return syms, nil +} + +func (f *machoFile) pcln() (textStart uint64, pclntab []byte, err error) { + if sect := f.macho.Section("__text"); sect != nil { + textStart = sect.Addr + } + if sect := f.macho.Section("__gopclntab"); sect != nil { + if pclntab, err = sect.Data(); err != nil { + return 0, nil, err + } + } + return textStart, pclntab, nil +} + +func (f *machoFile) text() (textStart uint64, text []byte, err error) { + sect := f.macho.Section("__text") + if sect == nil { + return 0, nil, fmt.Errorf("text section not found") + } + textStart = sect.Addr + text, err = sect.Data() + return +} + +func (f *machoFile) goarch() string { + switch f.macho.Cpu { + case macho.Cpu386: + return "386" + case macho.CpuAmd64: + return "amd64" + case macho.CpuArm: + return "arm" + case macho.CpuArm64: + return "arm64" + case macho.CpuPpc64: + return "ppc64" + } + return "" +} + +func (f *machoFile) loadAddress() (uint64, error) { + if seg := f.macho.Segment("__TEXT"); seg != nil { + return seg.Addr, nil + } + return 0, fmt.Errorf("unknown load address") +} + +func (f *machoFile) dwarf() (*dwarf.Data, error) { + return f.macho.DWARF() +} diff --git a/internal/go/src/objfile/objfile.go b/internal/go/src/objfile/objfile.go new file mode 100755 index 0000000..5cc0943 --- /dev/null +++ b/internal/go/src/objfile/objfile.go @@ -0,0 +1,185 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package objfile implements portable access to OS-specific executable files. +package objfile + +import ( + "loov.dev/lensm/internal/go/src/archive" + "cmp" + "debug/dwarf" + "debug/gosym" + "fmt" + "io" + "os" + "slices" +) + +type rawFile interface { + symbols() (syms []Sym, err error) + pcln() (textStart uint64, pclntab []byte, err error) + text() (textStart uint64, text []byte, err error) + goarch() string + loadAddress() (uint64, error) + dwarf() (*dwarf.Data, error) +} + +// A File is an opened executable file. +type File struct { + r *os.File + entries []*Entry +} + +type Entry struct { + name string + raw rawFile +} + +// A Sym is a symbol defined in an executable file. +type Sym struct { + Name string // symbol name + Addr uint64 // virtual address of symbol + Size int64 // size in bytes + Code rune // nm code (T for text, D for data, and so on) + Type string // XXX? + Relocs []Reloc // in increasing Addr order +} + +type Reloc struct { + Addr uint64 // Address of first byte that reloc applies to. + Size uint64 // Number of bytes + Stringer RelocStringer +} + +type RelocStringer interface { + // insnOffset is the offset of the instruction containing the relocation + // from the start of the symbol containing the relocation. + String(insnOffset uint64) string +} + +var openers = []func(io.ReaderAt) (rawFile, error){ + openElf, + openMacho, + openPE, + openPlan9, + openXcoff, +} + +// Open opens the named file. +// The caller must call f.Close when the file is no longer needed. +func Open(name string) (*File, error) { + r, err := os.Open(name) + if err != nil { + return nil, err + } + if f, err := openGoFile(r); err == nil { + return f, nil + } else if _, ok := err.(archive.ErrGoObjOtherVersion); ok { + return nil, fmt.Errorf("open %s: %v", name, err) + } + for _, try := range openers { + if raw, err := try(r); err == nil { + return &File{r, []*Entry{{raw: raw}}}, nil + } + } + r.Close() + return nil, fmt.Errorf("open %s: unrecognized object file", name) +} + +func (f *File) Close() error { + return f.r.Close() +} + +func (f *File) Entries() []*Entry { + return f.entries +} + +func (f *File) Symbols() ([]Sym, error) { + return f.entries[0].Symbols() +} + +func (f *File) PCLineTable() (Liner, error) { + return f.entries[0].PCLineTable() +} + +func (f *File) Text() (uint64, []byte, error) { + return f.entries[0].Text() +} + +func (f *File) GOARCH() string { + return f.entries[0].GOARCH() +} + +func (f *File) LoadAddress() (uint64, error) { + return f.entries[0].LoadAddress() +} + +func (f *File) DWARF() (*dwarf.Data, error) { + return f.entries[0].DWARF() +} + +func (e *Entry) Name() string { + return e.name +} + +func (e *Entry) Symbols() ([]Sym, error) { + syms, err := e.raw.symbols() + if err != nil { + return nil, err + } + slices.SortFunc(syms, func(a, b Sym) int { + return cmp.Compare(a.Addr, b.Addr) + }) + return syms, nil +} + +func (e *Entry) PCLineTable() (Liner, error) { + // If the raw file implements Liner directly, use that. + // Currently, only Go intermediate objects and archives (goobj) use this path. + if pcln, ok := e.raw.(Liner); ok { + return pcln, nil + } + // Otherwise, read the pcln tables and build a Liner out of that. + textStart, pclntab, err := e.raw.pcln() + if err != nil { + return nil, err + } + syms, err := e.raw.symbols() + if err == nil { + for _, s := range syms { + if s.Name == "runtime.text" { + textStart = s.Addr + break + } + } + } + return gosym.NewTable(nil, gosym.NewLineTable(pclntab, textStart)) +} + +func (e *Entry) Text() (uint64, []byte, error) { + return e.raw.text() +} + +func (e *Entry) GOARCH() string { + return e.raw.goarch() +} + +// LoadAddress returns the expected load address of the file. +// This differs from the actual load address for a position-independent +// executable. +func (e *Entry) LoadAddress() (uint64, error) { + return e.raw.loadAddress() +} + +// DWARF returns DWARF debug data for the file, if any. +// This is for cmd/pprof to locate cgo functions. +func (e *Entry) DWARF() (*dwarf.Data, error) { + return e.raw.dwarf() +} + +type Liner interface { + // Given a pc, returns the corresponding file, line, and function data. + // If unknown, returns "",0,nil. + PCToLine(uint64) (string, int, *gosym.Func) +} diff --git a/internal/go/src/objfile/pe.go b/internal/go/src/objfile/pe.go new file mode 100755 index 0000000..e948212 --- /dev/null +++ b/internal/go/src/objfile/pe.go @@ -0,0 +1,194 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of PE executables (Microsoft Windows). + +package objfile + +import ( + "debug/dwarf" + "debug/pe" + "fmt" + "io" + "slices" + "sort" +) + +type peFile struct { + pe *pe.File +} + +func openPE(r io.ReaderAt) (rawFile, error) { + f, err := pe.NewFile(r) + if err != nil { + return nil, err + } + return &peFile{f}, nil +} + +func (f *peFile) symbols() ([]Sym, error) { + // Build sorted list of addresses of all symbols. + // We infer the size of a symbol by looking at where the next symbol begins. + var addrs []uint64 + + imageBase, _ := f.imageBase() + + var syms []Sym + for _, s := range f.pe.Symbols { + const ( + N_UNDEF = 0 // An undefined (extern) symbol + N_ABS = -1 // An absolute symbol (e_value is a constant, not an address) + N_DEBUG = -2 // A debugging symbol + ) + sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'} + switch s.SectionNumber { + case N_UNDEF: + sym.Code = 'U' + case N_ABS: + sym.Code = 'C' + case N_DEBUG: + sym.Code = '?' + default: + if s.SectionNumber < 0 || len(f.pe.Sections) < int(s.SectionNumber) { + return nil, fmt.Errorf("invalid section number in symbol table") + } + sect := f.pe.Sections[s.SectionNumber-1] + const ( + text = 0x20 + data = 0x40 + bss = 0x80 + permW = 0x80000000 + ) + ch := sect.Characteristics + switch { + case ch&text != 0: + sym.Code = 'T' + case ch&data != 0: + if ch&permW == 0 { + sym.Code = 'R' + } else { + sym.Code = 'D' + } + case ch&bss != 0: + sym.Code = 'B' + } + sym.Addr += imageBase + uint64(sect.VirtualAddress) + } + syms = append(syms, sym) + addrs = append(addrs, sym.Addr) + } + + slices.Sort(addrs) + for i := range syms { + j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr }) + if j < len(addrs) { + syms[i].Size = int64(addrs[j] - syms[i].Addr) + } + } + + return syms, nil +} + +func (f *peFile) pcln() (textStart uint64, pclntab []byte, err error) { + imageBase, err := f.imageBase() + if err != nil { + return 0, nil, err + } + + if sect := f.pe.Section(".text"); sect != nil { + textStart = imageBase + uint64(sect.VirtualAddress) + } + if pclntab, err = loadPETable(f.pe, "runtime.pclntab", "runtime.epclntab"); err != nil { + // We didn't find the symbols, so look for the names used in 1.3 and earlier. + // TODO: Remove code looking for the old symbols when we no longer care about 1.3. + var err2 error + if pclntab, err2 = loadPETable(f.pe, "pclntab", "epclntab"); err2 != nil { + return 0, nil, err + } + } + return textStart, pclntab, nil +} + +func (f *peFile) text() (textStart uint64, text []byte, err error) { + imageBase, err := f.imageBase() + if err != nil { + return 0, nil, err + } + + sect := f.pe.Section(".text") + if sect == nil { + return 0, nil, fmt.Errorf("text section not found") + } + textStart = imageBase + uint64(sect.VirtualAddress) + text, err = sect.Data() + return +} + +func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) { + for _, s := range f.Symbols { + if s.Name != name { + continue + } + if s.SectionNumber <= 0 { + return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber) + } + if len(f.Sections) < int(s.SectionNumber) { + return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections)) + } + return s, nil + } + return nil, fmt.Errorf("no %s symbol found", name) +} + +func loadPETable(f *pe.File, sname, ename string) ([]byte, error) { + ssym, err := findPESymbol(f, sname) + if err != nil { + return nil, err + } + esym, err := findPESymbol(f, ename) + if err != nil { + return nil, err + } + if ssym.SectionNumber != esym.SectionNumber { + return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename) + } + sect := f.Sections[ssym.SectionNumber-1] + data, err := sect.Data() + if err != nil { + return nil, err + } + return data[ssym.Value:esym.Value], nil +} + +func (f *peFile) goarch() string { + switch f.pe.Machine { + case pe.IMAGE_FILE_MACHINE_I386: + return "386" + case pe.IMAGE_FILE_MACHINE_AMD64: + return "amd64" + case pe.IMAGE_FILE_MACHINE_ARM64: + return "arm64" + default: + return "" + } +} + +func (f *peFile) loadAddress() (uint64, error) { + return f.imageBase() +} + +func (f *peFile) imageBase() (uint64, error) { + switch oh := f.pe.OptionalHeader.(type) { + case *pe.OptionalHeader32: + return uint64(oh.ImageBase), nil + case *pe.OptionalHeader64: + return oh.ImageBase, nil + default: + return 0, fmt.Errorf("pe file format not recognized") + } +} + +func (f *peFile) dwarf() (*dwarf.Data, error) { + return f.pe.DWARF() +} diff --git a/internal/go/src/objfile/plan9obj.go b/internal/go/src/objfile/plan9obj.go new file mode 100755 index 0000000..edd4023 --- /dev/null +++ b/internal/go/src/objfile/plan9obj.go @@ -0,0 +1,150 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of Plan 9 a.out executables. + +package objfile + +import ( + "debug/dwarf" + "debug/plan9obj" + "errors" + "fmt" + "io" + "slices" + "sort" +) + +var validSymType = map[rune]bool{ + 'T': true, + 't': true, + 'D': true, + 'd': true, + 'B': true, + 'b': true, +} + +type plan9File struct { + plan9 *plan9obj.File +} + +func openPlan9(r io.ReaderAt) (rawFile, error) { + f, err := plan9obj.NewFile(r) + if err != nil { + return nil, err + } + return &plan9File{f}, nil +} + +func (f *plan9File) symbols() ([]Sym, error) { + plan9Syms, err := f.plan9.Symbols() + if err != nil { + return nil, err + } + + // Build sorted list of addresses of all symbols. + // We infer the size of a symbol by looking at where the next symbol begins. + var addrs []uint64 + for _, s := range plan9Syms { + if !validSymType[s.Type] { + continue + } + addrs = append(addrs, s.Value) + } + slices.Sort(addrs) + + var syms []Sym + + for _, s := range plan9Syms { + if !validSymType[s.Type] { + continue + } + sym := Sym{Addr: s.Value, Name: s.Name, Code: s.Type} + i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) + if i < len(addrs) { + sym.Size = int64(addrs[i] - s.Value) + } + syms = append(syms, sym) + } + + return syms, nil +} + +func (f *plan9File) pcln() (textStart uint64, pclntab []byte, err error) { + textStart = f.plan9.LoadAddress + f.plan9.HdrSize + if pclntab, err = loadPlan9Table(f.plan9, "runtime.pclntab", "runtime.epclntab"); err != nil { + // We didn't find the symbols, so look for the names used in 1.3 and earlier. + // TODO: Remove code looking for the old symbols when we no longer care about 1.3. + var err2 error + if pclntab, err2 = loadPlan9Table(f.plan9, "pclntab", "epclntab"); err2 != nil { + return 0, nil, err + } + } + return textStart, pclntab, nil +} + +func (f *plan9File) text() (textStart uint64, text []byte, err error) { + sect := f.plan9.Section("text") + if sect == nil { + return 0, nil, fmt.Errorf("text section not found") + } + textStart = f.plan9.LoadAddress + f.plan9.HdrSize + text, err = sect.Data() + return +} + +func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) { + syms, err := f.Symbols() + if err != nil { + return nil, err + } + for _, s := range syms { + if s.Name != name { + continue + } + return &s, nil + } + return nil, fmt.Errorf("no %s symbol found", name) +} + +func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) { + ssym, err := findPlan9Symbol(f, sname) + if err != nil { + return nil, err + } + esym, err := findPlan9Symbol(f, ename) + if err != nil { + return nil, err + } + sect := f.Section("text") + if sect == nil { + return nil, err + } + data, err := sect.Data() + if err != nil { + return nil, err + } + textStart := f.LoadAddress + f.HdrSize + return data[ssym.Value-textStart : esym.Value-textStart], nil +} + +func (f *plan9File) goarch() string { + switch f.plan9.Magic { + case plan9obj.Magic386: + return "386" + case plan9obj.MagicAMD64: + return "amd64" + case plan9obj.MagicARM: + return "arm" + } + return "" +} + +func (f *plan9File) loadAddress() (uint64, error) { + return 0, fmt.Errorf("unknown load address") +} + +func (f *plan9File) dwarf() (*dwarf.Data, error) { + return nil, errors.New("no DWARF data in Plan 9 file") +} diff --git a/internal/go/src/objfile/xcoff.go b/internal/go/src/objfile/xcoff.go new file mode 100755 index 0000000..b3f4265 --- /dev/null +++ b/internal/go/src/objfile/xcoff.go @@ -0,0 +1,162 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parsing of XCOFF executable (AIX) + +package objfile + +import ( + "debug/dwarf" + "fmt" + "loov.dev/lensm/internal/go/src/xcoff" + "io" + "unicode" +) + +type xcoffFile struct { + xcoff *xcoff.File +} + +func openXcoff(r io.ReaderAt) (rawFile, error) { + f, err := xcoff.NewFile(r) + if err != nil { + return nil, err + } + return &xcoffFile{f}, nil +} + +func (f *xcoffFile) symbols() ([]Sym, error) { + var syms []Sym + for _, s := range f.xcoff.Symbols { + const ( + N_UNDEF = 0 // An undefined (extern) symbol + N_ABS = -1 // An absolute symbol (e_value is a constant, not an address) + N_DEBUG = -2 // A debugging symbol + ) + sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'} + + switch s.SectionNumber { + case N_UNDEF: + sym.Code = 'U' + case N_ABS: + sym.Code = 'C' + case N_DEBUG: + sym.Code = '?' + default: + if s.SectionNumber < 0 || len(f.xcoff.Sections) < s.SectionNumber { + return nil, fmt.Errorf("invalid section number in symbol table") + } + sect := f.xcoff.Sections[s.SectionNumber-1] + + // debug/xcoff returns an offset in the section not the actual address + sym.Addr += sect.VirtualAddress + + if s.AuxCSect.SymbolType&0x3 == xcoff.XTY_LD { + // The size of a function is contained in the + // AUX_FCN entry + sym.Size = s.AuxFcn.Size + } else { + sym.Size = s.AuxCSect.Length + } + + sym.Size = s.AuxCSect.Length + + switch sect.Type { + case xcoff.STYP_TEXT: + if s.AuxCSect.StorageMappingClass == xcoff.XMC_RO { + sym.Code = 'R' + } else { + sym.Code = 'T' + } + case xcoff.STYP_DATA: + sym.Code = 'D' + case xcoff.STYP_BSS: + sym.Code = 'B' + } + + if s.StorageClass == xcoff.C_HIDEXT { + // Local symbol + sym.Code = unicode.ToLower(sym.Code) + } + + } + syms = append(syms, sym) + } + + return syms, nil +} + +func (f *xcoffFile) pcln() (textStart uint64, pclntab []byte, err error) { + if sect := f.xcoff.Section(".text"); sect != nil { + textStart = sect.VirtualAddress + } + if pclntab, err = loadXCOFFTable(f.xcoff, "runtime.pclntab", "runtime.epclntab"); err != nil { + return 0, nil, err + } + return textStart, pclntab, nil +} + +func (f *xcoffFile) text() (textStart uint64, text []byte, err error) { + sect := f.xcoff.Section(".text") + if sect == nil { + return 0, nil, fmt.Errorf("text section not found") + } + textStart = sect.VirtualAddress + text, err = sect.Data() + return +} + +func findXCOFFSymbol(f *xcoff.File, name string) (*xcoff.Symbol, error) { + for _, s := range f.Symbols { + if s.Name != name { + continue + } + if s.SectionNumber <= 0 { + return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber) + } + if len(f.Sections) < s.SectionNumber { + return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections)) + } + return s, nil + } + return nil, fmt.Errorf("no %s symbol found", name) +} + +func loadXCOFFTable(f *xcoff.File, sname, ename string) ([]byte, error) { + ssym, err := findXCOFFSymbol(f, sname) + if err != nil { + return nil, err + } + esym, err := findXCOFFSymbol(f, ename) + if err != nil { + return nil, err + } + if ssym.SectionNumber != esym.SectionNumber { + return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename) + } + sect := f.Sections[ssym.SectionNumber-1] + data, err := sect.Data() + if err != nil { + return nil, err + } + return data[ssym.Value:esym.Value], nil +} + +func (f *xcoffFile) goarch() string { + switch f.xcoff.TargetMachine { + case xcoff.U802TOCMAGIC: + return "ppc" + case xcoff.U64_TOCMAGIC: + return "ppc64" + } + return "" +} + +func (f *xcoffFile) loadAddress() (uint64, error) { + return 0, fmt.Errorf("unknown load address") +} + +func (f *xcoffFile) dwarf() (*dwarf.Data, error) { + return f.xcoff.DWARF() +} diff --git a/internal/go/src/saferio/io.go b/internal/go/src/saferio/io.go new file mode 100755 index 0000000..5c428e6 --- /dev/null +++ b/internal/go/src/saferio/io.go @@ -0,0 +1,132 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package saferio provides I/O functions that avoid allocating large +// amounts of memory unnecessarily. This is intended for packages that +// read data from an [io.Reader] where the size is part of the input +// data but the input may be corrupt, or may be provided by an +// untrustworthy attacker. +package saferio + +import ( + "io" + "unsafe" +) + +// chunk is an arbitrary limit on how much memory we are willing +// to allocate without concern. +const chunk = 10 << 20 // 10M + +// ReadData reads n bytes from the input stream, but avoids allocating +// all n bytes if n is large. This avoids crashing the program by +// allocating all n bytes in cases where n is incorrect. +// +// The error is io.EOF only if no bytes were read. +// If an io.EOF happens after reading some but not all the bytes, +// ReadData returns io.ErrUnexpectedEOF. +func ReadData(r io.Reader, n uint64) ([]byte, error) { + if int64(n) < 0 || n != uint64(int(n)) { + // n is too large to fit in int, so we can't allocate + // a buffer large enough. Treat this as a read failure. + return nil, io.ErrUnexpectedEOF + } + + if n < chunk { + buf := make([]byte, n) + _, err := io.ReadFull(r, buf) + if err != nil { + return nil, err + } + return buf, nil + } + + var buf []byte + buf1 := make([]byte, chunk) + for n > 0 { + next := n + if next > chunk { + next = chunk + } + _, err := io.ReadFull(r, buf1[:next]) + if err != nil { + if len(buf) > 0 && err == io.EOF { + err = io.ErrUnexpectedEOF + } + return nil, err + } + buf = append(buf, buf1[:next]...) + n -= next + } + return buf, nil +} + +// ReadDataAt reads n bytes from the input stream at off, but avoids +// allocating all n bytes if n is large. This avoids crashing the program +// by allocating all n bytes in cases where n is incorrect. +func ReadDataAt(r io.ReaderAt, n uint64, off int64) ([]byte, error) { + if int64(n) < 0 || n != uint64(int(n)) { + // n is too large to fit in int, so we can't allocate + // a buffer large enough. Treat this as a read failure. + return nil, io.ErrUnexpectedEOF + } + + if n < chunk { + buf := make([]byte, n) + _, err := r.ReadAt(buf, off) + if err != nil { + // io.SectionReader can return EOF for n == 0, + // but for our purposes that is a success. + if err != io.EOF || n > 0 { + return nil, err + } + } + return buf, nil + } + + var buf []byte + buf1 := make([]byte, chunk) + for n > 0 { + next := n + if next > chunk { + next = chunk + } + _, err := r.ReadAt(buf1[:next], off) + if err != nil { + return nil, err + } + buf = append(buf, buf1[:next]...) + n -= next + off += int64(next) + } + return buf, nil +} + +// SliceCapWithSize returns the capacity to use when allocating a slice. +// After the slice is allocated with the capacity, it should be +// built using append. This will avoid allocating too much memory +// if the capacity is large and incorrect. +// +// A negative result means that the value is always too big. +func SliceCapWithSize(size, c uint64) int { + if int64(c) < 0 || c != uint64(int(c)) { + return -1 + } + if size > 0 && c > (1<<64-1)/size { + return -1 + } + if c*size > chunk { + c = chunk / size + if c == 0 { + c = 1 + } + } + return int(c) +} + +// SliceCap is like SliceCapWithSize but using generics. +func SliceCap[E any](c uint64) int { + var v E + size := uint64(unsafe.Sizeof(v)) + return SliceCapWithSize(size, c) +} diff --git a/internal/go/src/src/pos.go b/internal/go/src/src/pos.go new file mode 100755 index 0000000..10609fc --- /dev/null +++ b/internal/go/src/src/pos.go @@ -0,0 +1,478 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements the encoding of source positions. + +package src + +import ( + "bytes" + "fmt" + "io" +) + +// A Pos encodes a source position consisting of a (line, column) number pair +// and a position base. A zero Pos is a ready to use "unknown" position (nil +// position base and zero line number). +// +// The (line, column) values refer to a position in a file independent of any +// position base ("absolute" file position). +// +// The position base is used to determine the "relative" position, that is the +// filename and line number relative to the position base. If the base refers +// to the current file, there is no difference between absolute and relative +// positions. If it refers to a //line directive, a relative position is relative +// to that directive. A position base in turn contains the position at which it +// was introduced in the current file. +type Pos struct { + base *PosBase + lico +} + +// NoPos is a valid unknown position. +var NoPos Pos + +// MakePos creates a new Pos value with the given base, and (file-absolute) +// line and column. +func MakePos(base *PosBase, line, col uint) Pos { + return Pos{base, makeLico(line, col)} +} + +// IsKnown reports whether the position p is known. +// A position is known if it either has a non-nil +// position base, or a non-zero line number. +func (p Pos) IsKnown() bool { + return p.base != nil || p.Line() != 0 +} + +// Before reports whether the position p comes before q in the source. +// For positions in different files, ordering is by filename. +func (p Pos) Before(q Pos) bool { + n, m := p.Filename(), q.Filename() + return n < m || n == m && p.lico < q.lico +} + +// After reports whether the position p comes after q in the source. +// For positions in different files, ordering is by filename. +func (p Pos) After(q Pos) bool { + n, m := p.Filename(), q.Filename() + return n > m || n == m && p.lico > q.lico +} + +func (p Pos) LineNumber() string { + if !p.IsKnown() { + return "?" + } + return p.lico.lineNumber() +} + +func (p Pos) LineNumberHTML() string { + if !p.IsKnown() { + return "?" + } + return p.lico.lineNumberHTML() +} + +// Filename returns the name of the actual file containing this position. +func (p Pos) Filename() string { return p.base.Pos().RelFilename() } + +// Base returns the position base. +func (p Pos) Base() *PosBase { return p.base } + +// SetBase sets the position base. +func (p *Pos) SetBase(base *PosBase) { p.base = base } + +// RelFilename returns the filename recorded with the position's base. +func (p Pos) RelFilename() string { return p.base.Filename() } + +// RelLine returns the line number relative to the position's base. +func (p Pos) RelLine() uint { + b := p.base + if b.Line() == 0 { + // base line is unknown => relative line is unknown + return 0 + } + return b.Line() + (p.Line() - b.Pos().Line()) +} + +// RelCol returns the column number relative to the position's base. +func (p Pos) RelCol() uint { + b := p.base + if b.Col() == 0 { + // base column is unknown => relative column is unknown + // (the current specification for line directives requires + // this to apply until the next PosBase/line directive, + // not just until the new newline) + return 0 + } + if p.Line() == b.Pos().Line() { + // p on same line as p's base => column is relative to p's base + return b.Col() + (p.Col() - b.Pos().Col()) + } + return p.Col() +} + +// AbsFilename() returns the absolute filename recorded with the position's base. +func (p Pos) AbsFilename() string { return p.base.AbsFilename() } + +// FileIndex returns the file index of the position's base's absolute +// filename within the PosTable that it was registered. +func (p Pos) FileIndex() int { return p.base.FileIndex() } + +func (p Pos) String() string { + return p.Format(true, true) +} + +// Format formats a position as "filename:line" or "filename:line:column", +// controlled by the showCol flag and if the column is known (!= 0). +// For positions relative to line directives, the original position is +// shown as well, as in "filename:line[origfile:origline:origcolumn]" if +// showOrig is set. +func (p Pos) Format(showCol, showOrig bool) string { + buf := new(bytes.Buffer) + p.WriteTo(buf, showCol, showOrig) + return buf.String() +} + +// WriteTo a position to w, formatted as Format does. +func (p Pos) WriteTo(w io.Writer, showCol, showOrig bool) { + if !p.IsKnown() { + io.WriteString(w, "") + return + } + + if b := p.base; b == b.Pos().base { + // base is file base (incl. nil) + format(w, p.Filename(), p.Line(), p.Col(), showCol) + return + } + + // base is relative + // Print the column only for the original position since the + // relative position's column information may be bogus (it's + // typically generated code and we can't say much about the + // original source at that point but for the file:line info + // that's provided via a line directive). + // TODO(gri) This may not be true if we have an inlining base. + // We may want to differentiate at some point. + format(w, p.RelFilename(), p.RelLine(), p.RelCol(), showCol) + if showOrig { + io.WriteString(w, "[") + format(w, p.Filename(), p.Line(), p.Col(), showCol) + io.WriteString(w, "]") + } +} + +// format formats a (filename, line, col) tuple as "filename:line" (showCol +// is false or col == 0) or "filename:line:column" (showCol is true and col != 0). +func format(w io.Writer, filename string, line, col uint, showCol bool) { + io.WriteString(w, filename) + io.WriteString(w, ":") + fmt.Fprint(w, line) + // col == 0 and col == colMax are interpreted as unknown column values + if showCol && 0 < col && col < colMax { + io.WriteString(w, ":") + fmt.Fprint(w, col) + } +} + +// formatstr wraps format to return a string. +func formatstr(filename string, line, col uint, showCol bool) string { + buf := new(bytes.Buffer) + format(buf, filename, line, col, showCol) + return buf.String() +} + +// ---------------------------------------------------------------------------- +// PosBase + +// A PosBase encodes a filename and base position. +// Typically, each file and line directive introduce a PosBase. +type PosBase struct { + pos Pos // position at which the relative position is (line, col) + filename string // file name used to open source file, for error messages + absFilename string // absolute file name, for PC-Line tables + line, col uint // relative line, column number at pos + inl int // inlining index (see cmd/internal/obj/inl.go) + fileIndex int // index of absFilename within PosTable.FileTable +} + +// NewFileBase returns a new *PosBase for a file with the given (relative and +// absolute) filenames. +func NewFileBase(filename, absFilename string) *PosBase { + base := &PosBase{ + filename: filename, + absFilename: absFilename, + line: 1, + col: 1, + inl: -1, + fileIndex: -1, + } + base.pos = MakePos(base, 1, 1) + return base +} + +// NewLinePragmaBase returns a new *PosBase for a line directive of the form +// +// //line filename:line:col +// /*line filename:line:col*/ +// +// at position pos. +func NewLinePragmaBase(pos Pos, filename, absFilename string, line, col uint) *PosBase { + return &PosBase{pos, filename, absFilename, line, col, -1, -1} +} + +// NewInliningBase returns a copy of the orig PosBase with the given inlining +// index. If orig == nil, NewInliningBase panics. +func NewInliningBase(orig *PosBase, inlTreeIndex int) *PosBase { + if orig == nil { + panic("no old PosBase") + } + base := *orig + base.inl = inlTreeIndex + base.fileIndex = -1 + if orig == orig.pos.base { + base.pos.base = &base + } + return &base +} + +var noPos Pos + +// Pos returns the position at which base is located. +// If b == nil, the result is the zero position. +func (b *PosBase) Pos() *Pos { + if b != nil { + return &b.pos + } + return &noPos +} + +// Filename returns the filename recorded with the base. +// If b == nil, the result is the empty string. +func (b *PosBase) Filename() string { + if b != nil { + return b.filename + } + return "" +} + +// AbsFilename returns the absolute filename recorded with the base. +// If b == nil, the result is the empty string. +func (b *PosBase) AbsFilename() string { + if b != nil { + return b.absFilename + } + return "" +} + +// FileSymPrefix is the linker symbol prefix that used to be used for +// linker pseudo-symbols representing file names. +const FileSymPrefix = "gofile.." + +// FileIndex returns the index of the base's absolute filename within +// its PosTable's FileTable. It panics if it hasn't been registered +// with a PosTable. If b == nil, the result is -1. +func (b *PosBase) FileIndex() int { + if b != nil { + if b.fileIndex < 0 { + panic("PosBase has no file index") + } + return b.fileIndex + } + return -1 +} + +// Line returns the line number recorded with the base. +// If b == nil, the result is 0. +func (b *PosBase) Line() uint { + if b != nil { + return b.line + } + return 0 +} + +// Col returns the column number recorded with the base. +// If b == nil, the result is 0. +func (b *PosBase) Col() uint { + if b != nil { + return b.col + } + return 0 +} + +// InliningIndex returns the index into the global inlining +// tree recorded with the base. If b == nil or the base has +// not been inlined, the result is < 0. +func (b *PosBase) InliningIndex() int { + if b != nil { + return b.inl + } + return -1 +} + +// ---------------------------------------------------------------------------- +// lico + +// A lico is a compact encoding of a LIne and COlumn number. +type lico uint32 + +// Layout constants: 20 bits for line, 8 bits for column, 2 for isStmt, 2 for pro/epilogue +// (If this is too tight, we can either make lico 64b wide, +// or we can introduce a tiered encoding where we remove column +// information as line numbers grow bigger; similar to what gcc +// does.) +// The bitfield order is chosen to make IsStmt be the least significant +// part of a position; its use is to communicate statement edges through +// instruction scrambling in code generation, not to impose an order. +// TODO: Prologue and epilogue are perhaps better handled as pseudo-ops for the assembler, +// because they have almost no interaction with other uses of the position. +const ( + lineBits, lineMax = 20, 1<= lineMax { + // cannot represent line, use max. line so we have some information + line = lineMax + // Drop column information if line number saturates. + // Ensures line+col is monotonic. See issue 51193. + col = 0 + } + if col > colMax { + // cannot represent column, use max. column so we have some information + col = colMax + } + // default is not-sure-if-statement + return makeLicoRaw(line, col) +} + +func (x lico) Line() uint { return uint(x) >> lineShift } +func (x lico) SameLine(y lico) bool { return 0 == (x^y)&^lico(1<> colShift & colMax } +func (x lico) IsStmt() uint { + if x == 0 { + return PosNotStmt + } + return uint(x) >> isStmtShift & isStmtMax +} +func (x lico) Xlogue() PosXlogue { + return PosXlogue(uint(x) >> xlogueShift & xlogueMax) +} + +// withNotStmt returns a lico for the same location, but not a statement +func (x lico) withNotStmt() lico { + return x.withStmt(PosNotStmt) +} + +// withDefaultStmt returns a lico for the same location, with default isStmt +func (x lico) withDefaultStmt() lico { + return x.withStmt(PosDefaultStmt) +} + +// withIsStmt returns a lico for the same location, tagged as definitely a statement +func (x lico) withIsStmt() lico { + return x.withStmt(PosIsStmt) +} + +// withXlogue attaches a prologue/epilogue attribute to a lico +func (x lico) withXlogue(xlogue PosXlogue) lico { + if x == 0 { + if xlogue == 0 { + return x + } + // Normalize 0 to "not a statement" + x = lico(PosNotStmt << isStmtShift) + } + return lico(uint(x) & ^uint(xlogueMax<%s%d", style, pfx, x.Line(), style) +} + +func (x lico) atColumn1() lico { + return makeLico(x.Line(), 1).withIsStmt() +} diff --git a/internal/go/src/src/xpos.go b/internal/go/src/src/xpos.go new file mode 100755 index 0000000..a745059 --- /dev/null +++ b/internal/go/src/src/xpos.go @@ -0,0 +1,183 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements the compressed encoding of source +// positions using a lookup table. + +package src + +// XPos is a more compact representation of Pos. +type XPos struct { + index int32 + lico +} + +// NoXPos is a valid unknown position. +var NoXPos XPos + +// IsKnown reports whether the position p is known. +// XPos.IsKnown() matches Pos.IsKnown() for corresponding +// positions. +func (p XPos) IsKnown() bool { + return p.index != 0 || p.Line() != 0 +} + +// Before reports whether the position p comes before q in the source. +// For positions with different bases, ordering is by base index. +func (p XPos) Before(q XPos) bool { + n, m := p.index, q.index + return n < m || n == m && p.lico < q.lico +} + +// SameFile reports whether p and q are positions in the same file. +func (p XPos) SameFile(q XPos) bool { + return p.index == q.index +} + +// SameFileAndLine reports whether p and q are positions on the same line in the same file. +func (p XPos) SameFileAndLine(q XPos) bool { + return p.index == q.index && p.lico.SameLine(q.lico) +} + +// After reports whether the position p comes after q in the source. +// For positions with different bases, ordering is by base index. +func (p XPos) After(q XPos) bool { + n, m := p.index, q.index + return n > m || n == m && p.lico > q.lico +} + +// WithNotStmt returns the same location to be marked with DWARF is_stmt=0 +func (p XPos) WithNotStmt() XPos { + p.lico = p.lico.withNotStmt() + return p +} + +// WithDefaultStmt returns the same location with undetermined is_stmt +func (p XPos) WithDefaultStmt() XPos { + p.lico = p.lico.withDefaultStmt() + return p +} + +// WithIsStmt returns the same location to be marked with DWARF is_stmt=1 +func (p XPos) WithIsStmt() XPos { + p.lico = p.lico.withIsStmt() + return p +} + +// WithBogusLine returns a bogus line that won't match any recorded for the source code. +// Its use is to disrupt the statements within an infinite loop so that the debugger +// will not itself loop infinitely waiting for the line number to change. +// gdb chooses not to display the bogus line; delve shows it with a complaint, but the +// alternative behavior is to hang. +func (p XPos) WithBogusLine() XPos { + if p.index == 0 { + // See #35652 + panic("Assigning a bogus line to XPos with no file will cause mysterious downstream failures.") + } + p.lico = makeBogusLico() + return p +} + +// WithXlogue returns the same location but marked with DWARF function prologue/epilogue +func (p XPos) WithXlogue(x PosXlogue) XPos { + p.lico = p.lico.withXlogue(x) + return p +} + +// LineNumber returns a string for the line number, "?" if it is not known. +func (p XPos) LineNumber() string { + if !p.IsKnown() { + return "?" + } + return p.lico.lineNumber() +} + +// FileIndex returns a smallish non-negative integer corresponding to the +// file for this source position. Smallish is relative; it can be thousands +// large, but not millions. +func (p XPos) FileIndex() int32 { + return p.index +} + +func (p XPos) LineNumberHTML() string { + if !p.IsKnown() { + return "?" + } + return p.lico.lineNumberHTML() +} + +// AtColumn1 returns the same location but shifted to column 1. +func (p XPos) AtColumn1() XPos { + p.lico = p.lico.atColumn1() + return p +} + +// A PosTable tracks Pos -> XPos conversions and vice versa. +// Its zero value is a ready-to-use PosTable. +type PosTable struct { + baseList []*PosBase + indexMap map[*PosBase]int + nameMap map[string]int // Maps file symbol name to index for debug information. +} + +// XPos returns the corresponding XPos for the given pos, +// adding pos to t if necessary. +func (t *PosTable) XPos(pos Pos) XPos { + return XPos{t.baseIndex(pos.base), pos.lico} +} + +func (t *PosTable) baseIndex(base *PosBase) int32 { + if base == nil { + return 0 + } + + if i, ok := t.indexMap[base]; ok { + return int32(i) + } + + if base.fileIndex >= 0 { + panic("PosBase already registered with a PosTable") + } + + if t.indexMap == nil { + t.baseList = append(t.baseList, nil) + t.indexMap = make(map[*PosBase]int) + t.nameMap = make(map[string]int) + } + + i := len(t.baseList) + t.indexMap[base] = i + t.baseList = append(t.baseList, base) + + fileIndex, ok := t.nameMap[base.absFilename] + if !ok { + fileIndex = len(t.nameMap) + t.nameMap[base.absFilename] = fileIndex + } + base.fileIndex = fileIndex + + return int32(i) +} + +// Pos returns the corresponding Pos for the given p. +// If p cannot be translated via t, the function panics. +func (t *PosTable) Pos(p XPos) Pos { + var base *PosBase + if p.index != 0 { + base = t.baseList[p.index] + } + return Pos{base, p.lico} +} + +// FileTable returns a slice of all files used to build this package. +func (t *PosTable) FileTable() []string { + // Create a LUT of the global package level file indices. This table is what + // is written in the debug_lines header, the file[N] will be referenced as + // N+1 in the debug_lines table. + fileLUT := make([]string, len(t.nameMap)) + for str, i := range t.nameMap { + fileLUT[i] = str + } + return fileLUT +} diff --git a/internal/go/src/sys/arch.go b/internal/go/src/sys/arch.go new file mode 100755 index 0000000..e0f0edd --- /dev/null +++ b/internal/go/src/sys/arch.go @@ -0,0 +1,287 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sys + +import ( + "encoding/binary" + "loov.dev/lensm/internal/go/src/goarch" +) + +// TODO: just use goarch.ArchFamilyType directly +type ArchFamily = goarch.ArchFamilyType + +const ( + AMD64 = goarch.AMD64 + ARM = goarch.ARM + ARM64 = goarch.ARM64 + I386 = goarch.I386 + Loong64 = goarch.LOONG64 + MIPS = goarch.MIPS + MIPS64 = goarch.MIPS64 + PPC64 = goarch.PPC64 + RISCV64 = goarch.RISCV64 + S390X = goarch.S390X + Wasm = goarch.WASM +) + +// Arch represents an individual architecture. +type Arch struct { + Name string + Family ArchFamily + + ByteOrder binary.ByteOrder + + // PtrSize is the size in bytes of pointers and the + // predeclared "int", "uint", and "uintptr" types. + PtrSize int + + // RegSize is the size in bytes of general purpose registers. + RegSize int + + // MinLC is the minimum length of an instruction code. + MinLC int + + // Alignment is maximum alignment required by the architecture + // for any (compiler-generated) load or store instruction. + // Loads or stores smaller than Alignment must be naturally aligned. + // Loads or stores larger than Alignment need only be Alignment-aligned. + Alignment int8 + + // CanMergeLoads reports whether the backend optimization passes + // can combine adjacent loads into a single larger, possibly unaligned, load. + // Note that currently the optimizations must be able to handle little endian byte order. + CanMergeLoads bool + + // CanJumpTable reports whether the backend can handle + // compiling a jump table. + CanJumpTable bool + + // HasLR indicates that this architecture uses a link register + // for calls. + HasLR bool + + // FixedFrameSize is the smallest possible offset from the + // hardware stack pointer to a local variable on the stack. + // Architectures that use a link register save its value on + // the stack in the function prologue and so always have a + // pointer between the hardware stack pointer and the local + // variable area. + FixedFrameSize int64 +} + +// InFamily reports whether a is a member of any of the specified +// architecture families. +func (a *Arch) InFamily(xs ...ArchFamily) bool { + for _, x := range xs { + if a.Family == x { + return true + } + } + return false +} + +var Arch386 = &Arch{ + Name: "386", + Family: I386, + ByteOrder: binary.LittleEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 1, + Alignment: 1, + CanMergeLoads: true, + HasLR: false, + FixedFrameSize: 0, +} + +var ArchAMD64 = &Arch{ + Name: "amd64", + Family: AMD64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 1, + Alignment: 1, + CanMergeLoads: true, + CanJumpTable: true, + HasLR: false, + FixedFrameSize: 0, +} + +var ArchARM = &Arch{ + Name: "arm", + Family: ARM, + ByteOrder: binary.LittleEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 4, + Alignment: 4, // TODO: just for arm5? + CanMergeLoads: false, + HasLR: true, + FixedFrameSize: 4, // LR +} + +var ArchARM64 = &Arch{ + Name: "arm64", + Family: ARM64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 1, + CanMergeLoads: true, + CanJumpTable: true, + HasLR: true, + FixedFrameSize: 8, // LR +} + +var ArchLoong64 = &Arch{ + Name: "loong64", + Family: Loong64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, // Unaligned accesses are not guaranteed to be fast + CanMergeLoads: true, + CanJumpTable: true, + HasLR: true, + FixedFrameSize: 8, // LR +} + +var ArchMIPS = &Arch{ + Name: "mips", + Family: MIPS, + ByteOrder: binary.BigEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 4, + Alignment: 4, + CanMergeLoads: false, + HasLR: true, + FixedFrameSize: 4, // LR +} + +var ArchMIPSLE = &Arch{ + Name: "mipsle", + Family: MIPS, + ByteOrder: binary.LittleEndian, + PtrSize: 4, + RegSize: 4, + MinLC: 4, + Alignment: 4, + CanMergeLoads: false, + HasLR: true, + FixedFrameSize: 4, // LR +} + +var ArchMIPS64 = &Arch{ + Name: "mips64", + Family: MIPS64, + ByteOrder: binary.BigEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, + CanMergeLoads: false, + HasLR: true, + FixedFrameSize: 8, // LR +} + +var ArchMIPS64LE = &Arch{ + Name: "mips64le", + Family: MIPS64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 8, + CanMergeLoads: false, + HasLR: true, + FixedFrameSize: 8, // LR +} + +var ArchPPC64 = &Arch{ + Name: "ppc64", + Family: PPC64, + ByteOrder: binary.BigEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 1, + CanMergeLoads: true, + HasLR: true, + // PIC code on ppc64le requires 32 bytes of stack, and it's + // easier to just use that much stack always. + FixedFrameSize: 4 * 8, +} + +var ArchPPC64LE = &Arch{ + Name: "ppc64le", + Family: PPC64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 4, + Alignment: 1, + CanMergeLoads: true, + HasLR: true, + FixedFrameSize: 4 * 8, +} + +var ArchRISCV64 = &Arch{ + Name: "riscv64", + Family: RISCV64, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 2, + Alignment: 8, // riscv unaligned loads work, but are really slow (trap + simulated by OS) + CanMergeLoads: false, + HasLR: true, + FixedFrameSize: 8, // LR +} + +var ArchS390X = &Arch{ + Name: "s390x", + Family: S390X, + ByteOrder: binary.BigEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 2, + Alignment: 1, + CanMergeLoads: true, + HasLR: true, + FixedFrameSize: 8, // LR +} + +var ArchWasm = &Arch{ + Name: "wasm", + Family: Wasm, + ByteOrder: binary.LittleEndian, + PtrSize: 8, + RegSize: 8, + MinLC: 1, + Alignment: 1, + CanMergeLoads: false, + HasLR: false, + FixedFrameSize: 0, +} + +var Archs = [...]*Arch{ + Arch386, + ArchAMD64, + ArchARM, + ArchARM64, + ArchLoong64, + ArchMIPS, + ArchMIPSLE, + ArchMIPS64, + ArchMIPS64LE, + ArchPPC64, + ArchPPC64LE, + ArchRISCV64, + ArchS390X, + ArchWasm, +} diff --git a/internal/go/src/sys/args.go b/internal/go/src/sys/args.go new file mode 100755 index 0000000..cc9fb64 --- /dev/null +++ b/internal/go/src/sys/args.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sys + +// ExecArgLengthLimit is the number of bytes we can safely +// pass as arguments to an exec.Command. +// +// Windows has a limit of 32 KB. To be conservative and not worry about whether +// that includes spaces or not, just use 30 KB. Darwin's limit is less clear. +// The OS claims 256KB, but we've seen failures with arglen as small as 50KB. +const ExecArgLengthLimit = (30 << 10) diff --git a/internal/go/src/unsafeheader/unsafeheader.go b/internal/go/src/unsafeheader/unsafeheader.go new file mode 100755 index 0000000..6d092c6 --- /dev/null +++ b/internal/go/src/unsafeheader/unsafeheader.go @@ -0,0 +1,37 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package unsafeheader contains header declarations for the Go runtime's slice +// and string implementations. +// +// This package allows packages that cannot import "reflect" to use types that +// are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader. +package unsafeheader + +import ( + "unsafe" +) + +// Slice is the runtime representation of a slice. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type Slice struct { + Data unsafe.Pointer + Len int + Cap int +} + +// String is the runtime representation of a string. +// It cannot be used safely or portably and its representation may +// change in a later release. +// +// Unlike reflect.StringHeader, its Data field is sufficient to guarantee the +// data it references will not be garbage collected. +type String struct { + Data unsafe.Pointer + Len int +} diff --git a/internal/go/src/xcoff/ar.go b/internal/go/src/xcoff/ar.go new file mode 100755 index 0000000..e616f37 --- /dev/null +++ b/internal/go/src/xcoff/ar.go @@ -0,0 +1,226 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xcoff + +import ( + "encoding/binary" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +const ( + SAIAMAG = 0x8 + AIAFMAG = "`\n" + AIAMAG = "\n" + AIAMAGBIG = "\n" + + // Sizeof + FL_HSZ_BIG = 0x80 + AR_HSZ_BIG = 0x70 +) + +type bigarFileHeader struct { + Flmagic [SAIAMAG]byte // Archive magic string + Flmemoff [20]byte // Member table offset + Flgstoff [20]byte // 32-bits global symtab offset + Flgst64off [20]byte // 64-bits global symtab offset + Flfstmoff [20]byte // First member offset + Fllstmoff [20]byte // Last member offset + Flfreeoff [20]byte // First member on free list offset +} + +type bigarMemberHeader struct { + Arsize [20]byte // File member size + Arnxtmem [20]byte // Next member pointer + Arprvmem [20]byte // Previous member pointer + Ardate [12]byte // File member date + Aruid [12]byte // File member uid + Argid [12]byte // File member gid + Armode [12]byte // File member mode (octal) + Arnamlen [4]byte // File member name length + // _ar_nam is removed because it's easier to get name without it. +} + +// Archive represents an open AIX big archive. +type Archive struct { + ArchiveHeader + Members []*Member + + closer io.Closer +} + +// ArchiveHeader holds information about a big archive file header +type ArchiveHeader struct { + magic string +} + +// Member represents a member of an AIX big archive. +type Member struct { + MemberHeader + sr *io.SectionReader +} + +// MemberHeader holds information about a big archive member +type MemberHeader struct { + Name string + Size uint64 +} + +// OpenArchive opens the named archive using os.Open and prepares it for use +// as an AIX big archive. +func OpenArchive(name string) (*Archive, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + arch, err := NewArchive(f) + if err != nil { + f.Close() + return nil, err + } + arch.closer = f + return arch, nil +} + +// Close closes the Archive. +// If the Archive was created using NewArchive directly instead of OpenArchive, +// Close has no effect. +func (a *Archive) Close() error { + var err error + if a.closer != nil { + err = a.closer.Close() + a.closer = nil + } + return err +} + +// NewArchive creates a new Archive for accessing an AIX big archive in an underlying reader. +func NewArchive(r io.ReaderAt) (*Archive, error) { + parseDecimalBytes := func(b []byte) (int64, error) { + return strconv.ParseInt(strings.TrimSpace(string(b)), 10, 64) + } + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read File Header + var magic [SAIAMAG]byte + if _, err := sr.ReadAt(magic[:], 0); err != nil { + return nil, err + } + + arch := new(Archive) + switch string(magic[:]) { + case AIAMAGBIG: + arch.magic = string(magic[:]) + case AIAMAG: + return nil, fmt.Errorf("small AIX archive not supported") + default: + return nil, fmt.Errorf("unrecognised archive magic: 0x%x", magic) + } + + var fhdr bigarFileHeader + if _, err := sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + if err := binary.Read(sr, binary.BigEndian, &fhdr); err != nil { + return nil, err + } + + off, err := parseDecimalBytes(fhdr.Flfstmoff[:]) + if err != nil { + return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) + } + + if off == 0 { + // Occurs if the archive is empty. + return arch, nil + } + + lastoff, err := parseDecimalBytes(fhdr.Fllstmoff[:]) + if err != nil { + return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) + } + + // Read members + for { + // Read Member Header + // The member header is normally 2 bytes larger. But it's easier + // to read the name if the header is read without _ar_nam. + // However, AIAFMAG must be read afterward. + if _, err := sr.Seek(off, io.SeekStart); err != nil { + return nil, err + } + + var mhdr bigarMemberHeader + if err := binary.Read(sr, binary.BigEndian, &mhdr); err != nil { + return nil, err + } + + member := new(Member) + arch.Members = append(arch.Members, member) + + size, err := parseDecimalBytes(mhdr.Arsize[:]) + if err != nil { + return nil, fmt.Errorf("error parsing size in member header(%q); %v", mhdr, err) + } + member.Size = uint64(size) + + // Read name + namlen, err := parseDecimalBytes(mhdr.Arnamlen[:]) + if err != nil { + return nil, fmt.Errorf("error parsing name length in member header(%q); %v", mhdr, err) + } + name := make([]byte, namlen) + if err := binary.Read(sr, binary.BigEndian, name); err != nil { + return nil, err + } + member.Name = string(name) + + fileoff := off + AR_HSZ_BIG + namlen + if fileoff&1 != 0 { + fileoff++ + if _, err := sr.Seek(1, io.SeekCurrent); err != nil { + return nil, err + } + } + + // Read AIAFMAG string + var fmag [2]byte + if err := binary.Read(sr, binary.BigEndian, &fmag); err != nil { + return nil, err + } + if string(fmag[:]) != AIAFMAG { + return nil, fmt.Errorf("AIAFMAG not found after member header") + } + + fileoff += 2 // Add the two bytes of AIAFMAG + member.sr = io.NewSectionReader(sr, fileoff, size) + + if off == lastoff { + break + } + off, err = parseDecimalBytes(mhdr.Arnxtmem[:]) + if err != nil { + return nil, fmt.Errorf("error parsing offset of first member in archive header(%q); %v", fhdr, err) + } + + } + + return arch, nil +} + +// GetFile returns the XCOFF file defined by member name. +// FIXME: This doesn't work if an archive has two members with the same +// name which can occur if an archive has both 32-bits and 64-bits files. +func (arch *Archive) GetFile(name string) (*File, error) { + for _, mem := range arch.Members { + if mem.Name == name { + return NewFile(mem.sr) + } + } + return nil, fmt.Errorf("unknown member %s in archive", name) +} diff --git a/internal/go/src/xcoff/file.go b/internal/go/src/xcoff/file.go new file mode 100755 index 0000000..3327ab7 --- /dev/null +++ b/internal/go/src/xcoff/file.go @@ -0,0 +1,693 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package xcoff implements access to XCOFF (Extended Common Object File Format) files. +package xcoff + +import ( + "debug/dwarf" + "encoding/binary" + "errors" + "fmt" + "loov.dev/lensm/internal/go/src/saferio" + "io" + "os" + "strings" +) + +// SectionHeader holds information about an XCOFF section header. +type SectionHeader struct { + Name string + VirtualAddress uint64 + Size uint64 + Type uint32 + Relptr uint64 + Nreloc uint32 +} + +type Section struct { + SectionHeader + Relocs []Reloc + io.ReaderAt + sr *io.SectionReader +} + +// AuxiliaryCSect holds information about an XCOFF symbol in an AUX_CSECT entry. +type AuxiliaryCSect struct { + Length int64 + StorageMappingClass int + SymbolType int +} + +// AuxiliaryFcn holds information about an XCOFF symbol in an AUX_FCN entry. +type AuxiliaryFcn struct { + Size int64 +} + +type Symbol struct { + Name string + Value uint64 + SectionNumber int + StorageClass int + AuxFcn AuxiliaryFcn + AuxCSect AuxiliaryCSect +} + +type Reloc struct { + VirtualAddress uint64 + Symbol *Symbol + Signed bool + InstructionFixed bool + Length uint8 + Type uint8 +} + +// ImportedSymbol holds information about an imported XCOFF symbol. +type ImportedSymbol struct { + Name string + Library string +} + +// FileHeader holds information about an XCOFF file header. +type FileHeader struct { + TargetMachine uint16 +} + +// A File represents an open XCOFF file. +type File struct { + FileHeader + Sections []*Section + Symbols []*Symbol + StringTable []byte + LibraryPaths []string + + closer io.Closer +} + +// Open opens the named file using os.Open and prepares it for use as an XCOFF binary. +func Open(name string) (*File, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + ff, err := NewFile(f) + if err != nil { + f.Close() + return nil, err + } + ff.closer = f + return ff, nil +} + +// Close closes the File. +// If the File was created using NewFile directly instead of Open, +// Close has no effect. +func (f *File) Close() error { + var err error + if f.closer != nil { + err = f.closer.Close() + f.closer = nil + } + return err +} + +// Section returns the first section with the given name, or nil if no such +// section exists. +// Xcoff have section's name limited to 8 bytes. Some sections like .gosymtab +// can be trunked but this method will still find them. +func (f *File) Section(name string) *Section { + for _, s := range f.Sections { + if s.Name == name || (len(name) > 8 && s.Name == name[:8]) { + return s + } + } + return nil +} + +// SectionByType returns the first section in f with the +// given type, or nil if there is no such section. +func (f *File) SectionByType(typ uint32) *Section { + for _, s := range f.Sections { + if s.Type == typ { + return s + } + } + return nil +} + +// cstring converts ASCII byte sequence b to string. +// It stops once it finds 0 or reaches end of b. +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[:i]) +} + +// getString extracts a string from an XCOFF string table. +func getString(st []byte, offset uint32) (string, bool) { + if offset < 4 || int(offset) >= len(st) { + return "", false + } + return cstring(st[offset:]), true +} + +// NewFile creates a new File for accessing an XCOFF binary in an underlying reader. +func NewFile(r io.ReaderAt) (*File, error) { + sr := io.NewSectionReader(r, 0, 1<<63-1) + // Read XCOFF target machine + var magic uint16 + if err := binary.Read(sr, binary.BigEndian, &magic); err != nil { + return nil, err + } + if magic != U802TOCMAGIC && magic != U64_TOCMAGIC { + return nil, fmt.Errorf("unrecognised XCOFF magic: 0x%x", magic) + } + + f := new(File) + f.TargetMachine = magic + + // Read XCOFF file header + if _, err := sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + var nscns uint16 + var symptr uint64 + var nsyms uint32 + var opthdr uint16 + var hdrsz int + switch f.TargetMachine { + case U802TOCMAGIC: + fhdr := new(FileHeader32) + if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil { + return nil, err + } + nscns = fhdr.Fnscns + symptr = uint64(fhdr.Fsymptr) + nsyms = fhdr.Fnsyms + opthdr = fhdr.Fopthdr + hdrsz = FILHSZ_32 + case U64_TOCMAGIC: + fhdr := new(FileHeader64) + if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil { + return nil, err + } + nscns = fhdr.Fnscns + symptr = fhdr.Fsymptr + nsyms = fhdr.Fnsyms + opthdr = fhdr.Fopthdr + hdrsz = FILHSZ_64 + } + + if symptr == 0 || nsyms <= 0 { + return nil, fmt.Errorf("no symbol table") + } + + // Read string table (located right after symbol table). + offset := symptr + uint64(nsyms)*SYMESZ + if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil { + return nil, err + } + // The first 4 bytes contain the length (in bytes). + var l uint32 + if err := binary.Read(sr, binary.BigEndian, &l); err != nil { + return nil, err + } + if l > 4 { + st, err := saferio.ReadDataAt(sr, uint64(l), int64(offset)) + if err != nil { + return nil, err + } + f.StringTable = st + } + + // Read section headers + if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), io.SeekStart); err != nil { + return nil, err + } + c := saferio.SliceCap[*Section](uint64(nscns)) + if c < 0 { + return nil, fmt.Errorf("too many XCOFF sections (%d)", nscns) + } + f.Sections = make([]*Section, 0, c) + for i := 0; i < int(nscns); i++ { + var scnptr uint64 + s := new(Section) + switch f.TargetMachine { + case U802TOCMAGIC: + shdr := new(SectionHeader32) + if err := binary.Read(sr, binary.BigEndian, shdr); err != nil { + return nil, err + } + s.Name = cstring(shdr.Sname[:]) + s.VirtualAddress = uint64(shdr.Svaddr) + s.Size = uint64(shdr.Ssize) + scnptr = uint64(shdr.Sscnptr) + s.Type = shdr.Sflags + s.Relptr = uint64(shdr.Srelptr) + s.Nreloc = uint32(shdr.Snreloc) + case U64_TOCMAGIC: + shdr := new(SectionHeader64) + if err := binary.Read(sr, binary.BigEndian, shdr); err != nil { + return nil, err + } + s.Name = cstring(shdr.Sname[:]) + s.VirtualAddress = shdr.Svaddr + s.Size = shdr.Ssize + scnptr = shdr.Sscnptr + s.Type = shdr.Sflags + s.Relptr = shdr.Srelptr + s.Nreloc = shdr.Snreloc + } + r2 := r + if scnptr == 0 { // .bss must have all 0s + r2 = &nobitsSectionReader{} + } + s.sr = io.NewSectionReader(r2, int64(scnptr), int64(s.Size)) + s.ReaderAt = s.sr + f.Sections = append(f.Sections, s) + } + + // Symbol map needed by relocation + var idxToSym = make(map[int]*Symbol) + + // Read symbol table + if _, err := sr.Seek(int64(symptr), io.SeekStart); err != nil { + return nil, err + } + f.Symbols = make([]*Symbol, 0) + for i := 0; i < int(nsyms); i++ { + var numaux int + var ok, needAuxFcn bool + sym := new(Symbol) + switch f.TargetMachine { + case U802TOCMAGIC: + se := new(SymEnt32) + if err := binary.Read(sr, binary.BigEndian, se); err != nil { + return nil, err + } + numaux = int(se.Nnumaux) + sym.SectionNumber = int(se.Nscnum) + sym.StorageClass = int(se.Nsclass) + sym.Value = uint64(se.Nvalue) + needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1 + zeroes := binary.BigEndian.Uint32(se.Nname[:4]) + if zeroes != 0 { + sym.Name = cstring(se.Nname[:]) + } else { + offset := binary.BigEndian.Uint32(se.Nname[4:]) + sym.Name, ok = getString(f.StringTable, offset) + if !ok { + goto skip + } + } + case U64_TOCMAGIC: + se := new(SymEnt64) + if err := binary.Read(sr, binary.BigEndian, se); err != nil { + return nil, err + } + numaux = int(se.Nnumaux) + sym.SectionNumber = int(se.Nscnum) + sym.StorageClass = int(se.Nsclass) + sym.Value = se.Nvalue + needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1 + sym.Name, ok = getString(f.StringTable, se.Noffset) + if !ok { + goto skip + } + } + if sym.StorageClass != C_EXT && sym.StorageClass != C_WEAKEXT && sym.StorageClass != C_HIDEXT { + goto skip + } + // Must have at least one csect auxiliary entry. + if numaux < 1 || i+numaux >= int(nsyms) { + goto skip + } + + if sym.SectionNumber > int(nscns) { + goto skip + } + if sym.SectionNumber == 0 { + sym.Value = 0 + } else { + sym.Value -= f.Sections[sym.SectionNumber-1].VirtualAddress + } + + idxToSym[i] = sym + + // If this symbol is a function, it must retrieve its size from + // its AUX_FCN entry. + // It can happen that a function symbol doesn't have any AUX_FCN. + // In this case, needAuxFcn is false and their size will be set to 0. + if needAuxFcn { + switch f.TargetMachine { + case U802TOCMAGIC: + aux := new(AuxFcn32) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxFcn.Size = int64(aux.Xfsize) + case U64_TOCMAGIC: + aux := new(AuxFcn64) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxFcn.Size = int64(aux.Xfsize) + } + } + + // Read csect auxiliary entry (by convention, it is the last). + if !needAuxFcn { + if _, err := sr.Seek(int64(numaux-1)*SYMESZ, io.SeekCurrent); err != nil { + return nil, err + } + } + i += numaux + numaux = 0 + switch f.TargetMachine { + case U802TOCMAGIC: + aux := new(AuxCSect32) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7) + sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas) + sym.AuxCSect.Length = int64(aux.Xscnlen) + case U64_TOCMAGIC: + aux := new(AuxCSect64) + if err := binary.Read(sr, binary.BigEndian, aux); err != nil { + return nil, err + } + sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7) + sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas) + sym.AuxCSect.Length = int64(aux.Xscnlenhi)<<32 | int64(aux.Xscnlenlo) + } + f.Symbols = append(f.Symbols, sym) + skip: + i += numaux // Skip auxiliary entries + if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil { + return nil, err + } + } + + // Read relocations + // Only for .data or .text section + for sectNum, sect := range f.Sections { + if sect.Type != STYP_TEXT && sect.Type != STYP_DATA { + continue + } + if sect.Relptr == 0 { + continue + } + c := saferio.SliceCap[Reloc](uint64(sect.Nreloc)) + if c < 0 { + return nil, fmt.Errorf("too many relocs (%d) for section %d", sect.Nreloc, sectNum) + } + sect.Relocs = make([]Reloc, 0, c) + if _, err := sr.Seek(int64(sect.Relptr), io.SeekStart); err != nil { + return nil, err + } + for i := uint32(0); i < sect.Nreloc; i++ { + var reloc Reloc + switch f.TargetMachine { + case U802TOCMAGIC: + rel := new(Reloc32) + if err := binary.Read(sr, binary.BigEndian, rel); err != nil { + return nil, err + } + reloc.VirtualAddress = uint64(rel.Rvaddr) + reloc.Symbol = idxToSym[int(rel.Rsymndx)] + reloc.Type = rel.Rtype + reloc.Length = rel.Rsize&0x3F + 1 + + if rel.Rsize&0x80 != 0 { + reloc.Signed = true + } + if rel.Rsize&0x40 != 0 { + reloc.InstructionFixed = true + } + + case U64_TOCMAGIC: + rel := new(Reloc64) + if err := binary.Read(sr, binary.BigEndian, rel); err != nil { + return nil, err + } + reloc.VirtualAddress = rel.Rvaddr + reloc.Symbol = idxToSym[int(rel.Rsymndx)] + reloc.Type = rel.Rtype + reloc.Length = rel.Rsize&0x3F + 1 + if rel.Rsize&0x80 != 0 { + reloc.Signed = true + } + if rel.Rsize&0x40 != 0 { + reloc.InstructionFixed = true + } + } + + sect.Relocs = append(sect.Relocs, reloc) + } + } + + return f, nil +} + +type nobitsSectionReader struct{} + +func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) { + return 0, errors.New("unexpected read from section with uninitialized data") +} + +// Data reads and returns the contents of the XCOFF section s. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[:n], err +} + +// CSect reads and returns the contents of a csect. +func (f *File) CSect(name string) []byte { + for _, sym := range f.Symbols { + if sym.Name == name && sym.AuxCSect.SymbolType == XTY_SD { + if i := sym.SectionNumber - 1; 0 <= i && i < len(f.Sections) { + s := f.Sections[i] + if sym.Value+uint64(sym.AuxCSect.Length) <= s.Size { + dat := make([]byte, sym.AuxCSect.Length) + _, err := s.sr.ReadAt(dat, int64(sym.Value)) + if err != nil { + return nil + } + return dat + } + } + break + } + } + return nil +} + +func (f *File) DWARF() (*dwarf.Data, error) { + // There are many other DWARF sections, but these + // are the ones the debug/dwarf package uses. + // Don't bother loading others. + var subtypes = [...]uint32{SSUBTYP_DWABREV, SSUBTYP_DWINFO, SSUBTYP_DWLINE, SSUBTYP_DWRNGES, SSUBTYP_DWSTR} + var dat [len(subtypes)][]byte + for i, subtype := range subtypes { + s := f.SectionByType(STYP_DWARF | subtype) + if s != nil { + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + dat[i] = b + } + } + + abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] + return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) +} + +// readImportID returns the import file IDs stored inside the .loader section. +// Library name pattern is either path/base/member or base/member +func (f *File) readImportIDs(s *Section) ([]string, error) { + // Read loader header + if _, err := s.sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + var istlen uint32 + var nimpid uint32 + var impoff uint64 + switch f.TargetMachine { + case U802TOCMAGIC: + lhdr := new(LoaderHeader32) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + istlen = lhdr.Listlen + nimpid = lhdr.Lnimpid + impoff = uint64(lhdr.Limpoff) + case U64_TOCMAGIC: + lhdr := new(LoaderHeader64) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + istlen = lhdr.Listlen + nimpid = lhdr.Lnimpid + impoff = lhdr.Limpoff + } + + // Read loader import file ID table + if _, err := s.sr.Seek(int64(impoff), io.SeekStart); err != nil { + return nil, err + } + table := make([]byte, istlen) + if _, err := io.ReadFull(s.sr, table); err != nil { + return nil, err + } + + offset := 0 + // First import file ID is the default LIBPATH value + libpath := cstring(table[offset:]) + f.LibraryPaths = strings.Split(libpath, ":") + offset += len(libpath) + 3 // 3 null bytes + all := make([]string, 0) + for i := 1; i < int(nimpid); i++ { + impidpath := cstring(table[offset:]) + offset += len(impidpath) + 1 + impidbase := cstring(table[offset:]) + offset += len(impidbase) + 1 + impidmem := cstring(table[offset:]) + offset += len(impidmem) + 1 + var path string + if len(impidpath) > 0 { + path = impidpath + "/" + impidbase + "/" + impidmem + } else { + path = impidbase + "/" + impidmem + } + all = append(all, path) + } + + return all, nil +} + +// ImportedSymbols returns the names of all symbols +// referred to by the binary f that are expected to be +// satisfied by other libraries at dynamic load time. +// It does not return weak symbols. +func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { + s := f.SectionByType(STYP_LOADER) + if s == nil { + return nil, nil + } + // Read loader header + if _, err := s.sr.Seek(0, io.SeekStart); err != nil { + return nil, err + } + var stlen uint32 + var stoff uint64 + var nsyms uint32 + var symoff uint64 + switch f.TargetMachine { + case U802TOCMAGIC: + lhdr := new(LoaderHeader32) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + stlen = lhdr.Lstlen + stoff = uint64(lhdr.Lstoff) + nsyms = lhdr.Lnsyms + symoff = LDHDRSZ_32 + case U64_TOCMAGIC: + lhdr := new(LoaderHeader64) + if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { + return nil, err + } + stlen = lhdr.Lstlen + stoff = lhdr.Lstoff + nsyms = lhdr.Lnsyms + symoff = lhdr.Lsymoff + } + + // Read loader section string table + if _, err := s.sr.Seek(int64(stoff), io.SeekStart); err != nil { + return nil, err + } + st := make([]byte, stlen) + if _, err := io.ReadFull(s.sr, st); err != nil { + return nil, err + } + + // Read imported libraries + libs, err := f.readImportIDs(s) + if err != nil { + return nil, err + } + + // Read loader symbol table + if _, err := s.sr.Seek(int64(symoff), io.SeekStart); err != nil { + return nil, err + } + all := make([]ImportedSymbol, 0) + for i := 0; i < int(nsyms); i++ { + var name string + var ifile uint32 + var ok bool + switch f.TargetMachine { + case U802TOCMAGIC: + ldsym := new(LoaderSymbol32) + if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil { + return nil, err + } + if ldsym.Lsmtype&0x40 == 0 { + continue // Imported symbols only + } + zeroes := binary.BigEndian.Uint32(ldsym.Lname[:4]) + if zeroes != 0 { + name = cstring(ldsym.Lname[:]) + } else { + offset := binary.BigEndian.Uint32(ldsym.Lname[4:]) + name, ok = getString(st, offset) + if !ok { + continue + } + } + ifile = ldsym.Lifile + case U64_TOCMAGIC: + ldsym := new(LoaderSymbol64) + if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil { + return nil, err + } + if ldsym.Lsmtype&0x40 == 0 { + continue // Imported symbols only + } + name, ok = getString(st, ldsym.Loffset) + if !ok { + continue + } + ifile = ldsym.Lifile + } + var sym ImportedSymbol + sym.Name = name + if ifile >= 1 && int(ifile) <= len(libs) { + sym.Library = libs[ifile-1] + } + all = append(all, sym) + } + + return all, nil +} + +// ImportedLibraries returns the names of all libraries +// referred to by the binary f that are expected to be +// linked with the binary at dynamic link time. +func (f *File) ImportedLibraries() ([]string, error) { + s := f.SectionByType(STYP_LOADER) + if s == nil { + return nil, nil + } + all, err := f.readImportIDs(s) + return all, err +} diff --git a/internal/go/src/xcoff/xcoff.go b/internal/go/src/xcoff/xcoff.go new file mode 100755 index 0000000..db81542 --- /dev/null +++ b/internal/go/src/xcoff/xcoff.go @@ -0,0 +1,367 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xcoff + +// File Header. +type FileHeader32 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat uint32 // Time and date of file creation + Fsymptr uint32 // Byte offset to symbol table start + Fnsyms uint32 // Number of entries in symbol table + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags +} + +type FileHeader64 struct { + Fmagic uint16 // Target machine + Fnscns uint16 // Number of sections + Ftimedat uint32 // Time and date of file creation + Fsymptr uint64 // Byte offset to symbol table start + Fopthdr uint16 // Number of bytes in optional header + Fflags uint16 // Flags + Fnsyms uint32 // Number of entries in symbol table +} + +const ( + FILHSZ_32 = 20 + FILHSZ_64 = 24 +) +const ( + U802TOCMAGIC = 0737 // AIX 32-bit XCOFF + U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF +) + +// Flags that describe the type of the object file. +const ( + F_RELFLG = 0x0001 + F_EXEC = 0x0002 + F_LNNO = 0x0004 + F_FDPR_PROF = 0x0010 + F_FDPR_OPTI = 0x0020 + F_DSA = 0x0040 + F_VARPG = 0x0100 + F_DYNLOAD = 0x1000 + F_SHROBJ = 0x2000 + F_LOADONLY = 0x4000 +) + +// Section Header. +type SectionHeader32 struct { + Sname [8]byte // Section name + Spaddr uint32 // Physical address + Svaddr uint32 // Virtual address + Ssize uint32 // Section size + Sscnptr uint32 // Offset in file to raw data for section + Srelptr uint32 // Offset in file to relocation entries for section + Slnnoptr uint32 // Offset in file to line number entries for section + Snreloc uint16 // Number of relocation entries + Snlnno uint16 // Number of line number entries + Sflags uint32 // Flags to define the section type +} + +type SectionHeader64 struct { + Sname [8]byte // Section name + Spaddr uint64 // Physical address + Svaddr uint64 // Virtual address + Ssize uint64 // Section size + Sscnptr uint64 // Offset in file to raw data for section + Srelptr uint64 // Offset in file to relocation entries for section + Slnnoptr uint64 // Offset in file to line number entries for section + Snreloc uint32 // Number of relocation entries + Snlnno uint32 // Number of line number entries + Sflags uint32 // Flags to define the section type + Spad uint32 // Needs to be 72 bytes long +} + +// Flags defining the section type. +const ( + STYP_DWARF = 0x0010 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + STYP_EXCEPT = 0x0100 + STYP_INFO = 0x0200 + STYP_TDATA = 0x0400 + STYP_TBSS = 0x0800 + STYP_LOADER = 0x1000 + STYP_DEBUG = 0x2000 + STYP_TYPCHK = 0x4000 + STYP_OVRFLO = 0x8000 +) +const ( + SSUBTYP_DWINFO = 0x10000 // DWARF info section + SSUBTYP_DWLINE = 0x20000 // DWARF line-number section + SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section + SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section + SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section + SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section + SSUBTYP_DWSTR = 0x70000 // DWARF strings section + SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section + SSUBTYP_DWLOC = 0x90000 // DWARF location lists section + SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section + SSUBTYP_DWMAC = 0xB0000 // DWARF macros section +) + +// Symbol Table Entry. +type SymEnt32 struct { + Nname [8]byte // Symbol name + Nvalue uint32 // Symbol value + Nscnum uint16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux uint8 // Number of auxiliary entries +} + +type SymEnt64 struct { + Nvalue uint64 // Symbol value + Noffset uint32 // Offset of the name in string table or .debug section + Nscnum uint16 // Section number of symbol + Ntype uint16 // Basic and derived type specification + Nsclass uint8 // Storage class of symbol + Nnumaux uint8 // Number of auxiliary entries +} + +const SYMESZ = 18 + +const ( + // Nscnum + N_DEBUG = -2 + N_ABS = -1 + N_UNDEF = 0 + + //Ntype + SYM_V_INTERNAL = 0x1000 + SYM_V_HIDDEN = 0x2000 + SYM_V_PROTECTED = 0x3000 + SYM_V_EXPORTED = 0x4000 + SYM_TYPE_FUNC = 0x0020 // is function +) + +// Storage Class. +const ( + C_NULL = 0 // Symbol table entry marked for deletion + C_EXT = 2 // External symbol + C_STAT = 3 // Static symbol + C_BLOCK = 100 // Beginning or end of inner block + C_FCN = 101 // Beginning or end of function + C_FILE = 103 // Source file name and compiler information + C_HIDEXT = 107 // Unnamed external symbol + C_BINCL = 108 // Beginning of include file + C_EINCL = 109 // End of include file + C_WEAKEXT = 111 // Weak external symbol + C_DWARF = 112 // DWARF symbol + C_GSYM = 128 // Global variable + C_LSYM = 129 // Automatic variable allocated on stack + C_PSYM = 130 // Argument to subroutine allocated on stack + C_RSYM = 131 // Register variable + C_RPSYM = 132 // Argument to function or procedure stored in register + C_STSYM = 133 // Statically allocated symbol + C_BCOMM = 135 // Beginning of common block + C_ECOML = 136 // Local member of common block + C_ECOMM = 137 // End of common block + C_DECL = 140 // Declaration of object + C_ENTRY = 141 // Alternate entry + C_FUN = 142 // Function or procedure + C_BSTAT = 143 // Beginning of static block + C_ESTAT = 144 // End of static block + C_GTLS = 145 // Global thread-local variable + C_STTLS = 146 // Static thread-local variable +) + +// File Auxiliary Entry +type AuxFile64 struct { + Xfname [8]byte // Name or offset inside string table + Xftype uint8 // Source file string type + Xauxtype uint8 // Type of auxiliary entry +} + +// Function Auxiliary Entry +type AuxFcn32 struct { + Xexptr uint32 // File offset to exception table entry + Xfsize uint32 // Size of function in bytes + Xlnnoptr uint32 // File pointer to line number + Xendndx uint32 // Symbol table index of next entry + Xpad uint16 // Unused +} +type AuxFcn64 struct { + Xlnnoptr uint64 // File pointer to line number + Xfsize uint32 // Size of function in bytes + Xendndx uint32 // Symbol table index of next entry + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +type AuxSect64 struct { + Xscnlen uint64 // section length + Xnreloc uint64 // Num RLDs + pad uint8 + Xauxtype uint8 // Type of auxiliary entry +} + +// csect Auxiliary Entry. +type AuxCSect32 struct { + Xscnlen uint32 // Length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xstab uint32 // Reserved + Xsnstab uint16 // Reserved +} + +type AuxCSect64 struct { + Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index + Xparmhash uint32 // Offset of parameter type-check string + Xsnhash uint16 // .typchk section number + Xsmtyp uint8 // Symbol alignment and type + Xsmclas uint8 // Storage-mapping class + Xscnlenhi uint32 // Upper 4 bytes of length or symbol table index + Xpad uint8 // Unused + Xauxtype uint8 // Type of auxiliary entry +} + +// Auxiliary type +const ( + _AUX_EXCEPT = 255 + _AUX_FCN = 254 + _AUX_SYM = 253 + _AUX_FILE = 252 + _AUX_CSECT = 251 + _AUX_SECT = 250 +) + +// Symbol type field. +const ( + XTY_ER = 0 // External reference + XTY_SD = 1 // Section definition + XTY_LD = 2 // Label definition + XTY_CM = 3 // Common csect definition +) + +// Defines for File auxiliary definitions: x_ftype field of x_file +const ( + XFT_FN = 0 // Source File Name + XFT_CT = 1 // Compile Time Stamp + XFT_CV = 2 // Compiler Version Number + XFT_CD = 128 // Compiler Defined Information +) + +// Storage-mapping class. +const ( + XMC_PR = 0 // Program code + XMC_RO = 1 // Read-only constant + XMC_DB = 2 // Debug dictionary table + XMC_TC = 3 // TOC entry + XMC_UA = 4 // Unclassified + XMC_RW = 5 // Read/Write data + XMC_GL = 6 // Global linkage + XMC_XO = 7 // Extended operation + XMC_SV = 8 // 32-bit supervisor call descriptor + XMC_BS = 9 // BSS class + XMC_DS = 10 // Function descriptor + XMC_UC = 11 // Unnamed FORTRAN common + XMC_TC0 = 15 // TOC anchor + XMC_TD = 16 // Scalar data entry in the TOC + XMC_SV64 = 17 // 64-bit supervisor call descriptor + XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit + XMC_TL = 20 // Read/Write thread-local data + XMC_UL = 21 // Read/Write thread-local data (.tbss) + XMC_TE = 22 // TOC entry +) + +// Loader Header. +type LoaderHeader32 struct { + Lversion uint32 // Loader section version number + Lnsyms uint32 // Number of symbol table entries + Lnreloc uint32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid uint32 // Number of import file IDs + Limpoff uint32 // Offset to start of import file IDs + Lstlen uint32 // Length of string table + Lstoff uint32 // Offset to start of string table +} + +type LoaderHeader64 struct { + Lversion uint32 // Loader section version number + Lnsyms uint32 // Number of symbol table entries + Lnreloc uint32 // Number of relocation table entries + Listlen uint32 // Length of import file ID string table + Lnimpid uint32 // Number of import file IDs + Lstlen uint32 // Length of string table + Limpoff uint64 // Offset to start of import file IDs + Lstoff uint64 // Offset to start of string table + Lsymoff uint64 // Offset to start of symbol table + Lrldoff uint64 // Offset to start of relocation entries +} + +const ( + LDHDRSZ_32 = 32 + LDHDRSZ_64 = 56 +) + +// Loader Symbol. +type LoaderSymbol32 struct { + Lname [8]byte // Symbol name or byte offset into string table + Lvalue uint32 // Address field + Lscnum uint16 // Section number containing symbol + Lsmtype uint8 // Symbol type, export, import flags + Lsmclas uint8 // Symbol storage class + Lifile uint32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type LoaderSymbol64 struct { + Lvalue uint64 // Address field + Loffset uint32 // Byte offset into string table of symbol name + Lscnum uint16 // Section number containing symbol + Lsmtype uint8 // Symbol type, export, import flags + Lsmclas uint8 // Symbol storage class + Lifile uint32 // Import file ID; ordinal of import file IDs + Lparm uint32 // Parameter type-check field +} + +type Reloc32 struct { + Rvaddr uint32 // (virtual) address of reference + Rsymndx uint32 // Index into symbol table + Rsize uint8 // Sign and reloc bit len + Rtype uint8 // Toc relocation type +} + +type Reloc64 struct { + Rvaddr uint64 // (virtual) address of reference + Rsymndx uint32 // Index into symbol table + Rsize uint8 // Sign and reloc bit len + Rtype uint8 // Toc relocation type +} + +const ( + R_POS = 0x00 // A(sym) Positive Relocation + R_NEG = 0x01 // -A(sym) Negative Relocation + R_REL = 0x02 // A(sym-*) Relative to self + R_TOC = 0x03 // A(sym-TOC) Relative to TOC + R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load. + + R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst + R_GL = 0x05 // A(external TOC of sym) Global Linkage + R_TCL = 0x06 // A(local TOC of sym) Local object TOC address + R_RL = 0x0C // A(sym) Pos indirect load. modifiable instruction + R_RLA = 0x0D // A(sym) Pos Load Address. modifiable instruction + R_REF = 0x0F // AL0(sym) Non relocating ref. No garbage collect + R_BA = 0x08 // A(sym) Branch absolute. Cannot modify instruction + R_RBA = 0x18 // A(sym) Branch absolute. modifiable instruction + R_BR = 0x0A // A(sym-*) Branch rel to self. non modifiable + R_RBR = 0x1A // A(sym-*) Branch rel to self. modifiable instr + + R_TLS = 0x20 // General-dynamic reference to TLS symbol + R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol + R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol + R_TLS_LE = 0x23 // Local-exec reference to TLS symbol + R_TLSM = 0x24 // Module reference to TLS symbol + R_TLSML = 0x25 // Module reference to local (own) module + + R_TOCU = 0x30 // Relative to TOC - high order bits + R_TOCL = 0x31 // Relative to TOC - low order bits +) diff --git a/internal/go/update.go b/internal/go/update.go new file mode 100644 index 0000000..c11fc5c --- /dev/null +++ b/internal/go/update.go @@ -0,0 +1,78 @@ +package main + +//go:generate go run update.go + +import ( + "bytes" + "io/fs" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func main() { + must0(os.RemoveAll("src")) + must0(os.Mkdir("src", 0755)) + + goroot := strings.TrimSpace(must(run("go", "env", "GOROOT"))) + for _, subdir := range []string{"archive", "bio", "goobj", "objabi", "objfile", "src", "sys", "disasm"} { + dir := filepath.Join(goroot, "src/cmd/internal", subdir) + copydir(dir, filepath.Join("src", subdir)) + } + for _, subdir := range []string{"abi", "goarch", "bisect", "buildcfg", "goexperiment", "saferio", "unsafeheader", "xcoff"} { + dir := filepath.Join(goroot, "src/internal", subdir) + copydir(dir, filepath.Join("src", subdir)) + } + + must0(os.WriteFile("src/disasm/expose.go", must(os.ReadFile("expose.go_")), 0644)) + must0(os.Remove("src/abi/abi_test.s")) +} + +func copydir(srcdir, dstdir string) { + must0(os.MkdirAll(dstdir, 0755)) + must0(filepath.WalkDir(srcdir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + base := must(filepath.Rel(srcdir, path)) + + if d.IsDir() { + if d.Name() == "testdata" { + return fs.SkipDir + } + return os.MkdirAll(filepath.Join(dstdir, base), 0755) + } + if strings.HasSuffix(d.Name(), "_test.go") { + return nil + } + + dstfile := filepath.Join(dstdir, base) + data := must(os.ReadFile(path)) + + data = bytes.ReplaceAll(data, []byte(` "cmd/internal/`), []byte(` "loov.dev/lensm/internal/go/src/`)) + data = bytes.ReplaceAll(data, []byte(`import "cmd/internal/`), []byte(`import "loov.dev/lensm/internal/go/src/`)) + data = bytes.ReplaceAll(data, []byte(` "internal/`), []byte(` "loov.dev/lensm/internal/go/src/`)) + data = bytes.ReplaceAll(data, []byte(`import "internal/`), []byte(`import "loov.dev/lensm/internal/go/src/`)) + + return os.WriteFile(dstfile, data, 0755) + })) +} + +func run(cmd string, args ...string) (string, error) { + out, err := exec.Command(cmd, args...).Output() + return string(out), err +} + +func must0(err error) { + if err != nil { + panic(err) + } +} + +func must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} diff --git a/internal/goobj/code.go b/internal/goobj/code.go new file mode 100644 index 0000000..b491a10 --- /dev/null +++ b/internal/goobj/code.go @@ -0,0 +1,254 @@ +package goobj + +import ( + "fmt" + "os" + "regexp" + "sort" + "strconv" + "strings" + + "loov.dev/lensm/internal/disasm" + godisasm "loov.dev/lensm/internal/go/src/disasm" +) + +var rxRefAbs = regexp.MustCompile(`\s0x[\da-fA-F]+$`) +var rxRefRel = regexp.MustCompile(`\s-?\d+\(PC\)$`) +var rxCallOrJump = regexp.MustCompile(`^(?:CALL|JMP)\s+(.+?)\(SB\)`) + +// Disassemble disassembles the specified symbol. +func Disassemble(dis *godisasm.Disasm, sym *Func, opts disasm.Options) (*disasm.Code, error) { + neededLines := make(map[string]*disasm.LineSet) + + file, _, _ := dis.PCLN().PCToLine(sym.sym.Addr) + needRefPCs := map[uint64]struct{}{} + + code := &disasm.Code{ + Name: sym.Name(), + File: file, + Arch: dis.GOARCH(), + } + var instructions []disasm.Inst + dis.DecodeSyntax(sym.sym.Addr, sym.sym.Addr+uint64(sym.sym.Size), sym.sym.Relocs, + func(pc, size uint64, file string, line int, text, nativeText, mnemonic string) { + // TODO: find a better way to calculate the jump target + var refPC uint64 + var call string + if match := rxRefAbs.FindString(text); match != "" { + if target, err := strconv.ParseInt(match[3:], 16, 64); err == nil { + refPC = uint64(target) + } + } else if match := rxRefRel.FindString(text); match != "" { + // TODO: this calculation seems incorrect + if target, err := strconv.ParseInt(match[1:len(match)-4], 10, 64); err == nil { + refPC = uint64(int64(pc) + target*4) + } else { + panic(err) + } + } else if match := rxCallOrJump.FindStringSubmatch(text); len(match) > 0 { + call = match[1] + } + + if refPC != 0 { + needRefPCs[refPC] = struct{}{} + } + instructions = append(instructions, disasm.Inst{ + PC: pc, + Text: text, + NativeText: nativeText, + Mnemonic: mnemonic, + File: file, + Line: line, + Call: call, + RefPC: refPC, + }) + + if file != "" && file != "" { + lineset, ok := neededLines[file] + if !ok { + lineset = &disasm.LineSet{} + neededLines[file] = lineset + } + lineset.Add(line) + } + }) + + pcToIndex := map[uint64]int{} + for _, ix := range instructions { + if _, ok := needRefPCs[ix.PC]; ok { + // add empty line + code.Insts = append(code.Insts, disasm.Inst{}) + } + pcToIndex[ix.PC] = len(code.Insts) + code.Insts = append(code.Insts, ix) + } + + type jumpInterval struct { + index int + ix *disasm.Inst + min, max uint64 + } + + var jumps []jumpInterval + for i := range code.Insts { + ix := &code.Insts[i] + if ix.RefPC != 0 { + target, ok := pcToIndex[ix.RefPC] + if !ok { + continue + } + ix.RefOffset = target - i + + if ix.PC <= ix.RefPC { + jumps = append(jumps, jumpInterval{ + index: i, + ix: ix, + min: ix.PC, + max: ix.RefPC, + }) + } else { + jumps = append(jumps, jumpInterval{ + index: i, + ix: ix, + min: ix.RefPC, + max: ix.PC, + }) + } + } + } + + sort.Slice(jumps, func(i, k int) bool { + if jumps[i].min == jumps[k].min { + return jumps[i].max > jumps[k].max + } + return jumps[i].min < jumps[k].min + }) + + var stackLayers []uint64 + insertToStack := func(ix *disasm.Inst, max uint64) { + found := false + for k, pc := range stackLayers { + if pc == 0 { + stackLayers[k] = max + ix.RefStack = k + found = true + break + } + } + if !found { + code.MaxJump = len(stackLayers) + ix.RefStack = len(stackLayers) + stackLayers = append(stackLayers, max) + } + } + + for _, jump := range jumps { + for i, pc := range stackLayers { + if pc <= jump.min { + stackLayers[i] = 0 + } + } + insertToStack(jump.ix, jump.max) + } + for i := range code.Insts { + ix := &code.Insts[i] + ix.RefStack = code.MaxJump - ix.RefStack + 1 + } + code.MaxJump++ + + // remove trailing interrupts from funcs + for len(code.Insts) > 0 && + (strings.HasPrefix(code.Insts[len(code.Insts)-1].Text, "INT ") || + code.Insts[len(code.Insts)-1].Text == "?") { + code.Insts = code.Insts[:len(code.Insts)-1] + } + + // load sources + code.Source = LoadSources(neededLines, code.File, opts.Context) + + // create a mapping from source code to disassembly + type fileLine struct { + file string + line int + } + + lineRefs := map[fileLine]*disasm.LineSet{} + for i, ix := range code.Insts { + k := fileLine{file: ix.File, line: ix.Line} + n, ok := lineRefs[k] + if !ok { + n = &disasm.LineSet{} + lineRefs[k] = n + } + n.Add(i) + } + for i := range code.Source { + src := &code.Source[i] + for k := range src.Blocks { + block := &src.Blocks[k] + block.Related = make([][]disasm.LineRange, len(block.Lines)) + for line := block.From; line <= block.To; line++ { // todo check: line <= block.To + if refs, ok := lineRefs[fileLine{file: src.File, line: line}]; ok { + block.Related[line-block.From] = refs.RangesZero() + } + } + } + } + + return code, nil +} + +var rxEnvVariable = regexp.MustCompile(`\$[a-zA-Z_]+[a-zA-Z0-9_]+\b`) + +func replaceEnvironmentVariables(s string) string { + return rxEnvVariable.ReplaceAllStringFunc(s, func(env string) string { + replacement := os.Getenv(env[1:]) + if replacement != "" { + return replacement + } + return env + }) +} + +// LoadSources loads the specified line sets. +func LoadSources(needed map[string]*disasm.LineSet, symbolFile string, context int) []disasm.Source { + var sources []disasm.Source + for file, set := range needed { + data, err := os.ReadFile(replaceEnvironmentVariables(file)) + if err != nil { + // TODO: should we create a stub source block instead? + fmt.Fprintf(os.Stderr, "unable to load source from %q: %v\n", file, err) + continue + } + lines := strings.Split(string(data), "\n") + source := disasm.Source{ + File: file, + } + for _, r := range set.Ranges(context) { + to := min(r.To-1, len(lines)) + lineBlock := lines[r.From-1 : to] + for i, v := range lineBlock { + lineBlock[i] = strings.Replace(v, "\t", " ", -1) + } + + source.Blocks = append(source.Blocks, disasm.SourceBlock{ + LineRange: r, + Lines: lineBlock, + }) + } + sources = append(sources, source) + } + + // Sort the sources and prioritize the file where the main symbol is located. + sort.Slice(sources, func(i, j int) bool { + if sources[i].File == symbolFile { + return true + } + if sources[j].File == symbolFile { + return false + } + return sources[i].File < sources[j].File + }) + + return sources +} diff --git a/internal/goobj/file.go b/internal/goobj/file.go new file mode 100644 index 0000000..d93cb58 --- /dev/null +++ b/internal/goobj/file.go @@ -0,0 +1,119 @@ +package goobj + +import ( + "regexp" + "sort" + "strings" + "sync" + + "loov.dev/lensm/internal/disasm" + godisasm "loov.dev/lensm/internal/go/src/disasm" + "loov.dev/lensm/internal/go/src/objfile" +) + +var _ disasm.File = (*File)(nil) +var _ disasm.Func = (*Func)(nil) + +// File contains information about the object file. +type File struct { + objfile *objfile.File + disasm *godisasm.Disasm + funcs []disasm.Func + + // mu guards cache and serializes Disassemble calls: disassembly + // lazily populates line-table caches inside disasm, which is not + // safe for concurrent use. + mu sync.Mutex + cache map[cacheKey]cacheEntry +} + +// cacheKey includes the options: MCP callers choose the source context +// per call, and a cache keyed by function alone would silently serve +// whichever context happened to load first. +type cacheKey struct { + fn *Func + context int +} + +// cacheEntry also caches failures, so an erroring function isn't +// re-disassembled on every frame. +type cacheEntry struct { + code *disasm.Code + err error +} + +func (file *File) Funcs() []disasm.Func { return file.funcs } + +// Function contains information about the executable. +type Func struct { + obj *File + sym objfile.Sym + + sortName string +} + +func (fn *Func) Name() string { return fn.sym.Name } + +func (file *File) Close() error { + return file.objfile.Close() +} + +func Load(path string) (*File, error) { + f, err := objfile.Open(path) + if err != nil { + return nil, err + } + + dis, err := godisasm.DisasmForFile(f) + if err != nil { + _ = f.Close() + return nil, err + } + + file := &File{ + objfile: f, + disasm: dis, + cache: make(map[cacheKey]cacheEntry), + } + + for _, sym := range dis.Syms() { + if sym.Code != 'T' && sym.Code != 't' || sym.Addr < dis.TextStart() { + continue + } + sym := &Func{ + obj: file, + sym: sym, + sortName: sortingName(sym.Name), + } + file.funcs = append(file.funcs, sym) + } + + sort.SliceStable(file.funcs, func(i, k int) bool { + return sortingName(file.funcs[i].Name()) < sortingName(file.funcs[k].Name()) + }) + + return file, nil +} + +func (fn *Func) Load(opts disasm.Options) (*disasm.Code, error) { + return fn.obj.LoadCode(fn, opts) +} + +func (file *File) LoadCode(fn *Func, opts disasm.Options) (*disasm.Code, error) { + file.mu.Lock() + defer file.mu.Unlock() + key := cacheKey{fn: fn, context: opts.Context} + entry, ok := file.cache[key] + if !ok { + entry.code, entry.err = Disassemble(fn.obj.disasm, fn, opts) + file.cache[key] = entry + } + return entry.code, entry.err +} + +var rxCodeDelimiter = regexp.MustCompile(`[ *().]+`) + +func sortingName(sym string) string { + sym = strings.ToLower(sym) + return rxCodeDelimiter.ReplaceAllString(sym, " ") +} diff --git a/internal/goobj/file_test.go b/internal/goobj/file_test.go new file mode 100644 index 0000000..d06eb27 --- /dev/null +++ b/internal/goobj/file_test.go @@ -0,0 +1,55 @@ +package goobj + +import ( + "os" + "os/exec" + "path/filepath" + "sync" + "testing" + + "loov.dev/lensm/internal/disasm" +) + +func TestLoadCode_ConcurrentCallsShareCache(t *testing.T) { + if testing.Short() { + t.Skip("builds a test binary") + } + + dir := t.TempDir() + src := filepath.Join(dir, "main.go") + err := os.WriteFile(src, []byte(`package main + +func main() { println(add(1, 2)) } + +//go:noinline +func add(a, b int) int { return a + b } +`), 0o644) + if err != nil { + t.Fatal(err) + } + bin := filepath.Join(dir, "example.exe") + if out, err := exec.Command("go", "build", "-o", bin, src).CombinedOutput(); err != nil { + t.Fatalf("go build: %v\n%s", err, out) + } + + file, err := Load(bin) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = file.Close() }) + + funcs := file.Funcs() + if len(funcs) > 32 { + funcs = funcs[:32] + } + + var wg sync.WaitGroup + for range 8 { + wg.Go(func() { + for _, fn := range funcs { + _, _ = fn.Load(disasm.Options{Context: 1}) + } + }) + } + wg.Wait() +} diff --git a/internal/gui/filterlist.go b/internal/gui/filterlist.go new file mode 100644 index 0000000..6e78399 --- /dev/null +++ b/internal/gui/filterlist.go @@ -0,0 +1,144 @@ +package gui + +import ( + "fmt" + "regexp" + + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" +) + +type FilterListItem interface { + Name() string +} + +// FilterList lists symbols for filtering and selection. +type FilterList[T FilterListItem] struct { + All []T + Filter widget.Editor + FilterError string + Filtered []T + + Selected string + SelectedItem T + + List SelectList +} + +// NewFilterList creates a new list with the specified theme. +func NewFilterList[T FilterListItem](theme *Theme) *FilterList[T] { + ui := &FilterList[T]{} + ui.Filter.SingleLine = true + ui.List = NewVerticalSelectList(unit.Dp(theme.TextSize) + 4) + return ui +} + +// SelectIndex selects the specified item. +func (ui *FilterList[T]) SelectIndex(index int) { + if !InRange(index, len(ui.Filtered)) { + return + } + + ui.List.Selected = index + ui.Selected = ui.Filtered[index].Name() + ui.SelectedItem = ui.Filtered[index] +} + +// SetItems updates the full list. +func (ui *FilterList[T]) SetItems(all []T) { + ui.All = all + ui.updateFiltered() + if ui.List.Selected == -1 && len(ui.Filtered) > 0 { + ui.SelectIndex(0) + } +} + +// SetFilter sets the filter. +func (ui *FilterList[T]) SetFilter(filter string) { + ui.Filter.SetText(filter) + ui.updateFiltered() +} + +// updateFiltered updates the filtered list from the unfiltered content. +func (ui *FilterList[T]) updateFiltered() { + defer func() { + ui.List.Selected = -1 + var zero T + ui.SelectedItem = zero + for i, item := range ui.Filtered { + if item.Name() == ui.Selected { + ui.List.Selected = i + ui.SelectedItem = item + // TODO, maybe scroll into view? + break + } + } + }() + + rx, err := regexp.Compile("(?i)" + ui.Filter.Text()) + ui.FilterError = "" + if err != nil { + ui.FilterError = err.Error() + return + } + + ui.Filtered = ui.Filtered[:0] + for _, item := range ui.All { + if rx.MatchString(item.Name()) { + ui.Filtered = append(ui.Filtered, item) + } + } +} + +// Layout draws the list. +func (ui *FilterList[T]) Layout(th *Theme, gtx layout.Context) layout.Dimensions { + paint.FillShape(gtx.Ops, th.Colors.SecondaryBackground, clip.Rect{Max: gtx.Constraints.Min}.Op()) + + ui.SelectIndex(ui.List.Selected) + + changed := false + for { + ev, ok := ui.Filter.Update(gtx) + if !ok { + break + } + if _, ok := ev.(widget.ChangeEvent); ok { + changed = true + } + } + + if changed { + ui.updateFiltered() + gtx.Execute(op.InvalidateCmd{}) + } + + return layout.Flex{ + Axis: layout.Vertical, + }.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return FocusBorder(th.Theme, gtx.Focused(&ui.Filter)).Layout(gtx, + material.Editor(th.Theme, &ui.Filter, "Filter (regexp)").Layout) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if ui.FilterError == "" { + return layout.Dimensions{} + } + return th.ErrorLabel(ui.FilterError, 1).Layout(gtx) + }), + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + return ui.List.Layout(th.Theme, gtx, len(ui.Filtered), + StringListItem(th.Theme, &ui.List, func(index int) string { + return ui.Filtered[index].Name() + })) + }), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + body := th.Label(fmt.Sprintf("%d / %d", len(ui.Filtered), len(ui.All)), 0.8) + return layout.Center.Layout(gtx, body.Layout) + }), + ) +} diff --git a/internal/gui/focus.go b/internal/gui/focus.go new file mode 100644 index 0000000..457ec26 --- /dev/null +++ b/internal/gui/focus.go @@ -0,0 +1,41 @@ +package gui + +import ( + "image/color" + + "gioui.org/layout" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" +) + +// FocusBorderStyle implements a styling a focused widget. +type FocusBorderStyle struct { + Focused bool + BorderWidth unit.Dp + Color color.NRGBA +} + +// FocusBorder creates a focus border for a focused widget. +func FocusBorder(th *material.Theme, focused bool) FocusBorderStyle { + return FocusBorderStyle{ + Focused: focused, + BorderWidth: unit.Dp(2), + Color: th.ContrastBg, + } +} + +// Layout adds a focus border and styling. +func (focus FocusBorderStyle) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { + inset := layout.UniformInset(focus.BorderWidth) + if !focus.Focused { + return inset.Layout(gtx, w) + } + + return widget.Border{ + Color: focus.Color, + Width: focus.BorderWidth, + }.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return inset.Layout(gtx, w) + }) +} diff --git a/internal/gui/geom.go b/internal/gui/geom.go new file mode 100644 index 0000000..c230087 --- /dev/null +++ b/internal/gui/geom.go @@ -0,0 +1,22 @@ +package gui + +type Bounds struct{ Min, Max float32 } + +func BoundsWidth(min, width int) Bounds { + return Bounds{Min: float32(min), Max: float32(min + width)} +} + +func (b Bounds) Width() float32 { return b.Max - b.Min } + +func (b Bounds) Lerp(p float32) float32 { + return b.Min + p*(b.Max-b.Min) +} + +func (b Bounds) Contains(v float32) bool { + return b.Min <= v && v <= b.Max +} + +// InRange checks whether v is in bounds for length. +func InRange(v int, length int) bool { + return 0 <= v && v < length +} diff --git a/internal/gui/scroll.go b/internal/gui/scroll.go new file mode 100644 index 0000000..4d55a3c --- /dev/null +++ b/internal/gui/scroll.go @@ -0,0 +1,68 @@ +package gui + +import ( + "image" + + "gioui.org/gesture" + "gioui.org/io/pointer" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/widget" + "gioui.org/widget/material" +) + +// ScrollRegion is the vertical scroll state for a manually drawn +// column: wheel/gesture scrolling, a scrollbar, and animated jumps. +// Offset is the pixel offset of the content top relative to the +// viewport top, so it goes negative as the content scrolls up. +type ScrollRegion struct { + Offset float32 + Gesture gesture.Scroll + Bar widget.Scrollbar + Anim ScrollAnimation +} + +// LayoutBar draws a vertical scrollbar at barX with the given width, +// applies scrollbar drags and scroll gestures, and clamps the offset +// so the viewport stays within [contentTop, contentBot]. +func (region *ScrollRegion) LayoutBar(gtx layout.Context, th *material.Theme, barX, barWidth int, contentTop, contentBot float32) { + viewTop := -region.Offset + viewBot := -region.Offset + float32(gtx.Constraints.Max.Y) + + stack := op.Offset(image.Pt(barX, 0)).Push(gtx.Ops) + bgtx := gtx + bgtx.Constraints = layout.Exact(image.Pt(barWidth, gtx.Constraints.Max.Y)) + material.Scrollbar(th, ®ion.Bar).Layout(bgtx, layout.Vertical, + (viewTop-contentTop)/(contentBot-contentTop), + (viewBot-contentTop)/(contentBot-contentTop), + ) + stack.Pop() + + if distance := region.Bar.ScrollDistance(); distance != 0 { + region.Offset -= distance * (contentBot - contentTop) + } + if distance := region.Gesture.Update(gtx.Metric, gtx.Source, gtx.Now, gesture.Vertical, + pointer.ScrollRange{}, + pointer.ScrollRange{Min: -1000, Max: 1000}, + ); distance != 0 { + region.Offset -= float32(distance) + } + region.Clamp(gtx, contentTop, contentBot) +} + +// Clamp keeps the viewport within the content, stopping any running +// jump animation when it hits an edge. +func (region *ScrollRegion) Clamp(gtx layout.Context, contentTop, contentBot float32) { + if -region.Offset < contentTop { + region.Offset = -contentTop + region.Anim.Stop() + } + if -region.Offset+float32(gtx.Constraints.Max.Y) > contentBot { + if contentBot < float32(gtx.Constraints.Max.Y) { + region.Offset = -contentTop + } else { + region.Offset = float32(gtx.Constraints.Max.Y) - contentBot + } + region.Anim.Stop() + } +} diff --git a/internal/gui/selectlist.go b/internal/gui/selectlist.go new file mode 100644 index 0000000..db882cd --- /dev/null +++ b/internal/gui/selectlist.go @@ -0,0 +1,208 @@ +package gui + +import ( + "image" + "image/color" + + "gioui.org/f32" + "gioui.org/font" + "gioui.org/io/event" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" +) + +// NewVerticalSelectList creates a new select list with the specified item height. +func NewVerticalSelectList(itemHeight unit.Dp) SelectList { + return SelectList{ + List: widget.List{ + List: layout.List{ + Axis: layout.Vertical, + }, + }, + ItemHeight: itemHeight, + } +} + +// SelectList draws a list where items can be selected. +type SelectList struct { + widget.List + + Selected int + Hovered int + + ItemHeight unit.Dp +} + +// Layout draws the list. +func (list *SelectList) Layout(th *material.Theme, gtx layout.Context, length int, element layout.ListElement) layout.Dimensions { + return FocusBorder(th, gtx.Focused(list)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { + size := gtx.Constraints.Max + gtx.Constraints = layout.Exact(size) + defer clip.Rect{Max: size}.Push(gtx.Ops).Pop() + + event.Op(gtx.Ops, list) + + changed := false + grabbed := false + + itemHeight := gtx.Metric.Dp(list.ItemHeight) + if itemHeight == 0 { + itemHeight = gtx.Metric.Sp(th.TextSize) + } + + pointerClicked := false + pointerHovered := false + pointerPosition := f32.Point{} + for { + // TODO: fix navigation when in filter. + ev, ok := gtx.Event( + key.FocusFilter{Target: list}, + key.Filter{Focus: list, Name: key.NameUpArrow}, + key.Filter{Focus: list, Name: key.NameDownArrow}, + key.Filter{Focus: list, Name: key.NameHome}, + key.Filter{Focus: list, Name: key.NameEnd}, + key.Filter{Focus: list, Name: key.NamePageUp}, + key.Filter{Focus: list, Name: key.NamePageDown}, + pointer.Filter{ + Target: list, + Kinds: pointer.Press | pointer.Move, + }, + ) + if !ok { + break + } + + switch ev := ev.(type) { + case key.Event: + if ev.State == key.Press { + offset := 0 + switch ev.Name { + case key.NameHome: + offset = -length + case key.NameEnd: + offset = length + case key.NameUpArrow: + offset = -1 + case key.NameDownArrow: + offset = 1 + case key.NamePageUp: + offset = -list.List.Position.Count + case key.NamePageDown: + offset = list.List.Position.Count + } + + if offset != 0 { + target := max(list.Selected+offset, 0) + if target >= length { + target = length - 1 + } + if list.Selected != target { + list.Selected = target + changed = true + } + } + + // if we get input and don't have a focus, then grab it + if !gtx.Focused(list) { + gtx.Execute(op.InvalidateCmd{}) + } + } + + case pointer.Event: + switch ev.Kind { + case pointer.Press: + if !gtx.Focused(list) && !grabbed { + grabbed = true + gtx.Execute(key.FocusCmd{Tag: list}) + } + pointerClicked = true + pointerPosition = ev.Position + case pointer.Move: + pointerHovered = true + pointerPosition = ev.Position + case pointer.Cancel: + list.Hovered = -1 + } + } + } + + if pointerClicked || pointerHovered { + // TODO: make this independent of fixed item height + clientClickY := list.Position.First*itemHeight + list.Position.Offset + int(pointerPosition.Y) + target := clientClickY / itemHeight + if 0 <= target && target <= length { + if pointerClicked && list.Selected != target { + list.Selected = target + } + if pointerHovered && list.Hovered != target { + list.Hovered = target + } + } + } + + if changed { + pos := &list.List.Position + switch { + case list.Selected < pos.First+1: + list.List.Position = layout.Position{First: list.Selected - 1} + case pos.First+pos.Count-1 <= list.Selected: + list.List.Position = layout.Position{First: list.Selected - pos.Count + 2} + } + } + + style := material.List(th, &list.List) + style.AnchorStrategy = material.Overlay + return style.Layout(gtx, length, + func(gtx layout.Context, index int) layout.Dimensions { + gtx.Constraints = layout.Exact(image.Point{ + X: gtx.Constraints.Max.X, + Y: itemHeight, + }) + return element(gtx, index) + }) + }) +} + +// StringListItem creates a string item drawer that reacts to hover and selection. +func StringListItem(th *material.Theme, state *SelectList, item func(int) string) layout.ListElement { + return func(gtx layout.Context, index int) layout.Dimensions { + defer clip.Rect{Max: gtx.Constraints.Max}.Push(gtx.Ops).Pop() + + bg := color.NRGBA{} + fg := th.Fg + weight := font.Normal + + switch { + case state.Selected == index: + if gtx.Focused(state) { + bg = th.ContrastBg + fg = th.ContrastFg + } + weight = font.Black + case state.Hovered == index: + bg = th.ContrastBg + bg.A /= 4 + } + + if bg != (color.NRGBA{}) { + paint.Fill(gtx.Ops, bg) + } + inset := layout.Inset{Top: 1, Right: 4, Bottom: 1, Left: 4} + return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + label := material.Body1(th, item(index)) + label.Color = fg + label.MaxLines = 1 + label.TextSize = th.TextSize * 8 / 10 + label.Font.Weight = weight + gtx.Constraints.Max.X = MaxLineWidth + return label.Layout(gtx) + }) + } +} diff --git a/internal/gui/theme.go b/internal/gui/theme.go new file mode 100644 index 0000000..293e3ef --- /dev/null +++ b/internal/gui/theme.go @@ -0,0 +1,109 @@ +package gui + +import ( + "image/color" + + "gioui.org/unit" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/syntax" +) + +// Theme bundles the material theme with the app palette so layout +// code takes one argument. Colors is recomputed by SetDark, not per +// frame. +type Theme struct { + *material.Theme + Colors UIColors +} + +func NewTheme(base *material.Theme, dark bool) *Theme { + th := &Theme{Theme: base} + th.SetDark(dark) + return th +} + +// SetDark switches the material palette and app colors between the +// dark and light schemes. +func (th *Theme) SetDark(dark bool) { + th.Colors = applyPalette(th.Theme, dark) +} + +// Label returns a body label in the app text color, scaled relative +// to the theme text size. +func (th *Theme) Label(text string, scale float32) material.LabelStyle { + label := material.Body1(th.Theme, text) + label.TextSize = th.TextSize * unit.Sp(scale) + label.Color = th.Colors.Text + return label +} + +// Muted is Label in the muted text color. +func (th *Theme) Muted(text string, scale float32) material.LabelStyle { + label := th.Label(text, scale) + label.Color = th.Colors.MutedText + return label +} + +// ErrorLabel is Label in the error color. +func (th *Theme) ErrorLabel(text string, scale float32) material.LabelStyle { + label := th.Label(text, scale) + label.Color = th.Colors.Error + return label +} + +type UIColors struct { + Background color.NRGBA + SecondaryBackground color.NRGBA + Splitter color.NRGBA + Gutter color.NRGBA + Text color.NRGBA + MutedText color.NRGBA + Error color.NRGBA + Selection color.NRGBA + RelationStroke color.NRGBA +} + +func (c UIColors) SyntaxColors() syntax.Colors { + return syntax.Colors{Text: c.Text, MutedText: c.MutedText, Background: c.Background} +} + +func applyPalette(th *material.Theme, dark bool) UIColors { + if dark { + th.Palette = material.Palette{ + Bg: color.NRGBA{R: 0x11, G: 0x13, B: 0x18, A: 0xff}, + Fg: color.NRGBA{R: 0xe8, G: 0xea, B: 0xed, A: 0xff}, + ContrastBg: color.NRGBA{R: 0x8a, G: 0xb4, B: 0xf8, A: 0xff}, + ContrastFg: color.NRGBA{R: 0x10, G: 0x23, B: 0x3f, A: 0xff}, + } + return UIColors{ + Background: th.Palette.Bg, + SecondaryBackground: color.NRGBA{R: 0x1b, G: 0x1f, B: 0x27, A: 0xff}, + Splitter: color.NRGBA{R: 0x4b, G: 0x55, B: 0x63, A: 0xff}, + Gutter: color.NRGBA{R: 0x18, G: 0x1c, B: 0x22, A: 0xff}, + Text: th.Palette.Fg, + MutedText: color.NRGBA{R: 0xa0, G: 0xa7, B: 0xb5, A: 0xff}, + Error: color.NRGBA{R: 0xff, G: 0xb4, B: 0xab, A: 0xff}, + Selection: color.NRGBA{R: 0x26, G: 0x32, B: 0x47, A: 0xff}, + RelationStroke: color.NRGBA{R: 0xe8, G: 0xea, B: 0xed, A: 0x70}, + } + } + + th.Palette = material.Palette{ + Bg: color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, + Fg: color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}, + ContrastBg: color.NRGBA{R: 0x3f, G: 0x51, B: 0xb5, A: 0xff}, + ContrastFg: color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}, + } + return UIColors{ + Background: th.Palette.Bg, + SecondaryBackground: color.NRGBA{R: 0xf0, G: 0xf0, B: 0xf0, A: 0xff}, + Splitter: color.NRGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}, + Gutter: color.NRGBA{R: 0xe8, G: 0xe8, B: 0xe8, A: 0xff}, + Text: th.Palette.Fg, + MutedText: color.NRGBA{R: 0x66, G: 0x66, B: 0x66, A: 0xff}, + Error: color.NRGBA{R: 0xb0, G: 0x00, B: 0x20, A: 0xff}, + Selection: color.NRGBA{R: 0xe8, G: 0xf0, B: 0xfe, A: 0xff}, + RelationStroke: color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0x40}, + } +} diff --git a/internal/gui/widgets.go b/internal/gui/widgets.go new file mode 100644 index 0000000..39e97aa --- /dev/null +++ b/internal/gui/widgets.go @@ -0,0 +1,172 @@ +package gui + +import ( + "image" + "image/color" + "time" + + "gioui.org/font" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/syntax" +) + +// SourceLine is a single-line of text. +type SourceLine struct { + TopLeft image.Point + Width int + Text string + Spans []syntax.Span + TextHeight unit.Sp + Italic bool + Bold bool + Color color.NRGBA +} + +// codeLineHeightScale leaves room for ascenders and descenders when a +// line is clipped to its width; rows themselves advance by the tighter +// CodeUIStyle.LineHeight. +const codeLineHeightScale = 4.0 / 3.0 + +func CodeLineHeightPx(gtx layout.Context, textHeight unit.Sp) int { + height := gtx.Metric.Sp(textHeight * codeLineHeightScale) + if height < 1 { + return 1 + } + return height +} + +// Layout draws the text. +func (line SourceLine) Layout(th *material.Theme, gtx layout.Context) { + gtx.Constraints.Min.X = 0 + gtx.Constraints.Max.X = MaxLineWidth + gtx.Constraints.Min.Y = 0 + gtx.Constraints.Max.Y = MaxLineWidth + + defer op.Offset(line.TopLeft).Push(gtx.Ops).Pop() + if line.Width > 0 { + maxSize := image.Pt(line.Width, CodeLineHeightPx(gtx, line.TextHeight)) + defer clip.Rect{Max: maxSize}.Push(gtx.Ops).Pop() + } + + spans := line.Spans + if len(spans) == 0 { + spans = []syntax.Span{{ + Text: line.Text, + Color: line.Color, + Italic: line.Italic, + Bold: line.Bold, + }} + } + + left := 0 + for _, span := range spans { + if span.Text == "" { + continue + } + dims := layoutSourceSpan(th, gtx, line, span, left) + left += dims.Size.X + } +} + +func layoutSourceSpan(th *material.Theme, gtx layout.Context, line SourceLine, span syntax.Span, left int) layout.Dimensions { + f := font.Font{Typeface: "override-monospace,Go,monospace", Weight: font.Normal} + if line.Italic || span.Italic { + f.Style = font.Italic + } + if line.Bold || span.Bold { + f.Weight = font.Black + } + col := span.Color + if col == (color.NRGBA{}) { + col = line.Color + } + + defer op.Offset(image.Pt(left, 0)).Push(gtx.Ops).Pop() + paint.ColorOp{Color: col}.Add(gtx.Ops) + return widget.Label{MaxLines: 1}.Layout(gtx, th.Shaper, f, line.TextHeight, span.Text, op.CallOp{}) +} + +type VerticalLine struct { + Width unit.Dp + Color color.NRGBA +} + +func (line VerticalLine) Layout(gtx layout.Context) layout.Dimensions { + size := image.Point{ + X: gtx.Metric.Dp(line.Width), + Y: gtx.Constraints.Min.Y, + } + paint.FillShape(gtx.Ops, line.Color, clip.Rect{Max: size}.Op()) + return layout.Dimensions{ + Size: size, + } +} + +type HorizontalLine struct { + Height unit.Dp + Color color.NRGBA +} + +func (line HorizontalLine) Layout(gtx layout.Context) layout.Dimensions { + size := image.Point{ + X: gtx.Constraints.Min.X, + Y: gtx.Metric.Dp(line.Height), + } + paint.FillShape(gtx.Ops, line.Color, clip.Rect{Max: size}.Op()) + return layout.Dimensions{ + Size: size, + } +} + +type ScrollAnimation struct { + active bool + from, to float32 + duration time.Duration + start time.Time +} + +func (anim *ScrollAnimation) Start(gtx layout.Context, from, to float32, duration time.Duration) { + anim.active = true + anim.from = from + anim.to = to + anim.duration = duration + anim.start = gtx.Now + gtx.Execute(op.InvalidateCmd{}) +} + +func (anim *ScrollAnimation) Stop() { anim.active = false } + +func (anim *ScrollAnimation) Update(gtx layout.Context) (float32, bool) { + if !anim.active { + return anim.to, false + } + gtx.Execute(op.InvalidateCmd{}) + + elapsed := gtx.Now.Sub(anim.start) + if elapsed > anim.duration { + anim.active = false + return anim.to, true + } + + progress := float32(elapsed) / float32(anim.duration) + progress = easeInOutCubic(progress) + + pos := anim.from + progress*(anim.to-anim.from) + return pos, true +} + +func easeInOutCubic(t float32) float32 { + if t < .5 { + return 4 * t * t * t + } + return (t-1)*(2*t-2)*(2*t-2) + 1 +} + +const MaxLineWidth = 10 * 1024 diff --git a/internal/gui/windows.go b/internal/gui/windows.go new file mode 100644 index 0000000..f558a14 --- /dev/null +++ b/internal/gui/windows.go @@ -0,0 +1,55 @@ +package gui + +import ( + "fmt" + "image" + "log" + "os" + "sync" + + "gioui.org/app" + "gioui.org/font" + "gioui.org/font/gofont" + "gioui.org/font/opentype" + "gioui.org/unit" +) + +type Windows struct { + active sync.WaitGroup +} + +func (windows *Windows) Open(title string, sizeDp image.Point, run func(*app.Window) error) { + windows.active.Go(func() { + + window := new(app.Window) + window.Option( + app.Title(title), + app.Size(unit.Dp(sizeDp.X), unit.Dp(sizeDp.Y)), + ) + if err := run(window); err != nil { + log.Println(err) + } + }) +} + +func (windows *Windows) Wait() { + windows.active.Wait() +} + +func LoadFonts(userfont string) []font.FontFace { + collection := gofont.Collection() + if userfont == "" { + return collection + } + b, err := os.ReadFile(userfont) + if err != nil { + panic(fmt.Errorf("failed to parse font: %v", err)) + } + face, err := opentype.Parse(b) + if err != nil { + panic(fmt.Errorf("failed to parse font: %v", err)) + } + fnt := font.Font{Typeface: "override-monospace,monospace", Weight: font.Normal} + fface := font.FontFace{Font: fnt, Face: face} + return append(collection, fface) +} diff --git a/internal/mcp/app.go b/internal/mcp/app.go new file mode 100644 index 0000000..9d84b54 --- /dev/null +++ b/internal/mcp/app.go @@ -0,0 +1,272 @@ +package mcp + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + "strings" + "sync" + "time" + + "loov.dev/lensm/internal/comments" +) + +type AppServer struct { + httpServer *http.Server + url string + load LoadFile + commentsPath string + + // mu guards the fields below. + mu sync.Mutex + session *Session + loadError error + generation uint64 + // active counts in-flight requests using session; a replaced + // session is closed only once they have finished. + active *sync.WaitGroup +} + +func StartAppServer(load LoadFile, commentsPath string) (*AppServer, error) { + listener, err := net.Listen("tcp", "127.0.0.1:7077") + if err != nil { + listener, err = net.Listen("tcp", "127.0.0.1:0") + } + if err != nil { + return nil, err + } + + server := &AppServer{ + url: "http://" + listener.Addr().String() + "/mcp", + load: load, + commentsPath: commentsPath, + active: &sync.WaitGroup{}, + } + mux := http.NewServeMux() + mux.HandleFunc("/", server.handleHTTP) + httpServer := &http.Server{ + Handler: mux, + ReadHeaderTimeout: 5 * time.Second, + ReadTimeout: time.Minute, + WriteTimeout: 5 * time.Minute, + IdleTimeout: 2 * time.Minute, + } + server.httpServer = httpServer + + go func() { + if err := httpServer.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { + fmt.Fprintf(os.Stderr, "lensm MCP server stopped: %v\n", err) + } + }() + return server, nil +} + +func (server *AppServer) URL() string { + if server == nil { + return "" + } + return server.url +} + +func (server *AppServer) SetPath(path string, store *comments.Store) { + if server == nil { + return + } + path = comments.CleanPath(path) + + server.mu.Lock() + server.generation++ + generation := server.generation + commentsPath := server.commentsPath + old, oldActive := server.session, server.active + server.session = nil + server.loadError = nil + server.active = &sync.WaitGroup{} + server.mu.Unlock() + // SetPath runs on the UI event loop: don't block on in-flight requests. + closeSessionWhenIdle(old, oldActive) + + if path == "" { + return + } + + go func() { + session, err := NewSessionWithComments(server.load, path, commentsPath, store) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to load MCP session for %q: %v\n", path, err) + server.replaceSession(generation, nil, err) + return + } + server.replaceSession(generation, session, nil) + }() +} + +func (server *AppServer) Close() error { + if server == nil { + return nil + } + server.mu.Lock() + server.generation++ + old, oldActive := server.session, server.active + server.session = nil + server.loadError = nil + server.active = &sync.WaitGroup{} + server.mu.Unlock() + if old != nil { + if oldActive != nil { + oldActive.Wait() + } + _ = old.Close() + } + if server.httpServer == nil { + return nil + } + return server.httpServer.Close() +} + +func (server *AppServer) replaceSession(generation uint64, session *Session, loadErr error) { + server.mu.Lock() + if generation != server.generation { + server.mu.Unlock() + if session != nil { + _ = session.Close() + } + return + } + old, oldActive := server.session, server.active + server.session = session + server.loadError = loadErr + server.active = &sync.WaitGroup{} + server.mu.Unlock() + closeSessionWhenIdle(old, oldActive) +} + +// closeSessionWhenIdle closes session once in-flight requests holding it +// have finished, without blocking the caller. +func closeSessionWhenIdle(session *Session, active *sync.WaitGroup) { + if session == nil { + return + } + go func() { + if active != nil { + active.Wait() + } + _ = session.Close() + }() +} + +func (server *AppServer) handleHTTP(w http.ResponseWriter, req *http.Request) { + if req.URL.Path != "/" && req.URL.Path != "/mcp" { + http.NotFound(w, req) + return + } + // The MCP streamable-HTTP spec requires validating Origin (and, for a + // loopback server, Host) to block DNS-rebinding pages from driving the + // server; the CORS headers below are advisory only. + if !isLoopbackHost(req.Host) { + http.Error(w, "forbidden host", http.StatusForbidden) + return + } + if origin := req.Header.Get("Origin"); origin != "" && !isLoopbackOrigin(origin) { + http.Error(w, "forbidden origin", http.StatusForbidden) + return + } + w.Header().Set("Access-Control-Allow-Origin", "http://localhost") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Mcp-Session-Id") + + switch req.Method { + case http.MethodOptions: + w.WriteHeader(http.StatusNoContent) + case http.MethodGet: + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(map[string]any{ + "name": "lensm", + "mcp": server.url, + }) + case http.MethodPost: + server.handleHTTPPost(w, req) + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + } +} + +func (server *AppServer) handleHTTPPost(w http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + data, err := io.ReadAll(io.LimitReader(req.Body, 64*1024*1024)) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + var msg rpcMessage + if err := json.Unmarshal(data, &msg); err != nil { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + _ = json.NewEncoder(w).Encode(rpcMessage{ + JSONRPC: "2.0", + // JSON-RPC 2.0 requires "id": null when the request id is + // unknowable, otherwise strict clients cannot correlate it. + ID: json.RawMessage("null"), + Error: &rpcError{Code: -32700, Message: "parse error"}, + }) + return + } + + response, ok := server.handleHTTPMessage(msg) + if !ok { + w.WriteHeader(http.StatusAccepted) + return + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(response) +} + +func (server *AppServer) handleHTTPMessage(msg rpcMessage) (rpcMessage, bool) { + session, loadErr, release := server.acquireSession() + defer release() + return (&mcpServer{session: session, loadErr: loadErr}).handle(msg) +} + +// acquireSession snapshots the current session and keeps it open until +// release is called. The server lock is not held while a request is +// handled, so concurrent requests and SetPath never wait on a handler. +func (server *AppServer) acquireSession() (session *Session, loadErr error, release func()) { + server.mu.Lock() + defer server.mu.Unlock() + if server.active == nil { + server.active = &sync.WaitGroup{} + } + active := server.active + active.Add(1) + return server.session, server.loadError, active.Done +} + +// isLoopbackHost reports whether a Host header (optionally host:port) +// refers to this machine. +func isLoopbackHost(hostport string) bool { + host := hostport + if h, _, err := net.SplitHostPort(hostport); err == nil { + host = h + } + host = strings.Trim(host, "[]") + if strings.EqualFold(host, "localhost") { + return true + } + ip := net.ParseIP(host) + return ip != nil && ip.IsLoopback() +} + +// isLoopbackOrigin reports whether an Origin header value refers to a +// page served from this machine. +func isLoopbackOrigin(origin string) bool { + u, err := url.Parse(origin) + if err != nil { + return false + } + return isLoopbackHost(u.Host) +} diff --git a/internal/mcp/export.go b/internal/mcp/export.go new file mode 100644 index 0000000..e0ecbc0 --- /dev/null +++ b/internal/mcp/export.go @@ -0,0 +1,173 @@ +package mcp + +import ( + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/disasm" +) + +type LineRangeDTO struct { + From int `json:"from"` + To int `json:"to"` +} + +type FunctionCodeDTO struct { + Binary string `json:"binary,omitempty"` + Name string `json:"name"` + File string `json:"file,omitempty"` + Source []SourceFileDTO `json:"source"` + GoAsm []AsmLineDTO `json:"go_asm"` + NativeAsm []AsmLineDTO `json:"native_asm"` + Comments []comments.Record `json:"comments,omitempty"` +} + +type SourceFileDTO struct { + File string `json:"file"` + Blocks []SourceBlockDTO `json:"blocks"` +} + +type SourceBlockDTO struct { + From int `json:"from"` + To int `json:"to"` + Lines []SourceLineDTO `json:"lines"` +} + +type SourceLineDTO struct { + File string `json:"file"` + Line int `json:"line"` + Text string `json:"text"` + Related []LineRangeDTO `json:"related,omitempty"` + Comment string `json:"comment,omitempty"` +} + +type AsmLineDTO struct { + Index int `json:"index"` + PC uint64 `json:"pc"` + PCHex string `json:"pc_hex"` + Text string `json:"text"` + File string `json:"file,omitempty"` + Line int `json:"line,omitempty"` + Call string `json:"call,omitempty"` + RefPC uint64 `json:"ref_pc,omitempty"` + RefPCHex string `json:"ref_pc_hex,omitempty"` + RefOffset int `json:"ref_offset,omitempty"` + Comment string `json:"comment,omitempty"` +} + +func BuildFunctionCodeDTO(binary string, code *disasm.Code, store *comments.Store) FunctionCodeDTO { + if code == nil { + return FunctionCodeDTO{Binary: comments.CleanPath(binary)} + } + + dto := FunctionCodeDTO{ + Binary: comments.CleanPath(binary), + Name: code.Name, + File: code.File, + Comments: store.Filter(code.Name, ""), + } + for _, src := range code.Source { + srcDTO := SourceFileDTO{File: src.File} + for _, block := range src.Blocks { + blockDTO := SourceBlockDTO{ + From: block.From, + To: block.To, + } + for off, text := range block.Lines { + line := block.From + off + lineDTO := SourceLineDTO{ + File: src.File, + Line: line, + Text: text, + } + if off < len(block.Related) { + lineDTO.Related = lineRangesDTO(block.Related[off]) + } + if store != nil { + lineDTO.Comment = store.ForSource(code.Name, src.File, line) + } + blockDTO.Lines = append(blockDTO.Lines, lineDTO) + } + srcDTO.Blocks = append(srcDTO.Blocks, blockDTO) + } + dto.Source = append(dto.Source, srcDTO) + } + + for i, inst := range code.Insts { + goLine := asmLineDTO(i, inst, inst.Text) + nativeLine := asmLineDTO(i, inst, inst.NativeText) + if store != nil && inst.Text != "" { + goLine.Comment = store.ForAsm(code.Name, comments.ViewGoAsm, inst.PC) + } + if store != nil && inst.NativeText != "" { + nativeLine.Comment = store.ForAsm(code.Name, comments.ViewNativeAsm, inst.PC) + } + dto.GoAsm = append(dto.GoAsm, goLine) + dto.NativeAsm = append(dto.NativeAsm, nativeLine) + } + + return dto +} + +func lineRangesDTO(ranges []disasm.LineRange) []LineRangeDTO { + if len(ranges) == 0 { + return nil + } + out := make([]LineRangeDTO, 0, len(ranges)) + for _, r := range ranges { + out = append(out, LineRangeDTO{From: r.From, To: r.To}) + } + return out +} + +func asmLineDTO(index int, inst disasm.Inst, text string) AsmLineDTO { + line := AsmLineDTO{ + Index: index, + PC: inst.PC, + PCHex: comments.FormatPC(inst.PC), + Text: text, + File: inst.File, + Line: inst.Line, + Call: inst.Call, + RefPC: inst.RefPC, + RefOffset: inst.RefOffset, + } + if inst.RefPC != 0 { + line.RefPCHex = comments.FormatPC(inst.RefPC) + } + return line +} + +func sourceLineExists(code *disasm.Code, file string, line int) bool { + if code == nil || file == "" || line <= 0 { + return false + } + for _, src := range code.Source { + if src.File != file && comments.CleanPath(src.File) != comments.CleanPath(file) { + continue + } + for _, block := range src.Blocks { + if block.From <= line && line < block.From+len(block.Lines) { + return true + } + } + } + return false +} + +func asmPCExists(code *disasm.Code, view comments.View, pc uint64) bool { + if code == nil { + return false + } + for _, inst := range code.Insts { + switch view { + case comments.ViewGoAsm: + if inst.PC == pc && inst.Text != "" { + return true + } + case comments.ViewNativeAsm: + if inst.PC == pc && inst.NativeText != "" { + return true + } + } + } + return false +} diff --git a/internal/mcp/mcp.go b/internal/mcp/mcp.go new file mode 100644 index 0000000..0ca0680 --- /dev/null +++ b/internal/mcp/mcp.go @@ -0,0 +1,538 @@ +package mcp + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "os" + "regexp" + "strconv" + "strings" + + "loov.dev/lensm/internal/comments" +) + +const mcpProtocolVersion = "2025-06-18" +const defaultMCPSourceContext = 3 + +// maxMCPSourceContext bounds the per-call context: an unbounded value +// overflows the line-range arithmetic and panics with a slice bounds +// error, letting one request kill the stdio server. +const maxMCPSourceContext = 1000 + +type rpcMessage struct { + JSONRPC string `json:"jsonrpc"` + ID json.RawMessage `json:"id,omitempty"` + Method string `json:"method,omitempty"` + Params json.RawMessage `json:"params,omitempty"` + Result any `json:"result,omitempty"` + Error *rpcError `json:"error,omitempty"` +} + +type rpcError struct { + Code int `json:"code"` + Message string `json:"message"` + Data any `json:"data,omitempty"` +} + +type mcpTool struct { + Name string `json:"name"` + Title string `json:"title,omitempty"` + Description string `json:"description"` + InputSchema any `json:"inputSchema"` +} + +type mcpToolResult struct { + Content []mcpTextContent `json:"content"` + StructuredContent any `json:"structuredContent,omitempty"` + IsError bool `json:"isError,omitempty"` +} + +type mcpTextContent struct { + Type string `json:"type"` + Text string `json:"text"` +} + +type mcpServer struct { + session *Session + loadErr error + out *bufio.Writer +} + +func RunCommand(load LoadFile, args []string) int { + fs := flag.NewFlagSet("lensm mcp", flag.ContinueOnError) + fs.SetOutput(os.Stderr) + commentsPath := fs.String("comments", "", "comments sidecar path") + if err := fs.Parse(args); err != nil { + return 2 + } + if fs.NArg() != 1 { + fmt.Fprintln(os.Stderr, "usage: lensm mcp [-comments path] ") + return 2 + } + + session, err := NewSessionWithComments(load, fs.Arg(0), *commentsPath, nil) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return 1 + } + defer session.Close() + + server := &mcpServer{ + session: session, + out: bufio.NewWriter(os.Stdout), + } + if err := server.serve(os.Stdin); err != nil && !errors.Is(err, io.EOF) { + fmt.Fprintln(os.Stderr, err) + return 1 + } + return 0 +} + +func (server *mcpServer) serve(input io.Reader) error { + scanner := bufio.NewScanner(input) + scanner.Buffer(make([]byte, 0, 64*1024), 64*1024*1024) + for scanner.Scan() { + line := bytes.TrimSpace(scanner.Bytes()) + if len(line) == 0 { + continue + } + var msg rpcMessage + if err := json.Unmarshal(line, &msg); err != nil { + _ = server.writeMessage(rpcMessage{ + JSONRPC: "2.0", + ID: json.RawMessage("null"), + Error: &rpcError{Code: -32700, Message: "parse error"}, + }) + continue + } + response, ok := server.handle(msg) + if ok { + if err := server.writeMessage(response); err != nil { + return err + } + } + } + return scanner.Err() +} + +func (server *mcpServer) writeMessage(msg rpcMessage) error { + msg.JSONRPC = "2.0" + data, err := json.Marshal(msg) + if err != nil { + return err + } + if _, err := server.out.Write(data); err != nil { + return err + } + if err := server.out.WriteByte('\n'); err != nil { + return err + } + return server.out.Flush() +} + +func (server *mcpServer) handle(msg rpcMessage) (rpcMessage, bool) { + if len(msg.ID) == 0 { + return rpcMessage{}, false + } + response := rpcMessage{JSONRPC: "2.0", ID: msg.ID} + result, err := server.handleRequest(msg.Method, msg.Params) + if err != nil { + response.Error = err + return response, true + } + response.Result = result + return response, true +} + +func (server *mcpServer) handleRequest(method string, params json.RawMessage) (any, *rpcError) { + switch method { + case "initialize": + return map[string]any{ + "protocolVersion": mcpProtocolVersion, + "capabilities": map[string]any{ + "tools": map[string]any{ + "listChanged": false, + }, + }, + "serverInfo": map[string]any{ + "name": "lensm", + "title": "lensm", + "version": "dev", + }, + "instructions": "Use tools to inspect Go source, Go assembly, native assembly, and comments for the loaded executable.", + }, nil + case "ping": + return map[string]any{}, nil + case "tools/list": + return map[string]any{"tools": mcpTools()}, nil + case "tools/call": + return server.handleToolCall(params) + default: + return nil, &rpcError{Code: -32601, Message: "method not found"} + } +} + +func (server *mcpServer) handleToolCall(params json.RawMessage) (any, *rpcError) { + var req struct { + Name string `json:"name"` + Arguments json.RawMessage `json:"arguments"` + } + if err := decodeJSON(params, &req); err != nil { + return nil, invalidParams(err) + } + if len(req.Arguments) == 0 { + req.Arguments = []byte("{}") + } + if server.session == nil { + if server.loadErr != nil { + return toolError(server.loadErr), nil + } + return toolError(errors.New("no executable loaded")), nil + } + + var ( + result any + err error + ) + switch req.Name { + case "list_functions": + result, err = server.toolListFunctions(req.Arguments) + case "get_function": + result, err = server.toolGetFunction(req.Arguments) + case "set_comment": + result, err = server.toolSetComment(req.Arguments) + case "get_comments": + result, err = server.toolGetComments(req.Arguments) + default: + return nil, &rpcError{Code: -32602, Message: "unknown tool: " + req.Name} + } + if err != nil { + return toolError(err), nil + } + return toolSuccess(result), nil +} + +func (server *mcpServer) toolListFunctions(args json.RawMessage) (any, error) { + var req struct { + Filter string `json:"filter"` + Limit int `json:"limit"` + Offset int `json:"offset"` + } + if err := decodeJSON(args, &req); err != nil { + return nil, err + } + if req.Limit <= 0 { + req.Limit = 100 + } + if req.Limit > 1000 { + req.Limit = 1000 + } + if req.Offset < 0 { + req.Offset = 0 + } + + var rx *regexp.Regexp + if req.Filter != "" { + var err error + rx, err = regexp.Compile("(?i)" + req.Filter) + if err != nil { + return nil, err + } + } + + type functionInfo struct { + Index int `json:"index"` + Name string `json:"name"` + } + var all []functionInfo + for i, fn := range server.session.Funcs() { + name := fn.Name() + if rx != nil && !rx.MatchString(name) { + continue + } + all = append(all, functionInfo{Index: i, Name: name}) + } + + var page []functionInfo + if req.Offset < len(all) { + page = all[req.Offset:min(req.Offset+req.Limit, len(all))] + } + return map[string]any{ + "binary": server.session.Path, + "functions": page, + "total": len(all), + "offset": req.Offset, + "limit": req.Limit, + }, nil +} + +func (server *mcpServer) toolGetFunction(args json.RawMessage) (any, error) { + var req struct { + Name string `json:"name"` + Context *int `json:"context"` + } + if err := decodeJSON(args, &req); err != nil { + return nil, err + } + if req.Name == "" { + return nil, errors.New("name is required") + } + context := defaultMCPSourceContext + if req.Context != nil { + context = *req.Context + } + if context < 0 { + return nil, errors.New("context must be non-negative") + } + if context > maxMCPSourceContext { + return nil, fmt.Errorf("context must be at most %d", maxMCPSourceContext) + } + code, err := server.session.LoadCode(req.Name, context) + if err != nil { + return nil, err + } + return BuildFunctionCodeDTO(server.session.Path, code, server.session.Comments), nil +} + +func (server *mcpServer) toolSetComment(args json.RawMessage) (any, error) { + var req struct { + Name string `json:"name"` + View comments.View `json:"view"` + File string `json:"file"` + Line int `json:"line"` + PC json.RawMessage `json:"pc"` + Text string `json:"text"` + } + if err := decodeJSON(args, &req); err != nil { + return nil, err + } + if req.Name == "" { + return nil, errors.New("name is required") + } + if server.session.Comments.Path() == "" { + return nil, errors.New("comments sidecar is unavailable; the comment would not be persisted") + } + code, err := server.session.LoadCode(req.Name, 0) + if err != nil { + return nil, err + } + + var coord comments.Coord + switch req.View { + case comments.ViewSource: + if !sourceLineExists(code, req.File, req.Line) { + return nil, fmt.Errorf("source line %s:%d is not present in function %q", req.File, req.Line, req.Name) + } + coord = comments.Coord{ + Function: req.Name, + View: comments.ViewSource, + File: req.File, + Line: req.Line, + } + case comments.ViewGoAsm, comments.ViewNativeAsm: + pc, ok, err := parsePC(req.PC) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("pc is required for asm comments") + } + if !asmPCExists(code, req.View, pc) { + return nil, fmt.Errorf("pc %s is not present in %s for function %q", comments.FormatPC(pc), req.View, req.Name) + } + coord = comments.Coord{ + Function: req.Name, + View: req.View, + PC: pc, + } + default: + return nil, fmt.Errorf("unsupported view %q", req.View) + } + + if err := server.session.Comments.Set(coord, req.Text); err != nil { + return nil, err + } + coord = server.session.Comments.Normalize(coord) + return map[string]any{ + "comment": coord, + "deleted": strings.TrimSpace(req.Text) == "", + "path": server.session.Comments.Path(), + }, nil +} + +func (server *mcpServer) toolGetComments(args json.RawMessage) (any, error) { + var req struct { + Name string `json:"name"` + View comments.View `json:"view"` + } + if err := decodeJSON(args, &req); err != nil { + return nil, err + } + if req.View != "" { + switch req.View { + case comments.ViewSource, comments.ViewGoAsm, comments.ViewNativeAsm: + default: + return nil, fmt.Errorf("unsupported view %q", req.View) + } + } + return map[string]any{ + "binary": server.session.Path, + "path": server.session.Comments.Path(), + "comments": server.session.Comments.Filter(req.Name, req.View), + }, nil +} + +func decodeJSON(data []byte, dst any) error { + if len(bytes.TrimSpace(data)) == 0 { + data = []byte("{}") + } + dec := json.NewDecoder(bytes.NewReader(data)) + dec.UseNumber() + return dec.Decode(dst) +} + +func parsePC(data json.RawMessage) (uint64, bool, error) { + data = bytes.TrimSpace(data) + if len(data) == 0 || bytes.Equal(data, []byte("null")) { + return 0, false, nil + } + var text string + if err := json.Unmarshal(data, &text); err == nil { + text = strings.TrimSpace(text) + pc, err := strconv.ParseUint(text, 0, 64) + if err != nil { + return 0, true, fmt.Errorf("invalid pc %q", text) + } + return pc, true, nil + } + var num json.Number + if err := decodeJSON(data, &num); err != nil { + return 0, true, fmt.Errorf("invalid pc") + } + pc, err := strconv.ParseUint(num.String(), 10, 64) + if err != nil { + return 0, true, fmt.Errorf("invalid pc %q", num.String()) + } + return pc, true, nil +} + +func invalidParams(err error) *rpcError { + return &rpcError{Code: -32602, Message: err.Error()} +} + +func toolSuccess(value any) mcpToolResult { + text, err := json.MarshalIndent(value, "", " ") + if err != nil { + text = fmt.Append(nil, value) + } + return mcpToolResult{ + Content: []mcpTextContent{{ + Type: "text", + Text: string(text), + }}, + StructuredContent: value, + } +} + +func toolError(err error) mcpToolResult { + return mcpToolResult{ + Content: []mcpTextContent{{ + Type: "text", + Text: err.Error(), + }}, + IsError: true, + } +} + +func mcpTools() []mcpTool { + return []mcpTool{ + { + Name: "list_functions", + Title: "List Functions", + Description: "List functions in the loaded executable. The optional filter is a case-insensitive Go regexp.", + InputSchema: objectSchema(map[string]any{ + "filter": stringSchema("Optional case-insensitive regexp matched against function names."), + "limit": integerSchema("Maximum number of functions to return. Defaults to 100, capped at 1000."), + "offset": integerSchema("Number of matching functions to skip."), + }, nil), + }, + { + Name: "get_function", + Title: "Get Function Code", + Description: "Return Go source, Go assembly, native assembly, source-to-asm mappings, and comments for a function.", + InputSchema: objectSchema(map[string]any{ + "name": stringSchema("Exact function name."), + "context": integerSchema("Number of extra source lines to include before and after referenced lines. Defaults to 3."), + }, []string{"name"}), + }, + { + Name: "set_comment", + Title: "Set Comment", + Description: "Set or delete a comment for a source line, Go asm instruction, or native asm instruction. Empty text deletes the comment.", + InputSchema: objectSchema(map[string]any{ + "name": stringSchema("Exact function name."), + "view": enumSchema("Code view for the comment.", []string{ + string(comments.ViewSource), + string(comments.ViewGoAsm), + string(comments.ViewNativeAsm), + }), + "file": stringSchema("Source file path. Required for source comments."), + "line": integerSchema("Source line number. Required for source comments."), + "pc": pcSchema(), + "text": stringSchema("Comment text. Empty string deletes the comment."), + }, []string{"name", "view", "text"}), + }, + { + Name: "get_comments", + Title: "Get Comments", + Description: "Return comments, optionally filtered by function name and view.", + InputSchema: objectSchema(map[string]any{ + "name": stringSchema("Optional exact function name."), + "view": enumSchema("Optional code view filter.", []string{ + string(comments.ViewSource), + string(comments.ViewGoAsm), + string(comments.ViewNativeAsm), + }), + }, nil), + }, + } +} + +func objectSchema(properties map[string]any, required []string) map[string]any { + schema := map[string]any{ + "type": "object", + "properties": properties, + "additionalProperties": false, + } + if len(required) > 0 { + schema["required"] = required + } + return schema +} + +func stringSchema(description string) map[string]any { + return map[string]any{"type": "string", "description": description} +} + +func integerSchema(description string) map[string]any { + return map[string]any{"type": "integer", "description": description} +} + +func enumSchema(description string, values []string) map[string]any { + return map[string]any{"type": "string", "description": description, "enum": values} +} + +func pcSchema() map[string]any { + return map[string]any{ + "description": "Instruction program counter for asm comments. Accepts an integer or hex string such as 0x1000.", + "oneOf": []map[string]any{ + {"type": "integer"}, + {"type": "string"}, + }, + } +} diff --git a/internal/mcp/mcp_test.go b/internal/mcp/mcp_test.go new file mode 100644 index 0000000..295c03d --- /dev/null +++ b/internal/mcp/mcp_test.go @@ -0,0 +1,113 @@ +package mcp + +import ( + "encoding/json" + "errors" + "net/http/httptest" + "path/filepath" + "strings" + "testing" + "time" + + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/goobj" +) + +func TestAppMCPServerSetPathClearsStaleSessionOnLoadFailure(t *testing.T) { + server := &AppServer{ + load: func(path string) (disasm.File, error) { return goobj.Load(path) }, + session: &Session{Path: "old"}, + } + + server.SetPath(filepath.Join(t.TempDir(), "missing-binary"), nil) + + server.mu.Lock() + if server.session != nil { + t.Fatal("old MCP session was not cleared immediately") + } + server.mu.Unlock() + + deadline := time.After(2 * time.Second) + tick := time.NewTicker(10 * time.Millisecond) + defer tick.Stop() + for { + server.mu.Lock() + loadErr := server.loadError + session := server.session + server.mu.Unlock() + if loadErr != nil { + if session != nil { + t.Fatal("MCP session was set after load failure") + } + return + } + select { + case <-deadline: + t.Fatal("timed out waiting for MCP load failure") + case <-tick.C: + } + } +} + +func TestMCPHandleHTTPRejectsRebinding(t *testing.T) { + server := &AppServer{} + + cases := []struct { + name string + host string + origin string + status int + }{ + {name: "loopback ip", host: "127.0.0.1:7077", status: 200}, + {name: "localhost", host: "localhost:7077", status: 200}, + {name: "ipv6 loopback", host: "[::1]:7077", status: 200}, + {name: "rebound host", host: "evil.example.com:7077", status: 403}, + {name: "loopback origin", host: "127.0.0.1:7077", origin: "http://localhost:8000", status: 200}, + {name: "cross-site origin", host: "127.0.0.1:7077", origin: "https://evil.example.com", status: 403}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + req := httptest.NewRequest("GET", "http://placeholder/", nil) + req.Host = tc.host + if tc.origin != "" { + req.Header.Set("Origin", tc.origin) + } + rec := httptest.NewRecorder() + server.handleHTTP(rec, req) + if rec.Code != tc.status { + t.Fatalf("status = %d, want %d", rec.Code, tc.status) + } + }) + } +} + +func TestMCPHandleHTTPParseErrorHasNullID(t *testing.T) { + server := &AppServer{} + req := httptest.NewRequest("POST", "http://127.0.0.1:7077/mcp", strings.NewReader("{not json")) + rec := httptest.NewRecorder() + server.handleHTTP(rec, req) + if rec.Code != 400 { + t.Fatalf("status = %d, want 400", rec.Code) + } + if !strings.Contains(rec.Body.String(), `"id":null`) { + t.Fatalf("parse error response missing null id: %s", rec.Body.String()) + } +} + +func TestMCPToolCallReportsLoadError(t *testing.T) { + server := &mcpServer{loadErr: errors.New("load failed")} + result, rpcErr := server.handleToolCall(json.RawMessage(`{"name":"list_functions"}`)) + if rpcErr != nil { + t.Fatal(rpcErr.Message) + } + toolResult, ok := result.(mcpToolResult) + if !ok { + t.Fatalf("result type = %T", result) + } + if !toolResult.IsError { + t.Fatal("tool result is not marked as error") + } + if len(toolResult.Content) != 1 || !strings.Contains(toolResult.Content[0].Text, "load failed") { + t.Fatalf("tool error content = %#v", toolResult.Content) + } +} diff --git a/internal/mcp/session.go b/internal/mcp/session.go new file mode 100644 index 0000000..76c46c4 --- /dev/null +++ b/internal/mcp/session.go @@ -0,0 +1,72 @@ +package mcp + +import ( + "fmt" + + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/disasm" +) + +type Session struct { + Path string + File disasm.File + Comments *comments.Store +} + +// LoadFile opens a binary for disassembly. The caller injects an +// implementation so sessions stay independent of the object-file formats. +type LoadFile func(path string) (disasm.File, error) + +func NewSessionWithComments(load LoadFile, path string, commentsPath string, store *comments.Store) (*Session, error) { + file, err := load(path) + if err != nil { + return nil, err + } + if store == nil { + if commentsPath == "" { + commentsPath = comments.DefaultPath(path) + } + var err error + store, err = comments.Open(commentsPath, path) + if err != nil { + _ = file.Close() + return nil, err + } + } + return &Session{ + Path: comments.CleanPath(path), + File: file, + Comments: store, + }, nil +} + +func (s *Session) Close() error { + if s == nil || s.File == nil { + return nil + } + return s.File.Close() +} + +func (s *Session) Funcs() []disasm.Func { + if s == nil || s.File == nil { + return nil + } + return s.File.Funcs() +} + +func (s *Session) FindFunc(name string) disasm.Func { + for _, fn := range s.Funcs() { + if fn.Name() == name { + return fn + } + } + return nil +} + +func (s *Session) LoadCode(name string, context int) (*disasm.Code, error) { + fn := s.FindFunc(name) + if fn == nil { + return nil, fmt.Errorf("function %q not found", name) + } + return fn.Load(disasm.Options{Context: context}) +} diff --git a/internal/syntax/syntax.go b/internal/syntax/syntax.go new file mode 100644 index 0000000..0bbf8db --- /dev/null +++ b/internal/syntax/syntax.go @@ -0,0 +1,453 @@ +package syntax + +import ( + "go/scanner" + "go/token" + "image/color" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +const ( + StyleGoLand = "goland-light" + StyleDarcula = "darcula" + StyleMono = "mono" +) + +type Palette struct { + Plain color.NRGBA + Keyword color.NRGBA + Builtin color.NRGBA + String color.NRGBA + Number color.NRGBA + Comment color.NRGBA + Operator color.NRGBA + Register color.NRGBA + Mnemonic color.NRGBA + Symbol color.NRGBA + LineNumber color.NRGBA + CallTarget color.NRGBA +} + +func NormalizeStyle(style string) string { + switch strings.ToLower(strings.TrimSpace(style)) { + case "", "goland", "goland-light", "jetbrains": + return StyleGoLand + case "darcula", "goland-dark", "jetbrains-dark": + return StyleDarcula + case "mono", "monochrome", "plain": + return StyleMono + default: + return StyleGoLand + } +} + +func StyleLabel(style string) string { + switch NormalizeStyle(style) { + case StyleDarcula: + return "Darcula" + case StyleMono: + return "Mono" + default: + return "GoLand" + } +} + +// Colors are the theme colors the palettes derive from. +type Colors struct { + Text color.NRGBA + MutedText color.NRGBA + Background color.NRGBA +} + +func PaletteFor(style string, colors Colors) Palette { + switch NormalizeStyle(style) { + case StyleDarcula: + if !syntaxBackgroundDark(colors.Background) { + return Palette{ + Plain: color.NRGBA{R: 0x2b, G: 0x2d, B: 0x30, A: 0xff}, + Keyword: color.NRGBA{R: 0xa8, G: 0x55, B: 0x22, A: 0xff}, + Builtin: color.NRGBA{R: 0x3a, G: 0x72, B: 0x80, A: 0xff}, + String: color.NRGBA{R: 0x3c, G: 0x78, B: 0x35, A: 0xff}, + Number: color.NRGBA{R: 0x17, G: 0x50, B: 0xeb, A: 0xff}, + Comment: color.NRGBA{R: 0x7a, G: 0x7a, B: 0x7a, A: 0xff}, + Operator: color.NRGBA{R: 0x2b, G: 0x2d, B: 0x30, A: 0xff}, + Register: color.NRGBA{R: 0x66, G: 0x0e, B: 0x7a, A: 0xff}, + Mnemonic: color.NRGBA{R: 0xa8, G: 0x55, B: 0x22, A: 0xff}, + Symbol: color.NRGBA{R: 0x2b, G: 0x68, B: 0x88, A: 0xff}, + LineNumber: colors.MutedText, + CallTarget: color.NRGBA{R: 0x00, G: 0x00, B: 0xee, A: 0xff}, + } + } + return Palette{ + Plain: color.NRGBA{R: 0xa9, G: 0xb7, B: 0xc6, A: 0xff}, + Keyword: color.NRGBA{R: 0xcc, G: 0x78, B: 0x32, A: 0xff}, + Builtin: color.NRGBA{R: 0x88, G: 0xc0, B: 0xd0, A: 0xff}, + String: color.NRGBA{R: 0x6a, G: 0x87, B: 0x59, A: 0xff}, + Number: color.NRGBA{R: 0x68, G: 0x97, B: 0xbb, A: 0xff}, + Comment: color.NRGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}, + Operator: color.NRGBA{R: 0xa9, G: 0xb7, B: 0xc6, A: 0xff}, + Register: color.NRGBA{R: 0x98, G: 0x76, B: 0xaa, A: 0xff}, + Mnemonic: color.NRGBA{R: 0xff, G: 0xc6, B: 0x6d, A: 0xff}, + Symbol: color.NRGBA{R: 0xa5, G: 0xc2, B: 0x61, A: 0xff}, + LineNumber: colors.MutedText, + CallTarget: color.NRGBA{R: 0x62, G: 0x9c, B: 0xf6, A: 0xff}, + } + case StyleMono: + return Palette{ + Plain: colors.Text, + Keyword: colors.Text, + Builtin: colors.Text, + String: colors.Text, + Number: colors.Text, + Comment: colors.MutedText, + Operator: colors.Text, + Register: colors.Text, + Mnemonic: colors.Text, + Symbol: colors.Text, + LineNumber: colors.MutedText, + CallTarget: colors.Text, + } + default: + if syntaxBackgroundDark(colors.Background) { + return Palette{ + Plain: color.NRGBA{R: 0xba, G: 0xbe, B: 0xc7, A: 0xff}, + Keyword: color.NRGBA{R: 0xcf, G: 0x8e, B: 0x6d, A: 0xff}, + Builtin: color.NRGBA{R: 0xbc, G: 0x94, B: 0xf9, A: 0xff}, + String: color.NRGBA{R: 0x6a, G: 0xab, B: 0x73, A: 0xff}, + Number: color.NRGBA{R: 0x2a, G: 0xa1, B: 0xf3, A: 0xff}, + Comment: color.NRGBA{R: 0x7a, G: 0x80, B: 0x87, A: 0xff}, + Operator: color.NRGBA{R: 0xba, G: 0xbe, B: 0xc7, A: 0xff}, + Register: color.NRGBA{R: 0xbc, G: 0x94, B: 0xf9, A: 0xff}, + Mnemonic: color.NRGBA{R: 0xcf, G: 0x8e, B: 0x6d, A: 0xff}, + Symbol: color.NRGBA{R: 0x56, G: 0xa8, B: 0xf5, A: 0xff}, + LineNumber: colors.MutedText, + CallTarget: color.NRGBA{R: 0x56, G: 0xa8, B: 0xf5, A: 0xff}, + } + } + return Palette{ + Plain: color.NRGBA{R: 0x08, G: 0x08, B: 0x08, A: 0xff}, + Keyword: color.NRGBA{R: 0x00, G: 0x00, B: 0x80, A: 0xff}, + Builtin: color.NRGBA{R: 0x87, G: 0x10, B: 0x94, A: 0xff}, + String: color.NRGBA{R: 0x00, G: 0x80, B: 0x00, A: 0xff}, + Number: color.NRGBA{R: 0x17, G: 0x50, B: 0xeb, A: 0xff}, + Comment: color.NRGBA{R: 0x8c, G: 0x8c, B: 0x8c, A: 0xff}, + Operator: color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}, + Register: color.NRGBA{R: 0x66, G: 0x0e, B: 0x7a, A: 0xff}, + Mnemonic: color.NRGBA{R: 0x00, G: 0x00, B: 0x80, A: 0xff}, + Symbol: color.NRGBA{R: 0x87, G: 0x10, B: 0x94, A: 0xff}, + LineNumber: colors.MutedText, + CallTarget: color.NRGBA{R: 0x00, G: 0x00, B: 0xee, A: 0xff}, + } + } +} + +func syntaxBackgroundDark(bg color.NRGBA) bool { + return int(bg.R)*299+int(bg.G)*587+int(bg.B)*114 < 128000 +} + +func HighlightSource(lineNo int, line string, palette Palette) []Span { + spans := []Span{{ + Text: lineNumberPrefix(lineNo), + Color: palette.LineNumber, + }} + return append(spans, HighlightGo(line, palette)...) +} + +func HighlightGo(line string, palette Palette) []Span { + if line == "" { + return nil + } + + var spans []Span + fset := token.NewFileSet() + file := fset.AddFile("", -1, len(line)) + var scan scanner.Scanner + scan.Init(file, []byte(line), nil, scanner.ScanComments) + + cursor := 0 + for { + pos, tok, lit := scan.Scan() + if tok == token.EOF { + break + } + if tok == token.SEMICOLON && lit == "\n" { + continue + } + + start := file.Offset(pos) + if start > len(line) { + break + } + if start < cursor { + continue + } + if start > cursor { + appendSyntaxSpan(&spans, line[cursor:start], palette.Plain, false, false) + } + + text := lit + if text == "" { + text = tok.String() + } + if text == "" { + continue + } + + end := start + len(text) + if end > len(line) { + end = len(line) + text = line[start:end] + } + col, italic := goTokenStyle(tok, text, palette) + appendSyntaxSpan(&spans, text, col, false, italic) + cursor = end + } + if cursor < len(line) { + appendSyntaxSpan(&spans, line[cursor:], palette.Plain, false, false) + } + return spans +} + +func HighlightAsm(line string, callTarget string, palette Palette) []Span { + return highlightAsmLine(line, callTarget, palette) +} + +func lineNumberPrefix(lineNo int) string { + var b strings.Builder + b.WriteString(strconv.Itoa(lineNo)) + for b.Len() < 4 { + b.WriteByte(' ') + } + b.WriteByte(' ') + return b.String() +} + +func goTokenStyle(tok token.Token, text string, palette Palette) (color.NRGBA, bool) { + switch { + case tok.IsKeyword(): + return palette.Keyword, false + case tok == token.COMMENT: + return palette.Comment, true + case tok == token.STRING || tok == token.CHAR: + return palette.String, false + case tok == token.INT || tok == token.FLOAT || tok == token.IMAG: + return palette.Number, false + case tok == token.IDENT && goBuiltinNames[text]: + return palette.Builtin, false + case tok.IsOperator(): + return palette.Operator, false + default: + return palette.Plain, false + } +} + +func highlightAsmLine(line string, callTarget string, palette Palette) []Span { + if line == "" { + return nil + } + + code, comment := splitAsmComment(line) + spans := highlightAsmCode(code, callTarget, palette) + if comment != "" { + appendSyntaxSpan(&spans, comment, palette.Comment, false, true) + } + return spans +} + +func splitAsmComment(line string) (string, string) { + commentAt := len(line) + for _, marker := range []string{"//", ";"} { + if ix := strings.Index(line, marker); ix >= 0 && ix < commentAt { + commentAt = ix + } + } + if commentAt == len(line) { + return line, "" + } + return line[:commentAt], line[commentAt:] +} + +func highlightAsmCode(code string, callTarget string, palette Palette) []Span { + var spans []Span + seenMnemonic := false + for i := 0; i < len(code); { + r, size := runeAt(code, i) + if unicode.IsSpace(r) { + start := i + i += size + for i < len(code) { + r, size = runeAt(code, i) + if !unicode.IsSpace(r) { + break + } + i += size + } + appendSyntaxSpan(&spans, code[start:i], palette.Plain, false, false) + continue + } + if isAsmPunctuation(r) { + appendSyntaxSpan(&spans, code[i:i+size], palette.Operator, false, false) + i += size + continue + } + + start := i + i += size + for i < len(code) { + r, size = runeAt(code, i) + if unicode.IsSpace(r) || isAsmPunctuation(r) { + break + } + i += size + } + word := code[start:i] + col := asmWordColor(word, seenMnemonic, callTarget, palette) + bold := false + if !seenMnemonic && strings.TrimSpace(word) != "" { + seenMnemonic = true + bold = true + } + appendSyntaxSpan(&spans, word, col, bold, false) + } + return spans +} + +func asmWordColor(word string, seenMnemonic bool, callTarget string, palette Palette) color.NRGBA { + trimmed := strings.TrimSpace(word) + trimmed = strings.Trim(trimmed, "[]{}") + upper := strings.ToUpper(strings.TrimPrefix(trimmed, "%")) + + switch { + case trimmed == "": + return palette.Plain + case !seenMnemonic: + return palette.Mnemonic + case callTarget != "" && strings.Contains(trimmed, callTarget): + return palette.CallTarget + case isAsmNumber(trimmed): + return palette.Number + case isAsmRegister(upper): + return palette.Register + case strings.ContainsAny(trimmed, "./:<>@") || strings.Contains(trimmed, "(SB)"): + return palette.Symbol + default: + return palette.Plain + } +} + +func isAsmNumber(word string) bool { + word = strings.TrimPrefix(word, "$") + word = strings.TrimPrefix(word, "#") + word = strings.TrimPrefix(word, "-") + word = strings.TrimPrefix(word, "+") + if word == "" { + return false + } + if strings.HasPrefix(word, "0x") || strings.HasPrefix(word, "0X") { + if len(word) <= 2 { + return false + } + for _, r := range word[2:] { + if !('0' <= r && r <= '9') && !('a' <= r && r <= 'f') && !('A' <= r && r <= 'F') { + return false + } + } + return true + } + for _, r := range word { + if !('0' <= r && r <= '9') { + return false + } + } + return true +} + +func isAsmRegister(word string) bool { + if asmRegisterNames[word] { + return true + } + if len(word) >= 2 { + prefix := word[0] + if prefix == 'R' || prefix == 'X' || prefix == 'W' || prefix == 'V' || prefix == 'Q' || prefix == 'D' || prefix == 'S' || prefix == 'B' { + for _, r := range word[1:] { + if r < '0' || r > '9' { + return false + } + } + return true + } + } + return false +} + +func isAsmPunctuation(r rune) bool { + switch r { + case ',', '(', ')', '[', ']', '{', '}', '+', '-', '*': + return true + default: + return false + } +} + +func appendSyntaxSpan(spans *[]Span, text string, col color.NRGBA, bold, italic bool) { + if text == "" { + return + } + last := len(*spans) - 1 + if last >= 0 { + prev := &(*spans)[last] + if prev.Color == col && prev.Bold == bold && prev.Italic == italic { + prev.Text += text + return + } + } + *spans = append(*spans, Span{ + Text: text, + Color: col, + Bold: bold, + Italic: italic, + }) +} + +func runeAt(s string, i int) (rune, int) { + r := rune(s[i]) + if r < utf8.RuneSelf { + return r, 1 + } + return utf8.DecodeRuneInString(s[i:]) +} + +var goBuiltinNames = map[string]bool{ + "any": true, "append": true, "bool": true, "byte": true, "cap": true, + "clear": true, "close": true, "comparable": true, "complex": true, + "complex64": true, "complex128": true, "copy": true, "delete": true, + "error": true, "false": true, "float32": true, "float64": true, + "imag": true, "int": true, "int8": true, "int16": true, "int32": true, + "int64": true, "iota": true, "len": true, "make": true, "max": true, + "min": true, "new": true, "nil": true, "panic": true, "print": true, + "println": true, "real": true, "recover": true, "rune": true, + "string": true, "true": true, "uint": true, "uint8": true, + "uint16": true, "uint32": true, "uint64": true, "uintptr": true, +} + +var asmRegisterNames = map[string]bool{ + "AH": true, "AL": true, "AX": true, "BH": true, "BL": true, "BP": true, + "BX": true, "CH": true, "CL": true, "CS": true, "CX": true, "DH": true, + "DI": true, "DL": true, "DS": true, "DX": true, "ES": true, "FLAGS": true, + "FP": true, "FS": true, "GS": true, "IP": true, "LR": true, "PC": true, + "SB": true, "SI": true, "SP": true, "TLS": true, "ZR": true, + "EAX": true, "EBP": true, "EBX": true, "ECX": true, "EDI": true, + "EDX": true, "EFLAGS": true, "EIP": true, "ESI": true, "ESP": true, + "RAX": true, "RBP": true, "RBX": true, "RCX": true, "RDI": true, + "RDX": true, "RIP": true, "RSB": true, "RSI": true, "RSP": true, +} + +type Span struct { + Text string + Color color.NRGBA + Italic bool + Bold bool +} diff --git a/internal/syntax/syntax_test.go b/internal/syntax/syntax_test.go new file mode 100644 index 0000000..5f2b1a4 --- /dev/null +++ b/internal/syntax/syntax_test.go @@ -0,0 +1,107 @@ +package syntax + +import ( + "image/color" + "strings" + "testing" +) + +func TestNormalizeSyntaxStyle(t *testing.T) { + if got := NormalizeStyle("jetbrains-dark"); got != StyleDarcula { + t.Fatalf("expected darcula alias, got %q", got) + } + if got := NormalizeStyle("unknown"); got != StyleGoLand { + t.Fatalf("expected unknown style to fall back to goland, got %q", got) + } +} + +func TestHighlightGoLine(t *testing.T) { + palette := testSyntaxPalette() + spans := HighlightGo(`if len(s) > 0 { return "x" // ok }`, palette) + + assertSpanColor(t, spans, "if", palette.Keyword) + assertSpanColor(t, spans, "len", palette.Builtin) + assertSpanColor(t, spans, "0", palette.Number) + assertSpanColor(t, spans, `"x"`, palette.String) + assertSpanStyle(t, spans, "// ok }", palette.Comment, false, true) +} + +func TestHighlightAsmLine(t *testing.T) { + palette := testSyntaxPalette() + spans := HighlightAsm("CALL runtime.morestack_noctxt(SB); tail", "runtime.morestack_noctxt", palette) + + assertSpanStyle(t, spans, "CALL", palette.Mnemonic, true, false) + assertSpanColor(t, spans, "runtime.morestack_noctxt", palette.CallTarget) + assertSpanColor(t, spans, "SB", palette.Register) + assertSpanStyle(t, spans, "; tail", palette.Comment, false, true) +} + +func TestHighlightNativeAsmLine(t *testing.T) { + palette := testSyntaxPalette() + spans := HighlightAsm("MOVQ $0X10, %RAX", "", palette) + + assertSpanStyle(t, spans, "MOVQ", palette.Mnemonic, true, false) + assertSpanColor(t, spans, "$0X10", palette.Number) + assertSpanColor(t, spans, "%RAX", palette.Register) +} + +func testSyntaxPalette() Palette { + return Palette{ + Plain: testColor(1), + Keyword: testColor(2), + Builtin: testColor(3), + String: testColor(4), + Number: testColor(5), + Comment: testColor(6), + Operator: testColor(7), + Register: testColor(8), + Mnemonic: testColor(9), + Symbol: testColor(10), + LineNumber: testColor(11), + CallTarget: testColor(12), + } +} + +func testColor(v uint8) color.NRGBA { + return color.NRGBA{R: v, G: v, B: v, A: 0xff} +} + +func assertSpanColor(t *testing.T, spans []Span, text string, col color.NRGBA) { + t.Helper() + span, ok := findSpan(spans, text) + if !ok { + t.Fatalf("span %q not found in %q", text, joinedSpanText(spans)) + } + if span.Color != col { + t.Fatalf("span %q color = %#v, want %#v", text, span.Color, col) + } +} + +func assertSpanStyle(t *testing.T, spans []Span, text string, col color.NRGBA, bold, italic bool) { + t.Helper() + span, ok := findSpan(spans, text) + if !ok { + t.Fatalf("span %q not found in %q", text, joinedSpanText(spans)) + } + if span.Color != col || span.Bold != bold || span.Italic != italic { + t.Fatalf("span %q style = %#v, bold=%v italic=%v; want %#v, bold=%v italic=%v", + text, span.Color, span.Bold, span.Italic, col, bold, italic) + } +} + +func findSpan(spans []Span, text string) (Span, bool) { + for _, span := range spans { + if span.Text == text { + return span, true + } + } + return Span{}, false +} + +func joinedSpanText(spans []Span) string { + var b strings.Builder + for _, span := range spans { + b.WriteString(span.Text) + } + return b.String() +} diff --git a/internal/wasmobj/file.go b/internal/wasmobj/file.go new file mode 100644 index 0000000..bf7e50c --- /dev/null +++ b/internal/wasmobj/file.go @@ -0,0 +1,140 @@ +package wasmobj + +import ( + "fmt" + "os" + "sort" + "strings" + + "github.com/tetratelabs/wabin/binary" + "github.com/tetratelabs/wabin/wasm" + + "loov.dev/lensm/internal/disasm" +) + +var _ disasm.File = (*File)(nil) +var _ disasm.Func = (*Func)(nil) + +// File contains information about the object file. +type File struct { + module *wasm.Module + + funcs []disasm.Func +} + +func (file *File) Funcs() []disasm.Func { return file.funcs } + +// Func contains information about the executable. +type Func struct { + obj *File + index wasm.Index + name string + code *wasm.Code + sortName string +} + +func (fn *Func) Name() string { return fn.name } + +func (file *File) Close() error { + return nil +} + +func Load(path string) (*File, error) { + obj := &File{} + + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + module, err := binary.DecodeModule(data, wasm.CoreFeaturesV2) + if err != nil { + return nil, err + } + obj.module = module + + for i, fnname := range module.NameSection.FunctionNames { + code := module.CodeSection[i] + sym := &Func{ + obj: obj, + index: fnname.Index, + name: fnname.Name, + code: code, + sortName: strings.ToLower(fnname.Name), + } + obj.funcs = append(obj.funcs, sym) + } + + sort.SliceStable(obj.funcs, func(i, k int) bool { + return strings.ToLower(obj.funcs[i].Name()) < strings.ToLower(obj.funcs[k].Name()) + }) + + return obj, nil +} + +func (fn *Func) Load(opts disasm.Options) (*disasm.Code, error) { + return fn.obj.LoadCode(fn, opts), nil +} + +func (file *File) LoadCode(fn *Func, opts disasm.Options) *disasm.Code { + code := &disasm.Code{ + Name: fn.name, + } + + // TODO: https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-instr + + for i, b := range fn.code.Body { + code.Insts = append(code.Insts, disasm.Inst{ + PC: uint64(i), + Text: fmt.Sprintf("BYTE 0x%02x", b), + }) + } + return code +} + +/* +func (file *File) LoadCode(fn *Func, opts disasm.Options) *disasm.Code { + dis, err := wasmdisasm.NewDisassembly(*fn.fn, file.module) + if err != nil { + return &disasm.Code{Name: err.Error()} + } + + code := &disasm.Code{ + Name: fn.fn.Name, + } + + for i, ix := range dis.Code { + code.Insts = append(code.Insts, file.toInstr(dis, i, ix)) + } + + return code +} +func (file *File) toInstr(dis *wasmdisasm.Disassembly, i int, ix wasmdisasm.Instr) disasm.Inst { + inst := disasm.Inst{ + PC: uint64(i), + Text: ix.Op.Name + " " + file.immediatesToString(ix.Immediates), + } + + switch ix.Op.Code { + case operators.Call: + target := ix.Immediates[0].(uint32) + fn := file.module.FunctionIndexSpace[target] + inst.Text = ix.Op.Name + " " + fn.Name + inst.Call = fn.Name + + // TODO: figure out ix.Branches and ix.Block.IfElseIndex (similar) + default: + + } + + return inst +} + +func (file *File) immediatesToString(xs []interface{}) string { + var str strings.Builder + for _, im := range xs { + fmt.Fprintf(&str, " %v", im) + } + return str.String() +} +*/ diff --git a/internal/wasmobj/file_test.go b/internal/wasmobj/file_test.go new file mode 100644 index 0000000..4f00e0d --- /dev/null +++ b/internal/wasmobj/file_test.go @@ -0,0 +1,35 @@ +package wasmobj + +import ( + "path/filepath" + "regexp" + "testing" + + "loov.dev/lensm/internal/disasm" +) + +var rxByteInst = regexp.MustCompile(`^BYTE 0x[0-9a-f]{2}$`) + +func TestLoadFormatsBytes(t *testing.T) { + file, err := Load(filepath.Join("..", "..", "testdata", "c-wasm", "example.wasm")) + if err != nil { + t.Fatal(err) + } + + for _, fn := range file.funcs { + code, err := fn.Load(disasm.Options{}) + if err != nil { + t.Fatal(err) + } + if len(code.Insts) == 0 { + continue + } + for _, inst := range code.Insts { + if !rxByteInst.MatchString(inst.Text) { + t.Fatalf("instruction text = %q", inst.Text) + } + } + return + } + t.Fatal("no wasm function with body found") +} diff --git a/loader.go b/loader.go new file mode 100644 index 0000000..cb33e9a --- /dev/null +++ b/loader.go @@ -0,0 +1,136 @@ +package main + +import ( + "os" + "strings" + "time" + + "loov.dev/lensm/internal/disasm" + "loov.dev/lensm/internal/goobj" + "loov.dev/lensm/internal/wasmobj" +) + +type fileLoadRequest struct { + generation uint64 + path string +} + +type fileLoadResult struct { + generation uint64 + file disasm.File + err error +} + +func loadDisasmFile(path string) (disasm.File, error) { + if workInProgressWASM { + return wasmobj.Load(path) + } + return goobj.Load(path) +} + +// loader loads disassembly files on its own goroutine and, when watch +// is set, polls the current path for modifications with a debounce so +// a binary is not reloaded mid-write. +type loader struct { + loadFile func(string) (disasm.File, error) + watch bool + requests chan fileLoadRequest + results chan fileLoadResult + stop chan struct{} +} + +func newLoader(loadFile func(string) (disasm.File, error), watch bool) *loader { + l := &loader{ + loadFile: loadFile, + watch: watch, + requests: make(chan fileLoadRequest, 1), + results: make(chan fileLoadResult, 1), + stop: make(chan struct{}), + } + go l.run() + return l +} + +func (l *loader) Results() <-chan fileLoadResult { return l.results } + +func (l *loader) Close() { close(l.stop) } + +// Request replaces any queued request; only the latest path matters. +// Main event loop only. +func (l *loader) Request(generation uint64, path string) { + select { + case <-l.requests: + default: + } + l.requests <- fileLoadRequest{generation: generation, path: path} +} + +// finish publishes a result, replacing an unconsumed previous one and +// closing its file. +func (l *loader) finish(result fileLoadResult) { + select { + case old := <-l.results: + if old.file != nil { + _ = old.file.Close() + } + default: + } + l.results <- result +} + +func (l *loader) run() { + var lastModTime time.Time + var pendingModTime time.Time + var pendingSince time.Time + var path string + var generation uint64 + tick := time.NewTicker(150 * time.Millisecond) + defer tick.Stop() + + load := func(force bool, now time.Time) { + if path == "" { + return + } + + stat, err := os.Stat(path) + if err != nil { + l.finish(fileLoadResult{generation: generation, err: err}) + return + } + if !force && stat.ModTime().Equal(lastModTime) { + return + } + if !force { + if !stat.ModTime().Equal(pendingModTime) { + pendingModTime = stat.ModTime() + pendingSince = now + return + } + if now.Sub(pendingSince) < 300*time.Millisecond { + return + } + } + lastModTime = stat.ModTime() + pendingModTime = time.Time{} + + file, err := l.loadFile(path) + l.finish(fileLoadResult{generation: generation, file: file, err: err}) + } + + for { + select { + case req := <-l.requests: + path = strings.TrimSpace(req.path) + generation = req.generation + lastModTime = time.Time{} + pendingModTime = time.Time{} + load(true, time.Now()) + case now := <-tick.C: + if l.watch { + load(false, now) + } + case <-l.stop: + return + } + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..896f2e3 --- /dev/null +++ b/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "flag" + "fmt" + "image" + "log" + "os" + "runtime/pprof" + + "gioui.org/app" + "gioui.org/text" + "gioui.org/unit" + "gioui.org/widget/material" + + "loov.dev/lensm/internal/gui" + "loov.dev/lensm/internal/mcp" +) + +func main() { + if len(os.Args) > 1 && os.Args[1] == "mcp" { + workInProgressWASM = os.Getenv("LENSM_EXPERIMENT_WASM") != "" + os.Exit(mcp.RunCommand(loadDisasmFile, os.Args[2:])) + } + + cpuprofile := flag.String("cpuprofile", "", "enable cpu profiling") + defaults := DefaultAppSettings() + textSize := flag.Int("text-size", defaults.TextSize, "default font size") + filter := flag.String("filter", "", "filter the functions by regexp") + watch := flag.Bool("watch", false, "auto reload executable") + context := flag.Int("context", 3, "source line context") + comments := flag.String("comments", "", "comments sidecar path") + font := flag.String("font", "", "user font") + + workInProgressWASM = os.Getenv("LENSM_EXPERIMENT_WASM") != "" + + flag.Parse() + exePath := flag.Arg(0) + explicitTextSize := false + flag.Visit(func(f *flag.Flag) { + switch f.Name { + case "text-size": + explicitTextSize = true + } + }) + + if flag.NArg() > 1 { + fmt.Fprintln(os.Stderr, "lensm [exePath]") + flag.Usage() + os.Exit(2) + } + + windows := &gui.Windows{} + + theme := material.NewTheme() + theme.Shaper = text.NewShaper(text.WithCollection(gui.LoadFonts(*font))) + theme.TextSize = unit.Sp(*textSize) + + ui := NewFileUI(windows, theme) + if !explicitTextSize { + theme.TextSize = unit.Sp(ui.Settings.TextSize) + } + if exePath == "" { + exePath = ui.Settings.LastPath + } + ui.Config = FileUIConfig{ + Path: exePath, + Watch: *watch, + Context: *context, + CommentsPath: *comments, + } + ui.Funcs.SetFilter(*filter) + + windows.Open("lensm", image.Pt(1400, 900), ui.Run) + + go func() { + profile(*cpuprofile, windows.Wait) + os.Exit(0) + }() + + // This starts Gio main. + app.Main() +} + +func profile(cpuprofile string, fn func()) { + if cpuprofile != "" { + f, err := os.Create(cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + fn() +} diff --git a/navigation.go b/navigation.go new file mode 100644 index 0000000..12e2045 --- /dev/null +++ b/navigation.go @@ -0,0 +1,100 @@ +package main + +const navigationHistoryLimit = 256 + +// NavigationHistory tracks function visits independently from open tabs. +type NavigationHistory struct { + entries []string + index int +} + +func (h *NavigationHistory) Reset() { + h.entries = nil + h.index = -1 +} + +func (h *NavigationHistory) Visit(name string) { + if name == "" { + return + } + if h.index >= 0 && h.index < len(h.entries) && h.entries[h.index] == name { + return + } + if h.index+1 < len(h.entries) { + h.entries = h.entries[:h.index+1] + } + h.entries = append(h.entries, name) + if len(h.entries) > navigationHistoryLimit { + drop := len(h.entries) - navigationHistoryLimit + h.entries = h.entries[drop:] + } + h.index = len(h.entries) - 1 +} + +func (h *NavigationHistory) CanBack() bool { + return h.index > 0 +} + +func (h *NavigationHistory) CanForward() bool { + return h.index >= 0 && h.index+1 < len(h.entries) +} + +func (h *NavigationHistory) Back() (string, bool) { + if !h.CanBack() { + return "", false + } + h.index-- + return h.entries[h.index], true +} + +func (h *NavigationHistory) Forward() (string, bool) { + if !h.CanForward() { + return "", false + } + h.index++ + return h.entries[h.index], true +} + +func (h *NavigationHistory) Current() string { + if h.index < 0 || h.index >= len(h.entries) { + return "" + } + return h.entries[h.index] +} + +func (ui *FileUI) recordNavigation(name string) { + if !ui.navigatingHistory { + ui.Navigation.Visit(name) + } +} + +func (ui *FileUI) navigateBack() { + for ui.Navigation.CanBack() { + name, _ := ui.Navigation.Back() + if ui.navigateHistoryEntry(name) { + return + } + } +} + +func (ui *FileUI) navigateForward() { + for ui.Navigation.CanForward() { + name, _ := ui.Navigation.Forward() + if ui.navigateHistoryEntry(name) { + return + } + } +} + +func (ui *FileUI) navigateHistoryEntry(name string) bool { + fn := ui.findFunc(name) + if fn == nil { + return false + } + ui.navigatingHistory = true + ui.openTab(fn, false) + ui.navigatingHistory = false + ui.copyStatus = "" + ui.invalidateMain() + return true +} diff --git a/navigation_test.go b/navigation_test.go new file mode 100644 index 0000000..42c8724 --- /dev/null +++ b/navigation_test.go @@ -0,0 +1,51 @@ +package main + +import "testing" + +func TestNavigationHistory(t *testing.T) { + var history NavigationHistory + history.Reset() + history.Visit("main.A") + history.Visit("main.B") + history.Visit("main.C") + + if got, ok := history.Back(); !ok || got != "main.B" { + t.Fatalf("Back() = %q, %v", got, ok) + } + if got, ok := history.Back(); !ok || got != "main.A" { + t.Fatalf("Back() = %q, %v", got, ok) + } + if _, ok := history.Back(); ok { + t.Fatal("Back succeeded at beginning") + } + if got, ok := history.Forward(); !ok || got != "main.B" { + t.Fatalf("Forward() = %q, %v", got, ok) + } +} + +func TestNavigationHistoryVisitTruncatesForward(t *testing.T) { + var history NavigationHistory + history.Reset() + history.Visit("A") + history.Visit("B") + history.Visit("C") + _, _ = history.Back() + history.Visit("D") + + if history.CanForward() { + t.Fatal("forward history was not truncated") + } + if got, _ := history.Back(); got != "B" { + t.Fatalf("Back() = %q, want B", got) + } +} + +func TestNavigationHistorySkipsDuplicateVisit(t *testing.T) { + var history NavigationHistory + history.Reset() + history.Visit("A") + history.Visit("A") + if history.CanBack() { + t.Fatal("duplicate visit created a history entry") + } +} diff --git a/packaging/macos/Info.plist b/packaging/macos/Info.plist new file mode 100644 index 0000000..5a55224 --- /dev/null +++ b/packaging/macos/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Lensm + CFBundleExecutable + lensm + CFBundleIdentifier + dev.loov.lensm + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Lensm + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.0 + CFBundleVersion + 1 + LSApplicationCategoryType + public.app-category.developer-tools + LSMinimumSystemVersion + 12.0 + NSHighResolutionCapable + + + diff --git a/screenshot.gif b/screenshot.gif new file mode 100644 index 0000000000000000000000000000000000000000..7053e42894f6668d08fe0736b3dc3aa09ed905ca GIT binary patch literal 184589 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�+wR}PC_Y#>Akg#$jChgjKTzR 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

`{3Be(5+5$H#(xLuAo8Mtl;Q)Kg-U=GPw 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

Y6KZ5Yi`Fh|FXzNM-97%FnU*Phg4 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 literal 0 HcmV?d00001 diff --git a/scripts/build-macos-dmg.sh b/scripts/build-macos-dmg.sh new file mode 100755 index 0000000..12c8685 --- /dev/null +++ b/scripts/build-macos-dmg.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +set -euo pipefail + +version="${1:-0.0.0}" +build_number="${2:-1}" +expected_arch="${3:-$(go env GOARCH)}" + +case "$version" in + *[!0-9.]* | "") + echo "version must contain only digits and dots: $version" >&2 + exit 2 + ;; +esac +case "$build_number" in + *[!0-9.]* | "") + echo "build number must contain only digits and dots: $build_number" >&2 + exit 2 + ;; +esac +case "$expected_arch" in + arm64 | amd64) ;; + *) + echo "unsupported macOS architecture: $expected_arch" >&2 + exit 2 + ;; +esac + +actual_arch="$(go env GOARCH)" +if [[ "$actual_arch" != "$expected_arch" ]]; then + echo "runner architecture is $actual_arch, expected $expected_arch" >&2 + exit 2 +fi + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +dist="$root/dist" +work="$(mktemp -d "${TMPDIR:-/tmp}/lensm-dmg.XXXXXX")" +trap 'rm -rf "$work"' EXIT + +app="$work/Lensm.app" +contents="$app/Contents" +mkdir -p "$contents/MacOS" +cp "$root/packaging/macos/Info.plist" "$contents/Info.plist" + +/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $version" "$contents/Info.plist" +/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build_number" "$contents/Info.plist" +plutil -lint "$contents/Info.plist" + +( + cd "$root" + CGO_ENABLED=1 go build -trimpath -ldflags="-s -w" -o "$contents/MacOS/lensm" . +) + +# --deep is deprecated for signing (macOS 13+); sign nested code first. +codesign --force --sign - "$contents/MacOS/lensm" +codesign --force --sign - "$app" +codesign --verify --deep --strict "$app" + +image="$work/image" +mkdir -p "$image" +cp -R "$app" "$image/Lensm.app" +ln -s /Applications "$image/Applications" + +mkdir -p "$dist" +dmg="$dist/Lensm-${version}-macos-${expected_arch}.dmg" +rm -f "$dmg" +hdiutil create -quiet -volname Lensm -srcfolder "$image" -format UDZO -ov "$dmg" +hdiutil verify -quiet "$dmg" +echo "Created $dmg" diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..5580608 --- /dev/null +++ b/settings.go @@ -0,0 +1,124 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "slices" + + "loov.dev/lensm/internal/atomicfile" + "loov.dev/lensm/internal/comments" + "loov.dev/lensm/internal/syntax" +) + +type AppSettings struct { + SyntaxStyle string `json:"syntax_style"` + Dark bool `json:"dark,omitempty"` + ShowNativeAsm bool `json:"show_native_asm"` + ShowAsmHelp bool `json:"show_asm_help"` + TextSize int `json:"text_size,omitempty"` + SidebarRatio float32 `json:"sidebar_ratio,omitempty"` + LastPath string `json:"last_path,omitempty"` + OpenTabs []string `json:"open_tabs,omitempty"` + ActiveTab string `json:"active_tab,omitempty"` +} + +func DefaultAppSettings() AppSettings { + return AppSettings{ + SyntaxStyle: syntax.StyleGoLand, + ShowNativeAsm: true, + ShowAsmHelp: true, + TextSize: 12, + SidebarRatio: 0.15, + } +} + +func LoadAppSettings() (AppSettings, error) { + settings := DefaultAppSettings() + path, err := appSettingsPath() + if err != nil { + return settings, err + } + + data, err := os.ReadFile(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return settings, nil + } + return settings, err + } + if err := json.Unmarshal(data, &settings); err != nil { + // Move the unreadable file aside: callers proceed with defaults, + // and the next save would otherwise silently replace every + // preference the user had. + backup := path + ".corrupt" + if renameErr := os.Rename(path, backup); renameErr == nil { + return DefaultAppSettings(), fmt.Errorf("decode %s (moved to %s): %w", path, backup, err) + } + return DefaultAppSettings(), fmt.Errorf("decode %s: %w", path, err) + } + settings.SyntaxStyle = syntax.NormalizeStyle(settings.SyntaxStyle) + if settings.TextSize <= 0 { + settings.TextSize = DefaultAppSettings().TextSize + } + if settings.SidebarRatio <= 0 || settings.SidebarRatio >= 1 { + settings.SidebarRatio = DefaultAppSettings().SidebarRatio + } + settings.LastPath = comments.CleanPath(settings.LastPath) + settings.OpenTabs = cleanFuncNames(settings.OpenTabs) + if settings.ActiveTab != "" && !slices.Contains(settings.OpenTabs, settings.ActiveTab) { + settings.ActiveTab = "" + } + return settings, nil +} + +func SaveAppSettings(settings AppSettings) error { + settings.SyntaxStyle = syntax.NormalizeStyle(settings.SyntaxStyle) + if settings.TextSize <= 0 { + settings.TextSize = DefaultAppSettings().TextSize + } + settings.LastPath = comments.CleanPath(settings.LastPath) + settings.OpenTabs = cleanFuncNames(settings.OpenTabs) + if settings.ActiveTab != "" && !slices.Contains(settings.OpenTabs, settings.ActiveTab) { + settings.ActiveTab = "" + } + path, err := appSettingsPath() + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + return err + } + data, err := json.MarshalIndent(settings, "", " ") + if err != nil { + return err + } + data = append(data, '\n') + return atomicfile.Write(path, data, 0o644) +} + +func appSettingsPath() (string, error) { + dir, err := os.UserConfigDir() + if err != nil { + return "", err + } + return filepath.Join(dir, "lensm", "config.json"), nil +} + +func cleanFuncNames(names []string) []string { + if len(names) == 0 { + return nil + } + out := make([]string, 0, len(names)) + seen := map[string]bool{} + for _, name := range names { + if name == "" || seen[name] { + continue + } + seen[name] = true + out = append(out, name) + } + return out +} diff --git a/settings_test.go b/settings_test.go new file mode 100644 index 0000000..c3713e2 --- /dev/null +++ b/settings_test.go @@ -0,0 +1,35 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "loov.dev/lensm/internal/atomicfile" +) + +func TestAtomicWriteFileReplacesContentsAndCleansTemporaryFile(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "config.json") + if err := os.WriteFile(path, []byte("old"), 0o644); err != nil { + t.Fatal(err) + } + + if err := atomicfile.Write(path, []byte("new"), 0o644); err != nil { + t.Fatal(err) + } + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if got := string(data); got != "new" { + t.Fatalf("settings contents = %q, want new", got) + } + matches, err := filepath.Glob(filepath.Join(dir, ".lensm-settings-*.tmp")) + if err != nil { + t.Fatal(err) + } + if len(matches) != 0 { + t.Fatalf("temporary settings files remain: %v", matches) + } +} diff --git a/testdata/c-wasm/.gitignore b/testdata/c-wasm/.gitignore new file mode 100644 index 0000000..b8e7818 --- /dev/null +++ b/testdata/c-wasm/.gitignore @@ -0,0 +1,2 @@ +*.s.o +*.s \ No newline at end of file diff --git a/testdata/c-wasm/Makefile b/testdata/c-wasm/Makefile new file mode 100644 index 0000000..740827d --- /dev/null +++ b/testdata/c-wasm/Makefile @@ -0,0 +1,16 @@ +PHONY := llc +llc: + clang --target=wasm32 -emit-llvm -g -c -S example.c + llc -O0 -march=wasm32 \ + --filetype=obj \ + example.s + wasm-ld --no-entry --export-all -o example.wasm example.s.o + +PHONY := clang +clang: + clang -g --target=wasm32 --no-standard-libraries -Wl,--no-entry -o example-clang.wasm example.c + +PHONY := odump-clang +odump: + wasm-objdump -x example-clang.wasm + wasm-objdump -x example.wasm diff --git a/testdata/c-wasm/example-clang.wasm b/testdata/c-wasm/example-clang.wasm new file mode 100755 index 0000000000000000000000000000000000000000..207eeb78528e54a5837efc350ca4ae8e11e73cd7 GIT binary patch literal 1016 zcmZuwOK;Oa5T03k9lzqdDz#NFyP_=>q5(t+Y7x+=DpI*~ixiw>iP9u?u$@9r>9Gem z^jN8Y#82SJf8rN#Vb)1YkyvSFcHZBNb|Fm12mn~Xw`?}UnPD&hlj8&txq-ke8)w)& zJ2$;TnOS2sj+0XcM%e2Cy622tB35R1&gg(-&9XEE6UI=Q!W7V8HVJGn(zci_d6P>^ za9I$Rv_(PKucgyO=?X`<@1@7~*}m`&i!hJ&wJ>E-_@X$J0aXeb%=gioyGwI-S(HTi zSXRhgA$RWdWmQy5-VmMtBU{hm&Jj`j?H*kazY;gt zg?(^aGn+RWurG8U66HycwZmB?`FYZ{X6H${X0i)Y!$D6+0?!#4Hj4|)Nf`B2s^MG_ z=?=Dj7T2e99T#>wi8{dwZUi-M1m)$mpn`9fX_Pt{tJeOC5$t)-yt}JMwU?_j2?w?? z9n}c(&Oa(&2P1gld7kcbb+f?HAX1;mVuB=25AkUS<2;KwTAR$Hs+6tAH5}wCF1x?P*C@uL zl%JF_<=o2HvJO-3VpVCD45ADYmBOpN)?<@5c6Y%iPgkX>>TP#Uk>?Gqd80p)8mIdJ literal 0 HcmV?d00001 diff --git a/testdata/c-wasm/example.c b/testdata/c-wasm/example.c new file mode 100644 index 0000000..90de3c9 --- /dev/null +++ b/testdata/c-wasm/example.c @@ -0,0 +1,14 @@ +int internal_add(int a, int b) { + if(a > 5) { + return a - b; + } + return a + b; +} + +__attribute__((export_name("add"))) +int add(int a, int b) +{ + int res; + res = internal_add(a, b); + return res; +} \ No newline at end of file diff --git a/testdata/c-wasm/example.wasm b/testdata/c-wasm/example.wasm new file mode 100755 index 0000000000000000000000000000000000000000..0a9d1f89024bf27b5a01776362e129e5d5f1addb GIT binary patch literal 908 zcmaJLO*dC1pP=W0lER8O@j{<%w3K8RFPFMYdI7>r87_bnx0*#hISLM)M7&;O&#f z?VMf(0*SaamsfFI=~J>y?zB6CwU@5nY%h})2i1B=t~k$cu=Zo`iFbE(uk&mb z#RSkfwbwzvn}0yD2CV(m^E@kdc=g0=Q0N0BID{ApA>Z3Ao2Cg~~Nq{nyMAJF<-x0*g|bS|vj{D$%&E;9y}?mFYM^4eDCC bG1@2W#m)}+SV8Ybr|a=K5mPg_{%hg~9I(Ol literal 0 HcmV?d00001