summaryrefslogtreecommitdiff
path: root/vendor/github.com/cloudwego/iasm/x86_64/program.go
diff options
context:
space:
mode:
authorLibravatar dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>2024-05-06 08:50:47 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-06 08:50:47 +0000
commita5f28fe0c923984c263592e82bbce99b0032b794 (patch)
tree403544ad5305eb171a85d2b4c59559f83abd87a7 /vendor/github.com/cloudwego/iasm/x86_64/program.go
parent[chore]: Bump golang.org/x/image from 0.15.0 to 0.16.0 (#2898) (diff)
downloadgotosocial-a5f28fe0c923984c263592e82bbce99b0032b794.tar.xz
[chore]: Bump github.com/gin-contrib/gzip from 1.0.0 to 1.0.1 (#2899)
Bumps [github.com/gin-contrib/gzip](https://github.com/gin-contrib/gzip) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/gin-contrib/gzip/releases) - [Changelog](https://github.com/gin-contrib/gzip/blob/master/.goreleaser.yaml) - [Commits](https://github.com/gin-contrib/gzip/compare/v1.0.0...v1.0.1) --- updated-dependencies: - dependency-name: github.com/gin-contrib/gzip dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Diffstat (limited to 'vendor/github.com/cloudwego/iasm/x86_64/program.go')
-rw-r--r--vendor/github.com/cloudwego/iasm/x86_64/program.go584
1 files changed, 584 insertions, 0 deletions
diff --git a/vendor/github.com/cloudwego/iasm/x86_64/program.go b/vendor/github.com/cloudwego/iasm/x86_64/program.go
new file mode 100644
index 000000000..f0c9b18c6
--- /dev/null
+++ b/vendor/github.com/cloudwego/iasm/x86_64/program.go
@@ -0,0 +1,584 @@
+//
+// Copyright 2024 CloudWeGo Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package x86_64
+
+import (
+ "fmt"
+ "math"
+ "math/bits"
+
+ "github.com/cloudwego/iasm/expr"
+)
+
+type (
+ _PseudoType int
+ _InstructionEncoder func(*Program, ...interface{}) *Instruction
+)
+
+const (
+ _PseudoNop _PseudoType = iota + 1
+ _PseudoByte
+ _PseudoWord
+ _PseudoLong
+ _PseudoQuad
+ _PseudoData
+ _PseudoAlign
+)
+
+func (self _PseudoType) String() string {
+ switch self {
+ case _PseudoNop:
+ return ".nop"
+ case _PseudoByte:
+ return ".byte"
+ case _PseudoWord:
+ return ".word"
+ case _PseudoLong:
+ return ".long"
+ case _PseudoQuad:
+ return ".quad"
+ case _PseudoData:
+ return ".data"
+ case _PseudoAlign:
+ return ".align"
+ default:
+ panic("unreachable")
+ }
+}
+
+type _Pseudo struct {
+ kind _PseudoType
+ data []byte
+ uint uint64
+ expr *expr.Expr
+}
+
+func (self *_Pseudo) free() {
+ if self.expr != nil {
+ self.expr.Free()
+ }
+}
+
+func (self *_Pseudo) encode(m *[]byte, pc uintptr) int {
+ switch self.kind {
+ case _PseudoNop:
+ return 0
+ case _PseudoByte:
+ self.encodeByte(m)
+ return 1
+ case _PseudoWord:
+ self.encodeWord(m)
+ return 2
+ case _PseudoLong:
+ self.encodeLong(m)
+ return 4
+ case _PseudoQuad:
+ self.encodeQuad(m)
+ return 8
+ case _PseudoData:
+ self.encodeData(m)
+ return len(self.data)
+ case _PseudoAlign:
+ self.encodeAlign(m, pc)
+ return self.alignSize(pc)
+ default:
+ panic("invalid pseudo instruction")
+ }
+}
+
+func (self *_Pseudo) evalExpr(low int64, high int64) int64 {
+ if v, err := self.expr.Evaluate(); err != nil {
+ panic(err)
+ } else if v < low || v > high {
+ panic(fmt.Sprintf("expression out of range [%d, %d]: %d", low, high, v))
+ } else {
+ return v
+ }
+}
+
+func (self *_Pseudo) alignSize(pc uintptr) int {
+ if !ispow2(self.uint) {
+ panic(fmt.Sprintf("aligment should be a power of 2, not %d", self.uint))
+ } else {
+ return align(int(pc), bits.TrailingZeros64(self.uint)) - int(pc)
+ }
+}
+
+func (self *_Pseudo) encodeData(m *[]byte) {
+ if m != nil {
+ *m = append(*m, self.data...)
+ }
+}
+
+func (self *_Pseudo) encodeByte(m *[]byte) {
+ if m != nil {
+ append8(m, byte(self.evalExpr(math.MinInt8, math.MaxUint8)))
+ }
+}
+
+func (self *_Pseudo) encodeWord(m *[]byte) {
+ if m != nil {
+ append16(m, uint16(self.evalExpr(math.MinInt16, math.MaxUint16)))
+ }
+}
+
+func (self *_Pseudo) encodeLong(m *[]byte) {
+ if m != nil {
+ append32(m, uint32(self.evalExpr(math.MinInt32, math.MaxUint32)))
+ }
+}
+
+func (self *_Pseudo) encodeQuad(m *[]byte) {
+ if m != nil {
+ if v, err := self.expr.Evaluate(); err != nil {
+ panic(err)
+ } else {
+ append64(m, uint64(v))
+ }
+ }
+}
+
+func (self *_Pseudo) encodeAlign(m *[]byte, pc uintptr) {
+ if m != nil {
+ if self.expr == nil {
+ expandmm(m, self.alignSize(pc), 0)
+ } else {
+ expandmm(m, self.alignSize(pc), byte(self.evalExpr(math.MinInt8, math.MaxUint8)))
+ }
+ }
+}
+
+// Operands represents a sequence of operand required by an instruction.
+type Operands [_N_args]interface{}
+
+// InstructionDomain represents the domain of an instruction.
+type InstructionDomain uint8
+
+const (
+ DomainGeneric InstructionDomain = iota
+ DomainMMXSSE
+ DomainAVX
+ DomainFMA
+ DomainCrypto
+ DomainMask
+ DomainAMDSpecific
+ DomainMisc
+ DomainPseudo
+)
+
+type (
+ _BranchType uint8
+)
+
+const (
+ _B_none _BranchType = iota
+ _B_conditional
+ _B_unconditional
+)
+
+// Instruction represents an unencoded instruction.
+type Instruction struct {
+ next *Instruction
+ pc uintptr
+ nb int
+ len int
+ argc int
+ name string
+ argv Operands
+ forms [_N_forms]_Encoding
+ pseudo _Pseudo
+ branch _BranchType
+ domain InstructionDomain
+ prefix []byte
+}
+
+func (self *Instruction) add(flags int, encoder func(m *_Encoding, v []interface{})) {
+ self.forms[self.len].flags = flags
+ self.forms[self.len].encoder = encoder
+ self.len++
+}
+
+func (self *Instruction) free() {
+ self.clear()
+ self.pseudo.free()
+ //freeInstruction(self)
+}
+
+func (self *Instruction) clear() {
+ for i := 0; i < self.argc; i++ {
+ if v, ok := self.argv[i].(Disposable); ok {
+ v.Free()
+ }
+ }
+}
+
+func (self *Instruction) check(e *_Encoding) bool {
+ if (e.flags & _F_rel1) != 0 {
+ return isRel8(self.argv[0])
+ } else if (e.flags & _F_rel4) != 0 {
+ return isRel32(self.argv[0]) || isLabel(self.argv[0])
+ } else {
+ return true
+ }
+}
+
+func (self *Instruction) encode(m *[]byte) int {
+ n := math.MaxInt64
+ p := (*_Encoding)(nil)
+
+ /* encode prefixes if any */
+ if self.nb = len(self.prefix); m != nil {
+ *m = append(*m, self.prefix...)
+ }
+
+ /* check for pseudo-instructions */
+ if self.pseudo.kind != 0 {
+ self.nb += self.pseudo.encode(m, self.pc)
+ return self.nb
+ }
+
+ /* find the shortest encoding */
+ for i := 0; i < self.len; i++ {
+ if e := &self.forms[i]; self.check(e) {
+ if v := e.encode(self.argv[:self.argc]); v < n {
+ n = v
+ p = e
+ }
+ }
+ }
+
+ /* add to buffer if needed */
+ if m != nil {
+ *m = append(*m, p.bytes[:n]...)
+ }
+
+ /* update the instruction length */
+ self.nb += n
+ return self.nb
+}
+
+/** Instruction Prefixes **/
+
+const (
+ _P_cs = 0x2e
+ _P_ds = 0x3e
+ _P_es = 0x26
+ _P_fs = 0x64
+ _P_gs = 0x65
+ _P_ss = 0x36
+ _P_lock = 0xf0
+)
+
+// CS overrides the memory operation of this instruction to CS.
+func (self *Instruction) CS() *Instruction {
+ self.prefix = append(self.prefix, _P_cs)
+ return self
+}
+
+// DS overrides the memory operation of this instruction to DS,
+// this is the default section for most instructions if not specified.
+func (self *Instruction) DS() *Instruction {
+ self.prefix = append(self.prefix, _P_ds)
+ return self
+}
+
+// ES overrides the memory operation of this instruction to ES.
+func (self *Instruction) ES() *Instruction {
+ self.prefix = append(self.prefix, _P_es)
+ return self
+}
+
+// FS overrides the memory operation of this instruction to FS.
+func (self *Instruction) FS() *Instruction {
+ self.prefix = append(self.prefix, _P_fs)
+ return self
+}
+
+// GS overrides the memory operation of this instruction to GS.
+func (self *Instruction) GS() *Instruction {
+ self.prefix = append(self.prefix, _P_gs)
+ return self
+}
+
+// SS overrides the memory operation of this instruction to SS.
+func (self *Instruction) SS() *Instruction {
+ self.prefix = append(self.prefix, _P_ss)
+ return self
+}
+
+// LOCK causes the processor's LOCK# signal to be asserted during execution of
+// the accompanying instruction (turns the instruction into an atomic instruction).
+// In a multiprocessor environment, the LOCK# signal insures that the processor
+// has exclusive use of any shared memory while the signal is asserted.
+func (self *Instruction) LOCK() *Instruction {
+ self.prefix = append(self.prefix, _P_lock)
+ return self
+}
+
+/** Basic Instruction Properties **/
+
+// Name returns the instruction name.
+func (self *Instruction) Name() string {
+ return self.name
+}
+
+// Domain returns the domain of this instruction.
+func (self *Instruction) Domain() InstructionDomain {
+ return self.domain
+}
+
+// Operands returns the operands of this instruction.
+func (self *Instruction) Operands() []interface{} {
+ return self.argv[:self.argc]
+}
+
+// Program represents a sequence of instructions.
+type Program struct {
+ arch *Arch
+ head *Instruction
+ tail *Instruction
+}
+
+const (
+ _N_near = 2 // near-branch (-128 ~ +127) takes 2 bytes to encode
+ _N_far_cond = 6 // conditional far-branch takes 6 bytes to encode
+ _N_far_uncond = 5 // unconditional far-branch takes 5 bytes to encode
+)
+
+func (self *Program) clear() {
+ for p, q := self.head, self.head; p != nil; p = q {
+ q = p.next
+ p.free()
+ }
+}
+
+func (self *Program) alloc(name string, argc int, argv Operands) *Instruction {
+ p := self.tail
+ q := newInstruction(name, argc, argv)
+
+ /* attach to tail if any */
+ if p != nil {
+ p.next = q
+ } else {
+ self.head = q
+ }
+
+ /* set the new tail */
+ self.tail = q
+ return q
+}
+
+func (self *Program) pseudo(kind _PseudoType) (p *Instruction) {
+ p = self.alloc(kind.String(), 0, Operands{})
+ p.domain = DomainPseudo
+ p.pseudo.kind = kind
+ return
+}
+
+func (self *Program) require(isa ISA) {
+ if !self.arch.HasISA(isa) {
+ panic("ISA '" + isa.String() + "' was not enabled")
+ }
+}
+
+func (self *Program) branchSize(p *Instruction) int {
+ switch p.branch {
+ case _B_none:
+ panic("p is not a branch")
+ case _B_conditional:
+ return _N_far_cond
+ case _B_unconditional:
+ return _N_far_uncond
+ default:
+ panic("invalid instruction")
+ }
+}
+
+/** Pseudo-Instructions **/
+
+// Byte is a pseudo-instruction to add raw byte to the assembled code.
+func (self *Program) Byte(v *expr.Expr) (p *Instruction) {
+ p = self.pseudo(_PseudoByte)
+ p.pseudo.expr = v
+ return
+}
+
+// Word is a pseudo-instruction to add raw uint16 as little-endian to the assembled code.
+func (self *Program) Word(v *expr.Expr) (p *Instruction) {
+ p = self.pseudo(_PseudoWord)
+ p.pseudo.expr = v
+ return
+}
+
+// Long is a pseudo-instruction to add raw uint32 as little-endian to the assembled code.
+func (self *Program) Long(v *expr.Expr) (p *Instruction) {
+ p = self.pseudo(_PseudoLong)
+ p.pseudo.expr = v
+ return
+}
+
+// Quad is a pseudo-instruction to add raw uint64 as little-endian to the assembled code.
+func (self *Program) Quad(v *expr.Expr) (p *Instruction) {
+ p = self.pseudo(_PseudoQuad)
+ p.pseudo.expr = v
+ return
+}
+
+// Data is a pseudo-instruction to add raw bytes to the assembled code.
+func (self *Program) Data(v []byte) (p *Instruction) {
+ p = self.pseudo(_PseudoData)
+ p.pseudo.data = v
+ return
+}
+
+// Align is a pseudo-instruction to ensure the PC is aligned to a certain value.
+func (self *Program) Align(align uint64, padding *expr.Expr) (p *Instruction) {
+ p = self.pseudo(_PseudoAlign)
+ p.pseudo.uint = align
+ p.pseudo.expr = padding
+ return
+}
+
+/** Program Assembler **/
+
+// Free returns the Program object into pool.
+// Any operation performed after Free is undefined behavior.
+//
+// NOTE: This also frees all the instructions, labels, memory
+//
+// operands and expressions associated with this program.
+func (self *Program) Free() {
+ self.clear()
+ //freeProgram(self)
+}
+
+// Link pins a label at the current position.
+func (self *Program) Link(p *Label) {
+ if p.Dest != nil {
+ panic("lable was alreay linked")
+ } else {
+ p.Dest = self.pseudo(_PseudoNop)
+ }
+}
+
+// Assemble assembles and links the entire program into machine code.
+func (self *Program) Assemble(pc uintptr) (ret []byte) {
+ orig := pc
+ next := true
+ offs := uintptr(0)
+
+ /* Pass 0: PC-precompute, assume all labeled branches are far-branches. */
+ for p := self.head; p != nil; p = p.next {
+ if p.pc = pc; !isLabel(p.argv[0]) || p.branch == _B_none {
+ pc += uintptr(p.encode(nil))
+ } else {
+ pc += uintptr(self.branchSize(p))
+ }
+ }
+
+ /* allocate space for the machine code */
+ nb := int(pc - orig)
+ ret = make([]byte, 0, nb)
+
+ /* Pass 1: adjust all the jumps */
+ for next {
+ next = false
+ offs = uintptr(0)
+
+ /* scan all the branches */
+ for p := self.head; p != nil; p = p.next {
+ var ok bool
+ var lb *Label
+
+ /* re-calculate the alignment here */
+ if nb = p.nb; p.pseudo.kind == _PseudoAlign {
+ p.pc -= offs
+ offs += uintptr(nb - p.encode(nil))
+ continue
+ }
+
+ /* adjust the program counter */
+ p.pc -= offs
+ lb, ok = p.argv[0].(*Label)
+
+ /* only care about labeled far-branches */
+ if !ok || p.nb == _N_near || p.branch == _B_none {
+ continue
+ }
+
+ /* calculate the jump offset */
+ size := self.branchSize(p)
+ diff := lb.offset(p.pc, size)
+
+ /* too far to be a near jump */
+ if diff > 127 || diff < -128 {
+ p.nb = size
+ continue
+ }
+
+ /* a far jump becomes a near jump, calculate
+ * the PC adjustment value and assemble again */
+ next = true
+ p.nb = _N_near
+ offs += uintptr(size - _N_near)
+ }
+ }
+
+ /* Pass 3: link all the cross-references */
+ for p := self.head; p != nil; p = p.next {
+ for i := 0; i < p.argc; i++ {
+ var ok bool
+ var lb *Label
+ var op *MemoryOperand
+
+ /* resolve labels */
+ if lb, ok = p.argv[i].(*Label); ok {
+ p.argv[i] = lb.offset(p.pc, p.nb)
+ continue
+ }
+
+ /* check for memory operands */
+ if op, ok = p.argv[i].(*MemoryOperand); !ok {
+ continue
+ }
+
+ /* check for label references */
+ if op.Addr.Type != Reference {
+ continue
+ }
+
+ /* replace the label with the real offset */
+ op.Addr.Type = Offset
+ op.Addr.Offset = op.Addr.Reference.offset(p.pc, p.nb)
+ }
+ }
+
+ /* Pass 4: actually encode all the instructions */
+ for p := self.head; p != nil; p = p.next {
+ p.encode(&ret)
+ }
+
+ /* all done */
+ return ret
+}
+
+// AssembleAndFree is like Assemble, but it frees the Program after assembling.
+func (self *Program) AssembleAndFree(pc uintptr) (ret []byte) {
+ ret = self.Assemble(pc)
+ self.Free()
+ return
+}