Q1 riscv裸机编程undefined reference to `_memcpy` :c代码中数组长度超过16,并在定义时做初始化,编译报错问题,
Q2 全局变量初始化问题
Q3 riscv裸机编程undefined reference to `_memcpy` :c代码中字符数组长度超过16,并在定义时做初始化,编译报错问题.
Q4 字符串常量初始化问题。
A.Q1,QQ3两个问题使用gcc三级优化选项解决
B.Q2,Q4两个问题是,反汇编时,objump,只选取了.text段的代码(使用的的是-d,只disassembly了.text段),而应该将.rodata段的也进行反汇编,应该使用-D将所有段进行反汇编。
objdump -d app.elf //对包含机器指令的段进行反汇编
objdump -D app.elf //-D 与 -d 类似,但对所有段进行反汇编
Q1.现象c代码中数组长度超过16,并在定义时做初始化,链接时报错undefined reference to `_memcpy`,如果长的字符串也是一样的。
代码:
报错,推测(对编译不是特别了解)是由于数组过程,直接初始化,会使得编译器调用memcpy这个函数(可能是编译器自身做的优化,如果数组长度比较小,那么可以直接赋值,如果长的化,编译器就会优化出来调用这个memcpy这个函数,如果你没有定义就会报错)
但是系统或者工具链里面没有这个函数,所以,编译完成后,在链接时,就会报错(因为链接器找不到这个函数):
如果定义成16长度的数组:
编译结果,汇编代码中会一个个去初始化数组的值。;
所以数组过长,避免这个错误的方式:这里采用的方法是,可以在c代码里面使用循环来初始化。或者使用一个个手动初始化。
Q2.另外全局变量定义后初始化时也不成功。
所以全局变量单独使用了一个函数来做初始化(这是其中一种做法)。
注意更新了一下链接脚本。
比如有如下代码:
func.c编译结果,如下,可以看见全局变量的初始化值被放入了data段(.sdata包含其中):
注意我把链接脚本做了修改,主要修改时是ram段的地址,因为若是原来的地址(见Q4中的链接脚本中的地址段),仿真时访问的是fffffff地址而不是5208000:
但是, 我仿真时没有,并没有读到初始化的值,读到是0,说明全局变量没有被初始化。但是从链接结果来看,全局变量的初始值是放入data段的。
仿真波形如下。可以看到5b000000读出来的值为X,说明并没有被初始化:
Q3.另外长字符串常量也编译不成功(这个和Q1是同一个问题)。
比如代码:
编译结果中可以看到,也是调用memcpy这个函数,所以链接时也会报错:
Q4.字符串常量的另外一个问题:
当我把字符串长度减少,可能是编译觉得不用优化,就没有调用memcpy这个函数。比如下面这个代码:
编译结果,注意这种情况下,没有调用memcpy,并且常量也没有放在rodata段。程序可以正常使用,也可以正常连接:
但是,如果我在一个函数的参数中使用了一个字符串常量.比如:
此时编译结果,会将这printstr("abcd\n")中所使用的字符串常量放在.rodata段:
连接结果的map中显示,的确存在firmware的常量连接到了rodata段(但是我实际去跑代码时这段地址仍然没有被初始化为X态):
rodata段地址,见下面的链接脚本:
波形如下,当去读取0xa000_0000这个rodata地址时,读出来的值是x.
所以没法这样使用字符串常量。
解决:
(一)GCC的三级优化。(解决Q1和Q3)
GNU编译器提供-O选项供程序优化使用:
-O 提供基础级别的优化
-O2 提供更加高级的代码优化,会占用更长的编译时间
-O3 提供最高级的代码优化
-O4 不优化,这是默认值
上面Q1,3这两个问题,会得到解决。编译器不会再调用memcpy函数。
(二)反汇编生成firmware时,只使用了.text段,其他段并没有使用。
如下所示,第31行,-d这个选项只会对对包含机器指令的段进行反汇编
比如Q2这个问题。使用riscv32-bst-elf-objdump -d isp_firmware得到的结果如下,结果中只对.text进行了disassembly。这样在
第35-36句时只会使用生成,.text段相关的地址和数据。
如果使用-D,就会包含所有段的disassembly:
按照-d,生成的最终仿真使用的文件coreisp.elf.txt如下,可以看见最终firmware中并没有5b00_0000地址段(Q2中链接脚本中ram地址段)。
Q4这个问题也是一样的,并没有将rodata段写入到最终firmware中(Q4中rodata段地址是a000_0000开始的)。
需要修改地方,0.对于data段(ram)地址范围也需要修改下。
1.makefile的31行改为-D。 这样会把所有段都包含进结果。
2.修改脚本od2txt.py脚本从包含的所有段的结果中筛选出需要的段。(除了.text段外还有,比如Q4的rodata段和Q2的.data(其中包含.sdata)段)
原来的:
修改后的:
修改后例子:
编译之后再使用objdump -D反汇编的得到:
然后使用修改后od2txt.py得到下面结果,最后几行是sdata和rodata的值,和代码是对应上的:
仿真波形,haddr依次是a000_0000,1000,5b00_0000,1000: