打印

彻底倒塌了~~~keil的CARM编译器好象不能设置生成相对跳转代码

[复制链接]
楼主: computer00
手机看帖
扫描二维码
随时随地手机跟帖
21
mybao| | 2007-4-12 13:33 | 只看该作者 回帖奖励 |倒序浏览

圈圈这个问题想复杂了

使用特权

评论回复
22
computer00|  楼主 | 2007-4-12 14:02 | 只看该作者

不是我把问题想复杂了,而是事实上它就如此,我也没办法

如果将代码指定在ROM区,那么它就有可能产生绝对跳转到ROM区的指令,如果我把代码区指定在RAM区,
那么我将没办法把代码烧录到FLASH中去,因为地址不对.

事实上,我现在的代码就是即在FLASH中运行,又要在RAM中运行.复位后在FLASH中运行,
然后有必要时,我在主函数中将代码复制到RAM中,再跳转到那里去执行。

当然,也可以如你所说,在FLASH中只保留复制代码的部分程序,然后平时都在RAM中运行.
但是,我没办法指定产生的函数地址是在RAM区间,而又把这个函数代码烧录到FLASH中去,因为
这个开发环境就这样,函数地址是多少,它就要烧录到哪里去. 如果你指定某个函数的
入口地址是在RAM区间的,那么在下载程序的时候,就出错了.所以我必须先让它产生
在FLASH里面运行的代码,再复制到RAM中,还能继续运行。

使用特权

评论回复
23
农民讲习所| | 2007-4-12 14:42 | 只看该作者

是否和优化有关?

一般是生成相对跳转,优化过分可能成为绝对跳转.

使用特权

评论回复
24
djyos| | 2007-4-12 15:01 | 只看该作者

这好像不是相对跳转和绝对跳转的问题

    我们知道,C语言中函数名是可以作为符号常量使用的,如果函数的地址不是绝对地址的话,那才真的倒塌了呢。
    因为这个,我一直不明白ads的位置无关是怎么实现的,也许我对“位置无关”理解错了吧,没仔细研究过,有空的时候好好看看。

使用特权

评论回复
25
computer00|  楼主 | 2007-4-12 15:37 | 只看该作者

因为有相对跳转的指令,还有PC也可以用

例如B指令,第一个字节是0xEA,后面的3个字节是相对地址.
例如下面这条指令: EA00000E,相对地址是0x0E,即14,由于ARM指令是
4字节宽度,因此地址可以以4字节为单位,这样可以节省2个没用的bit出来,
所以这个指令中的14实际上是除了4的结果,因此在计算实际地址时需要乘以4,即14*4=56.
再由于ARM7的三级流水线,执行这条指令时,PC值为当前地址+8,所以
这个指令实际上是跳转
到这条地址后面的第64(0x40)字节处执行,如果当前PC为0,即复位地址,那么就相当于一
条Jump 0x00000040的指令。如果我这时已经把这个指令复制到地址0x0C000000处去了,
那么这个地址偏移0x40,就是0x0C000040,这就相当于一条Jump 0x0C000040的指令了. 
这样就可以实现代码与地址无关了,放在哪里都可以正常运行.


但是看看下面这个生成的代码就不一样了,这个是一个函数调用,CARM编译器使用了BX指令
来跳转,是考虑到被调用的函数可能处于不同的模式(由R0的最低位来决定ARM和THUMB模式),
但是它在加载R0时,没有根据当前PC值来加载要跳转到的地址,而是直接加载了要跳转
到的函数的绝对地址.如果我这时运行在RAM里,那么一调用这个函数,程序就跑到
FLASH里面去了。


   155:  SysClkInit(); 
