Return 您要在其中操作堆栈指针的程序

Return the program where you want manipulating the stack pointer

大家好,

我正在尝试操作堆栈指针,在 "go_to_func" 函数的 return 之后,程序将 return 到 "func" 函数的开始并打开PORTD 位 3。我试过下面的代码,但它不起作用,程序仍然 returns 到 main 并打开 PORTD 位 2。我是不是遗漏了什么,或者我对堆栈的看法有误有效吗?

PS:我使用 Atmel Studio 7.0 for Atmega328p (Arduino uno)

#include <avr/io.h>

typedef unsigned char      uint8;
typedef unsigned short int uint16;

/*
    call go_to_func             SP -> |  somewhere in main  |
    *sp = (uint16_t)function;   SP -> |    start of func    |
*/

void func( void )
{
    /* PORTD ==> | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |  */
    PORTD = 0x08 ;
}

void go_to_func( void (*function)(void) )
{
    uint16 *sp = (uint16*)SP ;

    *sp = (uint16)function;

    /* after the return i want the SP to point to the start of func() */
}

int main(void)
{
    /* DDRD ==> | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |  */
    DDRD |= 0x0C ;

    go_to_func(func);

    /* PORTD ==> | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |  */
    PORTD = 0x04 ;

    while (1) 
    {
    }
}

更新:嗨,我做了一个更新,我在函数 noinline 中进行了更新,所以在汇编文件中调用现在出现了,但是当我传递 "func" 地址时 "go_to_func" 我希望将值 90 传递给 "func" 但是程序传递了值 72(在这一行 --> ldi r24, 0x48)

#define  CALL_FCT    __attribute__ ((noinline))

void  CALL_FCT  func( void )
{
    /* PORTD ==> | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |  */
    PORTD = 0x08 ;
    90: 88 e0           ldi r24, 0x08   ; 8
    92: 8b b9           out 0x0b, r24   ; 11
    94: 08 95           ret

    00000096 <go_to_func>:
}

void  CALL_FCT  go_to_func( void (*function)(void) )
{
    func_addr = (uint16)function ;
    96: 90 93 01 01     sts 0x0101, r25 ; 0x800101 <_edata+0x1>
    9a: 80 93 00 01     sts 0x0100, r24 ; 0x800100 <_edata>

    SP = (uint16)(&func_addr);
    9e: 80 e0           ldi r24, 0x00   ; 0
    a0: 91 e0           ldi r25, 0x01   ; 1
    a2: 9e bf           out 0x3e, r25   ; 62
    a4: 8d bf           out 0x3d, r24   ; 61
    a6: 08 95           ret

    000000a8 <main>:
}

int  CALL_FCT  main(void)
{
/* DDRD ==> | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |  */
DDRD |= 0x0C ;
a8: 8a b1           in  r24, 0x0a   ; 10
aa: 8c 60           ori r24, 0x0C   ; 12
ac: 8a b9           out 0x0a, r24   ; 10

go_to_func(func);
ae: 88 e4           ldi r24, 0x48   ; 72
b0: 90 e0           ldi r25, 0x00   ; 0
b2: 0e 94 4b 00     call    0x96    ; 0x96 <go_to_func>

/* PORTD ==> | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |  */
PORTD = 0x04 ;
b6: 84 e0           ldi r24, 0x04   ; 4
b8: 8b b9           out 0x0b, r24   ; 11
ba: ff cf           rjmp    .-2         ; 0xba <main+0x12>

000000bc <_exit>:
bc: f8 94           cli

000000be <__stop_program>:
be: ff cf           rjmp    .-2         ; 0xbe <__stop_program>

我的第一个建议:转到输出目录(例如 debug)并找到一个 '.lss' 文件,尝试在那里找到 go_to_func 过程的反汇编,看看到底发生了什么调用 go_to_func 时的处理器。

虽然您访问 SP 指针的想法是正确的(将其值转换为 uint16_t 指针),但是您关于堆栈将在其顶部保留 return 地址的建议是完全正确的错了。

堆栈不仅用于存储return地址,当调用例程时,还用于存储局部变量和保存寄存器。因此,如果编译器想要在例程为 运行 时分配一些变量或在堆栈中保留一些寄存器,则 SP 将不再指向 return 地址。 另外,编译器可能会使用"stack frame":SP的初始值在例程开始时存储在一些寄存器中,然后在退出时完全恢复。

UPD: 或者,如您的示例所示,例程是内联的,没有调用或执行 returns,修改 SP 不会给您带来什么你在期待。

因此,在运行 C 代码中,无法保证SP 指针如何使用和更改。因此,要执行您需要的操作,您可能需要完全控制堆栈指针的使用方式,并在汇编程序上完全重写例程。

顺便说一句:如果你在multi-tasking上工作,你可能更想在不同的堆栈之间进行更改,而不是使用相同的堆栈,更改return地址在里面。