Jul 11, 2013
Technology1. 有关异常向量
前面的例子中存在一个大BUG,内存布局中的前8个全字是为异常向量而保留的。当异常发生时控制逻辑将转到这些位置以执行对应的异常处理代码。异常向量和它们的地址如下:
- Exception Address
- Reset 0x00
- Undefined Instruction 0x04
- Software Interrupt (SWI) 0x08
- Prefetch Abort 0x0C
- Data Abort 0x10
- Reserved, not used 0x14
- IRQ 0x18
- FIQ 0x1C
按理说,这些个异常向量应该对应到异常处理程序中,既然我们代码中不会有异常发生,索性就用死循环来代替,如下:
.section "vectors"
reset: b start
undef: b undef
swi: b swi
pabt: b pabt
dabt: b dabt
nop
irq: b irq
fiq: b fiq
对应的,为了确保这些指令被放置在异常向量地址中,链接脚本也需要做相应的改动:
SECTIONS {
. = 0x00000000;
.text : {
* (vectors);
* (.text);
...
}
...
}
异常向量需要放置在所有代码之前,这确保了代了向量是从0x0地址开始。
2. C启动代码
直接执行C代码会造成CPU直接重启,因为和汇编代码不同的是C语言需要初始化运行环境。
static int arr[] = { 1, 10, 4, 5, 6, 7 };
static int sum;
static const int n = sizeof(arr) / sizeof(arr[0]);
int main()
{
int i;
for (i = 0; i < n; i++)
sum += arr[i];
}
C语言运行环境需要设置
- 栈
- 全局变量: 已初始化的 && 未初始化的
- 只读数据
2.1 栈设置
栈被用来存储自动变量,传递函数变量,存储返回地址等等。ARM Architecture
Procedure Call Standard
(AAPCS)是ARM体系结构中用于生成栈的规则。r13被用于作栈指针。
对于特定的开发板来说,栈开始地址可能不同,对于connex开发板来说,地址可以用下面的代码来定义:
ldr sp, =0xA4000000
2.2 全局变量
C代码在编译时会把已初始化的全局变量放在.data段中,因而在初始化的汇编代码中,需要把.data段从Flash搬移到RAM中。
C代码确保未初始化的全局变量被初始化成0.
当C程序被编译时,独立的.bss段被用作未初始化的变量。因为未初始化的值都是0,我们无需将其存储在FLASH中。只不过在搬移的时候,我们需要在程序中将它们初始化为0而已。
2.3 只读数据
const常量会被初始化为.rodata, .rodata也被用于存储字符常量。
.rodata在运行时不会被改变,所以它们可以被直接放置在FLASH中。
2.4 启动代码
Linker脚本需要做下面的事:
- .bss部分代替
- vectors部分代替
- .rodata部分代替
SECTIONS {
. = 0x00000000;
.text : {
* (vectors);
* (.text);
}
.rodata : {
* (.rodata);
}
flash_sdata = .;
. = 0xA0000000;
ram_sdata = .;
.data : AT (flash_sdata) {
* (.data);
}
ram_edata = .;
data_size = ram_edata - ram_sdata;
sbss = .;
.bss : {
* (.bss);
}
ebss = .;
bss_size = ebss - sbss;
}
启动代码需要完成下列任务:
- 中断向量设置
- 将.data部分从FLASH拷贝到RAM
- 将.bss置0后拷贝到RAM
- 设置栈指针(stack pointer)
- 分支程序到main函数
.section "vectors"
reset: b start
undef: b undef
swi: b swi
pabt: b pabt
dabt: b dabt
nop
irq: b irq
fiq: b fiq
.text
start:
@@ Copy data to RAM.
ldr r0, =flash_sdata
ldr r1, =ram_sdata
ldr r2, =data_size
@@ Handle data_size == 0
cmp r2, #0
beq init_bss
copy:
ldrb r4, [r0], #1
strb r4, [r1], #1
subs r2, r2, #1
bne copy
init_bss:
@@ Initialize .bss
ldr r0, =sbss
ldr r1, =ebss
ldr r2, =bss_size
@@ Handle bss_size == 0
cmp r2, #0
beq init_stack
mov r4, #0
zero:
strb r4, [r0], #1
subs r2, r2, #1
bne zero
init_stack:
@@ Initialize the stack pointer
ldr sp, =0xA4000000
bl main
stop: b stop
我们将直接用arm-none-eabi-gcc来编译所有程序:
arm-none-eabi-gcc -nostdlib -o csum.elf -T csum.lds csum.c startup.s
-nostdlib选项用于指定标准C不应该被链接。
查看符号信息:
arm-none-eabi-nm -n csum.elf
00000000 t reset
00000004 A bss_size
00000004 t undef
00000008 t swi
0000000c t pabt
00000010 t dabt
00000018 A data_size
00000018 t irq
0000001c t fiq
00000020 T main
00000094 t start
000000a8 t copy
000000b8 t init_bss
000000d0 t zero
000000dc t init_stack
000000e4 t stop
00000100 r n
00000104 R flash_sdata
a0000000 d arr
a0000000 D ram_sdata
a0000018 D ram_edata
a0000018 D sbss
a0000018 b sum
a000001c B ebss
可以看到: 中断向量从0x0开始; 汇编代码从8个全字后开始(0x20==32==84);
只读数据n放在代码之后; arr,初始化后的数据,放在RAM中;
未初始化的数据,sum放在6个int之后64==24==0x18
转化成.bin二进制格式后,在Qemu中运行之,检查结果:
$ arm-none-eabi-objcopy -O binary csum.elf csum.bin
$ dd if=/dev/zero of=./flash.bin bs=4K count=4K
$ dd if=csum.bin of=flash.bin bs=4096 conv=notrunc
$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
(qemu) xp /6dw 0xa0000000
a0000000: 1 10 4 5
a0000010: 6 7
(qemu) xp /1dw 0xa0000018
a0000018: 33
我们可以看到,1, 10, 4, 5, 6, 7 分别为数组元素,而结果为33,
储存在0x18的地址。如果感兴趣,我们大可查找出别的数据地址,这里就不一一述说了。
Jul 10, 2013
Technology研究两个汇编程序, 通过研究这两个程序,初步了解ARM汇编的知识:
1. 数组求和
.text
entry: b start @ Skip over the data
arr: .byte 10, 20, 25 @ Read-only array of bytes
eoa: @ Address of end of array + 1
.align
start:
ldr r0, =eoa @ r0 = &eoa
ldr r1, =arr @ r1 = &arr
mov r3, #0 @ r3 = 0
loop: ldrb r2, [r1], #1 @ r2 = *r1++
add r3, r2, r3 @ r3 += r2
cmp r1, r0 @ if (r1 != r2)
bne loop @ goto loop
stop: b stop
.byte声明:
.byte声明的变量在内存中以连续的比特存在,.2byte和.4byte与之类似,分别用于存储16位值和32位值。联想到C语言中的内建数据结构定义,不难想象char/int/long int 预编译完是哪一种类型。
通用语法结构如下:
.byte exp1, exp2, ...
.2byte exp1, exp2, ...
.4byte exp1, exp2, ...
你可以指定数据的格式,二进制用前缀0b/0B修饰,八进制以前缀0修饰,十进制/十六进制以0x/0X开头。整数也可以用字符常量来表示,加上单引号即可,在这种情况下ASCII码会被用到。
也可以用C表达式,包含文字和其他符号的组合,如下例:
pattern: .byte 0b01010101, 0b00110011, 0b00001111
npattern: .byte npattern - pattern
halpha: .byte 'A', 'B', 'C', 'D', 'E', 'F'
dummy: .4byte 0xDEADBEEF
nalpha: .byte 'Z' - 'A' + 1
.align声明:
ARM指令需要32位对齐,指令的起始内存地址需要是4的倍数,所以用.align指令来插入无用的byte,来确保下一条指令的起始地址是4的倍数。在代码中存在byte或是半字(half words)的时候,需要用到这条指令。
编译&运行:
# assemble
$ arm-none-eabi-as -o sum.o sum.s
# link to elf file
$ arm-none-eabi-ld -Ttext=0x0 -o sum.elf sum.o
# form bin file
$ arm-none-eabi-objcopy -O binary sum.elf sum.bin
# form flash image
$ dd if=/dev/zero of=flash.bin bs=4k count=4k
$ dd if=sum.bin of=flash.bin bs=4K conv=notrunc
# emulate with flash image
$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
# examine the result
R00=00000007 R01=00000007 R02=00000019 R03=00000037
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=00000024
PSR=600001d3 -ZC- A svc32
FPSCR: 00000000
R03的值正是我们求和的结果: 0x37==55==10+20+25, 可以用一张流程图来表示程序的运行过程
2. 字符串长度
.text
b start
str: .asciz "Hello World"
.equ nul, 0
.align
start: ldr r0, =str @ r0 = &str
mov r1, #0
loop: ldrb r2, [r0], #1 @ r2 = *(r0++)
add r1, r1, #1 @ r1 += 1
cmp r2, #nul @ if (r1 != nul)
bne loop @ goto loop
sub r1, r1, #1 @ r1 -= 1
stop: b stop
运行结果:
(qemu) info registers
R00=00000010 R01=0000000b R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=0000002c
PSR=600001d3 -ZC- A svc32
FPSCR: 00000000
r1是用来存储字符串长度的,计算结果为11。
.asciz声明:
.asciz声明接受一个字符串作为参数,字符串以双引号修饰的字符表示。汇编器自动在字符串后面加nul字符(即\0字符)
.equ声明
汇编器维持一张符号表,符号表维持键->值的格式。当汇编器遇到一个标号时,汇编器将在符号表中自动建立一个条目。以后当汇编器遇到一个关于label的引用时,将自动替换为符号表中储存的label的地址。
使用汇编器指令.equ,我们可以手动在符号表中插入条目。
.equ通常这样定义:
.equ name, expression
.equ不会分配任何内存,它们只是在符号表中插入条目罢了。
bne的意思是(!=) , b means bit. bit not equal. ble (<=), beq (==), bge (>=), bgt (>), and bne (!=).
Jul 10, 2013
Technology在多个文件构成的程序中,源文件首先被编译成多个对象(object)文件(.o文件),
然后交由链接器生成最终的可执行文件,如下图所示:
{% img img /images/linker.png %}
在组建可执行文件时,链接器主要完成下列操作:
1. 符号解析
在编译单个文件组成的程序时,所有标号的解析都可以由汇编器替代为对应的地址。而在多文件组成的程序中,如果有储存在其他文件中的符号引用,汇编器会将其标识为"unresolved”(未解析).当对象文件被传递给链接器时,链接器从这些文件中决定对应的值,并把code中的unresolved的值替代为正确的值。
我们用上一节的求和函数来演示链接器是如何进行符号解析的。
这两个文件汇编后,会在链接时被检查未被解析的引用。
main.s
.text
b start @ Skip over the data
arr: .byte 10, 20, 25 @ Read-only array of bytes
eoa: @ Address of end of array + 1
.align
start:
ldr r0, =arr @ r0 = &arr
ldr r1, =eoa @ r1 = &eoa
bl sum @ Invoke the sum subroutine
stop: b stop
more:
sum-sub.s
@ Args
@ r0: Start address of array
@ r1: End address of array
@
@ Result
@ r3: Sum of Array
.global sum
sum: mov r3, #0 @ r3 = 0
loop: ldrb r2, [r0], #1 @ r2 = *r0++ ; Get array element
add r3, r2, r3 @ r3 += r2 ; Calculate sum
cmp r0, r1 @ if (r0 != r1) ; Check if hit end-of-array
bne loop @ goto loop ; Loop
mov pc, lr @ pc = lr ; Return when done
查看.o文件符号信息:
$ arm-none-eabi-nm main.o
00000004 t arr
00000007 t eoa
00000008 t start
00000014 t stop
U sum
$ arm-none-eabi-nm sum-sub.o
00000004 t loop
00000000 T sum
t代表符号已经被定义了, 而u则代表符号未被定义。大写字母表示该符号是全局变量。
从上面的输出结果看,sum是被定义在sum-sub.o的全局变量,而该变量在main.o中未被解析到。当linker被调用时,符号引用将被解析到,对应的可执行文件将被生成。
总结: as程序负责把.s文件编译成object文件,而生成最终的可执行文件时,ld负责把未被定位的符号定位到实际的库函数所在的位置。
2. 重定位.
重定位用于改变已经分配给标号的地址。它包括将所有符号引用映射到新分配的内存地址。
合并段后的符号列表情况,可以对比于上面的main.o和sum-sub.o来看:
$ arm-none-eabi-ld -Ttext=0x0 -o sum.elf main.o sum-sub.o
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 00000000
$ arm-none-eabi-nm sum.elf
00000004 t arr
00008038 T __bss_end__
00008038 T _bss_end__
00008038 T __bss_start
00008038 T __bss_start__
00008038 T __data_start
00008038 T _edata
00008038 T _end
00008038 T __end__
00000007 t eoa
00000024 t loop
00080000 T _stack
00000008 t start
U _start
00000014 t stop
00000020 T sum
可以看到stop后面的sum已经被定位好了(之前是main.o中的U标记),而loop则相应延后,被定位到了再往后的00000024。
地址的变更: loop原本地址为00000004, 现在是00000024, 而sum原本为0x00000000,现在为00000020, 这是因为sum-sub.o中的.text和main.o中的.text部分一起组成了sum.elf中的.text部分。
整体移动某个段到指定内存位置, 注意在-Ttext中我们增加的0x100的偏移量,使得地址对比于上面的结果整体上移了0x100:
$ arm-none-eabi-ld -Ttext=0x100 -o sum100.elf main.o sum-sub.o
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 00000100
$ arm-none-eabi-nm sum100.elf
00000104 t arr
00008138 T __bss_end__
00008138 T _bss_end__
00008138 T __bss_start
00008138 T __bss_start__
00008138 T __data_start
00008138 T _edata
00008138 T _end
00008138 T __end__
00000107 t eoa
00000124 t loop
00080000 T _stack
00000108 t start
U _start
00000114 t stop
00000120 T sum
3. 重定位.data到RAM中。
我们可以通过撰写链接脚本,将程序的.data段放置在RAM中。这也是通常嵌入式系统所谓bootloader干的活儿,从Flash中加载启动代码到RAM中而后执行。
例程从RAM中加载两个数值,将两者相加而后将结果写回RAM,两个值和结果都放置在.data部分。
代码:
.data
val1: .4byte 10 @ First number
val2: .4byte 30 @ Second number
result: .4byte 0 @ 4 byte space for result
.text
.align
start:
ldr r0, =val1 @ r0 = &val1
ldr r1, =val2 @ r1 = &val2
ldr r2, [r0] @ r2 = *r0
ldr r3, [r1] @ r3 = *r1
add r4, r2, r3 @ r4 = r2 + r3
ldr r0, =result @ r0 = &result
str r4, [r0] @ *r0 = r4
stop: b stop
链接脚本:
SECTIONS {
. = 0x00000000;
.text : { * (.text); }
. = 0xA0000000;
.data : { * (.data); }
}
从connex的内存布局来看,内存地址为0xa000_0000到0xa400_0000,因而A0000000刚好在内存中。
查看链接后的内存符号地址:
$ arm-none-eabi-as -o sum.o sum.s
$ arm-none-eabi-ld -T sum_link.ld -o sum.elf sum.o
$ arm-none-eabi-nm -n sum.elf
00000000 t start
0000001c t stop
a0000000 d val1
a0000004 d val2
a0000008 d result
这样就完了?NO!!!!!!!!因为:RAM is Volatile! 内存是易变的!
RAM是易失性介质,怎可保证每次加电时就有代码洗干净PP在等着被运行?嵌入式系统里必然有非易失性存储,所有的代码和数据在加电前都需要放在这些非易失性存储介质中,例如在FLASH中。这样在加电后我们就可以利用一段启动代码把代码从FLASH搬到RAM中。
从这个设计思路出发,我们需要程序的.data有两个地址,一个是加载地址,另一个是运行地址。
这就是常说的:LMA(Load Memory Address) VS VMA(Virtual Memory Address)。
上面的代码需要做两个修改:
- 需要在.data中指定load地址和运行地址
- 需要写一段代码用于将数据从FLASH读取到RAM中, 从存储地址到运行地址。
SECTIONS {
. = 0x00000000;
.text : { * (.text); }
etext = .;
. = 0xA0000000;
.data : AT (etext) { * (.data); }
}
etext包含了FLASH中放置完地址后的空白地址,记住这个地址以便在接下来将这个数值传送给.data部分,以便程序将.data部分从FLASH拷贝到RAM中。etext只是符号表中的一个,本身并不占据任何内存(可以回去翻上一篇日志)。
关于AT关键字: 它指定了.data部分的加载地址,一个地址或符号被传递给AT关键字,以便它从该地址拷贝数据。 在这里,我们传递etext符号给AT。
要把代码从FLASH拷贝到RAM中,下列信息需要被提供:
- Flash中数据地址(flash_sdata)
- RAM中数据地址(ram_sdata)
- .data部分大小(data_size)
拷贝代码:
ldr r0, =flash_sdata
ldr r1, =ram_sdata
ldr r2, =data_size
copy:
ldrb r4, [r0], #1
strb r4, [r1], #1
subs r2, r2, #1
bne copy
由此,我们需要在链接脚本中生成这三个数值:
SECTIONS {
. = 0x00000000;
.text : {
* (.text);
}
flash_sdata = .;
. = 0xA0000000;
ram_sdata = .;
.data : AT (flash_sdata) {* (.data); }
ram_edata = .;
data_size = ram_edata - ram_sdata;
}
ram_sdata为ram中数据开始地址,而ram_edata为结束地址,两者相减则为数据块大小。
改变后的带有copy数据的代码:
.data
val1: .4byte 10 @ First number
val2: .4byte 30 @ Second number
result: .space 4 @ 1 byte space for result
.text
;; Copy data to RAM.
start:
ldr r0, =flash_sdata
ldr r1, =ram_sdata
ldr r2, =data_size
copy:
ldrb r4, [r0], #1
strb r4, [r1], #1
subs r2, r2, #1
bne copy
;; Add and store result.
ldr r0, =val1 @ r0 = &val1
ldr r1, =val2 @ r1 = &val2
ldr r2, [r0] @ r2 = *r0
ldr r3, [r1] @ r3 = *r1
add r4, r2, r3 @ r4 = r2 + r3
ldr r0, =result @ r0 = &result
str r4, [r0] @ *r0 = r4
stop: b stop
使用修改过的final_sum_ram.s和link脚本编译,并生成flash.bin后,就可以在qemu-system-arm中验证结果了。
$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
QEMU 1.4.2 monitor - type 'help' for more information
(qemu) info registers
R00=a0000008 R01=a0000004 R02=0000000a R03=0000001e
R04=00000028 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=00000038
PSR=600001d3 -ZC- A svc32
FPSCR: 00000000
(qemu) xp /4dw 0xA0000000
00000000a0000000: 10 30 40 0
R04包含了我们相加后的结果, 为0x28=40, R02/R03则分别为操作数10/30. 而通过显示0xA0000000也显示了内存中的值分别为val1/val2/result的值。
接下来的章节中,我们将讲到C代码入口。
Jul 9, 2013
Technology1. 汇编程序代码格式
汇编代码由一系列的声明所组成,每行一个。每条声明由下列格式组成:
label(标签): instruction @comment(注释)
说明:
- label:
标签的引入使得在内存中查询指令地址变得很方便,标签可以在任意一个内存地址使用,
例如分支指令中就可以用到标签, 标签可以包括字母、数字_和$符号。
- 注释:
注释内容必须在@符号之后
- 指令:
指令可以是ARM指令集或是汇编器指令,汇编器指令是需要传递给汇编器的命令,总是以.开头。
2. 一个简单的汇编语言文件:
.text
start: @ Label, not really required
mov r0, #5 @ Load register r0 with the value 5
mov r1, #4 @ Load register r1 with the value 4
add r2, r1, r0 @ Add r0 and r1 and store in r2
stop: b stop @ Infinite loop to stop execution
上面代码的意思是,把立即数5载入到寄存器r0, 4载入到r1,
以r1和r0相加的结果填充r2.
.text是汇编器指令,用于告知汇编器需要把代码组装到code段,而不是.data段。有关section的概念在后面将被讲到。
3. 编译二进制文件
GNU的汇编器名字叫as, 用下列命令将源文件编译成.o文件
$ arm-none-eabi-as -o add.o add.s
链接器的名字叫ld,用下列命令可以将二进制文件链接成elf文件
$ arm-none-eabi-ld -Ttext=0x0 -o add.elf add.o
-Ttext指明需要分配给label的地址,
这条指令告诉链接器从地址0x0开始装载指令。我们可以用nm来查看具体的地址分配信息。
$ arm-none-eabi-nm add.elf
00008010 T __bss_end__
00008010 T _bss_end__
00008010 T __bss_start
00008010 T __bss_start__
00008010 T __data_start
00008010 T _edata
00008010 T _end
00008010 T __end__
00080000 T _stack
00000000 t start
U _start
0000000c t stop
start和stop之间由0c个字节,因为stop是在start开始后三条指令,
每条指令的长度为4个Byte,3*4=12=0xc
更改链接的参数将得到不同的地址分配。
$ arm-none-eabi-ld -Ttext=0x20000000 -o add.elf add.o
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 20000000
$ arm-none-eabi-nm add.elf
20008010 T __bss_end__
20008010 T _bss_end__
20008010 T __bss_start
20008010 T __bss_start__
20008010 T __data_start
20008010 T _edata
20008010 T _end
20008010 T __end__
00080000 T _stack
20000000 t start
U _start
2000000c t stop
ld得到的文件一般是ELF文件,在有操作系统的时候ELF可以工作的很好,但是我们将在裸机模式下(Bare
Metal)运行此程序, 因此需要将文件类型转化为更简单的binary类型。
GNU编译链的objcopy可以完成不同可执行文件之间的转换:
$ arm-none-eabi-objcopy -O binary add.elf add.bin
$ file add.bin
add.bin: Hitachi SH big-endian COFF
4. 在Qemu中执行二进制文件。
我们将使用connex开发板来模拟运行此程序,它把16MB的Flash放在地址0x0,而通常arm处理器重启时都会执行0x0处的代码。
因而我们需要把add.bin写入到16MB Flash文件的头部。
首先创建一个空的16MB Flash文件
$ dd if=/dev/zero of=flash.bin bs=4k count=4k
4096+0 records in
4096+0 records out
16777216 bytes (17 MB) copied, 0.0153106 s, 1.1 GB/s
而后,使用下列命令将add.bin放到Flash头部
$ dd if=add.bin of=flash.bin bs=4K conv=notrunc
0+1 records in
0+1 records out
16 bytes (16 B) copied, 0.00011154 s, 143 kB/s
add.bin大小刚好为16B, notrunc参数代表no truncated,意思是直接覆盖掉原有内容。
用下列命令执行此改动后的flash文件:
$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
QEMU 1.4.2 monitor - type 'help' for more information
-M connex 指定connex机器, -pflash指定flash.bin代替flash闪存。
-pflash file use ‘file’ as a parallel flash image 并行flash镜像
-serial /dev/null 将connex的串口输出重定向到/dev/null
查看寄存器信息:
(qemu) info registers
R00=00000005 R01=00000004 R02=00000009 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=00000000 R14=00000000 R15=0000000c
PSR=400001d3 -Z-- A svc32
FPSCR: 00000000
R02的值正是计算后的结果4+5=9.
R15=0000000c 猜测应该为指令寄存器,指向stop(0xc)
5. 更多的查看命令:
help List available commands
quit Quits the emulator
xp /fmt addr Physical memory dump from addr
system_reset Reset the system.
(qemu) help xp
xp /fmt addr -- physical memory dump starting at 'addr'
(qemu) xp /4iw 0x0
0x00000000: e3a00005 mov r0, #5 ; 0x5
0x00000004: e3a01004 mov r1, #4 ; 0x4
0x00000008: e0812000 add r2, r1, r0
0x0000000c: eafffffe b 0xc
4: 4 个条目被显示, i表示打印出指令,即内建的反汇编,
w表明条目的大小为32个bit,即一个全字。
Jul 8, 2013
Technology1. 下载和准备镜像文件
$ wget http://downloads.raspberrypi.org/images/raspbian/2013-05-25-wheezy-raspbian/2013-05-25-wheezy-raspbian.zip
$ unzip 2013-05-25-wheezy-raspbian.zip
2. 查看镜像文件分区信息
$ fdisk -l 2013-05-25-wheezy-raspbian.img
Disk 2013-05-25-wheezy-raspbian.img: 1939 MB, 1939865600 bytes, 3788800 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000c7b31
Device Boot Start End Blocks Id
System
2013-05-25-wheezy-raspbian.img1 8192 122879 57344 c W95 FAT32 (LBA)
2013-05-25-wheezy-raspbian.img2 122880 3788799 1832960 83 Linux
从上面可以看到,根文件分区的地址偏移为512*122880=62914560
3. 更改根分区文件里preload信息:
$ sudo mount ./2013-05-25-wheezy-raspbian.img -o offset=62914560 /mnt3
$ sudo vim /mnt3/etc/ld.so.preload
#注释掉这一行,否则在qemu启动完系统后将自动提示配置rpi而造成系统无法登陆
#/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so
$ sudo umount /mnt3
4. 用qemu-system-arm启动raspberrypi镜像
$ qemu-system-arm -kernel kernel-qemu -cpu arm1176 -m 256 -M versatilepb \
-no-reboot -serial stdio -append "root=/dev/sda2 panic=1" -hda \
./2013-05-25-wheezy-raspbian.img
系统将启动到一个root登陆的无需密码的shell中,运行下列命令以修复文件系统:
$ fsck /dev/sda2
$ shutdown -r now
再次启动完毕后的登陆用户名和密码如下,接下来就等同于原机操作了。
Login as pi
Password raspberry
5. ArchLinux on RaspberryPI
基本步骤也是一样,挂在第2块分区后,需要更改etc/fstab做下列修改:
# <file system> <dir> <type> <options> <dump> <pass>
/dev/sda1 /boot vfat defaults 0 0
/dev/sda2 / auto defaults 0 0
之后挂载命令一样。