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
。