PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Stack Rücksprungadressen auslesen [gelöst!]



Felix G
22.12.2008, 20:29
Hallo Leute,

zu Debugzwecken brauche ich eine Funktion die mir alle Rücksprungadressen vom Stack holt. Dazu fehlen mir aber genaue Informationen zum Aufbau des Stacks.

Ich weiss zwar, daß bei einem Call die Rücksprungadresse auf den Stack geschrieben wird, aber es stellt sich die Frage wie ich mich von dort aus den Stack entlang hangeln kann bis ich letztendlich bei main() lande. Denn dummerweise liegen da ja nicht nur die für mich interessanten Rücksprungadressen rum, sondern z.B. auch lokale Variablen.


Naja, vielleicht hat ja Jemand noch ein paar hilfreiche Infos zu dem Thema.

vohopri
22.12.2008, 21:07
Hallo Felix,

dem Stack selbst und den Daten drauf kannst du nicht ansehen, was eine Rücksprungadresse ist, was ein Parameter und was eine Lokale Variable ist. Das zeigt sich erst beim Abarbeiten des Code, der den Inhalt hoffentlich als das verwendet, als das er vorgesehen war, als er auf den Stack geschrieben wurde.

Wenn du den Inhalt des Stacks interpretieren willst, musst du beim schreiben auf den Stack mit protokollieren, was die Bedeutung der Inhalte ist. Oder führst zu Debuggingzwecken einen zusätzlichen Stack mit, der nur die Rücksprungadressen enthält.

Drum ists ja so ein riesiger Aufwand einen betriebssicheren Debugger zu schreiben.

fürchte das hilft dir jetzt auch nicht weiter, aber isso,
grüsse,
vohopri

SprinterSB
22.12.2008, 22:02
Dazu müsste man wissen, wozu das gebraucht wird.

Im alten avr-gcc 3.x war __builtin_return_address(0) nicht implementiert (bzw. lieferte ein falsches Ergebnis).

Vielleicht gibt's inzwischen __builtin_return_address(0), damit ginge immerhin Ebene 1. Laut gcc Doku sollte von diesem Builtin mindestens Ebene 0 korrekt implementiert sein.

Wenn du von irgendwoher nach irgendwohin springen willst, dann
-- geht das mit longjump (weiß aber ebenso nicht, ob unwind etc für avr funzt)
-- evtl. hast du ein Problem mit deinem Software-Design
-- wenn es um ein embedded OS geht braucht's evtl. Compiler-Unterstützung die avr-gcc (noch) nicht bietet, um an eben solche Meta-Info wie Framegröße, Returnadresse, ... heranzukommen.

Felix G
23.12.2008, 00:04
Ich will nirgends hin springen, ich brauch nur die Rücksprungadresse(n) vom Stack (und die sollen nur ausgegeben werden, der Programmablauf soll davon unbeeinflusst bleiben)

Die primitivste Variante wäre es, bei einem Fehler den aktuellen Wert des Program Counters auszugeben, damit wäre zumindest schonmal klar wo der Fehler aufgetreten ist. Besser wäre es aber natürlich, wenn man auf dem Stack die Rücksprungadressen identifizieren könnte.

Ich weiß daß es auf anderen Architekturen nicht nur einen Stackpointer gibt, sondern noch einen zweiten, der angibt wo der Stack für die aktuelle Funktion beginnt. Damit wäre eine derartige Funktion vergleichsweise trivial zu implementieren, aber wenn ich mich nicht täusche fehlt beim AVR ein derartiger Pointer, richtig?

SprinterSB
23.12.2008, 01:51
Ich will nirgends hin springen, ich brauch nur die Rücksprungadresse(n) vom Stack

Vergiss es. Du hast keine Information über die Funktion, die die momentane Funktion aufgerufen hat. Wie groß ist ihr Frame? Wieviel Argumente liegen aufm Stack? Wie ist ihr Prototyp? Varargs vielleicht? War es eine ISR oder eine normale Funktion? Gibt es einen Framepointer? ...


Ich weiß daß es auf anderen Architekturen nicht nur einen Stackpointer gibt, sondern noch einen zweiten, der angibt wo der Stack für die aktuelle Funktion beginnt. Damit wäre eine derartige Funktion vergleichsweise trivial zu implementieren, aber wenn ich mich nicht täusche fehlt beim AVR ein derartiger Pointer, richtig?

