循序渐进学习开发RISCV-OS

exercises

ch3 编译与链接

练习3-1

使⽤ gcc 编译代码并使⽤ binutils ⼯具对⽣

成的⽬标文件和可执⾏文件(ELF 格式)进⾏分析。具体要求如下:

  • 编写⼀个简单的打印 “hello world!” 的程序源文件:hello.c
#include <stdio.h>

int main(void)
{
    printf("hello world!\n");
    return 0;
}
  • 对源文件进⾏本地编译,⽣成针对⽀持 x86_64 指令集架构处理器的⽬标文件 hello.o
 gcc hello.c -c -o hello.o

补充gcc常用选项:

gcc [options] [filename]

常用选项 含义
-E 只做预处理
-c 只编译不连接,生成目标文件".o"
-S 生成汇编代码
-o file 把输出生成到由file指定文件名的文件中
-g 在输出的文件中加入支持调试的信息
-v 显示输出详细的命令执行过程信息
  • 查看 hello.o 的文件的文件头信息。
readelf -h hell.o

信息如下:

ELF 头:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              REL (可重定位文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:               0x0
  程序头起点:          0 (bytes into file)
  Start of section headers:          792 (bytes into file)
  标志:             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         14
  Section header string table index: 13
  • 查看 hello.o 的 Section header table。
readelf -SW hello.c

信息如下:

 There are 14 section headers, starting at offset 0x318:
 
  节头:
    [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
    [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
    [ 1] .text             PROGBITS        0000000000000000 000040 00001b 00  AX  0   0  1
    [ 2] .rela.text        RELA            0000000000000000 000258 000030 18   I 11   1  8
    [ 3] .data             PROGBITS        0000000000000000 00005b 000000 00  WA  0   0  1
    [ 4] .bss              NOBITS          0000000000000000 00005b 000000 00  WA  0   0  1
    [ 5] .rodata           PROGBITS        0000000000000000 00005b 00000d 00   A  0   0  1
    [ 6] .comment          PROGBITS        0000000000000000 000068 00002b 01  MS  0   0  1
    [ 7] .note.GNU-stack   PROGBITS        0000000000000000 000093 000000 00      0   0  1
    [ 8] .note.gnu.property NOTE            0000000000000000 000098 000020 00   A  0   0  8
    [ 9] .eh_frame         PROGBITS        0000000000000000 0000b8 000038 00   A  0   0  8
    [10] .rela.eh_frame    RELA            0000000000000000 000288 000018 18   I 11   9  8
    [11] .symtab           SYMTAB          0000000000000000 0000f0 000138 18     12  10  8
    [12] .strtab           STRTAB          0000000000000000 000228 000029 00      0   0  1
    [13] .shstrtab         STRTAB          0000000000000000 0002a0 000074 00      0   0  1
  Key to Flags:
    W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
    L (link order), O (extra OS processing required), G (group), T (TLS),
    C (compressed), x (unknown), o (OS specific), E (exclude),
    l (large), p (processor specific)
  • hello.o 反汇编,并查看 hello.c 的 C 程序源码和机器指令的对应关系。
objdump -S hello.o

反汇编:

hello.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
#include <stdio.h>

int main(void)
{
   0:   f3 0f 1e fa             endbr64 
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
    printf("hello world!\n");
   8:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # f <main+0xf>
   f:   e8 00 00 00 00          callq  14 <main+0x14>
    return 0;
  14:   b8 00 00 00 00          mov    $0x0,%eax
  19:   5d                      pop    %rbp
  1a:   c3                      retq   

练习3-2

如下例⼦ C 语⾔代码:

#include <stdio.h>
int global_init = 0x11111111;
const int global_const = 0x22222222;
void main()
{
    static int static_var = 0x33333333;
    static int static_var_uninit;
    int auto_var = 0x44444444;
    printf("hello world!\n");
    return;
}

请问编译为 .o 文件后,global_init, global_const, static_var, static_var_uninit, auto_var 这些变 量分别存放在那些 section ⾥,“hello world!\n” 这个字符串⼜在哪⾥?并尝试⽤⼯具查看并验证你的猜测。

  • global_init -------> .data

  • global_const -------> .data .rodata

  • static_var -------> .data

  • static_var_uninit -------> .bss

  • auto_var -------> 栈区

    使用objdump -s -x -d test.o可以得到以下信息

    SYMBOL TABLE:
    0000000000000000 l    df *ABS*  0000000000000000 test.c
    0000000000000000 l    d  .text  0000000000000000 .text
    0000000000000000 l    d  .data  0000000000000000 .data
    0000000000000000 l    d  .bss   0000000000000000 .bss
    0000000000000000 l    d  .rodata        0000000000000000 .rodata
    0000000000000000 l     O .bss   0000000000000004 static_var_uninit.2318
    0000000000000004 l     O .data  0000000000000004 static_var.2317
    0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
    0000000000000000 l    d  .note.gnu.property     0000000000000000 .note.gnu.property
    0000000000000000 l    d  .eh_frame      0000000000000000 .eh_frame
    0000000000000000 l    d  .comment       0000000000000000 .comment
    0000000000000000 g     O .data  0000000000000004 global_init
    0000000000000000 g     O .rodata        0000000000000004 global_const
    0000000000000000 g     F .text  0000000000000022 main
    0000000000000000         *UND*  0000000000000000 _GLOBAL_OFFSET_TABLE_
    0000000000000000         *UND*  0000000000000000 puts
    

ch4 嵌入式开发介绍

练习4-1

熟悉交叉编译概念,使⽤ riscv gcc 编译代码并使⽤ binutils ⼯具对⽣成的⽬标文件和可执⾏文件(ELF 格式) 进⾏分析。具体要求如下:

  • 编写⼀个简单的打印"hello world!"的程序源文件hello.c
#include <stdio.h>

int main(void)
{
    printf("hello world!\n");
    return 0;
}

  • 对源文件进⾏编译,⽣成针对⽀持 rv32ima 指令集架构处理器的⽬标文件hello.o
riscv64-unknown-elf-gcc -march=rv32ima -mabi=ilp32 -c hello.c
  • 查看hello.o的文件的文件头信息。
readelf -h hello.o

信息如下:

ELF 头:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF32
  数据:                              2 补码,小端序 (little endian)
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              REL (可重定位文件)
  系统架构:                          RISC-V
  版本:                              0x1
  入口点地址:               0x0
  程序头起点:          0 (bytes into file)
  Start of section headers:          556 (bytes into file)
  标志:             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         11
  Section header string table index: 10
  • 查看 hello.o 的 Section header table。
readelf -S hello.o

信息如下:

There are 11 section headers, starting at offset 0x22c:

节头:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000038 00  AX  0   0  4
  [ 2] .rela.text        RELA            00000000 000190 000048 0c   I  8   1  4
  [ 3] .data             PROGBITS        00000000 00006c 000000 00  WA  0   0  1
  [ 4] .bss              NOBITS          00000000 00006c 000000 00  WA  0   0  1
  [ 5] .rodata           PROGBITS        00000000 00006c 00000d 00   A  0   0  4
  [ 6] .comment          PROGBITS        00000000 000079 000029 01  MS  0   0  1
  [ 7] .riscv.attributes RISCV_ATTRIBUTE 00000000 0000a2 000026 00      0   0  1
  [ 8] .symtab           SYMTAB          00000000 0000c8 0000b0 10      9   9  4
  [ 9] .strtab           STRTAB          00000000 000178 000018 00      0   0  1
  [10] .shstrtab         STRTAB          00000000 0001d8 000054 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)
  • hello.o 反汇编,并查看 hello.c 的 C 程序源码和机器指令的对应关系。

    **注意:**直接使用objdump不行,因为不支持riscv架构,所以使用以下命令

    riscv64-unknown-elf-objdump -S hello.o
    

    信息如下:

    hello.o:     文件格式 elf32-littleriscv
    
    
    Disassembly of section .text:
    
    00000000 <main>:
       0:   ff010113                addi    sp,sp,-16
       4:   00112623                sw      ra,12(sp)
       8:   00812423                sw      s0,8(sp)
       c:   01010413                addi    s0,sp,16
      10:   000007b7                lui     a5,0x0
      14:   00078513                mv      a0,a5
      18:   00000097                auipc   ra,0x0
      1c:   000080e7                jalr    ra # 18 <main+0x18>
      20:   00000793                li      a5,0
      24:   00078513                mv      a0,a5
      28:   00c12083                lw      ra,12(sp)
      2c:   00812403                lw      s0,8(sp)
      30:   01010113                addi    sp,sp,16
      34:   00008067                ret
    

练习4-2

基于 练习 4-1 继续熟悉 qemu/gdb 等⼯具的使⽤,具体要求如下:

  • hello.c 编译成可调式版本的可执⾏程序 a.out
riscv64-unknown-elf-gcc -g -march=rv32ima -mabi=ilp32  hello.c
  • 先执⾏ qemu-riscv32 运⾏ a.out
qemu-riscv32 ./a.out

可以看到控制台输出以下内容:

hello world!
  • 使⽤ qemu-riscv32 和 gdb 调试 a.out
上一篇:hdu 3172 Virtual Friends (并查集)


下一篇:Docker常用操作