Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARM64 call method which relative offset is out of range of CALL instruction. #3628

Open
AlphaLxy opened this issue Jan 12, 2024 · 8 comments

Comments

@AlphaLxy
Copy link

AlphaLxy commented Jan 12, 2024

ARM64 CALL instruction (BL <label>)

Branch with Link branches to a PC-relative offset, setting the register X30 to PC+4.
It provides a hint that this is a subroutine call.
<label>
Is the program label to be unconditionally branched to. 
Its offset from the address of this instruction, in the range +/-128MB, is encoded as "imm26" times 4.

So go compiler use another JMP instruction when calling method which relative offset is out of range of CALL instruction.

Here is my own program debug session breaking on a method call.

TEXT xxxx(SB) xxxx/xxxx.go
        xxxx.go:11       0x11c0e71e0     900b40f9        MOVD 16(R28), R16
        xxxx.go:11       0x11c0e71e4     ff6330eb        CMP R16, RSP
        xxxx.go:11       0x11c0e71e8     69050054        BLS 43(PC)
        xxxx.go:11       0x11c0e71ec     fe0f19f8        MOVD.W R30, -112(RSP)
        xxxx.go:11       0x11c0e71f0     fd831ff8        MOVD R29, -8(RSP)
        xxxx.go:11       0x11c0e71f4     fd2300d1        SUB $8, RSP, R29
        xxxx.go:11       0x11c0e71f8     e03f00f9        MOVD R0, 120(RSP)
        xxxx.go:12       0x11c0e71fc     b5f09c97        CALL -6491979(PC)
        xxxx.go:12       0x11c0e7200     e02300f9        MOVD R0, 64(RSP)
        xxxx.go:12       0x11c0e7204     e12700f9        MOVD R1, 72(RSP)
        xxxx.go:13       0x11c0e7208     60c300f0        ADRP 25620480(PC), R0
        xxxx.go:13       0x11c0e720c     00000791        ADD $448, R0, R0
        xxxx.go:13       0x11c0e7210     b85bfe97        CALL -107592(PC)
        xxxx.go:13       0x11c0e7214     e01f00f9        MOVD R0, 56(RSP)
        xxxx.go:13       0x11c0e7218     410480d2        MOVD $34, R1
        xxxx.go:13       0x11c0e721c     010400f9        MOVD R1, 8(R0)
        xxxx.go:13       0x11c0e7220     a1c40790        ADRP 260653056(PC), R1
        xxxx.go:13       0x11c0e7224     21401191        ADD $1104, R1, R1
        xxxx.go:13       0x11c0e7228     210040b9        MOVWU (R1), R1
        xxxx.go:13       0x11c0e722c     41000034        CBZW R1, 2(PC)
        xxxx.go:13       0x11c0e7230     05000014        JMP 5(PC)
        xxxx.go:13       0x11c0e7234     e60c00b0        ADRP 1691648(PC), R6
        xxxx.go:13       0x11c0e7238     c61c1091        ADD $1031, R6, R6
        xxxx.go:13       0x11c0e723c     060000f9        MOVD R6, (R0)
        xxxx.go:13       0x11c0e7240     06000014        JMP 6(PC)
        xxxx.go:13       0x11c0e7244     e20300aa        MOVD R0, R2
        xxxx.go:13       0x11c0e7248     e30c00b0        ADRP 1691648(PC), R3
        xxxx.go:13       0x11c0e724c     631c1091        ADD $1031, R3, R3
        xxxx.go:13       0x11c0e7250     b87fff97        CALL -32840(PC)
        xxxx.go:13       0x11c0e7254     01000014        JMP 1(PC)
        xxxx.go:13       0x11c0e7258     e31f40f9        MOVD 56(RSP), R3
        xxxx.go:13       0x11c0e725c     7b008039        MOVB (R3), R27
        xxxx.go:13       0x11c0e7260     01000014        JMP 1(PC)
        xxxx.go:13       0x11c0e7264     e32b00f9        MOVD R3, 80(RSP)
        xxxx.go:13       0x11c0e7268     e50340b2        ORR $1, ZR, R5
        xxxx.go:13       0x11c0e726c     e52f00f9        MOVD R5, 88(RSP)
        xxxx.go:13       0x11c0e7270     e53300f9        MOVD R5, 96(RSP)
=>      xxxx.go:14       0x11c0e7274     e02340f9        MOVD 64(RSP), R0
        xxxx.go:14       0x11c0e7278     e23f40f9        MOVD 120(RSP), R2
        xxxx.go:14       0x11c0e727c     e12740f9        MOVD 72(RSP), R1
        xxxx.go:14       0x11c0e7280     e40305aa        MOVD R5, R4
        xxxx.go:14       0x11c0e7284     0b000094        CALL 11(PC)
        xxxx.go:15       0x11c0e7288     fdfb7fa9        LDP -8(RSP), (R29, R30)
        xxxx.go:15       0x11c0e728c     ffc30191        ADD $112, RSP, RSP
        xxxx.go:15       0x11c0e7290     c0035fd6        RET
        xxxx.go:11       0x11c0e7294     e00700f9        MOVD R0, 8(RSP)
        xxxx.go:11       0x11c0e7298     e3031eaa        MOVD R30, R3
        xxxx.go:11       0x11c0e729c     c13fff97        CALL -49215(PC)
        xxxx.go:11       0x11c0e72a0     e00740f9        MOVD 8(RSP), R0
        xxxx.go:11       0x11c0e72a4     cfffff17        JMP xxxx
        xxxx.go:11       0x11c0e72a8     00000000        ?
        xxxx.go:11       0x11c0e72ac     00000000        ?

