Autor Thema: Symbol Table durchsuchen  (Gelesen 17103 mal)

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« am: 11. January 2011, 18:06 »
Hallo,

ich wollte mal fragen, ob jemand weiß, wie man an die Symbol-Tabelle des aktuell laufenden Prozesses kommt. Mein Vorhaben: Adressen eines Call-Stacks in Funktionsnamen und Lines auflösen. Ich weiß, wie man einen Call-Stack mit Adressen realisiert, und würde nun gern ein Mapping durchführen. Oder muss ich dafür die bfd.h in meinen Kernel einbauen? Oder geht es vielleicht auch "zu Fuss"? Ich würde ja auch gern verstehen, wie das funktioniert. Hat bestimmt was mit den Segmenten .text, .code und .bss zu tun, wenn ich mir objdump -t anschaue.

So habe ich meine Call-Stack-View gemacht.

void print_call_stack()
{
  void **ebp = 0;

  ASM("mov %%ebp, %0":"=a"(ebp));

  while(ebp)
  {
    kprintf("0x%x\n", ebp[1]); // The caller function address
    ebp = (void**)ebp[0]; // Get previous frame
  }
}

Grüße

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #1 am: 11. January 2011, 19:08 »
Moin

also erstens weshalb willst jetzt an die Symboltabelle des aktuellen Prozesses kommen?
Die Symboltabelle wird nicht mehr benutzt wenn der Prozess schon läuft. die Symboltabelle dient afaik nur für dynamisches linken und das ist wenn der Prozess läuft abgeschlossen.

zweitens für welches OS?
Linux verwendet auf jedenfall das ELF Format und da gibt es eine Solche Symboltabelle. Das Format von Winfows, PE, hat glaube ich auch eine Symboltabelle, diese ist aber ganz anders aufgebaut wie die von einer ELF Datei.
Und ich glaube nicht das die Tabellen noch alle bestehen, bzw. ob der Prozess da drauf zugreifen kann. weil es ja nicht benötigt wird und somit nur platz verbrauchen würde.

PNoob

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 11. January 2011, 19:25 »
Hallo und vielen Dank für dein Feedback.

Es geht darum, das ich ein Call-Stack ausgeben will, wenn mein Kernel eine Exception geworfen hat. Ich dachte, es funktioniert irgendwie genauso, wie bei http://www.jamesmolloy.co.uk/tutorial_html/1.-Environment%20setup.html mit der Referenz auf die end-Section. Dort wird im Linker-Script definiert, das am Ende aller Sektionen die end-Sektion liegt. Innerhalb des Kernels kann man dann mit

extern unsigned int end;

darauf zugreifen, um die placement addresse festlegen zu können.

Ich habe mir nun eingebildet, das ich eine Struktur mit den Elf-Informationen baue, und mittels Struktur-Pointer auf die .symtab-Sektion zeigen lassen kann, um dort an die Informationen zu gelangen. Leider scheint das so aber nicht zu funktionieren.

Nun stelle ich mir die Frage, wie man das angehen könnte. Sprich, was benötige ich (Elf-Strukturen sollten kein Problem darstellen) um einen Menschen-Lesbaren Call-Stack zu kommen.

Trotzdem vielen Dank für die Info, ich wusste nicht, das die Sektionen zur Laufzeit teilweise nicht mehr verfügbar sind. Wieder was dazu gelernt.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 11. January 2011, 20:06 »
Wenn du deinen Kernel mit Multiboot lädst, sind sie noch verfügbar. Der Eintrag shdr_addr im Multiboot-Header zeigt auf ein Array von Section-Headern, in dem du nach der Symbol Table durchsuchen kannst.
Dieser Text wird unter jedem Beitrag angezeigt.

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #4 am: 11. January 2011, 20:57 »
Porkchicken: das wusste ich gar nicht. nun hab ich aber auch wieder etwas dazu gelernt. ich dachte die wären genauso wie bei normal Programmen zum Teil mit anderem Kram überschrieben.

Aber irgendwie erschließt sich mir noch kein Sinn hinter deinem Vorhaben. es könnte auch sein das ich nicht verstehe was du jetzt genau willst. Was genau bringt die die Symboltabelle bei einer Exception?
Bei einer Exception sind eigentlich register werte und vor allem eip sehr Wichtig und interessant.

PNoob

stultus

  • Beiträge: 486
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 11. January 2011, 21:06 »
Wie bereits oben beschrieben, kann man aber ergänzend zu EIP und den Rücksprungadressen auch die _Namen_ der betroffenen Funktionen ausgeben lassen, wenn man die Tabelle hat. Und die sind genauso interessant - bzw beschleunigen das debuggen doch deutlich.
Diese Antwort hätte sich übrigens (wie einige andere) durch sorgfältiges lesen vermeiden lassen.
MSN: planetconquestdm@hotmail.de
ICQ: 190-084-185

... Wayne?

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 11. January 2011, 21:09 »
Wenn du deinen Kernel mit Multiboot lädst, sind sie noch verfügbar. Der Eintrag shdr_addr im Multiboot-Header zeigt auf ein Array von Section-Headern, in dem du nach der Symbol Table durchsuchen kannst.

Vielen herzlichen Dank, ich denke, da komm ich erstmal weiter.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 11. January 2011, 21:42 »
@PNoob

Es ist wesentlich einfacher wenn man die Funktionsnamen hat anstatt nur irgendwelcher Adressen, wo man erstmal die Funktion rausfinden muss und auch ist es einfacher wenn man nachvollziehen kann wie der Call-Graph aussieht.
Desweiteren müssen die Symboltabellen bei einigen Programmen verfügbar sein (ich werde sie auf jeden Fall immer im Speicher lassen). Denn es besteht durchaus die Möglichkeit auch später noch Module nachzuladen.
Jedes Executeable-Format welches Shared-Libraries unterstützt muss zwangsläufig Symboltabellen haben, da ansonsten die Symbole gar nicht aufgelöst werden können.

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #8 am: 12. January 2011, 07:33 »
Moin

Flashburn: OK stimmt aber ich persönlich baue meinen Kernel dann lieber mit -g und nutze im Fall der Fälle objdump Das reicht zum debuggen aus. Aber das mit den Funktionsnamen ist echt ne gute Idee.

Das jedes Executable Format welches Shared Libaries unterstützt auch Symboltabellen ist mir klar. ich glaube das hatte ich oben schonmal  gesagt.

PNoob

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 12. January 2011, 08:22 »
Zitat von: pnoob
OK stimmt aber ich persönlich baue meinen Kernel dann lieber mit -g und nutze im Fall der Fälle objdump Das reicht zum debuggen aus.
Das nutzt dir aber oft nichts, wenn du nicht weißt aus welcher anderen Funktion (usw. usw.) diese Funktion aufgerufen wurde und am besten ist es, wenn man noch wüsste mit welchen Parametern die jeweiligen Funktionen aufgerufen wurden, aber das umzusetzen ist dann wirklich nicht einfach.

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #10 am: 12. January 2011, 13:35 »
Moin

am besten ist es, wenn man noch wüsste mit welchen Parametern die jeweiligen Funktionen aufgerufen wurden, aber das umzusetzen ist dann wirklich nicht einfach.
Das stimmt nun auch wieder nict. es reicht ja, wenn man die letzten 30 aufrufe weiß. dann baut man sich so eine art printf, die aber ihr eigenes Array hat wo sie reinschreibt. und bei einer Exception kann man dann auf die gespeicherten Aufrufe zugreifen.

PNoob

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 12. January 2011, 14:08 »
Zitat von: pnoob
Das stimmt nun auch wieder nict. es reicht ja, wenn man die letzten 30 aufrufe weiß. dann baut man sich so eine art printf, die aber ihr eigenes Array hat wo sie reinschreibt. und bei einer Exception kann man dann auf die gespeicherten Aufrufe zugreifen.
Ich habe keine Ahnung was du damit meinst ;) Aber falls es um die Parameter geht, man müsste halt wissen, wieviele Parameter jede Funktion hat und wie groß ein Parameter ist und dafür gibt es ja Debugging-Formate und die zu parsen ist halt nicht einfach.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 12. January 2011, 14:45 »
Ich glaube PNoob will am Anfang jeder Funktion die Parameter in einer Art Ringpuffer speichern. Ich schätze mal dieses "printf" soll eine va_list der Parameter bekommen, und die abspeichern. Dazu muss man allerdings jede Funktion entsprechend anpassen und das widerspricht der Grundidee.

Einfacher ist es beim Stacktrace einfach die ersten 3-5 Parameter auszugeben. Meistens spielt sich da eh das wichtigste ab, und bei Funktionen, die weniger Parameter haben, steht da ein bisschen Müll, über den man halt hinwegsehen muss.
Dieser Text wird unter jedem Beitrag angezeigt.

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 12. January 2011, 16:56 »
Ich will das machen, weil ich zu meiner Schande gestehen muss, dass ich das mit den Registern noch nicht so verinnerlicht habe. Ich verzweifel bspw. an dieser Ausnahme:

============================================================================
Unhandled Interrupt occured
EDI = 0x57A18, ESI = 0x57A20, EBP = 0x82CB, EBX = 0x57A24
EDX = 0xC0081038, ECX = 0x1035F4, EAX = 0x0, ESP = 0xDFFFFFD0
INT = 5, ERR = 0x0
EIP = 0x57A20, CS = 0x8, EFLAGS = 0x10206, USRESP = 0x57A20, SS = 0x67E5C
============================================================================
EDI = 0x57A18, ESI = 0x57A20, EBP = 0x82CB, EBX = 0x57A24
EDX = 0xC0081038, ECX = 0x1035F4, EAX = 0x0, ESP = 0xDFFFFFD0
INT = 13, ERR = 0x0
EIP = 0x57A20, CS = 0x8, EFLAGS = 0x10206, USRESP = 0x57A20, SS = 0x67E5C
kernel/isr.c(62): General Protection fault!

Ich weiß nicht, wieso EIP auf diesem Wert steht, offensichtlich hats den Stack zerbröselt und ich finde den Fehler noch nicht. Aber vielleicht kann mir einer auf die Sprünge helfen und den Weg deuten, wie ich zur Ursache des Problems nur anhand der Register komme. Ich kann nur so viel sagen, das die Funktionen nach objdump erst bei 0x100000 beginnen:

Disassembly of section .text:

00100000 <__code>:

Daher wollte ich einen Call-Stack haben, weil ich annahm, das der mich weiter bringt.

Ich weiß vom Exception-Wiki, das 5 Bound Rage ist und damit eine Array-Grenze überschritten wird. Aber welcher Array das ist, weiß ich nicht und habe auch keine Idee, wo ich anfangen soll, zu suchen.

EDIT: Das ESP Register hat in der Ausgabe gefehlt

Edit 2: Ok, ich hab jetzt verstanden, das es mit ESI - EDI zusammen hängt, also dort ist der Source/Destination Index (richtig?). Allerdings weiß ich noch nicht, wie man anhand dieser Informationen an den Array kommt.

Edit 3: Was mir grad noch auffällt, warum ist der ESI addressen-technisch weiter hinter angesiedelt als der EDI? Das ist doch schon etwas seltsam.

Edit 4: Ok, ich hab die Ursache dafür nun gefunden, bei der Rückkehr aus dem ISR-Handler habe ich versehentlich aus dem falschen Register (ax statt bx) zurück kopiert. Nun habe ich nur noch meinen Page-Fault, den werde ich aber auch noch finden.
« Letzte Änderung: 12. January 2011, 18:21 von Beatkiller »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 12. January 2011, 19:17 »
Edit 2: Ok, ich hab jetzt verstanden, das es mit ESI - EDI zusammen hängt, also dort ist der Source/Destination Index (richtig?). Allerdings weiß ich noch nicht, wie man anhand dieser Informationen an den Array kommt.

