历史上的今天
返回首页

历史上的今天

今天是:2025年10月12日(星期日)

正在发生

2022年10月12日 | arm-linux-gcc 裸机程序开发(二)

2022-10-12 来源:csdn

NANDFLASH启动与标准库问题


把u-boot的start.S移植到我的程序上,这样程序可以用supervivi的D功能下载到内存中运行了,但是还不够。因为程序在内存里,如果掉电程序就没有了,所以我们得将程序固化在flash里面。这里我们要固化在NandFlash里,这就要求程序可以能够NandFlash启动。这里我参考了mini2440的nandlfash读写程序nand.c,里面有一个函数CopyProgramFromNand就是将Nandflash里的程序复制到内存里。在这之前我一直用u-boot默认的下载地址0x33f80000,这个是为了u-boot引导内核方便而定的,因为内核要下载到前面的内存中,既然我的程序没有这个功能,那下载到0x30000000里就可以了。把start.S中的TEXT_BASE改成0x30000000就可以了。还有在start.S里,复制完程序判断r0是否为零,如果不是那么程序就会进入死循环。根据AAPCS,C程序返回的值就保存在r0里,这里是判断程序是否返回了0,在CopyProgramFromNand里默认没有返回值,所以要在最后加上返回值。接下来就是消除编译错误。将nand.c链接到我们的程序里,注意一定要保证在程序的前4k的位置范围内,因为Nandflash启动的时候,S3C2440将Nandflsh的前4K的内容复制到芯片内部的SRAM里,如果NandFlash的读写程序不在这4k的代码里就无法完成程序的拷贝。


编译好的程序在我的下载资源里:http://download.csdn.net/detail/yaozhenguo2006/3752515


Nandflash启动进行的还算顺利,修改之后下到nandflash里,然后开发板切到nandflash启动,程序正常运行。这里我注意到流水灯不像下载到内存中运行的那样立即就运行,而是等了一段时间,说明拷贝代码用了一些时间。接下来就是要移植mini2440自带库函数了,对应的文件是2440lib.c,我本来以为简单的加到Makefile里就会成功。可是事情不是想象的那样简单,当编译的时候出现了一大堆错误。这些错误刚看起来都莫名奇妙,而且不是编译的错误,都是链接的错误。编译的时候没错说明不是语法错误,链接错误说明是库出了问题。主要提示的错误信息如下:

第一类:

2440lib.o: In function `Uart_Init':

2440lib.c:(.text+0x370): undefined reference to `__aeabi_i2d'

2440lib.c:(.text+0x390): undefined reference to `__aeabi_ddiv'

2440lib.c:(.text+0x3a8): undefined reference to `__aeabi_i2d'

2440lib.c:(.text+0x3c4): undefined reference to `__aeabi_ddiv'


在Uart_Init这个函数中,主要用了一些浮点数的运算和除法的运算,我们知道armv4指令集是没有除法和浮点运算指令的,所以必须软件模拟。编译的时候模拟除法与浮点运算需要的库,在链接的时候链接器没有找到,所以出现了这个问题。为了验证我的猜测,我把浮点数运算以及除法运算都注释掉,错误提示就没有了。说明就是没有找到浮点运算以及除法运算的库。

第二类:

2440lib.o: In function `Uart_GetIntNum':

2440lib.c:(.text+0x994): undefined reference to `strlen'

2440lib.c:(.text+0xa20): undefined reference to `atoi'

2440lib.c:(.text+0xa5c): undefined reference to `__ctype_b_loc'

2440lib.c:(.text+0xa90): undefined reference to `__ctype_b_loc'

2440lib.o: In function `Uart_Printf':

2440lib.c:(.text+0xd90): undefined reference to `vsprintf'


在Uart_GetIntNum这个函数中主要调用了标准C库中的strlen和atio函数,找不到符号,说明链接的时候没有找到标准C库。Uart_Printf也应该是同样的错误。


对于以上两种问题都是库的问题。既然链接器找不到相关的库。我们就加上一定的链接选项让他找到。在做这个之前我们先了解一下,gcc开发环境下程序链接的两种方式。第一种使用gcc自带的链接功能。以arm-linux-gcc来说明,这种链接方式命令基本形式如下

$(CC)  -static -Wl,-Tboot.lds,-Map,system.map -nostartfiles -o boot.elf $(objs)

     CC 这里就是arm-linux-gcc 

    -Wl,-Tboot.lds,-Map,system.map -Wl表示后面的参数是传递给链接器的,参数以逗号分割。-Tboot.lds 是指定的链接脚本,规划了程序在内存中的位置 -Map,system.map 这是链接后生成的符号表  

    -nostartfile 不让链接器加入默认启动代码,如果不加这个选项,链接器就会默认加入一个启动代码,因为我们要链接自己的启动代码,当然不需要它默认的了。

    -o boot.elf 最后生成的elf格式的文件。

    $(objs) 我们编译的.o文件