Note that last CALL instruction.

        xxxx.go:14       0x11c0e7284     0b000094        CALL 11(PC)

Then I use step command, it will run to 0x11c0e72b0 and show no source available.

(dlv) s
Stopped at: 0x11c0e72b0
=>   1:	no source available
(dlv) s
Stopped at: 0x11c0e72b0
=>   1:	no source available
Command failed: no source for PC 0x11c0e72b0

Then I disassemble 0x11c0e72b0.

(dlv) disassemble -a 0x11c0e72b0 0x11c0e72c0
	.:0	0x11c0e72b0	702af6f0	ADRP -329977856(PC), R16
	.:0	0x11c0e72b4	10c20691	ADD $432, R16, R16
	.:0	0x11c0e72b8	00021fd6	JMP (R16)
	.:0	0x11c0e72bc	00000000	?

Can Delve step into target method directly (or use another s Command) in this situation ?

  1. What version of Delve are you using (dlv version)?
Delve Debugger
Version: 1.21.2
  1. What version of Go are you using? (go version)?
go version go1.20.10 darwin/arm64
  1. What operating system and processor architecture are you using?
MacOS 14.2.1 (23C71)
Apple M1 Pro
@aarzilli
Copy link
Member

You can use step-instruction (si) three times. As for making delve do this, it looks complicated.

@AlphaLxy
Copy link
Author

I use plugin for GoLand, and do not have step-instruction (si) command.
image

@aarzilli
Copy link
Member

The GoLand UI is not something we develop.

@AlphaLxy
Copy link
Author

I tried this, add three StepInstruction after Step when next three instructions is ADRP ADD JMP. It seems ok.

pkg/proc/target_exec.go

	err = grp.Continue()
	if err != nil {
		return err
	}
	if grp.Selected.Process.BinInfo().Arch.Name == "arm64" {
		selg := grp.Selected.SelectedGoroutine()
		curthread := grp.Selected.CurrentThread()
		topframe, _, _ := topframe(grp.Selected, selg, curthread)
		if topframe.Current.Fn != nil {
			return nil
		}
		registers, _ := grp.Selected.currentThread.Registers()
		pc := registers.PC()
		mem := make([]byte, 12)
		n, _ := grp.Selected.Process.Memory().ReadMemory(mem, pc)
		if n == 12 {
			i0 := binary.LittleEndian.Uint32(mem[0:])
			i1 := binary.LittleEndian.Uint32(mem[4:])
			i2 := binary.LittleEndian.Uint32(mem[8:])
			i0ADRP := (i0 & 0b1_00_11111_0000000000000000000_00000) == 0b1_00_10000_0000000000000000000_00000
			i1ADD := (i1 & 0b1001000100_000000000000_00000_00000) == 0b1001000100_000000000000_00000_00000
			i2JMP := (i2 & 0b1101011000011111000000_0000011111) == 0b1101011000011111000000_00000_00000
			if i0ADRP && i1ADD && i2JMP {
				err = grp.StepInstruction()
				if err != nil {
					return err
				}
				err = grp.StepInstruction()
				if err != nil {
					return err
				}
				return grp.StepInstruction()
			}
		}
	}
	return err

@aarzilli
Copy link
Member

This is the wrong approach, it will break if a breakpoint is encountered during step, or a manual stop, or the process exit, or if the three instructions happen to be ADRP+ADD+JMP but they aren't a trampoline for a CALL instruction.

@AlphaLxy
Copy link
Author

I check if topframe.Current.Fn == nil.

Or I can add that code after s command failed.

if err = next(grp.Selected, true, false); err != nil {
	selg := grp.Selected.SelectedGoroutine()
	curthread := grp.Selected.CurrentThread()
	topframe, _, _ := topframe(grp.Selected, selg, curthread)
	if topframe.Current.Fn == nil && grp.Selected.Process.BinInfo().Arch.Name == "arm64" {
		// ...
		return err
	}
	_ = grp.Selected.ClearSteppingBreakpoints()
	return err
}

@AlphaLxy
Copy link
Author

Make second s command ok.

(dlv) s
Stopped at: 0x11c0e72b0
=>   1:	no source available
(dlv) s // !!! this !!!
Stopped at: 0x11c0e72b0
=>   1:	no source available
Command failed: no source for PC 0x11c0e72b0

@AlphaLxy
Copy link
Author

This is the wrong approach, it will break if a breakpoint is encountered during step, or a manual stop, or the process exit, or if the three instructions happen to be ADRP+ADD+JMP but they aren't a trampoline for a CALL instruction.

I see, is there any other way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants