编写一个 C 程序来调用另一个程序而不使用任何内置库
Writing a C program to call another program without using any built-in libraries
我正在尝试为引导加载程序编写一个简单的 'go-command',它将我带到 RAM 中的特定地址,比如 0x18000000,它应该执行一个闪烁 LED 的程序。我有两个 .c 文件说 led.c 和 go.c 其中 led.c 闪烁两个 LED。但我想知道并且不知道如何将 control/invoke 其 main() 传递给此 go.c 文件以转到该地址并开始闪烁 LED?但它应该在不包含其他头文件、库等的情况下完成。请帮助我!!提前致谢。以下代码适用于 led.c
void delay ()
{
volatile int i;
for(i=0;i<1000000;i++)
{}
}
int main(void)
{
*led0=0;
*led1=0;
while(1)
{
*led0=1;
delay();
*led0=0;
*led1=1;
delay();
*led1=0;
}
}
在我的 go.c 文件中,我想传递一个控件来调用这个 led.c main() func
GCC 有一个允许跳转到任意地址的扩展,所以如果你知道你的 led.c 主地址,你可以这样做:
void *func_ptr = (void *)0x1234567; // address of your led routine
goto *func_ptr;
但是,您可能没有 led 例程的地址,这不是一个非常安全的操作。跳转到某个未知地址可能会导致崩溃!!
所以我有一个 sifive riscv 板,与你的略有不同,但这是一个 led blinker 程序(编译版):
Disassembly of section .text:
80001000 <_start>:
80001000: 80004137 lui x2,0x80004
80001004: 016000ef jal x1,8000101a <notmain>
80001008: 9002 ebreak
8000100a: a001 j 8000100a <_start+0xa>
8000100c <dummy>:
8000100c: 8082 ret
8000100e <PUT32>:
8000100e: 00b52023 sw x11,0(x10)
80001012: 8082 ret
80001014 <GET32>:
80001014: 00052503 lw x10,0(x10)
80001018: 8082 ret
8000101a <notmain>:
8000101a: 1101 addi x2,x2,-32
8000101c: c84a sw x18,16(x2)
8000101e: 10012937 lui x18,0x10012
80001022: 00890513 addi x10,x18,8 # 10012008 <_start-0x6ffeeff8>
80001026: 006805b7 lui x11,0x680
8000102a: ce06 sw x1,28(x2)
8000102c: ca26 sw x9,20(x2)
8000102e: c64e sw x19,12(x2)
80001030: cc22 sw x8,24(x2)
80001032: 3ff1 jal 8000100e <PUT32>
80001034: 00c90513 addi x10,x18,12
80001038: 006805b7 lui x11,0x680
8000103c: 3fc9 jal 8000100e <PUT32>
8000103e: 04090513 addi x10,x18,64
80001042: 4581 li x11,0
80001044: 001e84b7 lui x9,0x1e8
80001048: 37d9 jal 8000100e <PUT32>
8000104a: 006809b7 lui x19,0x680
8000104e: 0931 addi x18,x18,12
80001050: 48048493 addi x9,x9,1152 # 1e8480 <_start-0x7fe18b80>
80001054: 85ce mv x11,x19
80001056: 854a mv x10,x18
80001058: 3f5d jal 8000100e <PUT32>
8000105a: 4401 li x8,0
8000105c: 8522 mv x10,x8
8000105e: 0405 addi x8,x8,1
80001060: 3775 jal 8000100c <dummy>
80001062: fe941de3 bne x8,x9,8000105c <notmain+0x42>
80001066: 4581 li x11,0
80001068: 854a mv x10,x18
8000106a: 3755 jal 8000100e <PUT32>
8000106c: 4401 li x8,0
8000106e: 8522 mv x10,x8
80001070: 0405 addi x8,x8,1
80001072: 3f69 jal 8000100c <dummy>
80001074: fe941de3 bne x8,x9,8000106e <notmain+0x54>
80001078: bff1 j 80001054 <notmain+0x3a>
拆卸间距有点破,不过没关系。对于普通程序,入口点不在 MAIN() 处,在这种情况下,入口点是 0x80001000 NOT 0x8000101A,我故意将其命名为 main 以外的名称(对于裸机)...您应该没有理由要在 main 进入,你应该在入口点进入...如果你继续尝试,我会让你失败,否则你就只能靠自己了。
所以这代表了上面的一个 srec。
S00F00006E6F746D61696E2E737265631F
S3158000100037410080EF006001029001A0828023209A
S31580001010B500828003250500828001114AC83729E0
S31580001020011013058900B705680006CE26CA4EC68C
S3158000103022CCF13F1305C900B7056800C93F1305E7
S3158000104009048145B7841E00D937B709680031097C
S3158000105093840448CE854A855D3F014422850504F4
S315800010607537E31D94FE81454A85553701442285AF
S30F800010700504693FE31D94FEF1BFFD
S705800010006A
I/we 假设你实际上并没有按原样将它下载到 ram,它永远不会执行,你的引导加载程序必须解析它然后将程序写入 ram,两个不同的东西(程序本身和文件描述该程序的格式)。
假设你达到了这一点,你需要做的就是分支到我的 0x80001000 或你的 0x18000000。
所以根据已经提供给您的答案,您可以执行此操作以启动下载的程序
void hop ( void )
{
void *func_ptr = (void *)0x80001000;
goto *func_ptr;
}
这导致
Disassembly of section .text:
00000000 <hop>:
0: 800017b7 lui x15,0x80001
4: 8782 jr x15
或者我个人的偏好是:
.globl HOP
HOP:
jr x11
我会用 C 调用哪个
HOP(0x80001000);
这样我就可以确保使用我想要的指令。 YMMV.
你在这条路上走了多远?你卡在最后一步了吗? Simone 提供了一个应该可以正常工作的答案,您可以使用 C 中该主题的其他变体,但那个似乎已经起作用了。
您省略了很多可能相关的内容,但从表面上看,您只需声明一个函数指针,用绝对地址初始化并通过该指针调用函数。
然而它可能不是那么简单,因为被调用程序将使用调用程序的 C 运行-time 环境;任何全局静态对象都不会被初始化——您需要调用程序入口点(不是main()
)来建立程序期望的运行-时间环境。在简单的情况下,它可能在某种程度上起作用或看起来起作用。
我正在尝试为引导加载程序编写一个简单的 'go-command',它将我带到 RAM 中的特定地址,比如 0x18000000,它应该执行一个闪烁 LED 的程序。我有两个 .c 文件说 led.c 和 go.c 其中 led.c 闪烁两个 LED。但我想知道并且不知道如何将 control/invoke 其 main() 传递给此 go.c 文件以转到该地址并开始闪烁 LED?但它应该在不包含其他头文件、库等的情况下完成。请帮助我!!提前致谢。以下代码适用于 led.c
void delay ()
{
volatile int i;
for(i=0;i<1000000;i++)
{}
}
int main(void)
{
*led0=0;
*led1=0;
while(1)
{
*led0=1;
delay();
*led0=0;
*led1=1;
delay();
*led1=0;
}
}
在我的 go.c 文件中,我想传递一个控件来调用这个 led.c main() func
GCC 有一个允许跳转到任意地址的扩展,所以如果你知道你的 led.c 主地址,你可以这样做:
void *func_ptr = (void *)0x1234567; // address of your led routine
goto *func_ptr;
但是,您可能没有 led 例程的地址,这不是一个非常安全的操作。跳转到某个未知地址可能会导致崩溃!!
所以我有一个 sifive riscv 板,与你的略有不同,但这是一个 led blinker 程序(编译版):
Disassembly of section .text:
80001000 <_start>:
80001000: 80004137 lui x2,0x80004
80001004: 016000ef jal x1,8000101a <notmain>
80001008: 9002 ebreak
8000100a: a001 j 8000100a <_start+0xa>
8000100c <dummy>:
8000100c: 8082 ret
8000100e <PUT32>:
8000100e: 00b52023 sw x11,0(x10)
80001012: 8082 ret
80001014 <GET32>:
80001014: 00052503 lw x10,0(x10)
80001018: 8082 ret
8000101a <notmain>:
8000101a: 1101 addi x2,x2,-32
8000101c: c84a sw x18,16(x2)
8000101e: 10012937 lui x18,0x10012
80001022: 00890513 addi x10,x18,8 # 10012008 <_start-0x6ffeeff8>
80001026: 006805b7 lui x11,0x680
8000102a: ce06 sw x1,28(x2)
8000102c: ca26 sw x9,20(x2)
8000102e: c64e sw x19,12(x2)
80001030: cc22 sw x8,24(x2)
80001032: 3ff1 jal 8000100e <PUT32>
80001034: 00c90513 addi x10,x18,12
80001038: 006805b7 lui x11,0x680
8000103c: 3fc9 jal 8000100e <PUT32>
8000103e: 04090513 addi x10,x18,64
80001042: 4581 li x11,0
80001044: 001e84b7 lui x9,0x1e8
80001048: 37d9 jal 8000100e <PUT32>
8000104a: 006809b7 lui x19,0x680
8000104e: 0931 addi x18,x18,12
80001050: 48048493 addi x9,x9,1152 # 1e8480 <_start-0x7fe18b80>
80001054: 85ce mv x11,x19
80001056: 854a mv x10,x18
80001058: 3f5d jal 8000100e <PUT32>
8000105a: 4401 li x8,0
8000105c: 8522 mv x10,x8
8000105e: 0405 addi x8,x8,1
80001060: 3775 jal 8000100c <dummy>
80001062: fe941de3 bne x8,x9,8000105c <notmain+0x42>
80001066: 4581 li x11,0
80001068: 854a mv x10,x18
8000106a: 3755 jal 8000100e <PUT32>
8000106c: 4401 li x8,0
8000106e: 8522 mv x10,x8
80001070: 0405 addi x8,x8,1
80001072: 3f69 jal 8000100c <dummy>
80001074: fe941de3 bne x8,x9,8000106e <notmain+0x54>
80001078: bff1 j 80001054 <notmain+0x3a>
拆卸间距有点破,不过没关系。对于普通程序,入口点不在 MAIN() 处,在这种情况下,入口点是 0x80001000 NOT 0x8000101A,我故意将其命名为 main 以外的名称(对于裸机)...您应该没有理由要在 main 进入,你应该在入口点进入...如果你继续尝试,我会让你失败,否则你就只能靠自己了。
所以这代表了上面的一个 srec。
S00F00006E6F746D61696E2E737265631F
S3158000100037410080EF006001029001A0828023209A
S31580001010B500828003250500828001114AC83729E0
S31580001020011013058900B705680006CE26CA4EC68C
S3158000103022CCF13F1305C900B7056800C93F1305E7
S3158000104009048145B7841E00D937B709680031097C
S3158000105093840448CE854A855D3F014422850504F4
S315800010607537E31D94FE81454A85553701442285AF
S30F800010700504693FE31D94FEF1BFFD
S705800010006A
I/we 假设你实际上并没有按原样将它下载到 ram,它永远不会执行,你的引导加载程序必须解析它然后将程序写入 ram,两个不同的东西(程序本身和文件描述该程序的格式)。
假设你达到了这一点,你需要做的就是分支到我的 0x80001000 或你的 0x18000000。
所以根据已经提供给您的答案,您可以执行此操作以启动下载的程序
void hop ( void )
{
void *func_ptr = (void *)0x80001000;
goto *func_ptr;
}
这导致
Disassembly of section .text:
00000000 <hop>:
0: 800017b7 lui x15,0x80001
4: 8782 jr x15
或者我个人的偏好是:
.globl HOP
HOP:
jr x11
我会用 C 调用哪个
HOP(0x80001000);
这样我就可以确保使用我想要的指令。 YMMV.
你在这条路上走了多远?你卡在最后一步了吗? Simone 提供了一个应该可以正常工作的答案,您可以使用 C 中该主题的其他变体,但那个似乎已经起作用了。
您省略了很多可能相关的内容,但从表面上看,您只需声明一个函数指针,用绝对地址初始化并通过该指针调用函数。
然而它可能不是那么简单,因为被调用程序将使用调用程序的 C 运行-time 环境;任何全局静态对象都不会被初始化——您需要调用程序入口点(不是main()
)来建立程序期望的运行-时间环境。在简单的情况下,它可能在某种程度上起作用或看起来起作用。