Jein. Ob Framepointer/Argpointer gegen den Stackpointer eliminiert werden, hängt von der Komplexität der Funktion und der Beschalterung von GCC ab. Evtl. hilft dir -fno-emit-framepointer, falls avr-gcc sich darum schert.

Eigentlich müsste man den FP auch über eine volatile auto-Variable erzwingen können.

Ein Debugger findet den nächsten Frame übrigens mithilfe der Debug-Infos, und ich glaub kaum, daß du .debug oder .stabs-Sections aufs Target packen und interpretieren willst?

Oder du müsstest gcc instrumentierten Code erzeugen lassen, der die Infos über Framesize etc. in den Frame legt. Ähnlich wie beim Profiling Zusatzinfos abgelegt werden. Dazu brauchst du die GCC-Quellen und dann in ./gcc/config/avr/avr.c Prolog- und Epilog- Emitter patchen sowie das komplette Framelayout...

Felix G
24.12.2008, 14:27
Es läuft :)

-fno-omit-frame-pointer war genau das was gefehlt hat


Falls noch Jemand Interesse an einer derartigen Funktion hat:

callstack.h

#ifndef CALLSTACK_H_INCLUDED
#define CALLSTACK_H_INCLUDED
#warning "please use compiler option -fno-omit-frame-pointer"

#include <stdint.h>

#define CALLSTACK_MAX_DEPTH 16

extern uint16_t callstack[CALLSTACK_MAX_DEPTH];

void stackwalk(const uint16_t ram_end);

#endif // CALLSTACK_H_INCLUDED


callstack.c

#include "callstack.h"
#include <stdlib.h>

uint16_t callstack[CALLSTACK_MAX_DEPTH];

void stackwalk(const uint16_t ram_end)
{
uint8_t n;
uint16_t FP_stackadr;
uint16_t RET_stackadr, RET_stackval;

FP_stackadr = ((uint16_t)&ram_end) + sizeof(ram_end);

for(n=0;(n < CALLSTACK_MAX_DEPTH) && (FP_stackadr <= ram_end);n++)
{
RET_stackadr = FP_stackadr + sizeof(uint16_t);
RET_stackval = ((uint16_t)(*(uint8_t*)RET_stackadr) << 8) | *((uint8_t*)RET_stackadr + 1);
FP_stackadr = *(uint16_t*)FP_stackadr + 1;

callstack[n] = RET_stackval;
}

callstack[n] = 0x0000;
}


Um an die Rücksprungadressen zu gelangen muss man nur einmal stackwalk(RAMEND) ausführen, und schon stehen sie im array callstack.

Daß man RAMEND explizit angeben muss erscheint auf den ersten Blick vielleicht etwas umständlich, aber so ist es z.B. auch möglich die Funktion in eine lib zu packen, ohne an einen bestimmten AVR gebunden zu sein.

Bei den Testfällen die ich mit der Funktion bisher durchgespielt habe stimmten die Ergebnisse, dennoch kann ich natürlich nicht dafür garantieren daß der Stack immer 100% fehlerfrei durchlaufen wird.

SprinterSB
26.12.2008, 10:07
hmmm, und das funktioniert auch, wenn eine Funktion einen Frame hat?

Felix G
26.12.2008, 12:15
Wie gesagt, ich habe nicht jede denkbare Konstellation ausprobiert...

aber bei meinem Test mit 5 oder 6 Funktionen (einige mit, einige ohne Parameter) wurde der Stack korrekt abgearbeitet und es standen tatsächlich alle Rücksprungadressen im Array.


Es steht natürlich Jedem hier frei meine Funktion zu verwenden bzw. zu testen.

SprinterSB
31.12.2008, 14:10
Ich denke es geht deshalb, weil die RET-Adressen direkt hintereinander stehen.

Erzwing mal in einer Funktion einen Frame, etwa so:

void foo (void)
{
char volatile i = 0;
// Funktionsaufrufe...
// bar();
}


Eigentlich bräuchte avr-gcc nen patch um an die Infos zu kommen, zB um es dem Programmierer zu ermöglichen an die Info zu kommen. Vielleicht find ich nächstes Jahr die Zeit nen Patch zu machen für GCC 4.4.x