OpenRISC—OR1200的编译及ELF文件介绍
admin 于 2021年01月03日 发表在 软核OR1200

上一篇《OpenRISC—OR1200编译链环境》介绍了OR1200编译链的安装,本篇接着介绍编译链的使用以及ELF文件格式。

1. 新建源码

在ubuntu下新建一个简单程序,名称为 example.s,输入以下内容:

	.section .text,"ax"
	.org 0x100
.global _start
_start:
	l.andi r0,r0,0
	l.extwz r1,r0
	l.extwz r2,r0
	l.addi r1,r1,0x0A
	l.add r2,r2,r1
	l.nop 0x0001

2. 进行编译

输入指令:

or1k-elf-as example.s -o example.o

目录下生成example.o文件,使用16进制方式查看文件内容,输入指令:

hexdump example.o -C

输出如下内容:

00000000  7f 45 4c 46 01 02 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  00 01 00 5c 00 00 00 01  00 00 00 00 00 00 00 00  |...\............|
00000020  00 00 01 d0 00 00 00 00  00 34 00 00 00 00 00 28  |.........4.....(|
00000030  00 07 00 04 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
......

00.输出example.o内容.png

3. 内容介绍

发现前4字节是:0x7F, 0x45, 0x4c, 0x46 ,说明这是一个ELF文件。

ELF格式说明:

ELF(Executable and linkable Format),可执行链接格式,是UNIX系统实验室作为应用程序二进制接口而开发和发布的。

ELF目标文件又三种类型:

(1)可重定向(Relocatable)文件:保存着代码和适当的数据,用来和其他Object文件一起创建一个可执行文件或共享文件。

(2)可执行(Executable)文件:保存着一个用来执行的程序,该文件指出了如何来创建程序进程映像。

(3)共享目标文件:包含两种使用环境中链接的代码和数据。首先,链接器(ld)可以将它和其余可重定向文件和共享目标文件一起处理,生成另外一个目标文件(比如,编译器和链接器把*.o和*.so一起装成一个*.exe文件)。其次,动态链接器(Dynamic Linker)可将它与某个可执行文件及其他共享目标文件组合在一起创建进程映像(比如,动态加载器把*.exe程序和*.so加载进内存执行)。

4. 可重定位ELF文件格式

ELF文件由4部分组成:ELF header, Program header table, Sections 和 Section header table。

(1)ELF header

内容定义如下:

/*
 * Elf32_Half表示2字节
 * Elf32_Word表示4字节
 */
#define EI_NIDENT   16
typedef struct
{
  unsigned char	e_ident[EI_NIDENT];
  Elf32_Half	e_type;			/* Object file type */
  Elf32_Half	e_machine;		/* Architecture */
  Elf32_Word	e_version;		/* Object file version,Elf32_Word表示4字节 */
  Elf32_Addr	e_entry;		/* Entry point virtual address */
  Elf32_Off	e_phoff;		    /* Program header table file offset */
  Elf32_Off	e_shoff;		    /* Section header table file offset */
  Elf32_Word	e_flags;		/* Processor-specific flags */
  Elf32_Half	e_ehsize;		/* ELF header size in bytes */
  Elf32_Half	e_phentsize;	/* Program header table entry size */
  Elf32_Half	e_phnum;		/* Program header table entry count */
  Elf32_Half	e_shentsize;	/* Section header table entry size */
  Elf32_Half	e_shnum;		/* Section header table entry count */
  Elf32_Half	e_shstrndx;		/* Section header string table index */
} Elf32_Ehdr;

使用指令:

or1k-elf-readelf -h example.o

输出ELF头的信息内容:

ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           OpenRISC 1000
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          464 (bytes into file)
  Flags:                             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:         7
  Section header string table index: 4

01.ELF_header内容.png

也可以自己写一个小程序实现读取bin文件以及相应字段的解析,详细见 get_elf_header.c。

02.get_elf_header.png

(2)查看section内容

借助GNU工具链中的or1k-elf-readelf,可以直接得到section的内容。输入指令:

or1k-elf-readelf -S example.o

输出以下内容:

There are 7 section headers, starting at offset 0x1d0:

Section Headers:
  [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 000118 00  AX  0   0  1
  [ 2] .data             PROGBITS        00000000 00014c 000000 00  WA  0   0  1
  [ 3] .bss              NOBITS          00000000 00014c 000000 00  WA  0   0  1
  [ 4] .shstrtab         STRTAB          00000000 0001a4 00002c 00      0   0  1
  [ 5] .symtab           SYMTAB          00000000 00014c 000050 10      6   4  4
  [ 6] .strtab           STRTAB          00000000 00019c 000008 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

03.elf-S输出section内容.png

总共7个section,其中“.text”这个Section,它的起始地址是0x34,长度为0x118。

(3)查看反汇编结果

使用objdump查看反汇编结果,输入指令:

or1k-elf-objdump -d example.o

输出:

example.o:     file format elf32-or1k


Disassembly of section .text:

00000000 <_start-0x100>:
	...

00000100 <_start>:
 100:	a4 00 00 00 	l.andi r0,r0,0x0
 104:	e0 20 00 4d 	l.extwz r1,r0
 108:	e0 40 00 4d 	l.extwz r2,r0
 10c:	9c 21 00 0a 	l.addi r1,r1,10
 110:	e0 42 08 00 	l.add r2,r2,r1
 114:	15 00 00 01 	l.nop 0x1

04.objdump查看反汇编结果.png

显示输出分为三栏,左边是指令执行的地址,在程序中我们的第一条指令是从0x100开始的;中间一栏是对应的二进制代码,右边一栏是对应的汇编指令。

在这0x118字节中,前0x100字节都是0x00;接下来的24字节,对比内容可以发现section .text最后24字节正好是6条汇编指令。

5. 可执行ELF文件

通过以上操作,得到一个可重定位的ELF文件,但这个文件还不能执行,需要链接转换为可执行文件。

(1)新建一个链接描述脚本

MEMORY
{
	ram : ORIGIN = 0x00000000, LENGTH = 0x00005000
}

SECTIONS
{
	.text :
	{
		*(.text)
	} > ram

	.data :
	{
		*(.data)
	} > ram

	.bss : 
	{
		*(.bss)
	} > ram
}

ENTRY (_start)

(2)使用链接器

or1k-elf-ld -T ram.ld example.o -o example.or

05.链接生成example.or.png

(3)可执行ELF的header内容

使用ELF工具分析可执行ELF程序的 header 内容。输入指令:

or1k-elf-readelf -h example.or

输出内容:

ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           OpenRISC 1000
  Version:                           0x1
  Entry point address:               0x100
  Start of program headers:          52 (bytes into file)
  Start of section headers:          8564 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         1
  Size of section headers:           40 (bytes)
  Number of section headers:         5
  Section header string table index: 2

06.可执行elf_header信息.png

(4)对比两个ELF文件

对比 example.oexample.or,发现example.or中多了一个 program headers。

Program header也可以通过结构体来描述:

typedef struct
{
  Elf32_Word    p_type;         /* Segment type */
  Elf32_Off p_offset;           /* Segment file offset */
  Elf32_Addr    p_vaddr;        /* Segment virtual address */
  Elf32_Addr    p_paddr;        /* Segment physical address */
  Elf32_Word    p_filesz;       /* Segment size in file */
  Elf32_Word    p_memsz;        /* Segment size in memory */
  Elf32_Word    p_flags;        /* Segment flags */
  Elf32_Word    p_align;        /* Segment alignment */
} Elf32_Phdr;

(5)分析program headers信息

使用ELF工具得到 program headers 的信息,指令如下:

or1k-elf-readelf -l example.or

输出:

Elf file type is EXEC (Executable file)
Entry point 0x100
There are 1 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x002000 0x00000000 0x00000000 0x00118 0x00118 R E 0x2000

 Section to Segment mapping:
  Segment Sections...
   00     .text

06_1.使用-l查看program header信息.png

说明:

该Program header 表示将 example.or 的 0x2000 开始的 0x118 字节放置在内存的 0x00 处。

打开 example.or 可以发现 0x2000 开始的总共 0x118 字节的内容与 example.o 中的 Section .text 的内容一样。

example.or从0x2000开始的总共0x118字节的内容如下:

07.example.or_2000开始内容.png

example.o的Section .text内容:

08.example.o_相关section内容.png

所以,当这个Program Section加载入内存后,会使得内存的0x100处存放的就是第一条指令,而example.or的入口地址正是0x100。

08_01.start起始地址.png

6. 模拟运行程序

使用or1ksim模拟器运行程序,输入指令:

or1k-sim -t example.or -m1M > example.trace

在目录下生成 example.trace 文件,内容如下:

Seeding random generator with value 0x1f5aaf19
Or1ksim 2012-04-27
Building automata... done, num uncovered: 0/215.
Parsing operands data... done.
Resetting PIC.
loadcode: filename example.or  startaddr=00000000  virtphy_transl=00000000
Not COFF file format
ELF type: 0x0002
ELF machine: 0x005c
ELF version: 0x00000001
ELF sec = 5
Section: .text, vaddr: 0x00000000, paddr: 0x0 offset: 0x00002000, size: 0x00000118
S 00000100: a4000000 l.andi  r0,r0,0         r0         = 00000000  flag: 0
S 00000104: e020004d l.extwz r1,r0           r1         = 00000000  flag: 0
S 00000108: e040004d l.extwz r2,r0           r2         = 00000000  flag: 0
S 0000010c: 9c21000a l.addi  r1,r1,0xa       r1         = 0000000a  flag: 0
S 00000110: e0420800 l.add   r2,r2,r1        r2         = 0000000a  flag: 0
exit(0)
@reset : cycles 0, insn #0
@exit  : cycles 5, insn #6
 diff  : cycles 5, insn #6

08.example_trace内容.png

其中包含5条指令,以第一条为例:

S 00000100: a4000000 l.andi  r0,r0,0         r0         = 00000000  flag: 0
S:                    表示处于特权模式
PC值:                0x00000100
指令的二进制数编码:  0xa4000000
指令的汇编代码:      l.andi  r0,r0,0
目的地址:            r0
目的地址的新值:      0x00000000
flag的值:            0x00

每一条指令PC加4,表示取下一条指令,程序执行最后r1为0xa,r2位0xa,满足预期。

附:相关示例源码src.zip

参考:

https://tanglinux.blog.csdn.net/article/details/102604380

下一篇:《OpenRISC—OR1200最小软核系统仿真》

注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。 标签:OpenRISC,ELF文件,or1ksim