天嵌 ARM开发社区

 找回密码
 注册
查看: 3148|回复: 0

arm体系结构与编程一arm开发板上运行Hello World

[复制链接]
冰帝 发表于 2014-7-27 17:39:11 | 显示全部楼层 |阅读模式
这里以s3c2440为主,我们都知道2440内存有效的应用范围是地址SRAM的0-1000(16进制),还有就是sdram的30000000-34000000。
那么我们要运行一个程序的话就要选用一个内存来运行我们的程序,要想办法把程序放进去,我们一般放到sdram里面运行,一般sram是用作一些初始化的。
在sdram中33000000以上的空间已被uboot所占用,我们只能用33000000以下的空间。
我们用C语言来写,最简单的莫过于hello world!我们就用这个例子来讲解,命令为test.c。
#include<stdio.h>
int main(int argc,char **argv)
{
printf("hello world!\n")
return 0;
}
该程序在x86平台上编译时很简单的,gcc test.c -o test
运行./test
在X86上面是装着linux操作系统的,但是在我们一开始开发板没有装操作系统的话那怎么办呢?
要是我们在arm开发板上运行怎么办?
我们知道编译的过程是先把C语言编程汇编,再把汇编编程机器代码,但是ARM和X86不是同一个体系。汇编指令也不一样,就是说运算的指令是不一样的!
那么我们就不能用上面的来编译了!那么怎么办呢?
我们只能用arm编译器,那么我们要安装交叉工具链,如何安装呢?
下面就讲解交叉编译器的安装
我们先下载arm-linux-gcc-3.4.1.tar.bz2(这个有不同版本,各取所爱)

首先我们用tar jxf arm-linux-gcc-3.4.1.tar.bz2解压出来
解压出来了就是我们的编译器,支持的汇编或者机器指令是arm的,为什么要叫arm-linux-gcc呢?
意思是在arm平台上装一个Linux,写出来的程序就能再我们arm开发板上运行。
解压后运行./3.4.1/bin/arm-linux-gcc -v
可以查看该版本的信息。
我们可以看到gcc version 3.4.1就是我们编译器的版本信息了!
再看看prefix=/usr/local/arm/3.4.1这里的信息就是我们要安装的路径。
那么我们先来看看我们系统的路径。
ls /usr/local有没有我们的arm。
没有的话我们建一个目录mkdir /usr/local/arm
然后我们就可以把刚刚解压的目录移动在我们新建的文件夹里面了!
mv 3.4.1 /usr/local/arm/
那么我们现在就试一下编译我们的test.c文件,
输入命令 /usr/local/arm/3.4.1/bin/arm-linux-gcc test.c -o test
这里就有人问,那么我每次编译时都需要输入/usr/local/arm/3.4.1/bin/这个路径吗?
我们来设置一下环境变量
vi /etc/bashrc
增加PATH=/usr/lpcal/arm3.4.1/binundefinedPATH 保存
那么我们下次编译就可以直接写arm-linux-gcc test.c -o test了。
现在编译的文件是在x86上运行不了的!那么我们能不能把该程序放到我们的arm开发板上运行呢?
我们就用tftp把程序拷到开发板上
我们先用tftp 30000000 test 把文件拷到30000000这个地址
如何不会用不清楚tftp服务的话可到论坛咨询。
那么我拷过去了之后,如何去执行它?
我们知道在30000000-34000000已经有uboot移植在那里执行了!
那么我们如何跳到30000000这个位置执行呢?
go 30000000这个命令就可以跳到该地址上执行。
那么它又如何返回去的呢?
这个就是按照C语言函数的规范,叫做APCS规范,如果是一个函数的话自然会有一个返回的值自然能返回去。
但是我们运行了之后却发现“垮”掉了,整个平台都死掉了!这是为什么呢?如图0.


因为有两点:
一:任何一个编译出来的程序将在哪里执行是谁决定的呢?也就是说我们在内存里面执行,但在内存哪个地址上执行是谁决定的呢?
    答:是编译器决定的,一个应用想在内存的哪个地址执行要一开始由编译器来决定的!
    那么我们可以看看test程序的信息。
    命令:arm-linux-objdump -d test
    我们可以看到里面有很多很多的函数,并且他要求我们的main函数在000083d0这个地址上执行,如图1。

   
    实际上我们板子上是没有这个地址的!那怎么办呢?那就需要我们自己来指定了!
二:printf我们并没有实现,printf这个是由库文件来实现的,我们执行时是调动态库来实现该函数的,要想用库的函数,前提是要我们平台上有操作系统。
    操作系统才会帮我们装载库,但在我们arm开发板上的bootloader是很简单的,不能和操作系统相比,库在它上面是用不了的!bootloader是连文件也不支持的!
    printf是不是也要我们自己来写呢?其实不用,在uboot上我们是不是可以看到它打印很多信息给我们看,其实它自身已实现了printf函数。
    我们通过uboot编译的文件最后输出的system.map可以看到printf的地址,其实在33000000-34000000的33f94aa8这个地址。
    那么我们不能用库里面的函数了!
    我们改一下我们写的代码:
我们不能再用库的函数了!把库去掉。
我们定义一个函数
void (*show)(char *,...);
int main(int argc,char **argv)
{
  show=0x33f94aa8;//0x33f94aa8是ubbot printf函数的地址,那么我们就可以把show当成printf来使用。
  show("hello-uboot.\n");
  return 0;
}
现在库的问题已经解决,那么第一点中在哪个地址运行的地址还没解决。
我们只能手动链接,arm-linux-gcc -c test.c -o test.o 我们先编译不链接, -o就是不链接。
然后我们链接arm-linux-ld -Ttext=0x3000000 test.o -o test
现在我们再看看arm-linux-objdump -d test该信息
那么我们现在来看已经没有其他函数只有main函数,且在30000000这个地址上运行了!
因为我们现在没有链接库了!因为链接库会产生很多的中间信息(函数),现在都没有了!只有我们写的代码,如图2.


-Ttext就是代码段的起始地址是30000000,也可以加上arm-linux-ld -Ttext=0x3000000 -nostdlib test.o -o test
-nostdlib 是指不需要和库进行连接。
现在的程序是否就能在arm开发板上运行呢?
我们tftp 30000000 test拷到开发板上, g0 30000000跳到该地址运行。
我们发现还是不行,报错。如图2-1

其实我们g0 30000000跳到该区域执行时,它想要执行的是一条指令,但我们的test的第一句话时是否是一条指令呢?
我们用vi test命令查看该程序,图3.


我们发现它第一条竟然不是指令,实际上是一个可执行文件的头信息,任何一个可执行文件都有一件(外衣)。
头里面装有一些描述性的信息。这个头是我们可执行文件的格式。通常格式有AOUT,ELF,当然window的格式问PE。
我们g0 30000000 跳到30000000运行时因为uboot过于简单,不能解释这个头信息,所以就报错了!
那么我们可以把头去掉。
输入命令arm-linux-objcopy test test.bin
其实这个软件可以去头和加头,那么我们可以明确告诉它
arm-linux-objcopy test -I elf32-littlearm -o binary test.bin
告诉编译器我们文件格式是32位elf格式,并且为小端格式,输出binary,这个就是一个纯粹没有头的文件,意思要去掉这个头。
我们可以看看有头和无头文件的大小,如图4.


有头文件为33k,实际上无头文件为100个字节。

那么现在我们把该bin文件拷进去开发板执行,终于能打印出hello-uboot了!如图5.
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

i.MX8系列ARM cortex A53 M4 工控板上一条 /1 下一条

Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )

GMT+8, 2024-5-6 16:21 , Processed in 1.078118 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表