0. 前言
- 目标:实现一个小型的 CRT 运行库
- 本质: 利用系统提供的api接口, 实现一个通用的 CRT 函数接口, 使得 C 语言程序可以自由的运行在各个不同的系统上
- 项目工程地址: https://github.com/zhyh2010/miniCRT
1. 遇到的一些问题
windows.h 不包含路径集
解决方法:
http://www.unjeep.com/q/869954156.htm先执行C:/Program Files/Microsoft Visual Studio 9.0/Common7/Tools/vsvars32.bat
再cl task.cpp命令行使用
参考文章: http://www.360doc.com/content/10/0228/10/111369_17077729.shtml
@REM @Author: anchen
@REM @Date: 2016-12-21 16:38:42
@REM @Last Modified by: anchen
@REM Modified time: 2016-12-22 15:57:18
@echo off
if "%1%"=="genlib" goto genlib
if "%1%"=="test" goto test
if "%1%"=="clean" goto clean
:help
echo "Usage: make [genlib | test]"
goto end
:genlib
call vsvars32
cl /c /DWIN32 /GS- entry.c malloc.c printf.c stdio.c string.c
lib entry.obj malloc.obj printf.obj stdio.obj string.obj /OUT:minicrt.lib
goto end
:test
call vsvars32
cl /c /DWIN32 test.c
link test.obj minicrt.lib kernel32.lib /NODEFAULTLIB /entry:mini_crt_entry
test arg1 arg2 123
goto end
:clean
echo "cleaning project"
for %%i in (*.obj, *.lib, *.txt) do del /f /q %%i
goto end
:end
2. 实现
我们的这个CRT 本质上只是实现一些非常简单的功能, 命令行参数解析与设置, 字符串打印, 堆内存分配,文件读写 etc
2.1 入口函数
- 程序运行的起点并不是main, 而是由运行库提供的入口函数, 主要负责:
- 准备程序运行环境以及初始化运行库
- 调用 main 函数执行程序主体
- 清理程序运行后的各种资源
- code
#include "minicrt.h"
#ifdef WIN32
#include <Windows.h>
#endif
extern int main(int argc, char * argv[]);
void exit(int);
static void crt_fatal_error(const char * msg){
// printf("fatal error: %s", msg);
exit(1);
}
void mini_crt_entry(void){
int ret;
#ifdef WIN32
int flag = 0;
int argc = 0;
char * argv[16];
char * cl = GetCommandLineA();
printf("origin commandline: %s\n", cl);
// parse the parameters
argv[0] = cl;
argc++;
while (*cl){
if (*cl == '\"'){
if (flag == 0)
flag = 1;
else
flag = 0;
}
else if (*cl == ' ' && flag == 0){
if (*(cl + 1)){
argv[argc] = cl + 1;
argc++;
//printf("%d %s\n", argc - 1, argv[argc - 1]);
}
*cl = '\0';
}
cl++;
}
#else
int argc;
char ** argv;
char * ebp_reg = 0;
// ebp_reg = %ebp
asm("movl %%ebp,%0 n":"=r"(ebp_reg));
argc = *(int *)(ebp_reg + 4);
argv = (char **)(ebp_reg + 8);
#endif
if (!mini_crt_heap_init())
crt_fatal_error("heap initialize failed");
if (!mini_crt_io_init())
crt_fatal_error("IO initialize failed");
ret = main(argc, argv);
exit(ret);
}
void exit(int exitCode){
// min_crt_call_exit_routine();
#ifdef WIN32
ExitProcess(exitCode);
#else
asm("movl %0, %%ebx \n\t"
"movl $1, %%eax \n\t"
"int $0x80 \n\t"
"hlt \n\t"::"m"(exitCode));
#endif
}
2.1 堆的实现
- 堆的实现:
- 使用空闲链表算法作为基础
- 堆大小固定为 32MB
- 需要特别注意,* 堆释放的时候, 如果有相连的空闲内存, 应该进行内存的合并处理操作 *
- 相关代码:
#include "minicrt.h"
typedef struct _heap_header{
enum {
HEAP_BLOCK_FREE = 0xABABABAB,
HEAP_BLOCK_USED = 0xCDCDCDCD,
} type;
unsigned size;
struct _heap_header * next;
struct _heap_header * prev;
} heap_header;
#define ADDR_ADD(a, o) (((char *)(a)) + o)
#define HEADER_SIZE (sizeof(heap_header))
static heap_header * list_head = NULL;
void free(void * ptr){
heap_header * header = (heap_header *)ADDR_ADD(ptr, -HEADER_SIZE);
if (header->type != HEAP_BLOCK_USED)
return;
header->type = HEAP_BLOCK_FREE;
if (header->prev != NULL && header->prev->type == HEAP_BLOCK_FREE){
// merge
header->prev->next = header->next;
if (header->next != NULL)
header->next->prev = header->prev;
header->prev->size += header->size;
header = header->prev;
}
if (header->next != NULL && header->next->type == HEAP_BLOCK_FREE){
// merge
header->size += header->next->size;
header->next = header->next->next;
}
}
void * malloc(unsigned size){
heap_header * header;
if (size == 0)
return NULL;
header = list_head;
while (header != 0){
if (header->type == HEAP_BLOCK_USED){
header = header->next;
continue;
}
if (header->size > size + HEADER_SIZE &&
header->size <= size + HEADER_SIZE * 2){
header->type = HEAP_BLOCK_USED;
}
if (header->size > size + HEADER_SIZE * 2){
// split
heap_header * next = (heap_header *)ADDR_ADD(header, size + HEADER_SIZE);
next->prev = header;
next->next = header->next;
next->type = HEAP_BLOCK_FREE;
next->size = header->size - (size - HEADER_SIZE);
header->next = next;
header->size = size + HEADER_SIZE;
header->type = HEAP_BLOCK_USED;
return ADDR_ADD(header, HEADER_SIZE);
}
header = header->next;
}
return NULL;
}
#ifndef WIN32
// Linux brk system call
static int brk(void * end_data_segment){
int ret = 0;
// brk system call number : 45
// in /usr/include/asm-i386/unistd.h:
// #define __NR_brk 45
asm("movl $45, %%eax \n\t"
"movl %1, %%ebx \n\t"
"int $0x80 \n\t"
"movl %%eax, %0 \n\t"
: "=r"(ret): "m"(end_data_segment));
}
#endif
#ifdef WIN32
#include <windows.h>
#endif
int mini_crt_heap_init(){
void * base = NULL;
heap_header * header = NULL;
// 32 MB heap size
unsigned heap_size = 1024 * 1024 * 32;
#ifdef WIN32
base = VirtualAlloc(0, heap_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (base == NULL)
return 0;
#else
base = (void *)brk(0);
void * end = ADDR_ADD(base, heap_size);
end = (void *)brk(end);
if (!end)
return 0;
#endif
header = (heap_header *) base;
header->size = heap_size;
header->type = HEAP_BLOCK_FREE;
header->next = NULL;
header->prev = NULL;
list_head = header;
return 1;
}
2.3 格式化字符串操作
- 实现一个printf 操作, 本质上就是变长参数的函数的处理
- code
#include "minicrt.h"
int fputc(int c, FILE * stream){
if (fwrite(&c, 1, 1, stream) != 1)
return EOF;
else
return c;
}
int fputs(const char * str, FILE * stream){
int len = strlen(str);
if (fwrite(str, 1, len, stream) != len)
return EOF;
else
return len;
}
#ifndef WIN32
#define va_list char *
#define va_start(ap, arg) (ap = (va_list)&arg + sizeof(arg))
#define va_arg(ap, t) (*(t*)((ap += sizeof(t)) - sizeof(t)))
#define va_end(ap) (ap = (va_list)0)
#else
#include <windows.h>
#endif
int vfprintf(FILE * stream, const char * format, va_list arglist){
int translating = 0;
int ret = 0;
const char * p = 0;
for (p = format; *p != '\0'; ++p){
switch (*p){
case '%':
if (!translating)
translating = 1;
else{
if (fputc('%', stream) < 0)
return EOF;
++ret;
translating = 0;
}
break;
case 'd':
if (translating){
char buf[16];
translating = 0;
itoa(va_arg(arglist, int), buf, 10);
if (fputs(buf, stream) < 0)
return EOF;
ret += strlen(buf);
}
else if (fputc('d', stream) < 0)
return EOF;
else
++ret;
break;
case 's':
if (translating){
const char * str = va_arg(arglist, const char *);
translating = 0;
if (fputs(str, stream) < 0)
return EOF;
ret += strlen(str);
}
else if (fputc('s', stream) < 0)
return EOF;
else
++ret;
break;
default:
if (translating)
translating = 0;
if (fputc(*p, stream) < 0)
return EOF;
else
++ret;
break;
}
}
return ret;
}
int printf(const char * format, ...){
va_list(arglist);
va_start(arglist, format);
return vfprintf(stdout, format, arglist);
}
int fprintf(FILE * stream, const char * format, ...){
va_list(arglist);
va_start(arglist, format);
return vfprintf(stream, format, arglist);
}