注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

天道酬勤 玩物丧志

用勇气去改变可以改变的事情,用胸怀去包容无法改变的事情,用智慧去判断两者的区别

 
 
 

日志

 
 

newlib的编译和使用(转)  

2014-07-30 11:15:12|  分类: 嵌入式系统 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

参考:http://blog.csdn.net/qb_2008/article/details/8228061

要熟悉一个新的处理器平台,第一件事是写一些裸机程序。这时我们最想要的,是实现一个printf打印函数,以便及时输出各种信息。

除去下层的字节输出驱动不说,printf本身的实现就有够麻烦,如果平时有保存相关的代码还好,不然就很浪费时间。除此之外,还有

一些诸如strlen、strcpy之类的函数,我们不愿意自己写,既麻烦而且效率不高,如果能借助已有的代码或库就好了。newlib就满足了

这点需求。

newlib c库是一个开源的c函数库,包括libc和libm两部分。它支持ANSI C库标准,针对不同处理器架构进行优化,轻量级,适用于嵌入式系统。

在我看来,newlib库有如下好处:

1. 支持printf和优化的字符串操作

2. 支持malloc和free等内存操作

3. 支持函数可重入功能(不过这种支持对内存有压力,总之是感觉弊大于利)

4. 支持libm数学库(不过一般嵌入式用不到浮点数,而且用模拟的开销略大)

        5. newlib的函数是分文件实现的,如果用不到,绝不加入链接,一般不会造成目标文件猛增的情况。


newlib虽有诸多好处,但要用起来还真有点坡度。它需要先编译成库再使用,而且要用特定的功能还需要写一些底层支持函数。

我就不献丑了,网上已有大牛专业的教程:Howto: porting newlib a simple guide,

http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html


这篇文章是介绍如何将newlib移植到一个开源软核OpenRISC 1000处理器上去。但我们一般用不到这种高难度动作,只是简单地在已有平台上

进行使用即可,下面就介绍我在stm32平台上使用newlib-1.20.0的经验。


下载newlib-1.20.0.tar.gz,解压缩,生成newlib-1.20.0。再创建newlib-1.20.0-build目录用于生成库,创建newlib-1.20.0-install用于安装(当然装完了随便拷贝)。

$cd newlib-1.20.0-build

$../newlib-1.20.0/configure --target=arm-none-eabi --with-newlib --prefix=`pwd`/../newlib-1.20.0-install

因为这里我用的事arm-none-eabi-gcc工具链,如果是arm-elf-gcc什么的要相应地修改--target选项。

$make all

$make install


