diff --git a/asm/dsl_test.go b/asm/dsl_test.go index ddbf8172a..b529678d6 100644 --- a/asm/dsl_test.go +++ b/asm/dsl_test.go @@ -25,21 +25,29 @@ func TestDSL(t *testing.T) { OpCode: 0x04, Dst: R1, Constant: 22, }}, {"JSGT.Imm", JSGT.Imm(R1, 4, "foo"), Instruction{ - OpCode: 0x65, Dst: R1, Constant: 4, Offset: -1, Reference: "foo", + OpCode: 0x65, Dst: R1, Constant: 4, Offset: -1, metadata: &metadata{ + reference: "foo", + }, }}, {"JSGT.Imm32", JSGT.Imm32(R1, -2, "foo"), Instruction{ - OpCode: 0x66, Dst: R1, Constant: -2, Offset: -1, Reference: "foo", + OpCode: 0x66, Dst: R1, Constant: -2, Offset: -1, metadata: &metadata{ + reference: "foo", + }, }}, {"JSLT.Reg", JSLT.Reg(R1, R2, "foo"), Instruction{ - OpCode: 0xcd, Dst: R1, Src: R2, Offset: -1, Reference: "foo", + OpCode: 0xcd, Dst: R1, Src: R2, Offset: -1, metadata: &metadata{ + reference: "foo", + }, }}, {"JSLT.Reg32", JSLT.Reg32(R1, R3, "foo"), Instruction{ - OpCode: 0xce, Dst: R1, Src: R3, Offset: -1, Reference: "foo", + OpCode: 0xce, Dst: R1, Src: R3, Offset: -1, metadata: &metadata{ + reference: "foo", + }, }}, } for _, tc := range testcases { - if tc.have != tc.want { + if !tc.have.equal(tc.want) { t.Errorf("%s: have %v, want %v", tc.name, tc.have, tc.want) } } diff --git a/asm/instruction.go b/asm/instruction.go index bc3b883f4..1060d763f 100644 --- a/asm/instruction.go +++ b/asm/instruction.go @@ -35,17 +35,39 @@ type Instruction struct { Offset int16 Constant int64 - // Reference denotes a reference (e.g. a jump) to another symbol. - Reference string + // Metadata contains optional metadata about this instruction + metadata *metadata +} + +// WithSymbol marks the Instruction as a Symbol, which other Instructions +// can point to using corresponding calls to WithReference. +func (ins Instruction) WithSymbol(name string) Instruction { + if (ins.metadata != nil && ins.metadata.symbol == name) || + (ins.metadata == nil && name == "") { + return ins + } - // Symbol denotes an instruction at the start of a function body. - Symbol string + ins.metadata = ins.metadata.copy() + ins.metadata.symbol = name + return ins } // Sym creates a symbol. +// +// Deprecated: use WithSymbol instead. func (ins Instruction) Sym(name string) Instruction { - ins.Symbol = name - return ins + return ins.WithSymbol(name) +} + +// Symbol returns the value ins has been marked with using WithSymbol, +// otherwise returns an empty string. A symbol is often an Instruction +// at the start of a function body. +func (ins Instruction) Symbol() string { + if ins.metadata == nil { + return "" + } + + return ins.metadata.symbol } // Unmarshal decodes a BPF instruction. @@ -299,16 +321,63 @@ func (ins Instruction) Format(f fmt.State, c rune) { } ref: - if ins.Reference != "" { - fmt.Fprintf(f, " <%s>", ins.Reference) + if ins.Reference() != "" { + fmt.Fprintf(f, " <%s>", ins.Reference()) } } +func (ins Instruction) equal(other Instruction) bool { + return ins.OpCode == other.OpCode && + ins.Dst == other.Dst && + ins.Src == other.Src && + ins.Offset == other.Offset && + ins.Constant == other.Constant +} + // Size returns the amount of bytes ins would occupy in binary form. func (ins Instruction) Size() uint64 { return uint64(InstructionSize * ins.OpCode.rawInstructions()) } +// WithReference makes ins reference another Instruction by name within the same +func (ins Instruction) WithReference(ref string) Instruction { + if (ins.metadata != nil && ins.metadata.reference == ref) || + (ins.metadata == nil && ref == "") { + return ins + } + + ins.metadata = ins.metadata.copy() + ins.metadata.reference = ref + return ins +} + +// Reference returns a reference (e.g. a jump) to another symbol. +func (ins Instruction) Reference() string { + if ins.metadata == nil { + return "" + } + + return ins.metadata.reference +} + +// metadata holds metadata about an Instruction. +type metadata struct { + // reference denotes a reference (e.g. a jump) to another symbol. + reference string + // symbol denotes an instruction at the start of a function body. + symbol string +} + +// copy returns a copy of metadata. +// Always returns a valid pointer, even when called on a nil metadata. +func (m *metadata) copy() *metadata { + var copy metadata + if m != nil { + copy = *m + } + return © +} + // Instructions is an eBPF program. type Instructions []Instruction @@ -342,7 +411,7 @@ func (insns Instructions) Name() string { if len(insns) == 0 { return "" } - return insns[0].Symbol + return insns[0].Symbol() } func (insns Instructions) String() string { @@ -369,7 +438,7 @@ func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { found := false for i := range insns { ins := &insns[i] - if ins.Reference != symbol { + if ins.Reference() != symbol { continue } @@ -393,15 +462,15 @@ func (insns Instructions) SymbolOffsets() (map[string]int, error) { offsets := make(map[string]int) for i, ins := range insns { - if ins.Symbol == "" { + if ins.Symbol() == "" { continue } - if _, ok := offsets[ins.Symbol]; ok { - return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol) + if _, ok := offsets[ins.Symbol()]; ok { + return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol()) } - offsets[ins.Symbol] = i + offsets[ins.Symbol()] = i } return offsets, nil @@ -418,7 +487,7 @@ func (insns Instructions) FunctionReferences() map[string]bool { continue } - if ins.Reference == "" { + if ins.Reference() == "" { continue } @@ -426,7 +495,7 @@ func (insns Instructions) FunctionReferences() map[string]bool { continue } - calls[ins.Reference] = true + calls[ins.Reference()] = true } return calls @@ -438,11 +507,11 @@ func (insns Instructions) ReferenceOffsets() map[string][]int { offsets := make(map[string][]int) for i, ins := range insns { - if ins.Reference == "" { + if ins.Reference() == "" { continue } - offsets[ins.Reference] = append(offsets[ins.Reference], i) + offsets[ins.Reference()] = append(offsets[ins.Reference()], i) } return offsets @@ -493,8 +562,8 @@ func (insns Instructions) Format(f fmt.State, c rune) { iter := insns.Iterate() for iter.Next() { - if iter.Ins.Symbol != "" { - fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol) + if iter.Ins.Symbol() != "" { + fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol()) } fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins) } @@ -552,15 +621,15 @@ func (insns Instructions) resolveFunctionReferences() error { for iter.Next() { ins := iter.Ins - if ins.Symbol == "" { + if ins.Symbol() == "" { continue } - if _, ok := symbolOffsets[ins.Symbol]; ok { - return fmt.Errorf("duplicate symbol %s", ins.Symbol) + if _, ok := symbolOffsets[ins.Symbol()]; ok { + return fmt.Errorf("duplicate symbol %s", ins.Symbol()) } - symbolOffsets[ins.Symbol] = iter.Offset + symbolOffsets[ins.Symbol()] = iter.Offset } // Find all instructions tagged as references to other symbols. @@ -572,23 +641,23 @@ func (insns Instructions) resolveFunctionReferences() error { offset := iter.Offset ins := iter.Ins - if ins.Reference == "" { + if ins.Reference() == "" { continue } switch { case ins.IsFunctionReference() && ins.Constant == -1: - symOffset, ok := symbolOffsets[ins.Reference] + symOffset, ok := symbolOffsets[ins.Reference()] if !ok { - return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference, ErrUnsatisfiedProgramReference) + return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) } ins.Constant = int64(symOffset - offset - 1) case ins.OpCode.Class().IsJump() && ins.Offset == -1: - symOffset, ok := symbolOffsets[ins.Reference] + symOffset, ok := symbolOffsets[ins.Reference()] if !ok { - return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference, ErrUnsatisfiedProgramReference) + return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) } ins.Offset = int16(symOffset - offset - 1) diff --git a/asm/instruction_test.go b/asm/instruction_test.go index b18c0f3a0..579c1b94e 100644 --- a/asm/instruction_test.go +++ b/asm/instruction_test.go @@ -154,7 +154,7 @@ func TestInstructionsRewriteMapPtr(t *testing.T) { LoadMapPtr(R1, 0), Return(), } - insns[0].Reference = "good" + insns[0] = insns[0].WithReference("good") if err := insns.RewriteMapPtr("good", 1); err != nil { t.Fatal(err) @@ -181,7 +181,7 @@ func TestInstructionsRewriteMapPtr(t *testing.T) { // program is stringified. func ExampleInstructions_Format() { insns := Instructions{ - FnMapLookupElem.Call().Sym("my_func"), + FnMapLookupElem.Call().WithSymbol("my_func"), LoadImm(R0, 42, DWord), Return(), } diff --git a/asm/jump.go b/asm/jump.go index 199c06940..0dc4bf494 100644 --- a/asm/jump.go +++ b/asm/jump.go @@ -63,11 +63,13 @@ func (op JumpOp) Op(source Source) OpCode { // Imm compares 64 bit dst to 64 bit value (sign extended), and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { return Instruction{ - OpCode: op.opCode(JumpClass, ImmSource), - Dst: dst, - Offset: -1, - Constant: int64(value), - Reference: label, + OpCode: op.opCode(JumpClass, ImmSource), + Dst: dst, + Offset: -1, + Constant: int64(value), + metadata: &metadata{ + reference: label, + }, } } @@ -75,22 +77,26 @@ func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { // Requires kernel 5.1. func (op JumpOp) Imm32(dst Register, value int32, label string) Instruction { return Instruction{ - OpCode: op.opCode(Jump32Class, ImmSource), - Dst: dst, - Offset: -1, - Constant: int64(value), - Reference: label, + OpCode: op.opCode(Jump32Class, ImmSource), + Dst: dst, + Offset: -1, + Constant: int64(value), + metadata: &metadata{ + reference: label, + }, } } // Reg compares 64 bit dst to 64 bit src, and adjusts PC by offset if the condition is fulfilled. func (op JumpOp) Reg(dst, src Register, label string) Instruction { return Instruction{ - OpCode: op.opCode(JumpClass, RegSource), - Dst: dst, - Src: src, - Offset: -1, - Reference: label, + OpCode: op.opCode(JumpClass, RegSource), + Dst: dst, + Src: src, + Offset: -1, + metadata: &metadata{ + reference: label, + }, } } @@ -98,11 +104,13 @@ func (op JumpOp) Reg(dst, src Register, label string) Instruction { // Requires kernel 5.1. func (op JumpOp) Reg32(dst, src Register, label string) Instruction { return Instruction{ - OpCode: op.opCode(Jump32Class, RegSource), - Dst: dst, - Src: src, - Offset: -1, - Reference: label, + OpCode: op.opCode(Jump32Class, RegSource), + Dst: dst, + Src: src, + Offset: -1, + metadata: &metadata{ + reference: label, + }, } } @@ -118,16 +126,20 @@ func (op JumpOp) opCode(class Class, source Source) OpCode { func (op JumpOp) Label(label string) Instruction { if op == Call { return Instruction{ - OpCode: OpCode(JumpClass).SetJumpOp(Call), - Src: PseudoCall, - Constant: -1, - Reference: label, + OpCode: OpCode(JumpClass).SetJumpOp(Call), + Src: PseudoCall, + Constant: -1, + metadata: &metadata{ + reference: label, + }, } } return Instruction{ - OpCode: OpCode(JumpClass).SetJumpOp(op), - Offset: -1, - Reference: label, + OpCode: OpCode(JumpClass).SetJumpOp(op), + Offset: -1, + metadata: &metadata{ + reference: label, + }, } } diff --git a/collection.go b/collection.go index 9bb0a2d65..3aab7ed95 100644 --- a/collection.go +++ b/collection.go @@ -464,7 +464,7 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { for i := range progSpec.Instructions { ins := &progSpec.Instructions[i] - if !ins.IsLoadFromMap() || ins.Reference == "" { + if !ins.IsLoadFromMap() || ins.Reference() == "" { continue } @@ -474,17 +474,17 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { continue } - m, err := cl.loadMap(ins.Reference) + m, err := cl.loadMap(ins.Reference()) if err != nil { return nil, fmt.Errorf("program %s: %w", progName, err) } fd := m.FD() if fd < 0 { - return nil, fmt.Errorf("map %s: %w", ins.Reference, sys.ErrClosedFd) + return nil, fmt.Errorf("map %s: %w", ins.Reference(), sys.ErrClosedFd) } if err := ins.RewriteMapPtr(m.FD()); err != nil { - return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference, err) + return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference(), err) } } diff --git a/collection_test.go b/collection_test.go index 714e74ac4..601c31a9a 100644 --- a/collection_test.go +++ b/collection_test.go @@ -33,7 +33,7 @@ func TestCollectionSpecNotModified(t *testing.T) { }, } - cs.Programs["test"].Instructions[0].Reference = "my-map" + cs.Programs["test"].Instructions[0] = cs.Programs["test"].Instructions[0].WithReference("my-map") coll, err := NewCollection(&cs) if err != nil { @@ -100,9 +100,9 @@ func TestCollectionSpecRewriteMaps(t *testing.T) { asm.FnMapLookupElem.Call(), asm.JEq.Imm(asm.R0, 0, "ret"), asm.LoadMem(asm.R0, asm.R0, 0, asm.Word), - asm.Return().Sym("ret"), + asm.Return().WithSymbol("ret"), } - insns[0].Reference = "test-map" + insns[0] = insns[0].WithReference("test-map") cs := &CollectionSpec{ Maps: map[string]*MapSpec{ diff --git a/elf_reader.go b/elf_reader.go index 774956c90..38cf176cb 100644 --- a/elf_reader.go +++ b/elf_reader.go @@ -349,10 +349,8 @@ func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructio insns asm.Instructions ) for { - ins := asm.Instruction{ - // Symbols denote the first instruction of a function body. - Symbol: section.symbols[offset].Name, - } + // Symbols denote the first instruction of a function body. + ins := asm.Instruction{}.WithSymbol(section.symbols[offset].Name) // Pull one instruction from the instruction stream. n, err := ins.Unmarshal(r, ec.ByteOrder) @@ -374,7 +372,7 @@ func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructio // Decoded the first instruction of a function body but insns already // holds a valid instruction stream. Store the result and flush insns. - if ins.Symbol != "" && insns.Name() != "" { + if ins.Symbol() != "" && insns.Name() != "" { funcs[insns.Name()] = insns insns = nil } @@ -399,7 +397,7 @@ func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructio return nil, fmt.Errorf("offset %d: no jump target found at offset %d", offset, tgt) } - ins.Reference = sym + ins = ins.WithReference(sym) ins.Constant = -1 } } @@ -579,7 +577,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err return fmt.Errorf("relocation to %q: %w", target.Name, ErrNotSupported) } - ins.Reference = name + *ins = ins.WithReference(name) return nil } diff --git a/elf_reader_test.go b/elf_reader_test.go index aea7e58b9..3ec5faa49 100644 --- a/elf_reader_test.go +++ b/elf_reader_test.go @@ -260,7 +260,7 @@ func TestInlineASMConstant(t *testing.T) { } spec := coll.Programs["asm_relocation"] - if spec.Instructions[0].Reference != "MY_CONST" { + if spec.Instructions[0].Reference() != "MY_CONST" { t.Fatal("First instruction is not a reference to MY_CONST") } diff --git a/example_sock_extract_dist_test.go b/example_sock_extract_dist_test.go index 25dccf65c..cbd1254fd 100644 --- a/example_sock_extract_dist_test.go +++ b/example_sock_extract_dist_test.go @@ -107,11 +107,11 @@ func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) { // 7th byte in IPv6 is Hop count // LDABS requires ctx in R6 - asm.Mov.Reg(asm.R6, asm.R1).Sym("ipv6"), + asm.Mov.Reg(asm.R6, asm.R1).WithSymbol("ipv6"), asm.LoadAbs(-0x100000+7, asm.Byte), // stash the load result into FP[-4] - asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).Sym("store-ttl"), + asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).WithSymbol("store-ttl"), // stash the &FP[-4] into r2 asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), @@ -128,7 +128,7 @@ func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) { // MapUpdate // r1 has map ptr - asm.LoadMapPtr(asm.R1, ttls.FD()).Sym("update-map"), + asm.LoadMapPtr(asm.R1, ttls.FD()).WithSymbol("update-map"), // r2 has key -> &FP[-4] asm.Mov.Reg(asm.R2, asm.RFP), asm.Add.Imm(asm.R2, -4), @@ -141,7 +141,7 @@ func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) { asm.FnMapUpdateElem.Call(), // set exit code to -1, don't trunc packet - asm.Mov.Imm(asm.R0, -1).Sym("exit"), + asm.Mov.Imm(asm.R0, -1).WithSymbol("exit"), asm.Return(), } diff --git a/features/misc.go b/features/misc.go index 1c2802dd9..8a81f001a 100644 --- a/features/misc.go +++ b/features/misc.go @@ -129,7 +129,7 @@ func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) { case boundedLoops: insns = asm.Instructions{ asm.Mov.Imm(asm.R0, 10), - asm.Sub.Imm(asm.R0, 1).Sym("loop"), + asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"), asm.JNE.Imm(asm.R0, 0, "loop"), asm.Return(), } @@ -138,14 +138,14 @@ func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) { asm.Mov.Imm(asm.R0, 0), asm.JLT.Imm(asm.R0, 0, "exit"), asm.Mov.Imm(asm.R0, 1), - asm.Return().Sym("exit"), + asm.Return().WithSymbol("exit"), } case v3ISA: insns = asm.Instructions{ asm.Mov.Imm(asm.R0, 0), asm.JLT.Imm32(asm.R0, 0, "exit"), asm.Mov.Imm(asm.R0, 1), - asm.Return().Sym("exit"), + asm.Return().WithSymbol("exit"), } default: return nil, fmt.Errorf("misc probe %d not implemented", mt) diff --git a/info.go b/info.go index cf692c762..c1ad3fa5a 100644 --- a/info.go +++ b/info.go @@ -233,7 +233,7 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) { } // Tag the first instruction with the name of the program, if available. - insns[0] = insns[0].Sym(pi.Name) + insns[0] = insns[0].WithSymbol(pi.Name) return insns, nil } diff --git a/linker.go b/linker.go index 6f2357d97..d45c00148 100644 --- a/linker.go +++ b/linker.go @@ -121,7 +121,7 @@ func fixupAndValidate(insns asm.Instructions) error { ins := iter.Ins if ins.IsLoadFromMap() && ins.MapPtr() == -1 { - return fmt.Errorf("instruction %d: map %s: %w", iter.Index, ins.Reference, asm.ErrUnsatisfiedMapReference) + return fmt.Errorf("instruction %d: map %s: %w", iter.Index, ins.Reference(), asm.ErrUnsatisfiedMapReference) } fixupProbeReadKernel(ins) diff --git a/linker_test.go b/linker_test.go index 34e028de9..38327aa11 100644 --- a/linker_test.go +++ b/linker_test.go @@ -24,13 +24,13 @@ func TestFindReferences(t *testing.T) { }, "my_other_func": { Instructions: asm.Instructions{ - asm.LoadImm(asm.R0, 1337, asm.DWord).Sym("my_other_func"), + asm.LoadImm(asm.R0, 1337, asm.DWord).WithSymbol("my_other_func"), asm.Return(), }, }, "my_func": { Instructions: asm.Instructions{ - asm.Call.Label("my_other_func").Sym("my_func"), + asm.Call.Label("my_other_func").WithSymbol("my_func"), asm.Return(), }, }, @@ -80,7 +80,7 @@ func TestForwardFunctionDeclaration(t *testing.T) { // Append the implementation of fwd(). spec.Instructions = append(spec.Instructions, - asm.Mov.Imm32(asm.R0, 23).Sym("fwd"), + asm.Mov.Imm32(asm.R0, 23).WithSymbol("fwd"), asm.Return(), ) diff --git a/prog.go b/prog.go index ccac3f28e..61714d405 100644 --- a/prog.go +++ b/prog.go @@ -180,7 +180,7 @@ func (spec *ProgramSpec) layout() ([]reference, error) { // Skip non-symbols and symbols that describe the ProgramSpec itself, // which is usually the first instruction in Instructions. // ProgramSpec itself is already included and not present in references. - if ins.Symbol == "" || ins.Symbol == name { + if ins.Symbol() == "" || ins.Symbol() == name { continue } @@ -188,7 +188,7 @@ func (spec *ProgramSpec) layout() ([]reference, error) { // with valid progs that contain multiple symbols and don't have references // populated. Assume ProgramSpec is used similarly in the wild, so don't // alter this behaviour. - ref := spec.references[ins.Symbol] + ref := spec.references[ins.Symbol()] if ref != nil { out = append(out, reference{iter.Offset.Bytes(), ref}) } diff --git a/prog_test.go b/prog_test.go index f48b3b4cf..78f1e9adf 100644 --- a/prog_test.go +++ b/prog_test.go @@ -49,7 +49,7 @@ func TestProgramRun(t *testing.T) { } ins = append(ins, // return 42 - asm.LoadImm(asm.R0, 42, asm.DWord).Sym("out"), + asm.LoadImm(asm.R0, 42, asm.DWord).WithSymbol("out"), asm.Return(), ) @@ -695,7 +695,7 @@ func TestProgramInstructions(t *testing.T) { Type: SocketFilter, Name: name, Instructions: asm.Instructions{ - asm.LoadImm(asm.R0, -1, asm.DWord).Sym(name), + asm.LoadImm(asm.R0, -1, asm.DWord).WithSymbol(name), asm.LoadMapPtr(asm.R1, arr.FD()), asm.Mov.Imm32(asm.R0, 0), asm.Return(), diff --git a/ringbuf/reader_test.go b/ringbuf/reader_test.go index 9da5622c3..71278fbb6 100644 --- a/ringbuf/reader_test.go +++ b/ringbuf/reader_test.go @@ -134,7 +134,7 @@ func outputSamplesProg(flags int32, sampleSizes ...int) (*ebpf.Program, *ebpf.Ma } insns = append(insns, - asm.Mov.Imm(asm.R0, int32(0)).Sym("exit"), + asm.Mov.Imm(asm.R0, int32(0)).WithSymbol("exit"), asm.Return(), )