编写C程序以调用另一个程序而不使用任何内置库

我正在尝试为boot-loader写一个简单的’go-command’,它将我带到RAM中的特定地址,例如0x18000000,它应该执行一个闪烁的程序。 我有两个.c文件说led.c和go.c,其中led.c闪烁两个LED。 但我想知道,我不知道如何将控制/调用其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()函数

GCC有一个扩展,允许跳转到任意地址,所以如果你知道你的led.c主的地址,你可以这样做:

 void *func_ptr = (void *)0x1234567; // address of your led routine goto *func_ptr; 

但是,您可能没有led例程的地址,并且它不是一个非常安全的操作。 跳到某个未知地址可能会导致崩溃!!

有很多你省略的可能是相关的,但是从表面值看,你只需要声明一个函数指针,用绝对地址初始化并通过该指针调用函数。

但是它可能不那么简单,因为被调用的程序将使用调用程序的C运行时环境; 任何全局静态对象都不会被初始化 – 您需要调用程序入口点(不是main() )来建立程序所期望的运行时环境。 在简单的情况下,它可能在某种程度上起作用或似乎起作用。

所以我有一个sifive riscv板,会比你的有点不同但这里是一个led闪光灯程序(编译版):

 Disassembly of section .text: 80001000 <_start>: 80001000: 80004137 lui x2,0x80004 80001004: 016000ef jal x1,8000101a  80001008: 9002 ebreak 8000100a: a001 j 8000100a <_start+0xa> 8000100c : 8000100c: 8082 ret 8000100e : 8000100e: 00b52023 sw x11,0(x10) 80001012: 8082 ret 80001014 : 80001014: 00052503 lw x10,0(x10) 80001018: 8082 ret 8000101a : 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  80001034: 00c90513 addi x10,x18,12 80001038: 006805b7 lui x11,0x680 8000103c: 3fc9 jal 8000100e  8000103e: 04090513 addi x10,x18,64 80001042: 4581 li x11,0 80001044: 001e84b7 lui x9,0x1e8 80001048: 37d9 jal 8000100e  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  8000105a: 4401 li x8,0 8000105c: 8522 mv x10,x8 8000105e: 0405 addi x8,x8,1 80001060: 3775 jal 8000100c  80001062: fe941de3 bne x8,x9,8000105c  80001066: 4581 li x11,0 80001068: 854a mv x10,x18 8000106a: 3755 jal 8000100e  8000106c: 4401 li x8,0 8000106e: 8522 mv x10,x8 80001070: 0405 addi x8,x8,1 80001072: 3f69 jal 8000100c  80001074: fe941de3 bne x8,x9,8000106e  80001078: bff1 j 80001054  

拆卸间距有点破,但没关系。 入口点IS不是MAIN()用于正常程序,在这种情况下它是开始的0x80001000 NOT 0x8000101A我故意为主要名称之外的东西命名(对于裸机)…应该没有理由你要进入主要区域,你应该进入入口点…如果你继续尝试,我会让你失败,你自己就是这样。

所以这代表了上述的结果。

 S00F00006E6F746D61696E2E737265631F S3158000100037410080EF006001029001A0828023209A S31580001010B500828003250500828001114AC83729E0 S31580001020011013058900B705680006CE26CA4EC68C S3158000103022CCF13F1305C900B7056800C93F1305E7 S3158000104009048145B7841E00D937B709680031097C S3158000105093840448CE854A855D3F014422850504F4 S315800010607537E31D94FE81454A85553701442285AF S30F800010700504693FE31D94FEF1BFFD S705800010006A 

我/我们假设您实际上并没有按原样下载它,它永远不会执行,您的引导加载程序必须解析然后将程序写入ram,两个不同的东西(程序本身和描述该程序的文件格式)。

假设你到达那一点,你需要做的就是在我的情况下分支到0x80001000或在你的情况下分支到0x18000000。

所以,如果已经提供给您的答案,您可以执行此操作以启动下载的程序

 void hop ( void ) { void *func_ptr = (void *)0x80001000; goto *func_ptr; } 

结果

 Disassembly of section .text: 00000000 : 0: 800017b7 lui x15,0x80001 4: 8782 jr x15 

或者我个人的偏好是:

 .globl HOP HOP: jr x11 

来自CI的人会打电话给

 HOP(0x80001000); 

这样我可以确保使用我想要的指令。 因人而异。

你走这条路有多远? 你是不是最后一步了? Simone提供了一个应该可以正常工作的答案,你可以使用C的那个主题还有其他的变化,但是那个已经看起来有用了。