0x00080478  E59F00FC  LDR       R0,[PC,#0x00FC]  //SysClkInit这个函数的地址保存在PC+0x00FC这个单元中(注意PC是当前地址+8)
0x0008047C  E1A0E00F  MOV       R14,PC  //保存返回地址到R14中,等下好返回
0x00080480  E12FFF10  BX        R0 //根据R0中的值跳转到函数中
............................................
//省略中间的了
...........................................
0x0008057C  00011000  DD        0x00011000  //这个地址就是前面计算出来的(8057C=80478+8+FC)


结果,R0的值就是0x11000了,SysClkInit这个函数的地址刚好就是0x11000,那么在FLASH中跳转过去是没问题的。
但是如果这时代码在RAM中运行,也跳到这个地址去,当然就不行了。

它应该保存这个这个函数与当前PC之间的差值. 运行时,取出相对地址,加上PC后再
放到R0中,然后再用BX R0跳转到指定的函数入口地址.


所长所说的优化问题,我也试过了,不管改成什么样的级别,这里的代码都是一样的,不变.




使用特权

评论回复
26
minmindede| | 2007-4-12 16:42 | 只看该作者

圈圈的问题,我是

通过scatter来解决的,你不能用,不知道怎么帮你。 如果有mem remap就不存在这个问题

使用特权

评论回复
27
computer00|  楼主 | 2007-4-12 17:23 | 只看该作者

恩,我现在已经把程序弄到RealView上来了,

修改了一些地方,编译通过,并且可以运行了。还没测试它是否会跳回到FLASH中......

在RealView中可以将Read-Only Position Independent勾上,不知道是否奏效了~~~~~来不及测试了,
先去吃饭~~~~还要自己修改一下keil的启动文件才行的~~~~~希望这次别再让我倒塌了.....

使用特权

评论回复
28
high| | 2007-4-12 17:43 | 只看该作者

估计有些人没有明白

对蛋蛋的应用,我提供2个办法参考:

1.继续死磕position imdependence.搞定後把结果通报一下。

2.简单办法:固定地址运行。

  串口接收的程序使用固定地址编译。恰好也放到对应地址运行。

  内存地址是 0x40004000 ro_base = 0x40004000生产一个bin
  串口接收并copy到0x40004000.
  run().肯定不会回去了。当然还要小心中断引起的跳转。

使用特权

评论回复
29
mybao| | 2007-4-12 17:51 | 只看该作者

建议圈圈研究一下偶贴的那段启动代码

当然,也可以如你所说,在FLASH中只保留复制代码的部分程序,然后平时都在RAM中运行.

不是这样的,启动代码和主程序是一个整体,一起烧到FLASH,启动代码部分的执行和地址无关,比如你设置存储器start address 是0XC000000(其实别的 地址也不用加了),生成的bin文件按理说要从0XC000000开始执行,但是把bin文件直接烧到起始地址为0x00的FLASH,启动代码首先上电执行,会把所有代码复制到0XC000000开始的SDRAM,然后在跳转到SDRAM执行,启动代码与地址无关


但是,我没办法指定产生的函数地址是在RAM区间,而又把这个函数代码烧录到FLASH中去


实际上确实就是这样做的,倒塌吧。

使用特权

评论回复
30
computer00|  楼主 | 2007-4-12 20:31 | 只看该作者

我用的是Ulink,Ulink负责把对应的地址的值烧到FLASH中

因此如果我在编程的时候告诉编译器,“生成的bin文件要从0XC000000开始执行”,那么,Ulink就会傻傻的把代码
下载到从0xC000000的地方去,而这个地址不是FLASH,是RAM,所以下载就会不成功。


至于high所说,第一点我是找了很久都没结果,要不就给Keil的技术支持发邮件了,问问他们应该怎么在CARM里面设置。
第二点,倒是可行,但是太麻烦了,而且将来也不大好用,我喜欢生成相对跳转的指令,
这样放哪都可以运行,不是很好么?并且如果把RO地址设置在0x0C000000处,那么产生
的*.bin文件会不会有200多M那么大?因为前面要填充很多很多0...虽然我可以写个
程序,在下载时省略掉前面的0,但是每次弄这么大的程序,还是蛮晕的...

按照high所说的方法二,我现在做的是一个boot程序,所以第一步就需要用ulink下载,
然后再把这个boot程序的RO地址改到RAM里,然后通过串口把新生成的boot代码加载到RAM中,
再来运行新的boot程序,那么这时的boot程序就是会在RAM里面跳转了,然后我再把这个
程序自己固化到FLASH中,然后下次启动时,就先复制代码到RAM,然后跳转到RAM,
这样就会在RAM中执行了。这么多步骤,蛮罗嗦的.........

还是先玩玩RealView吧,晚上回去测试一下,看是不是OK了,如果OK了,那么我就决定
以后用RealView来编译了~~~~~~~~

使用特权

评论回复
31
djyos| | 2007-4-12 21:06 | 只看该作者

好好研究

在C语言里,下列语句是合法的:
void func(void);
void (* const faddr)(void) = func;
void func(void)
{
    …………
}

如果可以实现运行时func地址的动态性,那faddr变量的const属性怎么办?圈圈要是研究明白了,别忘了把结果贴上来,翘首等待中.

使用特权

评论回复
32
mybao| | 2007-4-12 21:48 | 只看该作者

不能Ulink直接下载

Ulink就会傻傻的把代码下载到从0xC000000的地方

你说的下载什么意思,你也知道FLASH不能直接下载,bin文件用一个小程序烧写到FLASH不就行了吗,只不过这个小程序要下载到SDRAM运行。

把RO地址设置在0x0C000000,先放代码,再放常数,bin文件只包含这么多信息,代码段和常数段之间没有gap,怎么会填充0呢。

使用特权

评论回复
33
computer00|  楼主 | 2007-4-13 01:02 | 只看该作者

TO djyos: 函数的地址的确在变,

test是个函数,而addr是个全局变量,将函数test的地址赋给addr,结果在FLASH中运行和
把代码复制到RAM中运行时,addr的值变了.刚好相差那么多.从下面这个汇编结果也可以
看出来,它是将PC-0x170做为了test的地址,所以函数的地址不再是常量了。

  1015:      addr=(unsigned long int)test; 
0x0000351C  E24F0E17  SUB       R0,PC,#0x00000170
0x00003520  E59F11A8  LDR       R1,[PC,#0x01A8]
0x00003524  E5810000  STR       R0,[R1]
  1016:      test(); 
0x00003528  EBFFFFA1  BL        test(0x000033B4)
  1017:      ((void(*)(void))addr)(); 
  1018:       
0x0000352C  E59F019C  LDR       R0,[PC,#0x019C]
0x00003530  E5900000  LDR       R0,[R0]
0x00003534  E1A0E00F  MOV       R14,PC
0x00003538  E12FFF10  BX        R0





to mybao: 我不太清楚HEX是如何转成bin的,因为HEX中直接有地址信息,所以中间
空的可以跳过.但是bin文件就是二进制文件,它是如何知道第一个字节的地址是多少的?
所以我认为二进制文件的第一个字节的地址为0.那么到地址0x0C000000,就有几百M那么大了。

我把RO地址设置在0x0C000000,这样就编译通不过了,晕死,错误如下:
linking...
*** ERROR L138: CODE GENERATION: PROBLEM WHEN PROCESSING INSTRUCTIONS
     CAUSE:  Target is out of Range
    SEGMENT: STARTUPCODE
     ADDRESS: 01E0H
Program Size: data=10490 const=3207 code=21504
Target not created


今晚又试了RealView,结果还是不行,倒塌了~~~~~自己的函数调用没问题. 问题好象出在
系统的库函数上,调用到这些时就会跑到FLASH中,不过具体是不是我还没弄清楚,全是汇编,
看了就头疼.....

看来用让它全部产生相对跳转的代码是不大现实的了。但是把RO地址设置为RAM地址又
编译通不过,晕死.我现在的想法是,先编一个很小的串口程序,它可以通过串口将
一个bin文件加载(而这个bin文件就是我现在做的这个,把它的RO设置在RAM地址),
然后再跳转到这里执行。然后利用它,将自己再固化到FLASH中,然后下次复位后,
就把这些代码复制到RAM中,运行之...

使用特权

评论回复
34
ketp| | 2007-4-13 07:59 | 只看该作者

这是加载域和执行域的问题

在ads里是用scatter file搞定的,

注意,在ads里把Read-Only Position Independent勾上。也不一定能生成位置无关代码,carm可能相似。

使用特权

评论回复
35
djyos| | 2007-4-13 09:56 | 只看该作者

to:圈圈

你注意到定义中的const修饰符了没有,如果改成下面这样呢:
const void (* const faddr)(void) = func;

而且faddr是全局变量会怎么样?全局变量和局部变量是不一样的,

局部变量的初始值是在调用时赋的值,因为编译器确实不知道变量本身的存放地址,所以只能用代码实现.编译器也不知道函数的地址,只有连接器知道,而连接器不能改变代码

而全局变量的初始值却是编译器(准确地讲是连接器)赋的值,放在rodata段,由启动代码copy.

不知圈圈定义的addr是全局的还是局部的,从反汇编来看应该是局部变量吧,因为全局变量不会产生下列汇编代码的:
0x0000351C  E24F0E17  SUB       R0,PC,#0x00000170
0x00003520  E59F11A8  LDR       R1,[PC,#0x01A8]
0x00003524  E5810000  STR       R0,[R1]

使用特权

评论回复
36
mybao| | 2007-4-13 10:53 | 只看该作者

re

to 圈圈

bin文件没有地址信息,跟hex不一样,RO地址设置在0x0C000000,bin文件不会把0x0000000-0xbffffff填充 0,不会的。

如果你还想控制ASM的生成的话,真的想偏了。

to  ketp

在ads里是用scatter file搞定的,

这个观点是错误的。在ADS里并没有给程序分配独立的加载域和执行域,加载域和执行域还是通过启动代码实现的。TI的DSP编译器CCS可以实现给程序分配真正独立的加载域和执行域,ADS并没有,scatter file里面的load region和execution region有误导开发者之嫌,我也被误导过。

使用特权

评论回复
37
high| | 2007-4-13 12:51 | 只看该作者

晕!


some infomation from ads1.2 article.
-----------
/ropi This option generates (read-only) position-independent code. /pic is an alias for this option. If this option is selected the compiler:

 : addresses read-only code and data pc-relative
 : sets the Position Independent (PI) attribute on read-only output 
sections.

Note
 The ARM tools cannot determine if the final output image will be 
Read-Only Position Independent (ROPI) until the linker finishes 
processing input sections. This means that the linker might emit ROPI 
error messages, even though you have selected this option.

使用特权

评论回复
38
computer00|  楼主 | 2007-4-13 13:18 | 只看该作者

TO djyos: 我前面写得很清楚,addr是全局变量

0x0000351C  E24F0E17  SUB       R0,PC,#0x00000170  //取得函数的地址值放入R0中
0x00003520  E59F11A8  LDR       R1,[PC,#0x01A8]   //取得addr这个变量的地址放入R1中
0x00003524  E5810000  STR       R0,[R1] //将函数地址写回到addr变量的地址中


全局变量才会产生这样的代码,将结果写回到RAM中。如果我用局部变量的话,它是不会
压写回RAM的,而直接把这个变量放在寄存器中(我当时编译时它是在放在R7中),
然后我将代码复制到RAM中运行,就看不到局部变量addr的值了,我才将addr改成
全局变量的,这样任何时刻都可以查看它的值.结果在不同的地方运行,它的值就跟着变化.

如果照你这个const void (* const faddr)(void) = func;这么写,我下面就没办法
在运行中去取函数地址了,因为是常量,编译器会不让你修改它的值。由于初始化程序
在FLASH中,这时的地址肯定是在FLASH中。只有在程序运行过程中,取得函数地址才能反应出实际的情况.
实际上函数名的值已经变了。



你看我将RO地址设置在0x0C000000时,产生的HEX文件:



:020000040000FA   //地址高16位为0
:1000000018F09FE518F09FE518F09FE518F09FE5C0
.....省略.......
:100200002800F801E43D000CE4010000E9010000D1
:020000040C00EE    //地址高16位为0x0C00
:0F00000050726F6772616D202578206F6B0A0058
:06001000FFFFFFFFFFFFF0


从这个产生的代码来看,我将RO设置在0x0C000000,那么前面的启动代码的地址就
从地址0开始放,然后再跳到初始化代码以及C代码,从初始化代码后面的地址就是
0x0C000000了。它这样做的目的应该保证复位后代码正常运行。

那现在我的代码即有地址0,又有地址0x0C00000,如果中间不填充一些什么的话,
那又如何知道后面的代码是在0x0c000000呢?不过我用51的hex2bin的软件,
转换之后,它就忽略了前面那段了.



我现在的想法就是,先把这个程序的RO地址设置在RAM,编译之,然后把产生的HEX文件
前面的部分删除,只保留后面的RAM部分,然后转换成bin文件.之后再写一个小的加载
程序,将这个bin文件通过串口加载到RAM中,然后运行之.这时就可以利用这个程序,
把自己固化在FLASH中,并且再搞个启动代码也烧到FLASH中,那么下次开机时,
由这个启动代码将FLASH的内容复制到RAM中,然后运行之,这样就可以搞定了。
不过蛮晕菜的,要搞这么多步骤......



使用特权

评论回复
39
wlq_9| | 2007-4-13 13:43 | 只看该作者

先在RAM里运行bootloader

再把代码写到flash中去,这样就可以解决地址问题。
要不用IAR,利用它提供的framework,自己写flashloader,把原本编译到A地址的数据写到B地址,这样就可以一步完成。

使用特权

评论回复
40
mybao| | 2007-4-13 13:44 | 只看该作者

re

RO设置在0x0C000000,   启动代码的地址怎么会从地址0开始放呢?

你不至于用汇编伪指令强行把启动代码定位到 0 吧?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则