源代码
输出"hello world",然后退出;
vim a.c
char * str = "hello world\n";
void print(){
asm("movl $13,%%edx \n\t"
"movl %0,%%ecx \n\t"
"movl %0,%%ebx \n\t"
"movl %4,%%eax \n\t"
"int $0x80 \n\t"
::"r"(str):"edx","ecx","ebx");
}
void exit(){
asm("movl $42,%ebx \n\t"
"movl $1,%eax \n\t"
"int $0x80 \n\t");
}
void nomain(){
print();
exit();
}
程序“倒着读”;入口是nomain函数,调用print和exit函数,前者是输出hello world(write),后者是退出(exit)。
print函数:执行linux WRITE system Call;通过0x80中断(应该软中断),写入(CPU)参数(eax是调用,ebx,ecx,edx都是通用寄存器,传递参数);
exit函数:执行EXIT system Call;ebx是退出码(42),比如常见exit(0),退出码就是0;
参考GCC内嵌汇编手册
参考计算机组成原理的实验 X86汇编指令
编译
不使用gcc内置函数编译(再汇编)为汇编文件;(为链接做准备)
gcc -m32 -o a.o -c -fno-builtin a.c
gcc 编译器(内嵌/集成了汇编器)就不说了;
-c 参数;-fno-builtin 不使用内置函数(built-in Function);
-o 输出文件
输入文件
链接
参考编译过程,指定输入输出,指定编译环境(机器)架构,生成可执行文件(链接目标文件);
ld -static -e nomain -m elf_i386 -o a a.o
ld链接器
-static 静态链接
-e 入口函数,默认是main;
-m i386 architecture
总结
查看本次实验生成的ELF可执行文件;
可以看到有很多段/节(包含ELF头,魔数、ABI规范、每个段的起始地址、是否是有效文件):
.text 程序指令
.rodata 数据,保存的是“hello world”
.data (强符号)str全局变量(对比前面源码)
.comment 编译器、系统版本信息(比如使用的是elf_i386而不是i386x86 architecture)
.systab (全局)符号表,非常重要(链接器的核心“成果”)
还有其他段/节…
readelf -e a
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x80480c6
Start of program headers: 52 (bytes into file)
Start of section headers: 4540 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 3
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 8
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 08048094 000094 000042 00 AX 0 0 1
[ 2] .rodata PROGBITS 080480d6 0000d6 00000e 00 A 0 0 1
[ 3] .eh_frame PROGBITS 080480e4 0000e4 00007c 00 A 0 0 4
[ 4] .data PROGBITS 0804a000 001000 000004 00 WA 0 0 4
[ 5] .comment PROGBITS 00000000 001004 00002c 01 MS 0 0 1
[ 6] .symtab SYMTAB 00000000 001030 000100 10 7 9 4
[ 7] .strtab STRTAB 00000000 001130 000049 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 001179 000042 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)
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x00160 0x00160 R E 0x1000
LOAD 0x001000 0x0804a000 0x0804a000 0x00004 0x00004 RW 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
Section to Segment mapping:
Segment Sections...
00 .text .rodata .eh_frame
01 .data
02
查看一些其他信息
目标文件格式分析
nm a
0804a004 D __bss_start
0804a004 D _edata
0804a004 D _end
080480b4 T exit
0804a000 d _GLOBAL_OFFSET_TABLE_
080480c6 T nomain
08048094 T print
0804a000 D str
还有 readelf -S / -s 等等;objdump 、size 等工具,建议都试下,能看到很多有意思的东西。
另外可以丢弃一些编译段(比如.comment,类似程序的注释)
常见编译、链接错误
汇编指令错误比如mov(move)、movl(MOVEL)、
a.c: Assembler messages:
a.c:5: Error: no such instruction:
编译环境差异
比如这里是输入是i386,但是输出是i386:x86,需要强制指定:
-m elf_i386
[ad@localhost ~]$ ld -static -e nomain -o a a.o
ld: i386 architecture of input file `a.o' is incompatible with i386:x86-64 output
比如我本地虚拟机支持的(汇编指令集)有:
Supported emulations: elf_x86_64 elf32_x86_64 elf_i386 elf_iamcu i386linux elf_l1om elf_k1om i386pep i386pe
扩展阅读
1、符号表;
它是静态链接和动态链接的基本结构,同时二者的链接原理其实就是在Symbol Table中找到未在本目标文件中定义但是却在本目标文件中使用的符号的引用地址【也就是符号值】(此外符号还有强符号和弱符号,简单理解初始化了的全局变量是强符号,未初始化的全局变量是弱符号,当然还有其他条件);在工程实践中非常紧俏比如C/C++ 的静态/动态链接库、Golang的plugin 、Java的jar、Lua的热补丁;
2、ELF文件结构;
ELF header一般在/usr/include/elf.h可以看到描述和format header struct;
3、API与ABI;
API是源代码级别的接口(规范),比如POSIX;
而ABI是二进制(中间)文件的"接口"规范;比如链接时的符号定义、寄存器的定义和放置方式;
最常见的例子:
API:不同语言之间、系统间实现了POSIX就可以被广泛接受,反之不行;
ABI:不同编译器的编译过程不一样,导致的不能正常链接比如压栈的顺序就可能不一样(从右向左还是从左向右)、外部符号的存储不一样、寄存器的位置不一样、甚至内置数据类型的大小都不一样(32位和64位就不一样所以不能混用);在C++上尤其明显(连统一的ABI标准都没有),经常看到A公司的库不能被B公司使用使用他们的编译工具不同(:-;