用以上形式链接程序,arm-linux-gcc会调用collect2链接器,这个链接器有一定的默认选项,比如默认寻找库的路径,默认链接的库。通过给arm-linux-gcc 加上-v 选项,我们可以查看他的默认行为。现在以我的开发环境为例,链接的时候会有如下的输出:

 /home/sun/study/crosstools/4.4.3/bin/../libexec/gcc/arm-none-linux-gnueabi/4.4.3/collect2 

        从这个输出中,我们可以发现,arm-linux-gcc调用了collect2。

--sysroot=/home/sun/study/crosstools/4.4.3/bin/../arm-none-linux-gnueabi//sys-root 

-Bstatic -dynamic-linker /lib/ld-linux.so.3 -X -m armelf_linux_eabi -o boot.elf

        sysroot暂时没有发现是做什么的,-Bstatic 很显然是静态链接 

-L/home/sun/study/crosstools/4.4.3/bin/../lib/gcc/arm-none-linux-gnueabi/4.4.3 

-L/home/sun/study/crosstools/4.4.3/bin/../lib/gcc 

-L/home/sun/study/crosstools/4.4.3/bin/../lib/gcc/arm-none-linux-gnueabi/4.4.3/../../../../arm-none-linux-gnueabi/lib 

-L/home/sun/study/crosstools/4.4.3/bin/../arm-none-linux-gnueabi//sys-root/lib 

-L/home/sun/study/crosstools/4.4.3/bin/../arm-none-linux-gnueabi//sys-root/usr/lib

        以上就是collect2默认寻找的库路径 

-Tboot.lds -Map system.map 

        传递过来的链接器参数

start.o lowlevel_init.o nand.o interrupt.o main.o 2440lib.o print.o

        要链接的.o文件 

--start-group -lgcc -lgcc_eh -lc --end-group


这个选项很重要,链接器链接的静态库。-lc 代表链接标准c库, -lgcc 代表要链接libgcc.a,这个库应该是gcc扩展的库。


下面说一下另外一种链接方式,就是用ld命令来链接,这里就是arm-linux-ld,这种方式也就是我链接出错的链接方式。这种链接方式,没有默认的参数,寻找库的路径以及链接库都要自己添加。之所以链接出错就是因为我没有指定链接的库以及寻找库的路径。既然如此,那么使用第一种链接方式链接我的程序就应该是没错的。我将Makefile修改了一下再make,还是有错,令人欣喜的就是错误减少了,就只剩下如下的这一种错误:

/home/sun/study/crosstools/4.4.3/bin/../lib/gcc/arm-none-linux-gnueabi/4.4.3/libgcc_eh.a(unwind-arm.o): In function `get_eit_entry':

/opt/FriendlyARM/mini2440/build-toolschain/working/src/gcc-4.4.3/libgcc/../gcc/config/arm/unwind-arm.c:673: undefined reference to `__exidx_end'