从理论上来说,这样做之后,就可以生成newlib库到newlib-1.20.0-install目录中。但在此过程中确实出现了问题。newlib支持arm三个架构armv6m、thumb、thumb2,它试图编译对每个架构生成一个库,可惜部分汇编文件中包含swi指令,thumb和thumb2架构不支持,这就糟了,编译过程中就卡住了。当然,这些汇编文件都是系统启动或者进行系统调用时用的,我们用不到(我也不会用)。要想办法删掉它们,修改相应的Makefile.am。

   1. 在newlib-1.20.0/newlib/libc/sys/Makefile.am中:

  1. ## Process this file with automake to generate Makefile.in  
  2.   
  3. AUTOMAKE_OPTIONS = cygnus  
  4.   
  5. INCLUDES = $(NEWLIB_CFLAGS) $(CROSS_CFLAGS) $(TARGET_CFLAGS)  
  6.   
  7. AM_CCASFLAGS = $(INCLUDES)  
  8.   
  9. noinst_LIBRARIES = lib.a  
  10.   
  11. if MAY_SUPPLY_SYSCALLS  
  12. extra_objs = $(lpfx)libcfunc.o $(lpfx)trap.o $(lpfx)syscalls.o  
  13. else  
  14. extra_objs =  
  15. endif  
  16.   
  17. lib_a_SOURCES = aeabi_atexit.c  
  18. #lib_a_LIBADD = $(extra_objs)                                 #不生成syscalls.o  
  19. #EXTRA_lib_a_SOURCES = trap.S syscalls.c libcfunc.c           #不使用syscalls.c  
  20. #lib_a_DEPENDENCIES = $(extra_objs)                           #依赖也不要,因为都不需要,所以干脆全注释了  
  21. lib_a_CCASFLAGS = $(AM_CCASFLAGS)  
  22. lib_a_CFLAGS = $(AM_CFLAGS)  
  23.   
  24. if MAY_SUPPLY_SYSCALLS  
  25. #all-local: crt0.o                                            #这里的crt0.o也是不需要生成的  
  26. all-local:  
  27. endif  
  28.   
  29. ACLOCAL_AMFLAGS = -I ../../.. -I ../../../..  
  30. CONFIG_STATUS_DEPENDENCIES = $(newlib_basedir)/configure.host  

      改为makefile之后,要在相应目录下执行autoreconf,重新生成Makefile.in。

    2. 刚才修改Makefile.am,使其不生成crt0.o,但实际上上层目录还是要用它的。所以就又改了其上层目录的Makefile.am,让其凭空造一个crt0.o出来。修改文件newlib-1.20.0/newlib/libc/sys/Makefile.am:

  1. $(CRT0): $(sys_dir)/$(CRT0)  
  2.     rm -f $@  
  3.     ln $(sys_dir)/$(CRT0) $@ >/dev/null 2>/dev/null \  
  4.      || cp $(sys_dir)/$(CRT0) $@ || touch $@              #就是在这里最后加上 || touch $@,就凭空造出一个crt0.o,不至于在编译时出错  
   

      改完后同样不要忘了执行autoreconf。
    

   3. 编译时除了编译newlib目录,还编译了libgloss目录,所以还要修改其中的Makefile.in(这回改Makefile.in了)。修改文件newlib-1.20.0/libgloss/arm/Makefile.in:

  1. LINUX_CRT0    = linux-crt0.o  
  2. LINUX_BSP     = libgloss-linux.a  
  3. #LINUX_OBJS    = linux-syscalls0.o linux-syscalls1.o                       #这个有syscalls,去掉  
  4. LINUX_SCRIPTS = linux.specs  
  5. LINUX_INSTALL = install-linux  
  6.   
  7. REDBOOT_CRT0    = redboot-crt0.o   
  8. #REDBOOT_OBJS   = redboot-syscalls.o                                      #这个有syscalls,去掉  
  9. REDBOOT_SCRIPTS = redboot.ld redboot.specs  
  10. REDBOOT_INSTALL = install-redboot  
  11.   
  12. RDPMON_CRT0 = rdpmon-crt0.o  
  13. RDPMON_BSP  = librdpmon.a  
  14. #RDPMON_OBJS    = syscalls.o libcfunc.o trap.o _exit.o _kill.o             #这个有syscalls,去掉  
  15. RDPMON_SCRIPTS  = rdpmon.specs  
  16. RDPMON_INSTALL  = install-rdpmon  
  17.   
  18. RDIMON_CRT0 = rdimon-crt0.o  
  19. RDIMON_BSP  = librdimon.a  
  20. RDIMON_OBJS = $(patsubst %,rdimon-%,$(RDPMON_OBJS))  
  21. RDIMON_SCRIPTS  = rdimon.specs  
  22. RDIMON_INSTALL  = install-rdimon  
  23.   
  24. CFLAGS      = -g  
  25.   
  26. # Here is all of the eval board stuff  
  27. PID_SCRIPTS = pid.specs  
  28. PID_INSTALL = install-pid  
  29.   
  30. IQ80310_SCRIPTS = iq80310.specs  
  31. IQ80310_INSTALL = install-iq80310  
  32.   
  33.   
  34. # Host specific makefile fragmen  

    如此修改之后,应该就能编译安装成功。arm之外的架构,一般不会遇上这种问题。


    如此成功之后,可以再newlib-1.20.0-install/arm-none-eabi中看到一个include头文件目录,一个lib目录。我的stm32使用的事lib/thumb2/libc.a。


   接下来,还要再补齐一些底层支持函数。这个不需要libgloss生成的,而是要看newlib-1.20.0/newlib/configure.host文件。newlib根据$host不同预设了底层接口的支持方式,我们需要看$syscall_dir的值,以及MISSING_SYSCALL_NAMES、REENTRANT_SYSCALLS_PROVIDED两个宏是否添加进$newlib_cflags。下面是不同设定对应的情况:

     

