arm: use CFI directives for stack usage

Call Frame Information is stored in the .debug_frame section and is used
by debuggers for unwinding. For assembly, this information is not known.
Debuggers will normally use heuristics to figure out the parent function
in the absence of call frame information.

This usually works fine, but is not enough for determining stack sizes.
Instead, I hardcoded the stack size information in
stacksize/stacksize.go, which is somewhat fragile. This change uses CFI
assembly directives to store this information instead of hardcoding it.

This change also fixes the following error message that would appear in
GDB:

    Backtrace stopped: previous frame identical to this frame (corrupt stack?)

More information on CFI:
  * https://sourceware.org/binutils/docs/as/CFI-directives.html
  * https://www.imperialviolet.org/2017/01/18/cfi.html
This commit is contained in:
Ayke van Laethem
2020-07-17 00:18:50 +02:00
committed by Ron Evans
parent 6ad6f14a04
commit 50a677e9b7
3 changed files with 41 additions and 10 deletions
+11 -2
View File
@@ -207,6 +207,15 @@ func (fi *frameInfo) exec(bytecode []byte) ([]frameInfoLine, error) {
switch lowBits {
case 0: // DW_CFA_nop
// no operation
case 0x07: // DW_CFA_undefined
// Marks a single register as undefined. This is used to stop
// unwinding in tinygo_startTask using:
// .cfi_undefined lr
// Ignore this directive.
_, err := readULEB128(r)
if err != nil {
return nil, err
}
case 0x0c: // DW_CFA_def_cfa
register, err := readULEB128(r)
if err != nil {
@@ -225,10 +234,10 @@ func (fi *frameInfo) exec(bytecode []byte) ([]frameInfoLine, error) {
}
fi.cfaOffset = offset
default:
return nil, fmt.Errorf("could not decode .debug_frame bytecode op 0x%x", op)
return nil, fmt.Errorf("could not decode .debug_frame bytecode op 0x%x (for address 0x%x)", op, fi.loc)
}
default:
return nil, fmt.Errorf("could not decode .debug_frame bytecode op 0x%x", op)
return nil, fmt.Errorf("could not decode .debug_frame bytecode op 0x%x (for address 0x%x)", op, fi.loc)
}
}
}
-8
View File
@@ -181,14 +181,6 @@ func CallGraph(f *elf.File, callsIndirectFunction []string) (map[string][]*CallN
switch f.Machine {
case elf.EM_ARM:
knownFrameSizes := map[string]uint64{
// implemented in assembly in TinyGo
"tinygo_startTask": 0, // thunk
"tinygo_getSystemStackPointer": 0, // getter
"tinygo_switchToScheduler": 0, // calls tinygo_swapTask
"tinygo_switchToTask": 0, // calls tinygo_swapTask
"tinygo_swapTask": 9 * 4, // 9 registers saved
"tinygo_scanCurrentStack": 9 * 4, // 9 registers saved
// implemented with assembly in compiler-rt
"__aeabi_uidivmod": 3 * 4, // 3 registers on thumb1 but 1 register on thumb2
}