Edit 3: Was mir grad noch auffällt, warum ist der ESI addressen-technisch weiter hinter angesiedelt als der EDI? Das ist doch schon etwas seltsam.

Diese beiden Register werden vom Compiler für alles mögliche verwendet. Auf die Namen solltest du nicht so viel Wert legen. Deswegen ist die Relation der Werte auch nicht seltsam.
Dieser Text wird unter jedem Beitrag angezeigt.

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 13. January 2011, 13:17 »
Vielen Dank für alle eure Anregungen und Erklärungen. Ich habe mein Problem mit dem Pagefault noch nicht lösen können. Es geht aber um das Taskswitching. Folgender ASM:

001035c1 <task_switch>:
  1035c1:       55                      push   %ebp
  1035c2:       89 e5                   mov    %esp,%ebp
  1035c4:       53                      push   %ebx
  1035c5:       83 ec 14                sub    $0x14,%esp
  1035c8:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  1035cd:       85 c0                   test   %eax,%eax
  1035cf:       0f 84 b6 00 00 00       je     10368b <task_switch+0xca>
  1035d5:       89 e3                   mov    %esp,%ebx
  1035d7:       89 5d f4                mov    %ebx,-0xc(%ebp)
  1035da:       89 eb                   mov    %ebp,%ebx
  1035dc:       89 5d f0                mov    %ebx,-0x10(%ebp)
  1035df:       e8 84 cc ff ff          call   100268 <read_eip>
  1035e4:       89 45 ec                mov    %eax,-0x14(%ebp)
  1035e7:       81 7d ec 45 23 01 00    cmpl   $0x12345,-0x14(%ebp)
  1035ee:       0f 84 9a 00 00 00       je     10368e <task_switch+0xcd>
  1035f4:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  1035f9:       8b 55 f4                mov    -0xc(%ebp),%edx
  1035fc:       89 50 04                mov    %edx,0x4(%eax)
  1035ff:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  103604:       8b 55 f0                mov    -0x10(%ebp),%edx
  103607:       89 50 08                mov    %edx,0x8(%eax)
  10360a:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  10360f:       8b 55 ec                mov    -0x14(%ebp),%edx
  103612:       89 50 0c                mov    %edx,0xc(%eax)
  103615:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  10361a:       8b 40 14                mov    0x14(%eax),%eax
  10361d:       a3 2c 8d 10 00          mov    %eax,0x108d2c
  103622:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  103627:       85 c0                   test   %eax,%eax
  103629:       75 0a                   jne    103635 <task_switch+0x74>
  10362b:       a1 28 8d 10 00          mov    0x108d28,%eax
  103630:       a3 2c 8d 10 00          mov    %eax,0x108d2c
  103635:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  10363a:       8b 40 04                mov    0x4(%eax),%eax
  10363d:       89 45 f4                mov    %eax,-0xc(%ebp)
  103640:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  103645:       8b 40 08                mov    0x8(%eax),%eax
  103648:       89 45 f0                mov    %eax,-0x10(%ebp)
  10364b:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  103650:       8b 40 0c                mov    0xc(%eax),%eax
  103653:       89 45 ec                mov    %eax,-0x14(%ebp)
  103656:       a1 2c 8d 10 00          mov    0x108d2c,%eax
  10365b:       8b 40 10                mov    0x10(%eax),%eax
  10365e:       a3 10 70 10 00          mov    %eax,0x107010
  103663:       a1 10 70 10 00          mov    0x107010,%eax
  103668:       8b 98 00 20 00 00       mov    0x2000(%eax),%ebx
  10366e:       8b 45 ec                mov    -0x14(%ebp),%eax
  103671:       8b 55 f4                mov    -0xc(%ebp),%edx
  103674:       8b 4d f0                mov    -0x10(%ebp),%ecx
  103677:       fa                      cli   
  103678:       89 c1                   mov    %eax,%ecx
  10367a:       89 d4                   mov    %edx,%esp
  10367c:       89 cd                   mov    %ecx,%ebp
  10367e:       0f 22 db                mov    %ebx,%cr3
  103681:       b8 45 23 01 00          mov    $0x12345,%eax
  103686:       fb                      sti   
  103687:       ff e1                   jmp    *%ecx
  103689:       eb 04                   jmp    10368f <task_switch+0xce>
  10368b:       90                      nop
  10368c:       eb 01                   jmp    10368f <task_switch+0xce>
  10368e:       90                      nop
  10368f:       83 c4 14                add    $0x14,%esp
  103692:       5b                      pop    %ebx
  103693:       5d                      pop    %ebp
  103694:       c3                      ret   