/opt/FriendlyARM/mini2440/build-toolschain/working/src/gcc-4.4.3/libgcc/../gcc/config/arm/unwind-arm.c:673: undefined reference to `__exidx_start'


经过分析我认为是Uart_Printf函数里调用vsprintf的缘故,将Uart_Printf注释掉,然后再make。链接就通过了,说明就是这个vsprintf的问题。放下这个问题先不管,我试一下ld这种链接方式,在加上寻找库的路径以及要链接的库名的情况下是否可以成功make。修改Makefile如下:

CC = arm-linux-gcc

LD = arm-linux-ld

OBJCOPY = arm-linux-objcopy

objs := start.o lowlevel_init.o  main.o nand.o 2440lib.o

boot.bin: $(objs)

        $(LD) -Bstatic -Tboot.lds -Ttext 0x33F80000 $(objs)

        -L/home/sun/study/crosstools/4.4.3/lib/gcc/arm-none-linux-gnueabi/4.4.3

        -L/home/sun/study/crosstools/4.4.3/arm-none-linux-gnueabi/sys-root/usr/lib 

        -Map boot.map -o boot.elf --start-group -lgcc -lgcc_eh -lgcov -lc --end-group

        $(OBJCOPY)  -O binary  boot.elf boot.bin

%.o:%.c

        $(CC) -Wall  -c -o $@ {1}lt;

%.o:%.S

        $(CC) -Wall  -c -o $@ {1}lt;

clean:

        rm -f *.bin *elf  *.o 


用以上的Makefile,make通过了。程序下载到板子上现象也是正确的。这说明ld链接器正确链接了程序。现在两种链接方式都可以正确的链接程序了。那么就剩下一个问题,就是Uart_Printf函数里vsprintf。这个函数是一个格式化字符串转换函数,应该是标准C库里的函数,在链接器能够找到c库的情况下,两种链接方式都提示同样的错误。我猜测vsprintf这里面还调用了其他库的函数,我找了很久也没有找到到底调用了什么库。打印函数对于程序调试是必须的。既然vsprintf不能使用,那么就自己实现一个。参考u-boot的printf的实现,我自己编写一了一个vsprintf,然后结合Uart_SendString实现了串口打印功能。


简单的说一下实现printf的方法,首先要解决函数可变参数的问题,printf(char *fmt,...)显然是一个可变参数函数,第一个参数为字符串,后面是格式化输出参数列表。c语言中函数的参数都是压进栈里的,可变参数函数必须有一个参数表示参数的个数,才能让编译器知道要压进栈多少参数,以及函数返回时弹出多少参数,printf(char *fmt,...)实现这个功能的就是fmt字符串,里面有多少'%',就代表后面有多少个参数,所以我们必须提取出fmt字符串中'%'的个数,以及针对'%'后面不同的字符来处理参数。printf实现类似如下:

void myprintf(char *fmt,...)

{

    va_list ap;

    char string[256];

 

    va_start(ap,fmt);

    myvsprintf(string,fmt,ap);

    Uart_SendString(string);

    va_end(ap);

}


va_list 其实就是*char类型,va_list ap 也就是开始定义了一个char类型的指针变量ap。 va_start是一个宏,作用就是取得fmt指针地址,并跳过这个地址赋值给ap,这样ap就指向了除了fmt指针的第一个可变参数在内存中的地址,然后通过myvsprintf(string,fmt,ap)对fmt字符串结合可变参数进行转化,并把转化的结果赋值给string,最后通过Uart_SendString()函数将字符串发送给串口。具体实现可以看我的源代码。在我的资源里 http://download.csdn.net/detail/yaozhenguo2006/3774535


标准C库的问题就算暂时解决了,不过还是给我提了一个醒,在arm-linux-gcc上开发裸机程序,如果用到标准C库一定要注意,不是所有的函数都可以使用,比如vsnprintf就不能使用。其他还有什么函数不能使用还是个未知数,所以尽量不用标准C库的函数才是保证程序安全的办法。典型的例子就是u-boot,它就没有使用标准c库,所用的相关函数都是自己实现的,这样就保证u-boot很强的可移植性。也许arm-elf-gcc会没有这个问题,毕竟链接的是ulibc库,专门针对嵌入式的,以后有机会一定要验证一下。


推荐阅读

史海拾趣

Data Device Corporation公司的发展小趣事

Data Device Corporation(DDC)成立于1964年,总部位于美国纽约。在创立之初,DDC就专注于为军事和商业航空领域提供高质量的数据接口产品。凭借其卓越的技术实力和对市场的敏锐洞察,DDC迅速在电子行业中崭露头角。通过与各大军事机构和航空公司的紧密合作,DDC不断推出符合市场需求的新产品,逐步奠定了自己在行业中的领先地位。

Dailywell Electronics Co Ltd公司的发展小趣事

进入21世纪后,德利威电子加速了其国际化布局的步伐。2000年,公司在中国大陆东莞成立了德利威电子厂,并成功推动了5S运动(整理、整顿、清扫、清洁、素养),进一步提升了公司的管理水平。同时,公司还取得了ISO-9001国际认证标准,标志着其质量管理体系的进一步完善。此外,德利威电子还取得了德国TUV产品安规认证,进一步增强了其产品的国际竞争力。

Acculin Inc公司的发展小趣事

Acculin Inc深知人才是企业发展的核心。为了吸引和留住优秀人才,公司建立了完善的培训体系,并为员工提供了良好的晋升机会。此外,公司还注重团队建设,通过举办各种团建活动活动增强员工之间的凝聚力。这些举措使得Acculin在人才竞争激烈的市场中保持了优势。

Bomar公司的发展小趣事

Bomar公司成立于1963年,最初是一家专注于石英晶体制造的公司。在当时的电子行业,石英晶体作为频率控制元件,对于无线通信和电子设备的稳定运行至关重要。Bomar公司通过不断的研发和技术创新,成功突破了石英晶振在宽频率范围内的高稳定性问题,满足了严格的FCC规范。这一技术突破使得Bomar公司的石英晶振在市场上获得了广泛的认可,并为公司的发展奠定了坚实的基础。

FCI [First Components International]公司的发展小趣事

在环保意识日益增强的今天,Bomar公司积极响应全球环保号召,开始研发并应用环保材料。公司推出的生物基低聚物产品,完全或部分提取自植物或农作物等可再生原料,不仅降低了对石油产品的依赖,还具有低刺激性和低毒性的优势。这一创新举措不仅有助于提升Bomar公司产品的环保性能,也符合了市场对环保产品的需求趋势,为公司的可持续发展注入了新的动力。

这五个故事只是Bomar公司在电子行业发展过程中的一部分,但它们却展示了公司在技术创新、市场拓展、合作发展以及环保责任等方面的努力和成就。通过这些故事,我们可以看到Bomar公司如何紧跟时代步伐,不断适应和引领电子行业的发展趋势。

Cressall Power Resistors公司的发展小趣事

在电子行业的早期,Cressall Power Resistors公司以其对电阻技术的深入研究而脱颖而出。公司研发团队通过不懈的努力,成功开发出一种新型的功率电阻器,具有更高的耐热性、更低的电阻值变化率以及更长的使用寿命。这一技术突破迅速得到了市场的认可,公司因此获得了大量的订单,业绩迅速增长。随着技术的不断完善和产品的持续升级,Cressall逐渐在功率电阻器市场树立了技术领先的地位。

问答坊 | AI 解惑

wince平台中文版本和英文版本应用程序显示的界面大小不同

开发平台是vs2005下vc++下的智能设备开发 三星s2440下的wince平台 屏幕显示分辨率为800*600 在中文版本下应用程序可以全屏显示,但是到了英文平台,只能显示出程序的一部分内容,感觉像是应用程序的分辨率变小了 如果换成vs2005下C#下的智能设备 ...…

查看全部问答>

ini多国语言unicode问题求助

我的多国语言用ini文件的方式来实现,在记事本中写好了多国语言后,保存为unicode编码。 在读取这个ini文件时查看内存中读出的S为 53 00 00 00,而在程序中自己定义的宽字节S在内存中则为53 00。 看起来读的unicode的ini文件中的字符好像多了2个 ...…

查看全部问答>

有关bufil的

求问bufif的功能是什么,比如说这段程序 bufif mybuf(out,in,enable)是什么意思? 刚学Verilog 数字系统设计 以后可能常来这边求教了!…

查看全部问答>

关于PB5.0编译汇编文件的问题

系统wince5.0,2440硬件平台,修改了/kernel/hal/arm/fw.s文件后,在pb5.0上编译后整个系统都跑不起来,同样目录下的c文件修改后编译就没有问题,不知是什么原因,后来在PB4.2下编译好fw.s文件,直接把编译好的fw.obj  copy到5.0里链接, ...…

查看全部问答>

急!急!急!!!硬盘数据format后,重写过怎么找回?

nnd,今天冲装系统的时候,明明是看的是c盘,确把D盘个了,装好了才发现,气死了. 有什么工具可以恢复吗? …

查看全部问答>

求教C64x中EMIF中BE[7:0]的作用?

本帖最后由 dontium 于 2015-1-23 13:38 编辑 请大家帮忙,在C64x的EMIF在SDRAM接口中,如何通过BE[7:0]实现8bit、16bit、32bit、64bit的寻址方式? …

查看全部问答>

求助示波器2.0中的HSMC接口相关文献

现在正在做一个板子,需要做HSMC接口。可是网上找不到资料,ALTERA公司网站也没有。看到你们这边做示波器2.0中用到了这个接口,想跟你们求助一下,看看有没有封装等资料能共享一下,谢谢了………

查看全部问答>

初学者求各位大侠伸出援手:在IAR下编译出现了一个奇怪的问题

编译是出现了好多这样的错误: Fatal Error[Pe035]: #error directive: \"ERROR: Mismatch between specified board and selected microcontroller.\" F:\\学习资料\\无限油田监控项目资料(全)\\网上下的资料\\很有用的文章\\SimpliciTI-1.0.3\\ ...…

查看全部问答>

利用6517A对惰性气体或高真空中的小型晶体高值电阻测量的典型误差来源

屏蔽与防护 为了降低静电干扰,利用具有地电位的导体包围样本和线缆(屏蔽)。 为了防止漏电流影响测量,利用具有同样电势的导体对至静电计[1]输入端的连接进行包围(防护)。测量电流时,需要将防护装置连接至电压源的LO端。 在高真空系统( ...…

查看全部问答>