-DMISSING_SYSCALL_NAME和-DREENTRANT_SYSCALL_PROVIDED

-DMISSING_SYSCALL_NAME

         当使用-DMISSING_SYSCALL_NAME宏进行编译时,在_syslist.h中将原本的_close映射为close(等等),_syslist.h在ligloss/libnosys和libc/reent的c文件中被包含。并且,如果使用了-DMISSING_SYSCALL_NAME,则编译时不包括libc/syscall中的文件。而libc/syscall中文件正是有close等函数的定义。

         所以,如果定义了_DMISSING_SYSCALL_NAME,看起来就是bsp提供close等函数的实现,而libc/reent中的_close_r等函数能提供简单的可重入支持。

 

         此时,bsp提供一套close等函数的实现,而libc/syscall不被使用。系统流程虽然经过_close_r,但从意思上是不支持可重入性的。

-DREENTRANT_SYSCALLS_PROVIDED

         当使用-DREENTRANT_SYSCALLS_PROVIDED宏定义进行编译时,在libc/reent中的c文件全部没有函数体,即不定义_close_r等函数。所以-DREENTRANT_SYSCALLS_PROVIDED的意思,是指bsp提供了可重入的_close_r等函数版本。上层的库如stdio仍调用_close_r等可重入函数进行操作。同时,libc/syscall中的函数被使用。libc/syscall中定义bsp需提供实现的close等函数,这里close由_close_r实现。bsp不需要再提供close的实现,只需提供_close_r的实现。

 

         此时,bsp提供一套_close_r等函数的实现,libc/syscall被使用。整个系统的可重入性,压在bsp的_close_r等函数的可重入性上。从意思上说是支持可重入性的。

 

两者同时存在

         当两者都存在时,libc/syscall和libc/reent都未起作用。bsp提供了一套close等函数的实现。整个newlib的上层,从调用_close_r转而调用close。系统的可重入性,直接压在bsp的close等函数之上。但从意思上来说不支持可重入性。

两者都不存在

         两者都不存在的时候,使用libc/syscall中的函数。这个流程就简单了,先是libc/syscall中的close函数,调用libc/reent中的_close_r函数,然后_close_r函数又调用bsp应该实现的_close函数。

 

         此时bsp提供一套_close等函数的实现,libc/syscall被使用。从意思上来说是不支持可重入性的。


       对于arm来说,定义如下:

  1. arm-*-*)  
  2. yscall_dir=syscalls  

      符合两者都不存在的情况,我们只需提供一套_close等函数的实现即可。参照前面的专业教程实现即可。

      要实现printf,需要实现好_write函数,要实现malloc,需要实现好_sbrk函数,__malloc_lock函数,__malloc_unlock函数。这里就不多说了。

     printf的打印缓冲区是从_reent结构中获取的,因为我们没有实现可重入功能,是同一缓冲区,所以在打印过程中任务切换可能导致打印混乱。我希望它在打印时使用栈空间,有两个方法:一是自己实现printf,在其中调用sprintf打印到缓冲区,然后用puts输出;二是完全自己实现printf,也就是用使用newlib之前的printf函数。

     另外,在malloc调用时要做好互斥工作,但也不适合用关中断,最好用互斥信号量或普通的信号量。



PS: 在编译newlib过程中,重复好多遍,还要改Makefile.am,还必须编译libgloss。我尝试过在configure中添加选项的方式避免修改,但没有成功。更改地很暴力,如果大家有好的编译使用newlib的方式,还请不吝指教,多谢了。

  评论这张
 
阅读(826)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018