Toon posts:

[C/Asm] Stack problemen...

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik zit nu al een tijdje te kloten met een stukje assembly code. Allereerst het algemene idee. Er zijn 3 routines. Arbiter, proc1 en proc2. Het idee is dat de arbiter zal fungeren als zeer simpele scheduler. De 2 processen roepen zelf de arbiter aan dus dit maakt de zaak al een stuk simpler. Daarnaast is de startpositie van beide processen bekend.

Het probleem zit hem in het opslaan van de instructie pointer (EIP)

Hier de code van de arbiter (versimpeld):

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
unsigned int procPos[MAX_PROCESSEN];
int currentProc = 0;

void arbiter(void)
{
  unsigned int localEIP;

  if(currentProc == -1)
  {
     // Eerste aanroep, start 1e proces
     __asm pop dword ptr localEIP
     // FIXME: EIP bevat hier het adres waar de init routine ons heeft aangeroepen
     //             Sla deze ergens op zodat we ooit nog eens kunnen stoppen.
     localEIP = procPos[0];
     __asm push dword ptr localEIP
     __asm ret
  }
  else 
  {
    // Haal adres van de stack vanwaar deze methode is aangeroepen
    __asm pop dword ptr localEIP

    // Sla deze op en haal de nieuwe instructie pointer op
    procPos[currentProc] = localEIP;
    currentProc = (currentProc == 0) ? 1 : 0;
    localEIP = procPos[currentProc];
  
    // Gooi het adres op de stack.
    // De ret instructie haalt deze van de stack en zal hier executie vervolgen
    __asm push dword ptr localEIP
    __asm ret
  }
}


Hier is het stukje initializatie code:

code:
1
2
3
4
5
6
7
8
9
10
11
void init(void)
{
  // Startpositie van beide processen
  procPos[0] = ???;
  procPos[1] = ???;

  currentProc = -1;

  // Begin het proces
  arbiter();
}


De beide processen liggen direct achter elkaar in het geheugen en het ene proces draait prima. Echter zit het probleem bij het tweede proces. Hier komt een compleet ander adres tevoorschijn en langzamerhand loopt ie in de stront doordat ie willekeurige instructies aan het uitvoeren is.

Hier de code van 1 proces. (Ze zijn exact gelijk qua opbouw)

code:
1
2
3
4
5
proc1_loop:
   push dword ptr [adresvanstring]
   call my_printf          (my_printf is van het type void my_printf(char *); )
   call arbiter;
   jmp proc1_loop


Ik hoop dat een van de GoT devvers mij inzicht kan verschaffen in wat ik hier verkeerd doe :)
Let wel dat de code zo uit m'n hoofd getypt is dus er kunnen hier en daar wat kleine typefoutjes tevoorschijn zijn gekomen :)

Verwijderd

Alvast een bugje in Arbiter(): bij
C:
1
    procPos[currentProc] = localEIP;

is currentProc niet geinitialiseerd.

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Ik snap nog niet wat je precies wil, maar wat je vergeet is dat de Compiler ook de basepointer (in veel literatuur framepointer genoemd) opslaat direct na begin van de functie en een nieuwe maakt.
Daarna staan de lokale variabelen op de stack.

Met pop haal je "unsigned int localEIP;" van de stack. Om bij je eip te komen moet je EBP + 4 gebruiken. Het is niet verstandig om zomaar met je stack te kloten.

ret is niet nodig. In c heb je een return;


Waarom probeer je het niet gewoon in C met functiepointers en gooi je dat assembly eruit.

Verwijderd

Topicstarter
Verwijderd schreef op woensdag 30 maart 2005 @ 16:15:
Alvast een bugje in Arbiter(): bij
C:
1
    procPos[currentProc] = localEIP;

is currentProc niet geinitialiseerd.
Dit is niet het geval in de originele code. Ik had hem bij het posten van het topic niet bij de hand.
Daos schreef op woensdag 30 maart 2005 @ 16:16:
Ik snap nog niet wat je precies wil, maar wat je vergeet is dat de Compiler ook de basepointer (in veel literatuur framepointer genoemd) opslaat direct na begin van de functie en een nieuwe maakt.
Daarna staan de lokale variabelen op de stack.

