feat(payment): standalone ZarinPal broker on pay.flatrender.ir

A generic multi-client payment gateway so FlatRender, meezi.ir and
bargevasat.ir can all pay through ZarinPal's single verified callback
domain (pay.flatrender.ir).

New Go service services/payment (clones the notification skeleton +
vendored deps):
- migration 31_payment_broker.sql — `payment` schema: client_apps,
  transactions, webhook_deliveries.
- ZarinPal v4 client ported from the proven identity PaymentService
  (request.json -> StartPay -> verify.json; codes 100/101).
- client API: POST /v1/pay/request + /v1/pay/inquiry, authed by
  X-Api-Key + HMAC body signature; GET /callback/zarinpal (the single
  verified endpoint) verifies, then 302s the user back to the site's
  return_url (signed) and fires a signed, retried webhook.
- per-client ZarinPal merchant override (default = shared merchant);
  amount stored canonically in Rial, unit to ZarinPal env-configurable.
- admin API /v1/admin/* (FlatRender admin JWT): client-app CRUD +
  key issue/rotate + transactions list.

Deploy wiring: payment-svc in docker-compose.v2.yml (host port 1607),
pay.flatrender.ir server block in mirror-nginx conf, ENV_FILE +
README updates (cert SAN + manual migration note).

Admin UI: src/components/admin/PaymentsAdmin.tsx (client apps with
one-time key reveal + rotate, transactions table) + /admin/payments
page + nav link + fa/en strings; pay-admin proxy route to payment-svc.

Docs/SDK: deploy/PAYMENTS.md (integration contract) + deploy/sdk/flatpay.js
(zero-dep Node client + webhook verifier) for meezi/any site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-15 23:59:54 +03:30
parent 896ce3dfa9
commit ec51e87d2d
1830 changed files with 899129 additions and 8 deletions
+27
View File
@@ -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.
@@ -0,0 +1,716 @@
// 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 arch defines architecture-specific information and support functions.
package arch
import (
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/arm"
"github.com/twitchyliquid64/golang-asm/obj/arm64"
"github.com/twitchyliquid64/golang-asm/obj/mips"
"github.com/twitchyliquid64/golang-asm/obj/ppc64"
"github.com/twitchyliquid64/golang-asm/obj/riscv"
"github.com/twitchyliquid64/golang-asm/obj/s390x"
"github.com/twitchyliquid64/golang-asm/obj/wasm"
"github.com/twitchyliquid64/golang-asm/obj/x86"
"fmt"
"strings"
)
// Pseudo-registers whose names are the constant name without the leading R.
const (
RFP = -(iota + 1)
RSB
RSP
RPC
)
// Arch wraps the link architecture object with more architecture-specific information.
type Arch struct {
*obj.LinkArch
// Map of instruction names to enumeration.
Instructions map[string]obj.As
// Map of register names to enumeration.
Register map[string]int16
// Table of register prefix names. These are things like R for R(0) and SPR for SPR(268).
RegisterPrefix map[string]bool
// RegisterNumber converts R(10) into arm.REG_R10.
RegisterNumber func(string, int16) (int16, bool)
// Instruction is a jump.
IsJump func(word string) bool
}
// nilRegisterNumber is the register number function for architectures
// that do not accept the R(N) notation. It always returns failure.
func nilRegisterNumber(name string, n int16) (int16, bool) {
return 0, false
}
// Set configures the architecture specified by GOARCH and returns its representation.
// It returns nil if GOARCH is not recognized.
func Set(GOARCH string) *Arch {
switch GOARCH {
case "386":
return archX86(&x86.Link386)
case "amd64":
return archX86(&x86.Linkamd64)
case "arm":
return archArm()
case "arm64":
return archArm64()
case "mips":
return archMips(&mips.Linkmips)
case "mipsle":
return archMips(&mips.Linkmipsle)
case "mips64":
return archMips64(&mips.Linkmips64)
case "mips64le":
return archMips64(&mips.Linkmips64le)
case "ppc64":
return archPPC64(&ppc64.Linkppc64)
case "ppc64le":
return archPPC64(&ppc64.Linkppc64le)
case "riscv64":
return archRISCV64()
case "s390x":
return archS390x()
case "wasm":
return archWasm()
}
return nil
}
func jumpX86(word string) bool {
return word[0] == 'J' || word == "CALL" || strings.HasPrefix(word, "LOOP") || word == "XBEGIN"
}
func jumpRISCV(word string) bool {
switch word {
case "BEQ", "BEQZ", "BGE", "BGEU", "BGEZ", "BGT", "BGTU", "BGTZ", "BLE", "BLEU", "BLEZ",
"BLT", "BLTU", "BLTZ", "BNE", "BNEZ", "CALL", "JAL", "JALR", "JMP":
return true
}
return false
}
func jumpWasm(word string) bool {
return word == "JMP" || word == "CALL" || word == "Call" || word == "Br" || word == "BrIf"
}
func archX86(linkArch *obj.LinkArch) *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
for i, s := range x86.Register {
register[s] = int16(i + x86.REG_AL)
}
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
// Register prefix not used on this architecture.
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range x86.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseAMD64
}
}
// Annoying aliases.
instructions["JA"] = x86.AJHI /* alternate */
instructions["JAE"] = x86.AJCC /* alternate */
instructions["JB"] = x86.AJCS /* alternate */
instructions["JBE"] = x86.AJLS /* alternate */
instructions["JC"] = x86.AJCS /* alternate */
instructions["JCC"] = x86.AJCC /* carry clear (CF = 0) */
instructions["JCS"] = x86.AJCS /* carry set (CF = 1) */
instructions["JE"] = x86.AJEQ /* alternate */
instructions["JEQ"] = x86.AJEQ /* equal (ZF = 1) */
instructions["JG"] = x86.AJGT /* alternate */
instructions["JGE"] = x86.AJGE /* greater than or equal (signed) (SF = OF) */
instructions["JGT"] = x86.AJGT /* greater than (signed) (ZF = 0 && SF = OF) */
instructions["JHI"] = x86.AJHI /* higher (unsigned) (CF = 0 && ZF = 0) */
instructions["JHS"] = x86.AJCC /* alternate */
instructions["JL"] = x86.AJLT /* alternate */
instructions["JLE"] = x86.AJLE /* less than or equal (signed) (ZF = 1 || SF != OF) */
instructions["JLO"] = x86.AJCS /* alternate */
instructions["JLS"] = x86.AJLS /* lower or same (unsigned) (CF = 1 || ZF = 1) */
instructions["JLT"] = x86.AJLT /* less than (signed) (SF != OF) */
instructions["JMI"] = x86.AJMI /* negative (minus) (SF = 1) */
instructions["JNA"] = x86.AJLS /* alternate */
instructions["JNAE"] = x86.AJCS /* alternate */
instructions["JNB"] = x86.AJCC /* alternate */
instructions["JNBE"] = x86.AJHI /* alternate */
instructions["JNC"] = x86.AJCC /* alternate */
instructions["JNE"] = x86.AJNE /* not equal (ZF = 0) */
instructions["JNG"] = x86.AJLE /* alternate */
instructions["JNGE"] = x86.AJLT /* alternate */
instructions["JNL"] = x86.AJGE /* alternate */
instructions["JNLE"] = x86.AJGT /* alternate */
instructions["JNO"] = x86.AJOC /* alternate */
instructions["JNP"] = x86.AJPC /* alternate */
instructions["JNS"] = x86.AJPL /* alternate */
instructions["JNZ"] = x86.AJNE /* alternate */
instructions["JO"] = x86.AJOS /* alternate */
instructions["JOC"] = x86.AJOC /* overflow clear (OF = 0) */
instructions["JOS"] = x86.AJOS /* overflow set (OF = 1) */
instructions["JP"] = x86.AJPS /* alternate */
instructions["JPC"] = x86.AJPC /* parity clear (PF = 0) */
instructions["JPE"] = x86.AJPS /* alternate */
instructions["JPL"] = x86.AJPL /* non-negative (plus) (SF = 0) */
instructions["JPO"] = x86.AJPC /* alternate */
instructions["JPS"] = x86.AJPS /* parity set (PF = 1) */
instructions["JS"] = x86.AJMI /* alternate */
instructions["JZ"] = x86.AJEQ /* alternate */
instructions["MASKMOVDQU"] = x86.AMASKMOVOU
instructions["MOVD"] = x86.AMOVQ
instructions["MOVDQ2Q"] = x86.AMOVQ
instructions["MOVNTDQ"] = x86.AMOVNTO
instructions["MOVOA"] = x86.AMOVO
instructions["PSLLDQ"] = x86.APSLLO
instructions["PSRLDQ"] = x86.APSRLO
instructions["PADDD"] = x86.APADDL
return &Arch{
LinkArch: linkArch,
Instructions: instructions,
Register: register,
RegisterPrefix: nil,
RegisterNumber: nilRegisterNumber,
IsJump: jumpX86,
}
}
func archArm() *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// Note that there is no list of names as there is for x86.
for i := arm.REG_R0; i < arm.REG_SPSR; i++ {
register[obj.Rconv(i)] = int16(i)
}
// Avoid unintentionally clobbering g using R10.
delete(register, "R10")
register["g"] = arm.REG_R10
for i := 0; i < 16; i++ {
register[fmt.Sprintf("C%d", i)] = int16(i)
}
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
register["SP"] = RSP
registerPrefix := map[string]bool{
"F": true,
"R": true,
}
// special operands for DMB/DSB instructions
register["MB_SY"] = arm.REG_MB_SY
register["MB_ST"] = arm.REG_MB_ST
register["MB_ISH"] = arm.REG_MB_ISH
register["MB_ISHST"] = arm.REG_MB_ISHST
register["MB_NSH"] = arm.REG_MB_NSH
register["MB_NSHST"] = arm.REG_MB_NSHST
register["MB_OSH"] = arm.REG_MB_OSH
register["MB_OSHST"] = arm.REG_MB_OSHST
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range arm.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseARM
}
}
// Annoying aliases.
instructions["B"] = obj.AJMP
instructions["BL"] = obj.ACALL
// MCR differs from MRC by the way fields of the word are encoded.
// (Details in arm.go). Here we add the instruction so parse will find
// it, but give it an opcode number known only to us.
instructions["MCR"] = aMCR
return &Arch{
LinkArch: &arm.Linkarm,
Instructions: instructions,
Register: register,
RegisterPrefix: registerPrefix,
RegisterNumber: armRegisterNumber,
IsJump: jumpArm,
}
}
func archArm64() *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// Note that there is no list of names as there is for 386 and amd64.
register[obj.Rconv(arm64.REGSP)] = int16(arm64.REGSP)
for i := arm64.REG_R0; i <= arm64.REG_R31; i++ {
register[obj.Rconv(i)] = int16(i)
}
// Rename R18 to R18_PLATFORM to avoid accidental use.
register["R18_PLATFORM"] = register["R18"]
delete(register, "R18")
for i := arm64.REG_F0; i <= arm64.REG_F31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := arm64.REG_V0; i <= arm64.REG_V31; i++ {
register[obj.Rconv(i)] = int16(i)
}
// System registers.
for i := 0; i < len(arm64.SystemReg); i++ {
register[arm64.SystemReg[i].Name] = arm64.SystemReg[i].Reg
}
register["LR"] = arm64.REGLINK
register["DAIFSet"] = arm64.REG_DAIFSet
register["DAIFClr"] = arm64.REG_DAIFClr
register["PLDL1KEEP"] = arm64.REG_PLDL1KEEP
register["PLDL1STRM"] = arm64.REG_PLDL1STRM
register["PLDL2KEEP"] = arm64.REG_PLDL2KEEP
register["PLDL2STRM"] = arm64.REG_PLDL2STRM
register["PLDL3KEEP"] = arm64.REG_PLDL3KEEP
register["PLDL3STRM"] = arm64.REG_PLDL3STRM
register["PLIL1KEEP"] = arm64.REG_PLIL1KEEP
register["PLIL1STRM"] = arm64.REG_PLIL1STRM
register["PLIL2KEEP"] = arm64.REG_PLIL2KEEP
register["PLIL2STRM"] = arm64.REG_PLIL2STRM
register["PLIL3KEEP"] = arm64.REG_PLIL3KEEP
register["PLIL3STRM"] = arm64.REG_PLIL3STRM
register["PSTL1KEEP"] = arm64.REG_PSTL1KEEP
register["PSTL1STRM"] = arm64.REG_PSTL1STRM
register["PSTL2KEEP"] = arm64.REG_PSTL2KEEP
register["PSTL2STRM"] = arm64.REG_PSTL2STRM
register["PSTL3KEEP"] = arm64.REG_PSTL3KEEP
register["PSTL3STRM"] = arm64.REG_PSTL3STRM
// Conditional operators, like EQ, NE, etc.
register["EQ"] = arm64.COND_EQ
register["NE"] = arm64.COND_NE
register["HS"] = arm64.COND_HS
register["CS"] = arm64.COND_HS
register["LO"] = arm64.COND_LO
register["CC"] = arm64.COND_LO
register["MI"] = arm64.COND_MI
register["PL"] = arm64.COND_PL
register["VS"] = arm64.COND_VS
register["VC"] = arm64.COND_VC
register["HI"] = arm64.COND_HI
register["LS"] = arm64.COND_LS
register["GE"] = arm64.COND_GE
register["LT"] = arm64.COND_LT
register["GT"] = arm64.COND_GT
register["LE"] = arm64.COND_LE
register["AL"] = arm64.COND_AL
register["NV"] = arm64.COND_NV
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
register["SP"] = RSP
// Avoid unintentionally clobbering g using R28.
delete(register, "R28")
register["g"] = arm64.REG_R28
registerPrefix := map[string]bool{
"F": true,
"R": true,
"V": true,
}
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range arm64.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseARM64
}
}
// Annoying aliases.
instructions["B"] = arm64.AB
instructions["BL"] = arm64.ABL
return &Arch{
LinkArch: &arm64.Linkarm64,
Instructions: instructions,
Register: register,
RegisterPrefix: registerPrefix,
RegisterNumber: arm64RegisterNumber,
IsJump: jumpArm64,
}
}
func archPPC64(linkArch *obj.LinkArch) *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// Note that there is no list of names as there is for x86.
for i := ppc64.REG_R0; i <= ppc64.REG_R31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := ppc64.REG_F0; i <= ppc64.REG_F31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := ppc64.REG_V0; i <= ppc64.REG_V31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := ppc64.REG_VS0; i <= ppc64.REG_VS63; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := ppc64.REG_CR0; i <= ppc64.REG_CR7; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ {
register[obj.Rconv(i)] = int16(i)
}
register["CR"] = ppc64.REG_CR
register["XER"] = ppc64.REG_XER
register["LR"] = ppc64.REG_LR
register["CTR"] = ppc64.REG_CTR
register["FPSCR"] = ppc64.REG_FPSCR
register["MSR"] = ppc64.REG_MSR
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
// Avoid unintentionally clobbering g using R30.
delete(register, "R30")
register["g"] = ppc64.REG_R30
registerPrefix := map[string]bool{
"CR": true,
"F": true,
"R": true,
"SPR": true,
}
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range ppc64.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABasePPC64
}
}
// Annoying aliases.
instructions["BR"] = ppc64.ABR
instructions["BL"] = ppc64.ABL
return &Arch{
LinkArch: linkArch,
Instructions: instructions,
Register: register,
RegisterPrefix: registerPrefix,
RegisterNumber: ppc64RegisterNumber,
IsJump: jumpPPC64,
}
}
func archMips(linkArch *obj.LinkArch) *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// Note that there is no list of names as there is for x86.
for i := mips.REG_R0; i <= mips.REG_R31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_F0; i <= mips.REG_F31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_M0; i <= mips.REG_M31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_FCR0; i <= mips.REG_FCR31; i++ {
register[obj.Rconv(i)] = int16(i)
}
register["HI"] = mips.REG_HI
register["LO"] = mips.REG_LO
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
// Avoid unintentionally clobbering g using R30.
delete(register, "R30")
register["g"] = mips.REG_R30
registerPrefix := map[string]bool{
"F": true,
"FCR": true,
"M": true,
"R": true,
}
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range mips.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseMIPS
}
}
// Annoying alias.
instructions["JAL"] = mips.AJAL
return &Arch{
LinkArch: linkArch,
Instructions: instructions,
Register: register,
RegisterPrefix: registerPrefix,
RegisterNumber: mipsRegisterNumber,
IsJump: jumpMIPS,
}
}
func archMips64(linkArch *obj.LinkArch) *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// Note that there is no list of names as there is for x86.
for i := mips.REG_R0; i <= mips.REG_R31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_F0; i <= mips.REG_F31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_M0; i <= mips.REG_M31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_FCR0; i <= mips.REG_FCR31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := mips.REG_W0; i <= mips.REG_W31; i++ {
register[obj.Rconv(i)] = int16(i)
}
register["HI"] = mips.REG_HI
register["LO"] = mips.REG_LO
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
// Avoid unintentionally clobbering g using R30.
delete(register, "R30")
register["g"] = mips.REG_R30
// Avoid unintentionally clobbering RSB using R28.
delete(register, "R28")
register["RSB"] = mips.REG_R28
registerPrefix := map[string]bool{
"F": true,
"FCR": true,
"M": true,
"R": true,
"W": true,
}
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range mips.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseMIPS
}
}
// Annoying alias.
instructions["JAL"] = mips.AJAL
return &Arch{
LinkArch: linkArch,
Instructions: instructions,
Register: register,
RegisterPrefix: registerPrefix,
RegisterNumber: mipsRegisterNumber,
IsJump: jumpMIPS,
}
}
func archRISCV64() *Arch {
register := make(map[string]int16)
// Standard register names.
for i := riscv.REG_X0; i <= riscv.REG_X31; i++ {
name := fmt.Sprintf("X%d", i-riscv.REG_X0)
register[name] = int16(i)
}
for i := riscv.REG_F0; i <= riscv.REG_F31; i++ {
name := fmt.Sprintf("F%d", i-riscv.REG_F0)
register[name] = int16(i)
}
// General registers with ABI names.
register["ZERO"] = riscv.REG_ZERO
register["RA"] = riscv.REG_RA
register["SP"] = riscv.REG_SP
register["GP"] = riscv.REG_GP
register["TP"] = riscv.REG_TP
register["T0"] = riscv.REG_T0
register["T1"] = riscv.REG_T1
register["T2"] = riscv.REG_T2
register["S0"] = riscv.REG_S0
register["S1"] = riscv.REG_S1
register["A0"] = riscv.REG_A0
register["A1"] = riscv.REG_A1
register["A2"] = riscv.REG_A2
register["A3"] = riscv.REG_A3
register["A4"] = riscv.REG_A4
register["A5"] = riscv.REG_A5
register["A6"] = riscv.REG_A6
register["A7"] = riscv.REG_A7
register["S2"] = riscv.REG_S2
register["S3"] = riscv.REG_S3
register["S4"] = riscv.REG_S4
register["S5"] = riscv.REG_S5
register["S6"] = riscv.REG_S6
register["S7"] = riscv.REG_S7
register["S8"] = riscv.REG_S8
register["S9"] = riscv.REG_S9
register["S10"] = riscv.REG_S10
register["S11"] = riscv.REG_S11
register["T3"] = riscv.REG_T3
register["T4"] = riscv.REG_T4
register["T5"] = riscv.REG_T5
register["T6"] = riscv.REG_T6
// Go runtime register names.
register["g"] = riscv.REG_G
register["CTXT"] = riscv.REG_CTXT
register["TMP"] = riscv.REG_TMP
// ABI names for floating point register.
register["FT0"] = riscv.REG_FT0
register["FT1"] = riscv.REG_FT1
register["FT2"] = riscv.REG_FT2
register["FT3"] = riscv.REG_FT3
register["FT4"] = riscv.REG_FT4
register["FT5"] = riscv.REG_FT5
register["FT6"] = riscv.REG_FT6
register["FT7"] = riscv.REG_FT7
register["FS0"] = riscv.REG_FS0
register["FS1"] = riscv.REG_FS1
register["FA0"] = riscv.REG_FA0
register["FA1"] = riscv.REG_FA1
register["FA2"] = riscv.REG_FA2
register["FA3"] = riscv.REG_FA3
register["FA4"] = riscv.REG_FA4
register["FA5"] = riscv.REG_FA5
register["FA6"] = riscv.REG_FA6
register["FA7"] = riscv.REG_FA7
register["FS2"] = riscv.REG_FS2
register["FS3"] = riscv.REG_FS3
register["FS4"] = riscv.REG_FS4
register["FS5"] = riscv.REG_FS5
register["FS6"] = riscv.REG_FS6
register["FS7"] = riscv.REG_FS7
register["FS8"] = riscv.REG_FS8
register["FS9"] = riscv.REG_FS9
register["FS10"] = riscv.REG_FS10
register["FS11"] = riscv.REG_FS11
register["FT8"] = riscv.REG_FT8
register["FT9"] = riscv.REG_FT9
register["FT10"] = riscv.REG_FT10
register["FT11"] = riscv.REG_FT11
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range riscv.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseRISCV
}
}
return &Arch{
LinkArch: &riscv.LinkRISCV64,
Instructions: instructions,
Register: register,
RegisterPrefix: nil,
RegisterNumber: nilRegisterNumber,
IsJump: jumpRISCV,
}
}
func archS390x() *Arch {
register := make(map[string]int16)
// Create maps for easy lookup of instruction names etc.
// Note that there is no list of names as there is for x86.
for i := s390x.REG_R0; i <= s390x.REG_R15; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := s390x.REG_F0; i <= s390x.REG_F15; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := s390x.REG_V0; i <= s390x.REG_V31; i++ {
register[obj.Rconv(i)] = int16(i)
}
for i := s390x.REG_AR0; i <= s390x.REG_AR15; i++ {
register[obj.Rconv(i)] = int16(i)
}
register["LR"] = s390x.REG_LR
// Pseudo-registers.
register["SB"] = RSB
register["FP"] = RFP
register["PC"] = RPC
// Avoid unintentionally clobbering g using R13.
delete(register, "R13")
register["g"] = s390x.REG_R13
registerPrefix := map[string]bool{
"AR": true,
"F": true,
"R": true,
}
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range s390x.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseS390X
}
}
// Annoying aliases.
instructions["BR"] = s390x.ABR
instructions["BL"] = s390x.ABL
return &Arch{
LinkArch: &s390x.Links390x,
Instructions: instructions,
Register: register,
RegisterPrefix: registerPrefix,
RegisterNumber: s390xRegisterNumber,
IsJump: jumpS390x,
}
}
func archWasm() *Arch {
instructions := make(map[string]obj.As)
for i, s := range obj.Anames {
instructions[s] = obj.As(i)
}
for i, s := range wasm.Anames {
if obj.As(i) >= obj.A_ARCHSPECIFIC {
instructions[s] = obj.As(i) + obj.ABaseWasm
}
}
return &Arch{
LinkArch: &wasm.Linkwasm,
Instructions: instructions,
Register: wasm.Register,
RegisterPrefix: nil,
RegisterNumber: nilRegisterNumber,
IsJump: jumpWasm,
}
}
@@ -0,0 +1,257 @@
// 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.
// This file encapsulates some of the odd characteristics of the ARM
// instruction set, to minimize its interaction with the core of the
// assembler.
package arch
import (
"strings"
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/arm"
)
var armLS = map[string]uint8{
"U": arm.C_UBIT,
"S": arm.C_SBIT,
"W": arm.C_WBIT,
"P": arm.C_PBIT,
"PW": arm.C_WBIT | arm.C_PBIT,
"WP": arm.C_WBIT | arm.C_PBIT,
}
var armSCOND = map[string]uint8{
"EQ": arm.C_SCOND_EQ,
"NE": arm.C_SCOND_NE,
"CS": arm.C_SCOND_HS,
"HS": arm.C_SCOND_HS,
"CC": arm.C_SCOND_LO,
"LO": arm.C_SCOND_LO,
"MI": arm.C_SCOND_MI,
"PL": arm.C_SCOND_PL,
"VS": arm.C_SCOND_VS,
"VC": arm.C_SCOND_VC,
"HI": arm.C_SCOND_HI,
"LS": arm.C_SCOND_LS,
"GE": arm.C_SCOND_GE,
"LT": arm.C_SCOND_LT,
"GT": arm.C_SCOND_GT,
"LE": arm.C_SCOND_LE,
"AL": arm.C_SCOND_NONE,
"U": arm.C_UBIT,
"S": arm.C_SBIT,
"W": arm.C_WBIT,
"P": arm.C_PBIT,
"PW": arm.C_WBIT | arm.C_PBIT,
"WP": arm.C_WBIT | arm.C_PBIT,
"F": arm.C_FBIT,
"IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT,
"IAW": arm.C_WBIT | arm.C_UBIT,
"DBW": arm.C_WBIT | arm.C_PBIT,
"DAW": arm.C_WBIT,
"IB": arm.C_PBIT | arm.C_UBIT,
"IA": arm.C_UBIT,
"DB": arm.C_PBIT,
"DA": 0,
}
var armJump = map[string]bool{
"B": true,
"BL": true,
"BX": true,
"BEQ": true,
"BNE": true,
"BCS": true,
"BHS": true,
"BCC": true,
"BLO": true,
"BMI": true,
"BPL": true,
"BVS": true,
"BVC": true,
"BHI": true,
"BLS": true,
"BGE": true,
"BLT": true,
"BGT": true,
"BLE": true,
"CALL": true,
"JMP": true,
}
func jumpArm(word string) bool {
return armJump[word]
}
// IsARMCMP reports whether the op (as defined by an arm.A* constant) is
// one of the comparison instructions that require special handling.
func IsARMCMP(op obj.As) bool {
switch op {
case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST:
return true
}
return false
}
// IsARMSTREX reports whether the op (as defined by an arm.A* constant) is
// one of the STREX-like instructions that require special handling.
func IsARMSTREX(op obj.As) bool {
switch op {
case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
return true
}
return false
}
// MCR is not defined by the obj/arm; instead we define it privately here.
// It is encoded as an MRC with a bit inside the instruction word,
// passed to arch.ARMMRCOffset.
const aMCR = arm.ALAST + 1
// IsARMMRC reports whether the op (as defined by an arm.A* constant) is
// MRC or MCR
func IsARMMRC(op obj.As) bool {
switch op {
case arm.AMRC, aMCR: // Note: aMCR is defined in this package.
return true
}
return false
}
// IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the
// BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg".
func IsARMBFX(op obj.As) bool {
switch op {
case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI:
return true
}
return false
}
// IsARMFloatCmp reports whether the op is a floating comparison instruction.
func IsARMFloatCmp(op obj.As) bool {
switch op {
case arm.ACMPF, arm.ACMPD:
return true
}
return false
}
// ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
// The difference between MRC and MCR is represented by a bit high in the word, not
// in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
// we return the opcode for MRC so that asm doesn't need to import obj/arm.
func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) {
op1 := int64(0)
if op == arm.AMRC {
op1 = 1
}
bits, ok := ParseARMCondition(cond)
if !ok {
return
}
offset = (0xe << 24) | // opcode
(op1 << 20) | // MCR/MRC
((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond
((x0 & 15) << 8) | //coprocessor number
((x1 & 7) << 21) | // coprocessor operation
((x2 & 15) << 12) | // ARM register
((x3 & 15) << 16) | // Crn
((x4 & 15) << 0) | // Crm
((x5 & 7) << 5) | // coprocessor information
(1 << 4) /* must be set */
return offset, arm.AMRC, true
}
// IsARMMULA reports whether the op (as defined by an arm.A* constant) is
// MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions.
func IsARMMULA(op obj.As) bool {
switch op {
case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT:
return true
}
return false
}
var bcode = []obj.As{
arm.ABEQ,
arm.ABNE,
arm.ABCS,
arm.ABCC,
arm.ABMI,
arm.ABPL,
arm.ABVS,
arm.ABVC,
arm.ABHI,
arm.ABLS,
arm.ABGE,
arm.ABLT,
arm.ABGT,
arm.ABLE,
arm.AB,
obj.ANOP,
}
// ARMConditionCodes handles the special condition code situation for the ARM.
// It returns a boolean to indicate success; failure means cond was unrecognized.
func ARMConditionCodes(prog *obj.Prog, cond string) bool {
if cond == "" {
return true
}
bits, ok := ParseARMCondition(cond)
if !ok {
return false
}
/* hack to make B.NE etc. work: turn it into the corresponding conditional */
if prog.As == arm.AB {
prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf]
bits = (bits &^ 0xf) | arm.C_SCOND_NONE
}
prog.Scond = bits
return true
}
// ParseARMCondition parses the conditions attached to an ARM instruction.
// The input is a single string consisting of period-separated condition
// codes, such as ".P.W". An initial period is ignored.
func ParseARMCondition(cond string) (uint8, bool) {
return parseARMCondition(cond, armLS, armSCOND)
}
func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
cond = strings.TrimPrefix(cond, ".")
if cond == "" {
return arm.C_SCOND_NONE, true
}
names := strings.Split(cond, ".")
bits := uint8(0)
for _, name := range names {
if b, present := ls[name]; present {
bits |= b
continue
}
if b, present := scond[name]; present {
bits = (bits &^ arm.C_SCOND) | b
continue
}
return 0, false
}
return bits, true
}
func armRegisterNumber(name string, n int16) (int16, bool) {
if n < 0 || 15 < n {
return 0, false
}
switch name {
case "R":
return arm.REG_R0 + n, true
case "F":
return arm.REG_F0 + n, true
}
return 0, false
}
@@ -0,0 +1,350 @@
// 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.
// This file encapsulates some of the odd characteristics of the ARM64
// instruction set, to minimize its interaction with the core of the
// assembler.
package arch
import (
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/arm64"
"errors"
)
var arm64LS = map[string]uint8{
"P": arm64.C_XPOST,
"W": arm64.C_XPRE,
}
var arm64Jump = map[string]bool{
"B": true,
"BL": true,
"BEQ": true,
"BNE": true,
"BCS": true,
"BHS": true,
"BCC": true,
"BLO": true,
"BMI": true,
"BPL": true,
"BVS": true,
"BVC": true,
"BHI": true,
"BLS": true,
"BGE": true,
"BLT": true,
"BGT": true,
"BLE": true,
"CALL": true,
"CBZ": true,
"CBZW": true,
"CBNZ": true,
"CBNZW": true,
"JMP": true,
"TBNZ": true,
"TBZ": true,
}
func jumpArm64(word string) bool {
return arm64Jump[word]
}
// IsARM64CMP reports whether the op (as defined by an arm.A* constant) is
// one of the comparison instructions that require special handling.
func IsARM64CMP(op obj.As) bool {
switch op {
case arm64.ACMN, arm64.ACMP, arm64.ATST,
arm64.ACMNW, arm64.ACMPW, arm64.ATSTW,
arm64.AFCMPS, arm64.AFCMPD,
arm64.AFCMPES, arm64.AFCMPED:
return true
}
return false
}
// IsARM64STLXR reports whether the op (as defined by an arm64.A*
// constant) is one of the STLXR-like instructions that require special
// handling.
func IsARM64STLXR(op obj.As) bool {
switch op {
case arm64.ASTLXRB, arm64.ASTLXRH, arm64.ASTLXRW, arm64.ASTLXR,
arm64.ASTXRB, arm64.ASTXRH, arm64.ASTXRW, arm64.ASTXR,
arm64.ASTXP, arm64.ASTXPW, arm64.ASTLXP, arm64.ASTLXPW:
return true
}
// atomic instructions
if arm64.IsAtomicInstruction(op) {
return true
}
return false
}
// ARM64Suffix handles the special suffix for the ARM64.
// It returns a boolean to indicate success; failure means
// cond was unrecognized.
func ARM64Suffix(prog *obj.Prog, cond string) bool {
if cond == "" {
return true
}
bits, ok := parseARM64Suffix(cond)
if !ok {
return false
}
prog.Scond = bits
return true
}
// parseARM64Suffix parses the suffix attached to an ARM64 instruction.
// The input is a single string consisting of period-separated condition
// codes, such as ".P.W". An initial period is ignored.
func parseARM64Suffix(cond string) (uint8, bool) {
if cond == "" {
return 0, true
}
return parseARMCondition(cond, arm64LS, nil)
}
func arm64RegisterNumber(name string, n int16) (int16, bool) {
switch name {
case "F":
if 0 <= n && n <= 31 {
return arm64.REG_F0 + n, true
}
case "R":
if 0 <= n && n <= 30 { // not 31
return arm64.REG_R0 + n, true
}
case "V":
if 0 <= n && n <= 31 {
return arm64.REG_V0 + n, true
}
}
return 0, false
}
// IsARM64TBL reports whether the op (as defined by an arm64.A*
// constant) is one of the table lookup instructions that require special
// handling.
func IsARM64TBL(op obj.As) bool {
return op == arm64.AVTBL
}
// ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
Rnum := (reg & 31) + int16(num<<5)
if isAmount {
if num < 0 || num > 7 {
return errors.New("index shift amount is out of range")
}
}
switch ext {
case "UXTB":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTB + Rnum
case "UXTH":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTH + Rnum
case "UXTW":
if !isAmount {
return errors.New("invalid register extension")
}
// effective address of memory is a base register value and an offset register value.
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_UXTW + Rnum
} else {
a.Reg = arm64.REG_UXTW + Rnum
}
case "UXTX":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTX + Rnum
case "SXTB":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_SXTB + Rnum
case "SXTH":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_SXTH + Rnum
case "SXTW":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTW + Rnum
} else {
a.Reg = arm64.REG_SXTW + Rnum
}
case "SXTX":
if !isAmount {
return errors.New("invalid register extension")
}
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTX + Rnum
} else {
a.Reg = arm64.REG_SXTX + Rnum
}
case "LSL":
if !isAmount {
return errors.New("invalid register extension")
}
a.Index = arm64.REG_LSL + Rnum
case "B8":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
case "B16":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
case "H4":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
case "H8":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
case "S2":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
case "S4":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
case "D1":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1D & 15) << 5)
case "D2":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
case "Q1":
if isIndex {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1Q & 15) << 5)
case "B":
if !isIndex {
return nil
}
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
a.Index = num
case "H":
if !isIndex {
return nil
}
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
a.Index = num
case "S":
if !isIndex {
return nil
}
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
a.Index = num
case "D":
if !isIndex {
return nil
}
a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
a.Index = num
default:
return errors.New("unsupported register extension type: " + ext)
}
return nil
}
// ARM64RegisterArrangement parses an ARM64 vector register arrangement.
func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
var curQ, curSize uint16
if name[0] != 'V' {
return 0, errors.New("expect V0 through V31; found: " + name)
}
if reg < 0 {
return 0, errors.New("invalid register number: " + name)
}
switch arng {
case "B8":
curSize = 0
curQ = 0
case "B16":
curSize = 0
curQ = 1
case "H4":
curSize = 1
curQ = 0
case "H8":
curSize = 1
curQ = 1
case "S2":
curSize = 2
curQ = 0
case "S4":
curSize = 2
curQ = 1
case "D1":
curSize = 3
curQ = 0
case "D2":
curSize = 3
curQ = 1
default:
return 0, errors.New("invalid arrangement in ARM64 register list")
}
return (int64(curQ) & 1 << 30) | (int64(curSize&3) << 10), nil
}
// ARM64RegisterListOffset generates offset encoding according to AArch64 specification.
func ARM64RegisterListOffset(firstReg, regCnt int, arrangement int64) (int64, error) {
offset := int64(firstReg)
switch regCnt {
case 1:
offset |= 0x7 << 12
case 2:
offset |= 0xa << 12
case 3:
offset |= 0x6 << 12
case 4:
offset |= 0x2 << 12
default:
return 0, errors.New("invalid register numbers in ARM64 register list")
}
offset |= arrangement
// arm64 uses the 60th bit to differentiate from other archs
// For more details, refer to: obj/arm64/list7.go
offset |= 1 << 60
return offset, nil
}
@@ -0,0 +1,72 @@
// 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.
// This file encapsulates some of the odd characteristics of the
// MIPS (MIPS64) instruction set, to minimize its interaction
// with the core of the assembler.
package arch
import (
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/mips"
)
func jumpMIPS(word string) bool {
switch word {
case "BEQ", "BFPF", "BFPT", "BGEZ", "BGEZAL", "BGTZ", "BLEZ", "BLTZ", "BLTZAL", "BNE", "JMP", "JAL", "CALL":
return true
}
return false
}
// IsMIPSCMP reports whether the op (as defined by an mips.A* constant) is
// one of the CMP instructions that require special handling.
func IsMIPSCMP(op obj.As) bool {
switch op {
case mips.ACMPEQF, mips.ACMPEQD, mips.ACMPGEF, mips.ACMPGED,
mips.ACMPGTF, mips.ACMPGTD:
return true
}
return false
}
// IsMIPSMUL reports whether the op (as defined by an mips.A* constant) is
// one of the MUL/DIV/REM/MADD/MSUB instructions that require special handling.
func IsMIPSMUL(op obj.As) bool {
switch op {
case mips.AMUL, mips.AMULU, mips.AMULV, mips.AMULVU,
mips.ADIV, mips.ADIVU, mips.ADIVV, mips.ADIVVU,
mips.AREM, mips.AREMU, mips.AREMV, mips.AREMVU,
mips.AMADD, mips.AMSUB:
return true
}
return false
}
func mipsRegisterNumber(name string, n int16) (int16, bool) {
switch name {
case "F":
if 0 <= n && n <= 31 {
return mips.REG_F0 + n, true
}
case "FCR":
if 0 <= n && n <= 31 {
return mips.REG_FCR0 + n, true
}
case "M":
if 0 <= n && n <= 31 {
return mips.REG_M0 + n, true
}
case "R":
if 0 <= n && n <= 31 {
return mips.REG_R0 + n, true
}
case "W":
if 0 <= n && n <= 31 {
return mips.REG_W0 + n, true
}
}
return 0, false
}
@@ -0,0 +1,102 @@
// 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.
// This file encapsulates some of the odd characteristics of the
// 64-bit PowerPC (PPC64) instruction set, to minimize its interaction
// with the core of the assembler.
package arch
import (
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/ppc64"
)
func jumpPPC64(word string) bool {
switch word {
case "BC", "BCL", "BEQ", "BGE", "BGT", "BL", "BLE", "BLT", "BNE", "BR", "BVC", "BVS", "CALL", "JMP":
return true
}
return false
}
// IsPPC64RLD reports whether the op (as defined by an ppc64.A* constant) is
// one of the RLD-like instructions that require special handling.
// The FMADD-like instructions behave similarly.
func IsPPC64RLD(op obj.As) bool {
switch op {
case ppc64.ARLDC, ppc64.ARLDCCC, ppc64.ARLDCL, ppc64.ARLDCLCC,
ppc64.ARLDCR, ppc64.ARLDCRCC, ppc64.ARLDMI, ppc64.ARLDMICC,
ppc64.ARLWMI, ppc64.ARLWMICC, ppc64.ARLWNM, ppc64.ARLWNMCC:
return true
case ppc64.AFMADD, ppc64.AFMADDCC, ppc64.AFMADDS, ppc64.AFMADDSCC,
ppc64.AFMSUB, ppc64.AFMSUBCC, ppc64.AFMSUBS, ppc64.AFMSUBSCC,
ppc64.AFNMADD, ppc64.AFNMADDCC, ppc64.AFNMADDS, ppc64.AFNMADDSCC,
ppc64.AFNMSUB, ppc64.AFNMSUBCC, ppc64.AFNMSUBS, ppc64.AFNMSUBSCC:
return true
}
return false
}
func IsPPC64ISEL(op obj.As) bool {
return op == ppc64.AISEL
}
// IsPPC64CMP reports whether the op (as defined by an ppc64.A* constant) is
// one of the CMP instructions that require special handling.
func IsPPC64CMP(op obj.As) bool {
switch op {
case ppc64.ACMP, ppc64.ACMPU, ppc64.ACMPW, ppc64.ACMPWU, ppc64.AFCMPU:
return true
}
return false
}
// IsPPC64NEG reports whether the op (as defined by an ppc64.A* constant) is
// one of the NEG-like instructions that require special handling.
func IsPPC64NEG(op obj.As) bool {
switch op {
case ppc64.AADDMECC, ppc64.AADDMEVCC, ppc64.AADDMEV, ppc64.AADDME,
ppc64.AADDZECC, ppc64.AADDZEVCC, ppc64.AADDZEV, ppc64.AADDZE,
ppc64.ACNTLZDCC, ppc64.ACNTLZD, ppc64.ACNTLZWCC, ppc64.ACNTLZW,
ppc64.AEXTSBCC, ppc64.AEXTSB, ppc64.AEXTSHCC, ppc64.AEXTSH,
ppc64.AEXTSWCC, ppc64.AEXTSW, ppc64.ANEGCC, ppc64.ANEGVCC,
ppc64.ANEGV, ppc64.ANEG, ppc64.ASLBMFEE, ppc64.ASLBMFEV,
ppc64.ASLBMTE, ppc64.ASUBMECC, ppc64.ASUBMEVCC, ppc64.ASUBMEV,
ppc64.ASUBME, ppc64.ASUBZECC, ppc64.ASUBZEVCC, ppc64.ASUBZEV,
ppc64.ASUBZE:
return true
}
return false
}
func ppc64RegisterNumber(name string, n int16) (int16, bool) {
switch name {
case "CR":
if 0 <= n && n <= 7 {
return ppc64.REG_CR0 + n, true
}
case "VS":
if 0 <= n && n <= 63 {
return ppc64.REG_VS0 + n, true
}
case "V":
if 0 <= n && n <= 31 {
return ppc64.REG_V0 + n, true
}
case "F":
if 0 <= n && n <= 31 {
return ppc64.REG_F0 + n, true
}
case "R":
if 0 <= n && n <= 31 {
return ppc64.REG_R0 + n, true
}
case "SPR":
if 0 <= n && n <= 1024 {
return ppc64.REG_SPR0 + n, true
}
}
return 0, false
}
@@ -0,0 +1,28 @@
// 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.
// This file encapsulates some of the odd characteristics of the RISCV64
// instruction set, to minimize its interaction with the core of the
// assembler.
package arch
import (
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/riscv"
)
// IsRISCV64AMO reports whether the op (as defined by a riscv.A*
// constant) is one of the AMO instructions that requires special
// handling.
func IsRISCV64AMO(op obj.As) bool {
switch op {
case riscv.ASCW, riscv.ASCD, riscv.AAMOSWAPW, riscv.AAMOSWAPD, riscv.AAMOADDW, riscv.AAMOADDD,
riscv.AAMOANDW, riscv.AAMOANDD, riscv.AAMOORW, riscv.AAMOORD, riscv.AAMOXORW, riscv.AAMOXORD,
riscv.AAMOMINW, riscv.AAMOMIND, riscv.AAMOMINUW, riscv.AAMOMINUD,
riscv.AAMOMAXW, riscv.AAMOMAXD, riscv.AAMOMAXUW, riscv.AAMOMAXUD:
return true
}
return false
}
@@ -0,0 +1,81 @@
// 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 encapsulates some of the odd characteristics of the
// s390x instruction set, to minimize its interaction
// with the core of the assembler.
package arch
import (
"github.com/twitchyliquid64/golang-asm/obj/s390x"
)
func jumpS390x(word string) bool {
switch word {
case "BRC",
"BC",
"BCL",
"BEQ",
"BGE",
"BGT",
"BL",
"BLE",
"BLEU",
"BLT",
"BLTU",
"BNE",
"BR",
"BVC",
"BVS",
"BRCT",
"BRCTG",
"CMPBEQ",
"CMPBGE",
"CMPBGT",
"CMPBLE",
"CMPBLT",
"CMPBNE",
"CMPUBEQ",
"CMPUBGE",
"CMPUBGT",
"CMPUBLE",
"CMPUBLT",
"CMPUBNE",
"CRJ",
"CGRJ",
"CLRJ",
"CLGRJ",
"CIJ",
"CGIJ",
"CLIJ",
"CLGIJ",
"CALL",
"JMP":
return true
}
return false
}
func s390xRegisterNumber(name string, n int16) (int16, bool) {
switch name {
case "AR":
if 0 <= n && n <= 15 {
return s390x.REG_AR0 + n, true
}
case "F":
if 0 <= n && n <= 15 {
return s390x.REG_F0 + n, true
}
case "R":
if 0 <= n && n <= 15 {
return s390x.REG_R0 + n, true
}
case "V":
if 0 <= n && n <= 31 {
return s390x.REG_V0 + n, true
}
}
return 0, false
}
@@ -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
}
@@ -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.
// +build darwin dragonfly freebsd linux netbsd openbsd
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
}
@@ -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.
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
package bio
func (r *Reader) sliceOS(length uint64) ([]byte, bool) {
return nil, false
}
@@ -0,0 +1,43 @@
// 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 bio
import (
"io"
"log"
)
// MustClose closes Closer c and calls log.Fatal if it returns a non-nil error.
func MustClose(c io.Closer) {
if err := c.Close(); err != nil {
log.Fatal(err)
}
}
// MustWriter returns a Writer that wraps the provided Writer,
// except that it calls log.Fatal instead of returning a non-nil error.
func MustWriter(w io.Writer) io.Writer {
return mustWriter{w}
}
type mustWriter struct {
w io.Writer
}
func (w mustWriter) Write(b []byte) (int, error) {
n, err := w.w.Write(b)
if err != nil {
log.Fatal(err)
}
return n, nil
}
func (w mustWriter) WriteString(s string) (int, error) {
n, err := io.WriteString(w.w, s)
if err != nil {
log.Fatal(err)
}
return n, nil
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,493 @@
// Copyright 2010 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 dwarf
// Cut, pasted, tr-and-awk'ed from tables in
// http://dwarfstd.org/doc/Dwarf3.pdf
// Table 18
const (
DW_TAG_array_type = 0x01
DW_TAG_class_type = 0x02
DW_TAG_entry_point = 0x03
DW_TAG_enumeration_type = 0x04
DW_TAG_formal_parameter = 0x05
DW_TAG_imported_declaration = 0x08
DW_TAG_label = 0x0a
DW_TAG_lexical_block = 0x0b
DW_TAG_member = 0x0d
DW_TAG_pointer_type = 0x0f
DW_TAG_reference_type = 0x10
DW_TAG_compile_unit = 0x11
DW_TAG_string_type = 0x12
DW_TAG_structure_type = 0x13
DW_TAG_subroutine_type = 0x15
DW_TAG_typedef = 0x16
DW_TAG_union_type = 0x17
DW_TAG_unspecified_parameters = 0x18
DW_TAG_variant = 0x19
DW_TAG_common_block = 0x1a
DW_TAG_common_inclusion = 0x1b
DW_TAG_inheritance = 0x1c
DW_TAG_inlined_subroutine = 0x1d
DW_TAG_module = 0x1e
DW_TAG_ptr_to_member_type = 0x1f
DW_TAG_set_type = 0x20
DW_TAG_subrange_type = 0x21
DW_TAG_with_stmt = 0x22
DW_TAG_access_declaration = 0x23
DW_TAG_base_type = 0x24
DW_TAG_catch_block = 0x25
DW_TAG_const_type = 0x26
DW_TAG_constant = 0x27
DW_TAG_enumerator = 0x28
DW_TAG_file_type = 0x29
DW_TAG_friend = 0x2a
DW_TAG_namelist = 0x2b
DW_TAG_namelist_item = 0x2c
DW_TAG_packed_type = 0x2d
DW_TAG_subprogram = 0x2e
DW_TAG_template_type_parameter = 0x2f
DW_TAG_template_value_parameter = 0x30
DW_TAG_thrown_type = 0x31
DW_TAG_try_block = 0x32
DW_TAG_variant_part = 0x33
DW_TAG_variable = 0x34
DW_TAG_volatile_type = 0x35
// Dwarf3
DW_TAG_dwarf_procedure = 0x36
DW_TAG_restrict_type = 0x37
DW_TAG_interface_type = 0x38
DW_TAG_namespace = 0x39
DW_TAG_imported_module = 0x3a
DW_TAG_unspecified_type = 0x3b
DW_TAG_partial_unit = 0x3c
DW_TAG_imported_unit = 0x3d
DW_TAG_condition = 0x3f
DW_TAG_shared_type = 0x40
// Dwarf4
DW_TAG_type_unit = 0x41
DW_TAG_rvalue_reference_type = 0x42
DW_TAG_template_alias = 0x43
// User defined
DW_TAG_lo_user = 0x4080
DW_TAG_hi_user = 0xffff
)
// Table 19
const (
DW_CHILDREN_no = 0x00
DW_CHILDREN_yes = 0x01
)
// Not from the spec, but logically belongs here
const (
DW_CLS_ADDRESS = 0x01 + iota
DW_CLS_BLOCK
DW_CLS_CONSTANT
DW_CLS_FLAG
DW_CLS_PTR // lineptr, loclistptr, macptr, rangelistptr
DW_CLS_REFERENCE
DW_CLS_ADDRLOC
DW_CLS_STRING
// Go-specific internal hackery.
DW_CLS_GO_TYPEREF
)
// Table 20
const (
DW_AT_sibling = 0x01 // reference
DW_AT_location = 0x02 // block, loclistptr
DW_AT_name = 0x03 // string
DW_AT_ordering = 0x09 // constant
DW_AT_byte_size = 0x0b // block, constant, reference
DW_AT_bit_offset = 0x0c // block, constant, reference
DW_AT_bit_size = 0x0d // block, constant, reference
DW_AT_stmt_list = 0x10 // lineptr
DW_AT_low_pc = 0x11 // address
DW_AT_high_pc = 0x12 // address
DW_AT_language = 0x13 // constant
DW_AT_discr = 0x15 // reference
DW_AT_discr_value = 0x16 // constant
DW_AT_visibility = 0x17 // constant
DW_AT_import = 0x18 // reference
DW_AT_string_length = 0x19 // block, loclistptr
DW_AT_common_reference = 0x1a // reference
DW_AT_comp_dir = 0x1b // string
DW_AT_const_value = 0x1c // block, constant, string
DW_AT_containing_type = 0x1d // reference
DW_AT_default_value = 0x1e // reference
DW_AT_inline = 0x20 // constant
DW_AT_is_optional = 0x21 // flag
DW_AT_lower_bound = 0x22 // block, constant, reference
DW_AT_producer = 0x25 // string
DW_AT_prototyped = 0x27 // flag
DW_AT_return_addr = 0x2a // block, loclistptr
DW_AT_start_scope = 0x2c // constant
DW_AT_bit_stride = 0x2e // constant
DW_AT_upper_bound = 0x2f // block, constant, reference
DW_AT_abstract_origin = 0x31 // reference
DW_AT_accessibility = 0x32 // constant
DW_AT_address_class = 0x33 // constant
DW_AT_artificial = 0x34 // flag
DW_AT_base_types = 0x35 // reference
DW_AT_calling_convention = 0x36 // constant
DW_AT_count = 0x37 // block, constant, reference
DW_AT_data_member_location = 0x38 // block, constant, loclistptr
DW_AT_decl_column = 0x39 // constant
DW_AT_decl_file = 0x3a // constant
DW_AT_decl_line = 0x3b // constant
DW_AT_declaration = 0x3c // flag
DW_AT_discr_list = 0x3d // block
DW_AT_encoding = 0x3e // constant
DW_AT_external = 0x3f // flag
DW_AT_frame_base = 0x40 // block, loclistptr
DW_AT_friend = 0x41 // reference
DW_AT_identifier_case = 0x42 // constant
DW_AT_macro_info = 0x43 // macptr
DW_AT_namelist_item = 0x44 // block
DW_AT_priority = 0x45 // reference
DW_AT_segment = 0x46 // block, loclistptr
DW_AT_specification = 0x47 // reference
DW_AT_static_link = 0x48 // block, loclistptr
DW_AT_type = 0x49 // reference
DW_AT_use_location = 0x4a // block, loclistptr
DW_AT_variable_parameter = 0x4b // flag
DW_AT_virtuality = 0x4c // constant
DW_AT_vtable_elem_location = 0x4d // block, loclistptr
// Dwarf3
DW_AT_allocated = 0x4e // block, constant, reference
DW_AT_associated = 0x4f // block, constant, reference
DW_AT_data_location = 0x50 // block
DW_AT_byte_stride = 0x51 // block, constant, reference
DW_AT_entry_pc = 0x52 // address
DW_AT_use_UTF8 = 0x53 // flag
DW_AT_extension = 0x54 // reference
DW_AT_ranges = 0x55 // rangelistptr
DW_AT_trampoline = 0x56 // address, flag, reference, string
DW_AT_call_column = 0x57 // constant
DW_AT_call_file = 0x58 // constant
DW_AT_call_line = 0x59 // constant
DW_AT_description = 0x5a // string
DW_AT_binary_scale = 0x5b // constant
DW_AT_decimal_scale = 0x5c // constant
DW_AT_small = 0x5d // reference
DW_AT_decimal_sign = 0x5e // constant
DW_AT_digit_count = 0x5f // constant
DW_AT_picture_string = 0x60 // string
DW_AT_mutable = 0x61 // flag
DW_AT_threads_scaled = 0x62 // flag
DW_AT_explicit = 0x63 // flag
DW_AT_object_pointer = 0x64 // reference
DW_AT_endianity = 0x65 // constant
DW_AT_elemental = 0x66 // flag
DW_AT_pure = 0x67 // flag
DW_AT_recursive = 0x68 // flag
DW_AT_lo_user = 0x2000 // ---
DW_AT_hi_user = 0x3fff // ---
)
// Table 21
const (
DW_FORM_addr = 0x01 // address
DW_FORM_block2 = 0x03 // block
DW_FORM_block4 = 0x04 // block
DW_FORM_data2 = 0x05 // constant
DW_FORM_data4 = 0x06 // constant, lineptr, loclistptr, macptr, rangelistptr
DW_FORM_data8 = 0x07 // constant, lineptr, loclistptr, macptr, rangelistptr
DW_FORM_string = 0x08 // string
DW_FORM_block = 0x09 // block
DW_FORM_block1 = 0x0a // block
DW_FORM_data1 = 0x0b // constant
DW_FORM_flag = 0x0c // flag
DW_FORM_sdata = 0x0d // constant
DW_FORM_strp = 0x0e // string
DW_FORM_udata = 0x0f // constant
DW_FORM_ref_addr = 0x10 // reference
DW_FORM_ref1 = 0x11 // reference
DW_FORM_ref2 = 0x12 // reference
DW_FORM_ref4 = 0x13 // reference
DW_FORM_ref8 = 0x14 // reference
DW_FORM_ref_udata = 0x15 // reference
DW_FORM_indirect = 0x16 // (see Section 7.5.3)
// Dwarf4
DW_FORM_sec_offset = 0x17 // lineptr, loclistptr, macptr, rangelistptr
DW_FORM_exprloc = 0x18 // exprloc
DW_FORM_flag_present = 0x19 // flag
DW_FORM_ref_sig8 = 0x20 // reference
// Pseudo-form: expanded to data4 on IOS, udata elsewhere.
DW_FORM_udata_pseudo = 0x99
)
// Table 24 (#operands, notes)
const (
DW_OP_addr = 0x03 // 1 constant address (size target specific)
DW_OP_deref = 0x06 // 0
DW_OP_const1u = 0x08 // 1 1-byte constant
DW_OP_const1s = 0x09 // 1 1-byte constant
DW_OP_const2u = 0x0a // 1 2-byte constant
DW_OP_const2s = 0x0b // 1 2-byte constant
DW_OP_const4u = 0x0c // 1 4-byte constant
DW_OP_const4s = 0x0d // 1 4-byte constant
DW_OP_const8u = 0x0e // 1 8-byte constant
DW_OP_const8s = 0x0f // 1 8-byte constant
DW_OP_constu = 0x10 // 1 ULEB128 constant
DW_OP_consts = 0x11 // 1 SLEB128 constant
DW_OP_dup = 0x12 // 0
DW_OP_drop = 0x13 // 0
DW_OP_over = 0x14 // 0
DW_OP_pick = 0x15 // 1 1-byte stack index
DW_OP_swap = 0x16 // 0
DW_OP_rot = 0x17 // 0
DW_OP_xderef = 0x18 // 0
DW_OP_abs = 0x19 // 0
DW_OP_and = 0x1a // 0
DW_OP_div = 0x1b // 0
DW_OP_minus = 0x1c // 0
DW_OP_mod = 0x1d // 0
DW_OP_mul = 0x1e // 0
DW_OP_neg = 0x1f // 0
DW_OP_not = 0x20 // 0
DW_OP_or = 0x21 // 0
DW_OP_plus = 0x22 // 0
DW_OP_plus_uconst = 0x23 // 1 ULEB128 addend
DW_OP_shl = 0x24 // 0
DW_OP_shr = 0x25 // 0
DW_OP_shra = 0x26 // 0
DW_OP_xor = 0x27 // 0
DW_OP_skip = 0x2f // 1 signed 2-byte constant
DW_OP_bra = 0x28 // 1 signed 2-byte constant
DW_OP_eq = 0x29 // 0
DW_OP_ge = 0x2a // 0
DW_OP_gt = 0x2b // 0
DW_OP_le = 0x2c // 0
DW_OP_lt = 0x2d // 0
DW_OP_ne = 0x2e // 0
DW_OP_lit0 = 0x30 // 0 ...
DW_OP_lit31 = 0x4f // 0 literals 0..31 = (DW_OP_lit0 + literal)
DW_OP_reg0 = 0x50 // 0 ..
DW_OP_reg31 = 0x6f // 0 reg 0..31 = (DW_OP_reg0 + regnum)
DW_OP_breg0 = 0x70 // 1 ...
DW_OP_breg31 = 0x8f // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum)
DW_OP_regx = 0x90 // 1 ULEB128 register
DW_OP_fbreg = 0x91 // 1 SLEB128 offset
DW_OP_bregx = 0x92 // 2 ULEB128 register followed by SLEB128 offset
DW_OP_piece = 0x93 // 1 ULEB128 size of piece addressed
DW_OP_deref_size = 0x94 // 1 1-byte size of data retrieved
DW_OP_xderef_size = 0x95 // 1 1-byte size of data retrieved
DW_OP_nop = 0x96 // 0
DW_OP_push_object_address = 0x97 // 0
DW_OP_call2 = 0x98 // 1 2-byte offset of DIE
DW_OP_call4 = 0x99 // 1 4-byte offset of DIE
DW_OP_call_ref = 0x9a // 1 4- or 8-byte offset of DIE
DW_OP_form_tls_address = 0x9b // 0
DW_OP_call_frame_cfa = 0x9c // 0
DW_OP_bit_piece = 0x9d // 2
DW_OP_lo_user = 0xe0
DW_OP_hi_user = 0xff
)
// Table 25
const (
DW_ATE_address = 0x01
DW_ATE_boolean = 0x02
DW_ATE_complex_float = 0x03
DW_ATE_float = 0x04
DW_ATE_signed = 0x05
DW_ATE_signed_char = 0x06
DW_ATE_unsigned = 0x07
DW_ATE_unsigned_char = 0x08
DW_ATE_imaginary_float = 0x09
DW_ATE_packed_decimal = 0x0a
DW_ATE_numeric_string = 0x0b
DW_ATE_edited = 0x0c
DW_ATE_signed_fixed = 0x0d
DW_ATE_unsigned_fixed = 0x0e
DW_ATE_decimal_float = 0x0f
DW_ATE_lo_user = 0x80
DW_ATE_hi_user = 0xff
)
// Table 26
const (
DW_DS_unsigned = 0x01
DW_DS_leading_overpunch = 0x02
DW_DS_trailing_overpunch = 0x03
DW_DS_leading_separate = 0x04
DW_DS_trailing_separate = 0x05
)
// Table 27
const (
DW_END_default = 0x00
DW_END_big = 0x01
DW_END_little = 0x02
DW_END_lo_user = 0x40
DW_END_hi_user = 0xff
)
// Table 28
const (
DW_ACCESS_public = 0x01
DW_ACCESS_protected = 0x02
DW_ACCESS_private = 0x03
)
// Table 29
const (
DW_VIS_local = 0x01
DW_VIS_exported = 0x02
DW_VIS_qualified = 0x03
)
// Table 30
const (
DW_VIRTUALITY_none = 0x00
DW_VIRTUALITY_virtual = 0x01
DW_VIRTUALITY_pure_virtual = 0x02
)
// Table 31
const (
DW_LANG_C89 = 0x0001
DW_LANG_C = 0x0002
DW_LANG_Ada83 = 0x0003
DW_LANG_C_plus_plus = 0x0004
DW_LANG_Cobol74 = 0x0005
DW_LANG_Cobol85 = 0x0006
DW_LANG_Fortran77 = 0x0007
DW_LANG_Fortran90 = 0x0008
DW_LANG_Pascal83 = 0x0009
DW_LANG_Modula2 = 0x000a
// Dwarf3
DW_LANG_Java = 0x000b
DW_LANG_C99 = 0x000c
DW_LANG_Ada95 = 0x000d
DW_LANG_Fortran95 = 0x000e
DW_LANG_PLI = 0x000f
DW_LANG_ObjC = 0x0010
DW_LANG_ObjC_plus_plus = 0x0011
DW_LANG_UPC = 0x0012
DW_LANG_D = 0x0013
// Dwarf4
DW_LANG_Python = 0x0014
// Dwarf5
DW_LANG_Go = 0x0016
DW_LANG_lo_user = 0x8000
DW_LANG_hi_user = 0xffff
)
// Table 32
const (
DW_ID_case_sensitive = 0x00
DW_ID_up_case = 0x01
DW_ID_down_case = 0x02
DW_ID_case_insensitive = 0x03
)
// Table 33
const (
DW_CC_normal = 0x01
DW_CC_program = 0x02
DW_CC_nocall = 0x03
DW_CC_lo_user = 0x40
DW_CC_hi_user = 0xff
)
// Table 34
const (
DW_INL_not_inlined = 0x00
DW_INL_inlined = 0x01
DW_INL_declared_not_inlined = 0x02
DW_INL_declared_inlined = 0x03
)
// Table 35
const (
DW_ORD_row_major = 0x00
DW_ORD_col_major = 0x01
)
// Table 36
const (
DW_DSC_label = 0x00
DW_DSC_range = 0x01
)
// Table 37
const (
DW_LNS_copy = 0x01
DW_LNS_advance_pc = 0x02
DW_LNS_advance_line = 0x03
DW_LNS_set_file = 0x04
DW_LNS_set_column = 0x05
DW_LNS_negate_stmt = 0x06
DW_LNS_set_basic_block = 0x07
DW_LNS_const_add_pc = 0x08
DW_LNS_fixed_advance_pc = 0x09
// Dwarf3
DW_LNS_set_prologue_end = 0x0a
DW_LNS_set_epilogue_begin = 0x0b
DW_LNS_set_isa = 0x0c
)
// Table 38
const (
DW_LNE_end_sequence = 0x01
DW_LNE_set_address = 0x02
DW_LNE_define_file = 0x03
DW_LNE_lo_user = 0x80
DW_LNE_hi_user = 0xff
)
// Table 39
const (
DW_MACINFO_define = 0x01
DW_MACINFO_undef = 0x02
DW_MACINFO_start_file = 0x03
DW_MACINFO_end_file = 0x04
DW_MACINFO_vendor_ext = 0xff
)
// Table 40.
const (
// operand,...
DW_CFA_nop = 0x00
DW_CFA_set_loc = 0x01 // address
DW_CFA_advance_loc1 = 0x02 // 1-byte delta
DW_CFA_advance_loc2 = 0x03 // 2-byte delta
DW_CFA_advance_loc4 = 0x04 // 4-byte delta
DW_CFA_offset_extended = 0x05 // ULEB128 register, ULEB128 offset
DW_CFA_restore_extended = 0x06 // ULEB128 register
DW_CFA_undefined = 0x07 // ULEB128 register
DW_CFA_same_value = 0x08 // ULEB128 register
DW_CFA_register = 0x09 // ULEB128 register, ULEB128 register
DW_CFA_remember_state = 0x0a
DW_CFA_restore_state = 0x0b
DW_CFA_def_cfa = 0x0c // ULEB128 register, ULEB128 offset
DW_CFA_def_cfa_register = 0x0d // ULEB128 register
DW_CFA_def_cfa_offset = 0x0e // ULEB128 offset
DW_CFA_def_cfa_expression = 0x0f // BLOCK
DW_CFA_expression = 0x10 // ULEB128 register, BLOCK
DW_CFA_offset_extended_sf = 0x11 // ULEB128 register, SLEB128 offset
DW_CFA_def_cfa_sf = 0x12 // ULEB128 register, SLEB128 offset
DW_CFA_def_cfa_offset_sf = 0x13 // SLEB128 offset
DW_CFA_val_offset = 0x14 // ULEB128, ULEB128
DW_CFA_val_offset_sf = 0x15 // ULEB128, SLEB128
DW_CFA_val_expression = 0x16 // ULEB128, BLOCK
DW_CFA_lo_user = 0x1c
DW_CFA_hi_user = 0x3f
// Opcodes that take an addend operand.
DW_CFA_advance_loc = 0x1 << 6 // +delta
DW_CFA_offset = 0x2 << 6 // +register (ULEB128 offset)
DW_CFA_restore = 0x3 << 6 // +register
)
@@ -0,0 +1,45 @@
// 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
// 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 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
}
}
@@ -0,0 +1,245 @@
// 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.printbool", 1},
{"runtime.printfloat", 1},
{"runtime.printint", 1},
{"runtime.printhex", 1},
{"runtime.printuint", 1},
{"runtime.printcomplex", 1},
{"runtime.printstring", 1},
{"runtime.printpointer", 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.cmpstring", 1},
{"runtime.intstring", 1},
{"runtime.slicebytetostring", 1},
{"runtime.slicebytetostringtmp", 1},
{"runtime.slicerunetostring", 1},
{"runtime.stringtoslicebyte", 1},
{"runtime.stringtoslicerune", 1},
{"runtime.slicecopy", 1},
{"runtime.slicestringcopy", 1},
{"runtime.decoderune", 1},
{"runtime.countrunes", 1},
{"runtime.convI2I", 1},
{"runtime.convT16", 1},
{"runtime.convT32", 1},
{"runtime.convT64", 1},
{"runtime.convTstring", 1},
{"runtime.convTslice", 1},
{"runtime.convT2E", 1},
{"runtime.convT2Enoptr", 1},
{"runtime.convT2I", 1},
{"runtime.convT2Inoptr", 1},
{"runtime.assertE2I", 1},
{"runtime.assertE2I2", 1},
{"runtime.assertI2I", 1},
{"runtime.assertI2I2", 1},
{"runtime.panicdottypeE", 1},
{"runtime.panicdottypeI", 1},
{"runtime.panicnildottype", 1},
{"runtime.ifaceeq", 1},
{"runtime.efaceeq", 1},
{"runtime.fastrand", 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.mapiterinit", 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.writeBarrier", 0},
{"runtime.typedmemmove", 1},
{"runtime.typedmemclr", 1},
{"runtime.typedslicecopy", 1},
{"runtime.selectnbsend", 1},
{"runtime.selectnbrecv", 1},
{"runtime.selectnbrecv2", 1},
{"runtime.selectsetpc", 1},
{"runtime.selectgo", 1},
{"runtime.block", 1},
{"runtime.makeslice", 1},
{"runtime.makeslice64", 1},
{"runtime.makeslicecopy", 1},
{"runtime.growslice", 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.uint64tofloat64", 1},
{"runtime.uint32tofloat64", 1},
{"runtime.complex128div", 1},
{"runtime.racefuncenter", 1},
{"runtime.racefuncenterfp", 1},
{"runtime.racefuncexit", 1},
{"runtime.raceread", 1},
{"runtime.racewrite", 1},
{"runtime.racereadrange", 1},
{"runtime.racewriterange", 1},
{"runtime.msanread", 1},
{"runtime.msanwrite", 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.x86HasPOPCNT", 0},
{"runtime.x86HasSSE41", 0},
{"runtime.x86HasFMA", 0},
{"runtime.armHasVFPv4", 0},
{"runtime.arm64HasATOMICS", 0},
{"runtime.deferproc", 1},
{"runtime.deferprocStack", 1},
{"runtime.deferreturn", 1},
{"runtime.newproc", 1},
{"runtime.panicoverflow", 1},
{"runtime.sigpanic", 1},
{"runtime.gcWriteBarrier", 0},
{"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},
}
@@ -0,0 +1,233 @@
// 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"
"github.com/twitchyliquid64/golang-asm/objabi"
"encoding/binary"
)
// 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.
//
// TODO: make each pcdata a separate symbol?
type FuncInfo struct {
Args uint32
Locals uint32
FuncID objabi.FuncID
Pcsp uint32
Pcfile uint32
Pcline uint32
Pcinline uint32
Pcdata []uint32
PcdataEnd uint32
Funcdataoff []uint32
File []CUFileIndex
InlTree []InlTreeNode
}
func (a *FuncInfo) Write(w *bytes.Buffer) {
var b [4]byte
writeUint32 := func(x uint32) {
binary.LittleEndian.PutUint32(b[:], x)
w.Write(b[:])
}
writeUint32(a.Args)
writeUint32(a.Locals)
writeUint32(uint32(a.FuncID))
writeUint32(a.Pcsp)
writeUint32(a.Pcfile)
writeUint32(a.Pcline)
writeUint32(a.Pcinline)
writeUint32(uint32(len(a.Pcdata)))
for _, x := range a.Pcdata {
writeUint32(x)
}
writeUint32(a.PcdataEnd)
writeUint32(uint32(len(a.Funcdataoff)))
for _, x := range a.Funcdataoff {
writeUint32(x)
}
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)
}
}
func (a *FuncInfo) Read(b []byte) {
readUint32 := func() uint32 {
x := binary.LittleEndian.Uint32(b)
b = b[4:]
return x
}
a.Args = readUint32()
a.Locals = readUint32()
a.FuncID = objabi.FuncID(readUint32())
a.Pcsp = readUint32()
a.Pcfile = readUint32()
a.Pcline = readUint32()
a.Pcinline = readUint32()
pcdatalen := readUint32()
a.Pcdata = make([]uint32, pcdatalen)
for i := range a.Pcdata {
a.Pcdata[i] = readUint32()
}
a.PcdataEnd = readUint32()
funcdataofflen := readUint32()
a.Funcdataoff = make([]uint32, funcdataofflen)
for i := range a.Funcdataoff {
a.Funcdataoff[i] = readUint32()
}
filelen := readUint32()
a.File = make([]CUFileIndex, filelen)
for i := range a.File {
a.File[i] = CUFileIndex(readUint32())
}
inltreelen := readUint32()
a.InlTree = make([]InlTreeNode, inltreelen)
for i := range a.InlTree {
b = a.InlTree[i].Read(b)
}
}
// 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 {
NumPcdata uint32
PcdataOff uint32
NumFuncdataoff uint32
FuncdataoffOff uint32
NumFile uint32
FileOff uint32
NumInlTree uint32
InlTreeOff uint32
Initialized bool
}
func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
var result FuncInfoLengths
const numpcdataOff = 28
result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:])
result.PcdataOff = numpcdataOff + 4
numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1)
result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:])
result.FuncdataoffOff = numfuncdataoffOff + 4
numfileOff := result.FuncdataoffOff + 4*result.NumFuncdataoff
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) uint32 { return binary.LittleEndian.Uint32(b[8:]) }
// return start and end offsets.
func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) {
return binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])
}
// return start and end offsets.
func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) {
return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:])
}
// return start and end offsets.
func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) {
return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])
}
// return start and end offsets.
func (*FuncInfo) ReadPcinline(b []byte, pcdataoffset uint32) (uint32, uint32) {
return binary.LittleEndian.Uint32(b[24:]), binary.LittleEndian.Uint32(b[pcdataoffset:])
}
// return start and end offsets.
func (*FuncInfo) ReadPcdata(b []byte, pcdataoffset uint32, k uint32) (uint32, uint32) {
return binary.LittleEndian.Uint32(b[pcdataoffset+4*k:]), binary.LittleEndian.Uint32(b[pcdataoffset+4+4*k:])
}
func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 {
return int64(binary.LittleEndian.Uint32(b[funcdataofffoff+4*k:]))
}
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
}
@@ -0,0 +1,871 @@
// 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 (
"bytes"
"github.com/twitchyliquid64/golang-asm/bio"
"crypto/sha1"
"encoding/binary"
"errors"
"fmt"
"github.com/twitchyliquid64/golang-asm/unsafeheader"
"io"
"unsafe"
)
// New object file format.
//
// Header struct {
// Magic [...]byte // "\x00go116ld"
// 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
// }
// 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 uint8
// Add int64
// Sym symRef
// }
//
// Aux [...]struct {
// Type uint8
// Sym symRef
// }
//
// Data [...]byte
// Pcdata [...]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 natually 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
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
BlkPcdata
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 = "\x00go116ld"
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) + 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
ObjFlagNeedNameExpansion // the linker needs to expand `"".` to package path in symbol names
ObjFlagFromAssembly // object is from asm src, not go
)
// Sym.Flag
const (
SymFlagDupok = 1 << iota
SymFlagLocal
SymFlagTypelink
SymFlagLeaf
SymFlagNoSplit
SymFlagReflectMethod
SymFlagGoType
SymFlagTopFrame
)
// Sym.Flag2
const (
SymFlagUsedInIface = 1 << iota
SymFlagItab
)
// 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) TopFrame() bool { return s.Flag()&SymFlagTopFrame != 0 }
func (s *Sym) UsedInIface() bool { return s.Flag2()&SymFlagUsedInIface != 0 }
func (s *Sym) IsItab() bool { return s.Flag2()&SymFlagItab != 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
}
// Hash64
type Hash64Type [Hash64Size]byte
const Hash64Size = 8
// Hash
type HashType [HashSize]byte
const HashSize = sha1.Size
// Relocation.
//
// Serialized format:
// Reloc struct {
// Off int32
// Siz uint8
// Type uint8
// Add int64
// Sym SymRef
// }
type Reloc [RelocSize]byte
const RelocSize = 4 + 1 + 1 + 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() uint8 { return r[5] }
func (r *Reloc) Add() int64 { return int64(binary.LittleEndian.Uint64(r[6:])) }
func (r *Reloc) Sym() SymRef {
return SymRef{binary.LittleEndian.Uint32(r[14:]), binary.LittleEndian.Uint32(r[18:])}
}
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 uint8) { r[5] = x }
func (r *Reloc) SetAdd(x int64) { binary.LittleEndian.PutUint64(r[6:], uint64(x)) }
func (r *Reloc) SetSym(x SymRef) {
binary.LittleEndian.PutUint32(r[14:], x.PkgIdx)
binary.LittleEndian.PutUint32(r[18:], x.SymIdx)
}
func (r *Reloc) Set(off int32, size uint8, typ uint8, 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
// TODO: more. Pcdata?
)
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[:]) }
// 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
}
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) {
var b [8]byte
binary.LittleEndian.PutUint64(b[:], x)
w.wr.Write(b[:])
w.off += 8
}
func (w *Writer) Uint32(x uint32) {
var b [4]byte
binary.LittleEndian.PutUint32(b[:], x)
w.wr.Write(b[:])
w.off += 4
}
func (w *Writer) Uint16(x uint16) {
var b [2]byte
binary.LittleEndian.PutUint16(b[:], x)
w.wr.Write(b[:])
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
rd io.ReaderAt
start uint32
h Header // keep block offsets
}
func NewReaderFromBytes(b []byte, readonly bool) *Reader {
r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), 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) int64At(off uint32) int64 {
return int64(r.uint64At(off))
}
func (r *Reader) uint32At(off uint32) uint32 {
b := r.BytesAt(off, 4)
return binary.LittleEndian.Uint32(b)
}
func (r *Reader) int32At(off uint32) int32 {
return int32(r.uint32At(off))
}
func (r *Reader) uint16At(off uint32) uint16 {
b := r.BytesAt(off, 2)
return binary.LittleEndian.Uint16(b)
}
func (r *Reader) uint8At(off uint32) uint8 {
b := r.BytesAt(off, 1)
return b[0]
}
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 ""
}
var s string
hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(&b[0])
hdr.Len = len(b)
return s
}
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] + uint32(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] + uint32(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] + uint32(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] + uint32(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] + uint32(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 (*[1 << 20]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 (*[1 << 20]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))
}
// AuxDataBase returns the base offset of the aux data block.
func (r *Reader) PcdataBase() uint32 {
return r.h.Offsets[BlkPcdata]
}
// 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) NeedNameExpansion() bool { return r.Flags()&ObjFlagNeedNameExpansion != 0 }
func (r *Reader) FromAssembly() bool { return r.Flags()&ObjFlagFromAssembly != 0 }
@@ -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
)
@@ -0,0 +1,162 @@
// 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"
"io"
"io/ioutil"
"log"
"os"
"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 := ioutil.ReadFile(s[1:])
if err != nil {
log.Fatal(err)
}
args := strings.Split(strings.TrimSpace(strings.Replace(string(slurp), "\r", "", -1)), "\n")
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() interface{} { 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")
// If there's an active experiment, include that,
// to distinguish go1.10.2 with an experiment
// from go1.10.2 without an experiment.
p := Expstring()
if p == DefaultExpstring() {
p = ""
}
sep := ""
if p != "" {
sep = " "
}
// 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.HasPrefix(Version, "devel") {
p += " buildID=" + buildID
}
}
fmt.Printf("%s version %s%s%s\n", name, Version, sep, 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() interface{} {
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 "" }
@@ -0,0 +1,54 @@
// 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 objabi
// This file defines the IDs for PCDATA and FUNCDATA instructions
// in Go binaries.
//
// These must agree with ../../../runtime/funcdata.h and
// ../../../runtime/symtab.go.
const (
PCDATA_RegMapIndex = 0 // if !go115ReduceLiveness
PCDATA_UnsafePoint = 0 // if go115ReduceLiveness
PCDATA_StackMapIndex = 1
PCDATA_InlTreeIndex = 2
FUNCDATA_ArgsPointerMaps = 0
FUNCDATA_LocalsPointerMaps = 1
FUNCDATA_RegPointerMaps = 2 // if !go115ReduceLiveness
FUNCDATA_StackObjects = 3
FUNCDATA_InlTree = 4
FUNCDATA_OpenCodedDeferInfo = 5
// 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.
ArgsSizeUnknown = -0x80000000
)
// Special PCDATA values.
const (
// PCDATA_RegMapIndex values.
//
// Only if !go115ReduceLiveness.
PCDATA_RegMapUnsafe = PCDATA_UnsafePointUnsafe // Unsafe for async preemption
// PCDATA_UnsafePoint values.
PCDATA_UnsafePointSafe = -1 // Safe for async preemption
PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption
// PCDATA_Restart1(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.
PCDATA_Restart1 = -3
PCDATA_Restart2 = -4
// Like PCDATA_Restart1, but back to function entry if async preempted.
PCDATA_RestartAtEntry = -5
)
@@ -0,0 +1,100 @@
// 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
// 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.
// Note: this list must match the list in runtime/symtab.go.
type FuncID uint8
const (
FuncID_normal FuncID = iota // not a special function
FuncID_runtime_main
FuncID_goexit
FuncID_jmpdefer
FuncID_mcall
FuncID_morestack
FuncID_mstart
FuncID_rt0_go
FuncID_asmcgocall
FuncID_sigpanic
FuncID_runfinq
FuncID_gcBgMarkWorker
FuncID_systemstack_switch
FuncID_systemstack
FuncID_cgocallback_gofunc
FuncID_gogo
FuncID_externalthreadhandler
FuncID_debugCallV1
FuncID_gopanic
FuncID_panicwrap
FuncID_handleAsyncEvent
FuncID_asyncPreempt
FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
)
// Get the function ID for the named function in the named file.
// The function should be package-qualified.
func GetFuncID(name string, isWrapper bool) FuncID {
if isWrapper {
return FuncID_wrapper
}
switch name {
case "runtime.main":
return FuncID_runtime_main
case "runtime.goexit":
return FuncID_goexit
case "runtime.jmpdefer":
return FuncID_jmpdefer
case "runtime.mcall":
return FuncID_mcall
case "runtime.morestack":
return FuncID_morestack
case "runtime.mstart":
return FuncID_mstart
case "runtime.rt0_go":
return FuncID_rt0_go
case "runtime.asmcgocall":
return FuncID_asmcgocall
case "runtime.sigpanic":
return FuncID_sigpanic
case "runtime.runfinq":
return FuncID_runfinq
case "runtime.gcBgMarkWorker":
return FuncID_gcBgMarkWorker
case "runtime.systemstack_switch":
return FuncID_systemstack_switch
case "runtime.systemstack":
return FuncID_systemstack
case "runtime.cgocallback_gofunc":
return FuncID_cgocallback_gofunc
case "runtime.gogo":
return FuncID_gogo
case "runtime.externalthreadhandler":
return FuncID_externalthreadhandler
case "runtime.debugCallV1":
return FuncID_debugCallV1
case "runtime.gopanic":
return FuncID_gopanic
case "runtime.panicwrap":
return FuncID_panicwrap
case "runtime.handleAsyncEvent":
return FuncID_handleAsyncEvent
case "runtime.asyncPreempt":
return FuncID_asyncPreempt
case "runtime.deferreturn":
// Don't show in the call stack (used when invoking defer functions)
return FuncID_wrapper
case "runtime.runOpenDeferFrame":
// Don't show in the call stack (used when invoking defer functions)
return FuncID_wrapper
case "runtime.reflectcallSave":
// Don't show in the call stack (used when invoking defer functions)
return FuncID_wrapper
}
return FuncID_normal
}
@@ -0,0 +1,109 @@
// 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
Hwindows
Haix
)
func (h *HeadType) Set(s string) error {
switch s {
case "aix":
*h = Haix
case "darwin":
*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 "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 Hwindows:
return "windows"
}
return fmt.Sprintf("HeadType(%d)", *h)
}
@@ -0,0 +1,114 @@
// 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 (
"os"
"path/filepath"
"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)
}
start := 0
for i := 0; i <= len(rewrites); i++ {
if i == len(rewrites) || rewrites[i] == ';' {
if new, ok := applyRewrite(abs, rewrites[start:i]); ok {
abs = new
goto Rewritten
}
start = i + 1
}
}
if hasPathPrefix(abs, GOROOT) {
abs = "$GOROOT" + abs[len(GOROOT):]
}
Rewritten:
if abs == "" {
abs = "??"
}
return abs
}
// 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] == '\\'
}
@@ -0,0 +1,41 @@
// 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 "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)
}
@@ -0,0 +1,269 @@
// 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_WEAKADDROFF resolves just like R_ADDROFF but is a weak relocation.
// 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_WEAKADDROFF
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_CALLRISCV marks RISC-V CALLs for stack checking.
R_CALLRISCV
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_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_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
// R_DWARFFILEREF resolves to an index into the DWARF .debug_line
// file table for the specified file symbol. Must be applied to an
// attribute of form DW_FORM_data4.
R_DWARFFILEREF
// 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_LDST8 sets a LD/ST immediate value to bits [11:0] of a local address.
R_ARM64_LDST8
// 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 inserts this value into the low 16 bits of an
// instruction word.
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 "MOVD 0(R13)(R31*1), g" as
// accessing a particular thread-local symbol. It does not affect code generation
// but is used by the system linker when relaxing "initial exec" model code to
// "local exec" model code.
R_POWER_TLS
// 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_PCREL relocates a D-form, DS-form instruction sequence like
// R_ADDRPOWER_DS but inserts the offset of the GOT slot for the referenced symbol
// from the TOC rather than the symbol's address.
R_ADDRPOWER_GOT
// 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 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
// RISC-V.
// R_RISCV_PCREL_ITYPE resolves a 32-bit PC-relative address using an
// AUIPC + I-type instruction pair.
R_RISCV_PCREL_ITYPE
// R_RISCV_PCREL_STYPE resolves a 32-bit PC-relative address using an
// AUIPC + S-type instruction pair.
R_RISCV_PCREL_STYPE
// R_PCRELDBL relocates s390x 2-byte aligned PC-relative addresses.
// TODO(mundaym): remove once variants can be serialized - see issue 14218.
R_PCRELDBL
// 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
)
// 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, 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_CALLMIPS, R_CALLPOWER, R_CALLRISCV:
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
}
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()
}
@@ -0,0 +1,17 @@
// Code generated by "stringer -type=RelocType"; DO NOT EDIT.
package objabi
import "strconv"
const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 219, 230, 240, 249, 262, 276, 290, 304, 320, 331, 344, 357, 371, 385, 400, 414, 428, 439, 453, 468, 485, 503, 524, 543, 562, 572, 583, 596, 607, 619, 629}
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]]
}
@@ -0,0 +1,33 @@
// 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
// For the linkers. Must match Go definitions.
const (
STACKSYSTEM = 0
StackSystem = STACKSYSTEM
StackBig = 4096
StackSmall = 128
)
const (
StackPreempt = -1314 // 0xfff...fade
)
// Initialize StackGuard and StackLimit according to target system.
var StackGuard = 928*stackGuardMultiplier() + StackSystem
var StackLimit = StackGuard - StackSystem - StackSmall
// 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() int {
// On AIX, a larger stack is needed for syscalls.
if GOOS == "aix" {
return 2
}
return 1
}
@@ -0,0 +1,79 @@
// 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
// Read only static data
SRODATA
// Static data that does not contain any pointers
SNOPTRDATA
// Static data
SDATA
// 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
// ABI alias. An ABI alias symbol is an empty symbol with a
// single relocation with 0 size that references the native
// function implementation symbol.
//
// TODO(austin): Remove this and all uses once the compiler
// generates real ABI wrappers rather than symbol aliases.
SABIALIAS
// Coverage instrumentation counter for libfuzzer.
SLIBFUZZER_EXTRA_COUNTER
// Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.
)
@@ -0,0 +1,41 @@
// 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[SRODATA-2]
_ = x[SNOPTRDATA-3]
_ = x[SDATA-4]
_ = x[SBSS-5]
_ = x[SNOPTRBSS-6]
_ = x[STLSBSS-7]
_ = x[SDWARFCUINFO-8]
_ = x[SDWARFCONST-9]
_ = x[SDWARFFCN-10]
_ = x[SDWARFABSFCN-11]
_ = x[SDWARFTYPE-12]
_ = x[SDWARFVAR-13]
_ = x[SDWARFRANGE-14]
_ = x[SDWARFLOC-15]
_ = x[SDWARFLINES-16]
_ = x[SABIALIAS-17]
_ = x[SLIBFUZZER_EXTRA_COUNTER-18]
}
const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFCUINFOSDWARFCONSTSDWARFFCNSDWARFABSFCNSDWARFTYPESDWARFVARSDWARFRANGESDWARFLOCSDWARFLINESSABIALIASSLIBFUZZER_EXTRA_COUNTER"
var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 63, 74, 83, 95, 105, 114, 125, 134, 145, 154, 178}
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]]
}
@@ -0,0 +1,40 @@
// Copyright 2012 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
// Must match runtime and reflect.
// Included by cmd/gc.
const (
KindBool = 1 + iota
KindInt
KindInt8
KindInt16
KindInt32
KindInt64
KindUint
KindUint8
KindUint16
KindUint32
KindUint64
KindUintptr
KindFloat32
KindFloat64
KindComplex64
KindComplex128
KindArray
KindChan
KindFunc
KindInterface
KindMap
KindPtr
KindSlice
KindString
KindStruct
KindUnsafePointer
KindDirectIface = 1 << 5
KindGCProg = 1 << 6
KindMask = (1 << 5) - 1
)
@@ -0,0 +1,203 @@
// 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"
"log"
"os"
"strings"
)
func envOr(key, value string) string {
if x := os.Getenv(key); x != "" {
return x
}
return value
}
var (
defaultGOROOT string // set by linker
GOROOT = envOr("GOROOT", defaultGOROOT)
GOARCH = envOr("GOARCH", "amd64")
GOOS = envOr("GOOS", "linux")
GO386 = envOr("GO386", "")
GOAMD64 = goamd64()
GOARM = goarm()
GOMIPS = gomips()
GOMIPS64 = gomips64()
GOPPC64 = goppc64()
GOWASM = gowasm()
GO_LDSO = ""
Version = ""
)
const (
ElfRelocOffset = 256
MachoRelocOffset = 2048 // reserve enough space for ELF relocations
Go115AMD64 = "alignedjumps" // Should be "alignedjumps" or "normaljumps"; this replaces environment variable introduced in CL 219357.
)
// TODO(1.16): assuming no issues in 1.15 release, remove this and related constant.
func goamd64() string {
return Go115AMD64
}
func goarm() int {
switch v := envOr("GOARM", "7"); v {
case "5":
return 5
case "6":
return 6
case "7":
return 7
}
// Fail here, rather than validate at multiple call sites.
log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
panic("unreachable")
}
func gomips() string {
switch v := envOr("GOMIPS", "hardfloat"); v {
case "hardfloat", "softfloat":
return v
}
log.Fatalf("Invalid GOMIPS value. Must be hardfloat or softfloat.")
panic("unreachable")
}
func gomips64() string {
switch v := envOr("GOMIPS64", "hardfloat"); v {
case "hardfloat", "softfloat":
return v
}
log.Fatalf("Invalid GOMIPS64 value. Must be hardfloat or softfloat.")
panic("unreachable")
}
func goppc64() int {
switch v := envOr("GOPPC64", "power8"); v {
case "power8":
return 8
case "power9":
return 9
}
log.Fatalf("Invalid GOPPC64 value. Must be power8 or power9.")
panic("unreachable")
}
type gowasmFeatures struct {
SignExt bool
SatConv bool
}
func (f gowasmFeatures) String() string {
var flags []string
if f.SatConv {
flags = append(flags, "satconv")
}
if f.SignExt {
flags = append(flags, "signext")
}
return strings.Join(flags, ",")
}
func gowasm() (f gowasmFeatures) {
for _, opt := range strings.Split(envOr("GOWASM", ""), ",") {
switch opt {
case "satconv":
f.SatConv = true
case "signext":
f.SignExt = true
case "":
// ignore
default:
log.Fatalf("Invalid GOWASM value. No such feature: " + opt)
}
}
return
}
func Getgoextlinkenabled() string {
return envOr("GO_EXTLINK_ENABLED", "")
}
func init() {
for _, f := range strings.Split("", ",") {
if f != "" {
addexp(f)
}
}
// regabi is only supported on amd64.
if GOARCH != "amd64" {
Regabi_enabled = 0
}
}
// Note: must agree with runtime.framepointer_enabled.
var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" && (GOOS == "linux" || GOOS == "darwin")
func addexp(s string) {
// Could do general integer parsing here, but the runtime copy doesn't yet.
v := 1
name := s
if len(name) > 2 && name[:2] == "no" {
v = 0
name = name[2:]
}
for i := 0; i < len(exper); i++ {
if exper[i].name == name {
if exper[i].val != nil {
*exper[i].val = v
}
return
}
}
fmt.Printf("unknown experiment %s\n", s)
os.Exit(2)
}
var (
Fieldtrack_enabled int
Preemptibleloops_enabled int
Staticlockranking_enabled int
Regabi_enabled int
)
// Toolchain experiments.
// These are controlled by the GOEXPERIMENT environment
// variable recorded when the toolchain is built.
// This list is also known to cmd/gc.
var exper = []struct {
name string
val *int
}{
{"fieldtrack", &Fieldtrack_enabled},
{"preemptibleloops", &Preemptibleloops_enabled},
{"staticlockranking", &Staticlockranking_enabled},
{"regabi", &Regabi_enabled},
}
var defaultExpstring = Expstring()
func DefaultExpstring() string {
return defaultExpstring
}
func Expstring() string {
buf := "X"
for i := range exper {
if *exper[i].val != 0 {
buf += "," + exper[i].name
}
}
if buf == "X" {
buf += ",none"
}
return "X:" + buf[2:]
}
@@ -0,0 +1,470 @@
// 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() }
// SymFilename() returns the absolute filename recorded with the position's base,
// prefixed by FileSymPrefix to make it appropriate for use as a linker symbol.
func (p Pos) SymFilename() string { return p.base.SymFilename() }
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, "<unknown line number>")
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
symFilename string // cached symbol file name, to avoid repeated string concatenation
line, col uint // relative line, column number at pos
inl int // inlining index (see cmd/internal/obj/inl.go)
}
// 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,
symFilename: FileSymPrefix + absFilename,
line: 1,
col: 1,
inl: -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, FileSymPrefix + absFilename, line, col, -1}
}
// NewInliningBase returns a copy of the old PosBase with the given inlining
// index. If old == nil, the resulting PosBase has no filename.
func NewInliningBase(old *PosBase, inlTreeIndex int) *PosBase {
if old == nil {
base := &PosBase{line: 1, col: 1, inl: inlTreeIndex}
base.pos = MakePos(base, 1, 1)
return base
}
copy := *old
base := &copy
base.inl = inlTreeIndex
if old == old.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 ""
}
const FileSymPrefix = "gofile.."
// SymFilename returns the absolute filename recorded with the base,
// prefixed by FileSymPrefix to make it appropriate for use as a linker symbol.
// If b is nil, SymFilename returns FileSymPrefix + "??".
func (b *PosBase) SymFilename() string {
if b != nil {
return b.symFilename
}
return FileSymPrefix + "??"
}
// 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<<lineBits - 2
bogusLine = 1 // Used to disrupt infinite loops to prevent debugger looping
isStmtBits, isStmtMax = 2, 1<<isStmtBits - 1
xlogueBits, xlogueMax = 2, 1<<xlogueBits - 1
colBits, colMax = 32 - lineBits - xlogueBits - isStmtBits, 1<<colBits - 1
isStmtShift = 0
isStmtMask = isStmtMax << isStmtShift
xlogueShift = isStmtBits + isStmtShift
xlogueMask = xlogueMax << xlogueShift
colShift = xlogueBits + xlogueShift
lineShift = colBits + colShift
)
const (
// It is expected that the front end or a phase in SSA will usually generate positions tagged with
// PosDefaultStmt, but note statement boundaries with PosIsStmt. Simple statements will have a single
// boundary; for loops with initialization may have one for their entry and one for their back edge
// (this depends on exactly how the loop is compiled; the intent is to provide a good experience to a
// user debugging a program; the goal is that a breakpoint set on the loop line fires both on entry
// and on iteration). Proper treatment of non-gofmt input with multiple simple statements on a single
// line is TBD.
//
// Optimizing compilation will move instructions around, and some of these will become known-bad as
// step targets for debugging purposes (examples: register spills and reloads; code generated into
// the entry block; invariant code hoisted out of loops) but those instructions will still have interesting
// positions for profiling purposes. To reflect this these positions will be changed to PosNotStmt.
//
// When the optimizer removes an instruction marked PosIsStmt; it should attempt to find a nearby
// instruction with the same line marked PosDefaultStmt to be the new statement boundary. I.e., the
// optimizer should make a best-effort to conserve statement boundary positions, and might be enhanced
// to note when a statement boundary is not conserved.
//
// Code cloning, e.g. loop unrolling or loop unswitching, is an exception to the conservation rule
// because a user running a debugger would expect to see breakpoints active in the copies of the code.
//
// In non-optimizing compilation there is still a role for PosNotStmt because of code generation
// into the entry block. PosIsStmt statement positions should be conserved.
//
// When code generation occurs any remaining default-marked positions are replaced with not-statement
// positions.
//
PosDefaultStmt uint = iota // Default; position is not a statement boundary, but might be if optimization removes the designated statement boundary
PosIsStmt // Position is a statement boundary; if optimization removes the corresponding instruction, it should attempt to find a new instruction to be the boundary.
PosNotStmt // Position should not be a statement boundary, but line should be preserved for profiling and low-level debugging purposes.
)
type PosXlogue uint
const (
PosDefaultLogue PosXlogue = iota
PosPrologueEnd
PosEpilogueBegin
)
func makeLicoRaw(line, col uint) lico {
return lico(line<<lineShift | col<<colShift)
}
// This is a not-position that will not be elided.
// Depending on the debugger (gdb or delve) it may or may not be displayed.
func makeBogusLico() lico {
return makeLicoRaw(bogusLine, 0).withIsStmt()
}
func makeLico(line, col uint) lico {
if line > lineMax {
// cannot represent line, use max. line so we have some information
line = lineMax
}
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<<lineShift-1) }
func (x lico) Col() uint { return uint(x) >> 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)
}
// withLogue 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<<xlogueShift) | (uint(xlogue) << xlogueShift))
}
// withStmt returns a lico for the same location with specified is_stmt attribute
func (x lico) withStmt(stmt uint) lico {
if x == 0 {
return lico(0)
}
return lico(uint(x) & ^uint(isStmtMax<<isStmtShift) | (stmt << isStmtShift))
}
func (x lico) lineNumber() string {
return fmt.Sprintf("%d", x.Line())
}
func (x lico) lineNumberHTML() string {
if x.IsStmt() == PosDefaultStmt {
return fmt.Sprintf("%d", x.Line())
}
style, pfx := "b", "+"
if x.IsStmt() == PosNotStmt {
style = "s" // /strike not supported in HTML5
pfx = ""
}
return fmt.Sprintf("<%s>%s%d</%s>", style, pfx, x.Line(), style)
}
func (x lico) atColumn1() lico {
return makeLico(x.Line(), 1).withIsStmt()
}
@@ -0,0 +1,176 @@
// 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 {
m := t.indexMap
if m == nil {
// Create new list and map and populate with nil
// base so that NoPos always gets index 0.
t.baseList = append(t.baseList, nil)
m = map[*PosBase]int{nil: 0}
t.indexMap = m
t.nameMap = make(map[string]int)
}
i, ok := m[pos.base]
if !ok {
i = len(t.baseList)
t.baseList = append(t.baseList, pos.base)
t.indexMap[pos.base] = i
if _, ok := t.nameMap[pos.base.symFilename]; !ok {
t.nameMap[pos.base.symFilename] = len(t.nameMap)
}
}
return XPos{int32(i), pos.lico}
}
// 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}
}
// FileIndex returns the index of the given filename(symbol) in the PosTable, or -1 if not found.
func (t *PosTable) FileIndex(filename string) int {
if v, ok := t.nameMap[filename]; ok {
return v
}
return -1
}
// 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
}
@@ -0,0 +1,187 @@
// 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"
// ArchFamily represents a family of one or more related architectures.
// For example, ppc64 and ppc64le are both members of the PPC64 family.
type ArchFamily byte
const (
NoArch ArchFamily = iota
AMD64
ARM
ARM64
I386
MIPS
MIPS64
PPC64
RISCV64
S390X
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
}
// 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,
}
var ArchAMD64 = &Arch{
Name: "amd64",
Family: AMD64,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 1,
}
var ArchARM = &Arch{
Name: "arm",
Family: ARM,
ByteOrder: binary.LittleEndian,
PtrSize: 4,
RegSize: 4,
MinLC: 4,
}
var ArchARM64 = &Arch{
Name: "arm64",
Family: ARM64,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
var ArchMIPS = &Arch{
Name: "mips",
Family: MIPS,
ByteOrder: binary.BigEndian,
PtrSize: 4,
RegSize: 4,
MinLC: 4,
}
var ArchMIPSLE = &Arch{
Name: "mipsle",
Family: MIPS,
ByteOrder: binary.LittleEndian,
PtrSize: 4,
RegSize: 4,
MinLC: 4,
}
var ArchMIPS64 = &Arch{
Name: "mips64",
Family: MIPS64,
ByteOrder: binary.BigEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
var ArchMIPS64LE = &Arch{
Name: "mips64le",
Family: MIPS64,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
var ArchPPC64 = &Arch{
Name: "ppc64",
Family: PPC64,
ByteOrder: binary.BigEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
var ArchPPC64LE = &Arch{
Name: "ppc64le",
Family: PPC64,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
var ArchRISCV64 = &Arch{
Name: "riscv64",
Family: RISCV64,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 4,
}
var ArchS390X = &Arch{
Name: "s390x",
Family: S390X,
ByteOrder: binary.BigEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 2,
}
var ArchWasm = &Arch{
Name: "wasm",
Family: Wasm,
ByteOrder: binary.LittleEndian,
PtrSize: 8,
RegSize: 8,
MinLC: 1,
}
var Archs = [...]*Arch{
Arch386,
ArchAMD64,
ArchARM,
ArchARM64,
ArchMIPS,
ArchMIPSLE,
ArchMIPS64,
ArchMIPS64LE,
ArchPPC64,
ArchPPC64LE,
ArchRISCV64,
ArchS390X,
ArchWasm,
}
@@ -0,0 +1,116 @@
// 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 sys
// RaceDetectorSupported reports whether goos/goarch supports the race
// detector. There is a copy of this function in cmd/dist/test.go.
// Race detector only supports 48-bit VMA on arm64. But it will always
// return true for arm64, because we don't have VMA size information during
// the compile time.
func RaceDetectorSupported(goos, goarch string) bool {
switch goos {
case "linux":
return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64"
case "darwin", "freebsd", "netbsd", "windows":
return goarch == "amd64"
default:
return false
}
}
// MSanSupported reports whether goos/goarch supports the memory
// sanitizer option. There is a copy of this function in cmd/dist/test.go.
func MSanSupported(goos, goarch string) bool {
switch goos {
case "linux":
return goarch == "amd64" || goarch == "arm64"
default:
return false
}
}
// MustLinkExternal reports whether goos/goarch requires external linking.
func MustLinkExternal(goos, goarch string) bool {
switch goos {
case "android":
if goarch != "arm64" {
return true
}
case "darwin":
if goarch == "arm64" {
return true
}
}
return false
}
// BuildModeSupported reports whether goos/goarch supports the given build mode
// using the given compiler.
func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
if compiler == "gccgo" {
return true
}
platform := goos + "/" + goarch
switch buildmode {
case "archive":
return true
case "c-archive":
// TODO(bcmills): This seems dubious.
// Do we really support c-archive mode on js/wasm‽
return platform != "linux/ppc64"
case "c-shared":
switch platform {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
"darwin/amd64",
"windows/amd64", "windows/386":
return true
}
return false
case "default":
return true
case "exe":
return true
case "pie":
switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
"darwin/amd64",
"aix/ppc64",
"windows/386", "windows/amd64", "windows/arm":
return true
}
return false
case "shared":
switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x":
return true
}
return false
case "plugin":
switch platform {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",
"android/amd64", "android/arm", "android/arm64", "android/386",
"darwin/amd64",
"freebsd/amd64":
return true
}
return false
default:
return false
}
}
@@ -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
}