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地址在里面。
大家好,
我正在尝试操作堆栈指针,在 "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地址在里面。