Met pop haal je "unsigned int localEIP;" van de stack. Om bij je eip te komen moet je EBP + 4 gebruiken. Het is niet verstandig om zomaar met je stack te kloten.

ret is niet nodig. In c heb je een return;


Waarom probeer je het niet gewoon in C met functiepointers en gooi je dat assembly eruit.
Allereerst de reden waarom ik dit niet gewoon met C doe. Het gaat hier om dynamisch gegenereerde assembly code. Deze code springt om de zoveel tijd terug naar een soort van controle element. Afhankelijk van een aantal condities bepaald dit controle element (de arbiter) of de executie van de 2 losstaande procedures doorgang mag vinden of niet.

Ik heb geen controle op de gegenereerde assembly code. Ik weet enkel dat er een call wordt gemaakt naar een arbiter routine. Van hieruit moet ik er voor zorgen dat ik de executie van de code weet te organiseren. Voor zover ik zie kan ik dit niet makkelijk met een C oplossing doen.

Is het niet zo dat bij een call als allerlaatste het adres op de stack wordt gegooid waar na de return de executie verder zou moeten gaan? Iig vind ik op ebp+4 niet het juiste adres om naar terug te keren.

Als iemand nog andere ideen heeft, graag :)

[ Voor 70% gewijzigd door Verwijderd op 30-03-2005 16:57 ]


Verwijderd

nee, als laatste staat je laaste locale variable

een stack ziet er als volg uit
bovenkant stack
local variable 2
locale variable 1
SFP
terugkeeradres
parameter 1
parameter 2

dus je terugkeer adres is inderdaad ebp+4

[ Voor 36% gewijzigd door Verwijderd op 30-03-2005 17:05 ]


Verwijderd

Topicstarter
Wellicht dat het volgende stukje ASM nog enige hulp kan bieden.
Het is het begin van de arbiter methode.

code:
1
2
3
4
5
6
7
    push    ebp
    mov ebp, esp
    sub esp, 192                ; 000000c0H
    push    ebx
    push    esi
    push    edi
    lea edi, DWORD PTR [ebp-192]


Momenteel werkt ie nog niet, maar we blijven puzzelen ;)

Verwijderd

Topicstarter
Verwijderd schreef op woensdag 30 maart 2005 @ 17:04:
nee, als laatste staat je laaste locale variable

een stack ziet er als volg uit
bovenkant stack
local variable 2
locale variable 1
SFP
terugkeeradres
parameter 1
parameter 2

dus je terugkeer adres is inderdaad ebp+4
Dit geld dus alleen wanneer ik geen lokale variabelen heb neem ik aan :)
Dan zou ebp+2 SFP zijn en ebp+4 het terugkeeradres

Correct?

Verwijderd

bij een call wordt de return adres op de stack gepushed, dan volgt de functie prolog die het register ebp pushed(sfp) en de eip in ebp stopt, dus ook zonder locale variablen blijf je ret ebp+4, lokale variabelen roep je btw aan door ebp-4 etc. Je werkt altijd met 4 bytes omdat adressen 32bits zijn

[ Voor 23% gewijzigd door Verwijderd op 30-03-2005 17:20 ]


Verwijderd

Topicstarter
Okee, ik heb zojuist het volgende eens geprobeerd:

code:
1
2
3
__asm mov eax, [een willekeurig adres waar ik heen wens te springen]
__asm mov [ebp+4], eax
__asm ret


Deze code zou dus naar dat willekeurige adres moeten springen. Wanneer ik echter stapsgewijs door het programma heen loop springt ie continu naar adres: 0012FEDC.

Voor de ret zijn dit mijn registers:

code:
1
2
EAX = 0048BB76 EBX = 7FFDF000 ECX = 00000001 EDX = 000000FF ESI = 00000000 EDI = 0012FDD8 EIP = 0048B96E 
ESP = 0012FD0C EBP = 0012FDD8 EFL = 00000246


En na de ret zijn dit mijn registers:

code:
1
2
EAX = 0048BB76 EBX = 7FFDF000 ECX = 00000001 EDX = 000000FF ESI = 00000000 EDI = 0012FDD8 EIP = 0012FEDC 
ESP = 0012FD10 EBP = 0012FDD8 EFL = 00000246


Door de bovenstaande asm code zou ik toch theoretisch het terugkeeradres vervangen met een ander adres? Of zie ik dit verkeerd >:)

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Verwijderd schreef op woensdag 30 maart 2005 @ 17:14:
bij een call wordt de return adres op de stack gepushed, dan volgt de functie prolog die het register ebp pushed(sfp) en de esp in ebp stopt, dus ook zonder locale variablen blijf je ret ebp+4, lokale variabelen roep je btw aan door ebp-4 etc. Je werkt altijd met 4 bytes omdat adressen 32bits zijn
De functie prolog is dit stukje:
Het reserveren ruimte voor lokale vars:
code:
1
    sub esp, 192                ; 000000c0H
Het opslaan van registers die niet veranderd mogen worden door een functie (eax, ecx en edx mogen wel vrij gebruikt worden):
code:
1
2
3
    push    ebx
    push    esi
    push    edi
Stopt het adres van je laatste variabele in edi om er wat mee te doen
code:
1
    lea edi, DWORD PTR [ebp-192]
Verwijderd schreef op woensdag 30 maart 2005 @ 17:22:
Okee, ik heb zojuist het volgende eens geprobeerd:

code:
1
2
3
__asm mov eax, [een willekeurig adres waar ik heen wens te springen]
__asm mov [ebp+4], eax
__asm ret


Door de bovenstaande asm code zou ik toch theoretisch het terugkeeradres vervangen met een ander adres? Of zie ik dit verkeerd >:)
Ja, Bovenaan je stack staat nog steeds de oude basepointer (en vaak ook lokale variabelen). Met de ret gebruikt hij die oude basepointer als returnadres.

[edit]
Gebruik "__asm leave" eens voordat je "__asm ret" aanroept. Of zoiets als: "__asm mov esp, ebp", "__asm pop ebp"

[ Voor 4% gewijzigd door Daos op 30-03-2005 17:48 ]


Verwijderd

Topicstarter
Iedereen hartelijk bedankt, het werkt eindelijk. _/-\o_

Ik had inderdaad even over het hoofd gezien dat __asm ret de acties van de functie prolog niet ongedaan zou maken en dus daardoor een andere waarde van de stap zou poppen als return adres. 8)7

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Let er overigens op dat dit nogal errorprone is, aangezien beide functies met dezelfde stackframe werken. Als beide bijvoorbeeld een locale array definieren, en proc1 roept de arbiter aan, waardoor de cpu proc2 uit gaat voeren, en proc2 past vervolgens z'n locale array aan, dan zijn die changes weer te zien in proc1's array (in het minst erge geval iig, in het ergste geval verneuk je gewoon de stackframe van de andere proc). Je zult dus nog wat meer moeten doen behalve eip aanpassen, zoals registerinhoud opslaan en ervoor zorgen dat beide procs hun eigen stackspace hebben

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Daos
  • Registratie: Oktober 2004
  • Niet online
Assembly in C code is niet mooi, werkt vaak niet en is bovendien niet volgens de Ansi-C standaard.

Ansi-C manier zonder assembly:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <setjmp.h>
#include <stdio.h>

#define MAX_PROCESSEN 2

void *procPos[MAX_PROCESSEN];
int currentProc = 0;

jmp_buf jmpb;

void proc1() {
    printf("In proc1\n");
    longjmp(jmpb, 1);
}

void proc2() {
    printf("In proc2\n");
    longjmp(jmpb, 2);
}

int iterations = 10;
void arbiter(void)
{
    void (*localEIP)();
    int retval;

    retval = setjmp(jmpb);
    currentProc = currentProc % MAX_PROCESSEN + 1;
    localEIP = procPos[currentProc - 1];

    if (iterations--)
        (*localEIP)();
}

int main(void)
{
    /* Startpositie van beide processen */
    procPos[0] = proc1;
    procPos[1] = proc2;

    /* Begin het proces */
    arbiter();

    return 0;
}

Enig probleem is dat "int iterations = 10;" buiten de arbiter moet blijven. Waarom het er niet in kan weet ik ook niet.
Pagina: 1