Nun bekomme ich folgende Register in der PF:

Page fault at 0xF45D8A15! (present = 1, rw = 1, user = 0, reserved = 0, id = 0)
EDI = 0x57A17,  ESI = 0x57A20,  EBP = 0xDFFFFF78,       EBX = 0x30DCC
EDX = 0x3,      ECX = 0x32,     EAX = 0xC008100C,       ESP = 0x7DB
EIP = 0x1035D4, CS = 0x8,       DS = 0xC0080010,        SS = 0x0
INT = 14,       ERR = 0x2
EFLAGS = 0x10082, USRESP = 0x0

Wenn ich nun nach der EIP suche, stell ich fest, das es keine 0x1035D4 zu geben scheint. An besagter Stelle im C-Source ist nur eine if:

void task_switch()
{

  if(!current_task)
  {
    return;
  }

current_task selbst ist ein globaler volatile Pointer auf eine Struktur, und stellt immer den derzeit ausgeführten Task dar. Geh ich recht in der Annahme, das der Stack hier ziemlich kaputt ist und vermutlich sogar der Stack der Funktion task_switch überschrieben wurde? Wenn ja, wo könnte ich anfangen, den Fehler zu suchen? Ich bin vermutlich schon ein bisschen betriebsblind.

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #16 am: 13. January 2011, 13:50 »
Moin

Es wird ja auf die Variabe zugegriffen in dieser IF Abfrage. ein Pagefault wird beim lesen ausgelößt wenn die Page nicht gemappt ist oder  der Zugreifer keine Berechtigungen hat. Ich vermute mal das dein Kernel nicht in allen Prozessen/tasks an gleicher stelle gemappt ist.
Liege ich richtig mit der Vermutung?

PNoob

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 14. January 2011, 08:53 »
Ich hab noch nicht verstanden, was du mit deiner Frage meinst. Falls du meinst, ob jeder Prozess seinen eigenen Kernel-Heap hat, ja, aber das ist wie bei JM's Tutorial jeweils immer ein Zeiger auf das Paging-Directory des Kernels. Zusätzlich hat jeder Prozess noch seinen eigenen privaten Stack.

Der private Stack ist dann natürlich an unterschiedlichen Stellen gemappt. So wie hier beschrieben: http://www.jamesmolloy.co.uk/tutorial_html/9.-Multitasking.html.

Ich habe bisher nur einen Prozess laufen, der mit fork() erzeugt wurde.

Interessant ist vielleicht außerdem zu erwähnen, das wenn ich, vor der if-Condition noch ein kprintf() einfüge, dass das System dann läuft, also ohne Pagefault. Daher meine Vermutung, das der Stack der task_switch()-Funktion überschrieben wurde.

PNoob

  • Beiträge: 106
    • Profil anzeigen
    • Mein Blog
Gespeichert
« Antwort #18 am: 14. January 2011, 13:45 »
Der Kernel also der code muss in jedem Prozess an die gleiche stelle gemappt werden. ist das bei dir so oder nicht? mit dem Heap und so hat das nichts zu tun.

PNoob

Beatkiller

  • Beiträge: 17
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 14. January 2011, 18:32 »
Kannst du mir vielleicht anhand eines Code-Beispiels (Link genügt) zeigen, was du meinst? Ich habe nicht gewusst, das man den Kernel-Code irgendwo hin kopieren muss.

 

Einloggen