Orange'S:一个操作系统的实现

内核雏形

Posted by BINBIN Blog on November 3, 2019

在Linux下用汇编写程序

内核还是离不开汇编的!

代码示例:


1 	; 编译链接方法
2 	; (ld 的‘-s’选项意为“strip all”)
3 	;
4 	; $ nasm -f elf hello.asm -o hello.o
5 	; $ ld -s hello.o -o hello
6 	; $ ./hello
7 	; Hello, world!
8 	; $
9 
10	[section .data]	; 数据在此
11
12	strHello	db	"Hello, world!", 0Ah
13	STRLEN		equ	$ - strHello
14
15	[section .text]	; 代码在此
16
17	global _start	; 我们必须导出 _start 这个入口,以便让链接器识别
18
19	_start:
20		mov	edx, STRLEN
21		mov	ecx, strHello
22		mov	ebx, 1
23		mov	eax, 4		; sys_write
24		int	0x80		; 系统调用
25		mov	ebx, 0
26		mov	eax, 1		; sys_exit
27		int	0x80		; 系统调用
28

编译链接(每一步都用ls命令查看文件的增加情况)


-> ls
hello.asm
-> sudo nasm -f elf hello.asm -o hello.o
->ls
hello.asm hello.o
->ld -s hello.o -o hello
 

报错信息:

ld: i386 architecture of input file `hello.o' is incompatible with i386:x86-64 output
解决方法:
-> ld -m elf
  ld: unrecognised emulation mode: elf
                        Supported emulations: elf_x86_64 elf32_x86_64 elf_i386 i386linux elf_l1om elf_k1om i386pep i386pe

根据输出选择对应的架构选项,将链接命令修改为:ld -m elf_i386 -s hello.o -o hello

 

-ls
hello hello.asm hello.o

生成了elf格式文件, 链接选项“-s” 意为strip,可以去掉符号表的内容!

执行 ` ./hello ` hello,world!

汇编和C互相调用

如下的小例子,源代码 foo.asm和bar.c

程序的入口_start在foo.asm中,一开始就是启动 程序将会调用bar.c 中的函数choose(),choose()将会比较传入的两个参数,根据比较结果的不同打印不同的字符串。打印字符串的工作是由foo.asm找那个的函数myprint()来完成!

这样能熟悉点,汇编代码和c语言互相调用!

代码如下:

foo.asm


1 	; 编译链接方法
2 	; (ld 的‘-s’选项意为“strip all”)
3 	;
4 	; $ nasm -f elf foo.asm -o foo.o
5 	; $ gcc -c bar.c -o bar.o
6 	; $ ld -s hello.o bar.o -o foobar
7 	; $ ./foobar
8 	; the 2nd one
9 	; $
10
11	extern choose	; int choose(int a, int b);
12
13	[section .data]	; 数据在此
14
15	num1st		dd	3
16	num2nd		dd	4
17
18	[section .text]	; 代码在此
19
20	global _start	; 我们必须导出 _start 这个入口,以便让链接器识别
21	global myprint	; 导出这个函数为了让 bar.c 使用
22
23	_start:
24		push	dword [num2nd]	; `.
25		push	dword [num1st]	;  |
26		call	choose		;  | choose(num1st, num2nd);
27		add	esp, 8		; /
28
29		mov	ebx, 0
30		mov	eax, 1		; sys_exit
31		int	0x80		; 系统调用
32
33	; void myprint(char* msg, int len)
34	myprint:
35		mov	edx, [esp + 8]	; len
36		mov	ecx, [esp + 4]	; msg
37		mov	ebx, 1
38		mov	eax, 4		; sys_write
39		int	0x80		; 系统调用
40		ret
41		
42

  1. bar.c中要用函数myprint()。所以要用关键字global导出来!
  2. 用到本文件外定义的函数choose(),所以要用关键字extern声明!
  3. 不管myprint还是choose ,遵循的都是C调用约定(C Calling Convention),后面参数先入栈,并由调用者Caller 清理堆栈!

1 	void myprint(char* msg, int len);
2 
3 	int choose(int a, int b)
4 	{
5 		if(a >= b){
6 			myprint("the 1st one\n", 13);
7 		}
8 		else{
9 			myprint("the 2nd one\n", 13);
10		}
11
12		return 0;
13	}
14

所以主要就是global 和 extern 关键字!

编译实践

编译链接指令

nasm -f elf foo.s -o foo.o

gcc -c bar.c -o bar.o

ld -s -o foobar bar.o foo.o

汇编语言用nasm编写并用nasm编译器编译,而C语言用的是gcc编译,这些都没有问题,但是在链接的时候出错了,提示如下:


ld: i386 architecture of input file 'foo.o' is incompatible with i386:x86-64 output

意思就是nasm 编译产生的是32位的目标代码,gcc 在64位平台上默认产生的是64位的目标代码,这两者在链接的时候出错,gcc在64位平台上默认以64位的方式链接。 这样在解决的时候就会有两种解决方案:

  1. 让gcc 产生32位的代码,并在链接的时候以32位的方式进行链接 在这种情况下只需要修改编译和链接指令即可,具体如下: 32位的编译链接指令

nasm -f elf foo.s -o foo.o

gcc -m32 -c bar.c -o bar.o

ld -m elf_i386 -s -o foobar foo.o bar.o

具体的-m32 和 -m elf_i386 请自行查阅gcc (man gcc)

如果你是高版本的gcc(可能是由于更新内核造成的),可能简单的使用-m32 的时候会提示以下错误(使用别人的历程,自己未曾遇到):

In file included from /usr/include/stdio.h:28:0, from test.c:1: /usr/include/features.h:323:26: fatal error: bits/predefs.h: No such file or directory compilation terminated. 这应该是缺少构建32 位可执行程序缺少的包,使用以下指令安装: sudo apt-get install libc6-dev-i386 此时应该就没有什么问题了。

使用的makefile如下!

$@ 代表目标 $^ 代表所有的依赖对象 $< 代表第一个依赖对象


#######################
# Makefile for foobar #
#######################

# Programs, flags, etc.
ASM		= nasm
CC		= gcc
LD		= ld
ASMFLAGS	= -f elf
CFLAGS		= -c
LDFLAGS		= -s

# This Program
BIN		= foobar
OBJS		= foo.o bar.o

# All Phony Targets
.PHONY : everything final image clean realclean disasm all buildimg

# Default starting position
everything : $(BIN)

all : realclean everything

final : all clean

clean :
	rm -f $(OBJS)

realclean :
	rm -f $(OBJS) $(BIN)

$(BIN) : $(OBJS)
	$(LD) -m elf_i386 $(LDFLAGS) -o $(BIN) $(OBJS)

foo.o : foo.asm
	$(ASM) $(ASMFLAGS) -o $@ $<

bar.o: bar.c
	$(CC) -m32 -c $(CFLAGS) -o $@ $<