第27天 LDT与库
第27天 LDT與庫
2020.5.7
1. 先來修復bug(harib24a)
-
在harib23j中有一個bug,就是用ncst運行的應用程序,使用Shift+F1和點擊“X”按鈕都無法關(guān)閉窗口。
- 其實這個bug在很久之前就有了,只是一直沒有發(fā)現(xiàn)而已。
-
修改HariMain:
void HariMain(void) {……for (;;) {……if (fifo32_status(&fifo) == 0) {……} else {……if (256 <= i && i <= 511) { /* 鍵盤數(shù)據(jù) */……if (i == 256 + 0x3b && key_shift != 0 && key_win != 0) { /* Shift+F1 */task = key_win->task;if (task != 0 && task->tss.ss0 != 0) {cons_putstr0(task->cons, "\nBreak(key) :\n");io_cli(); task->tss.eax = (int) &(task->tss.esp0);task->tss.eip = (int) asm_end_app;io_sti();task_run(task, -1, 0); /* 為了確實執(zhí)行結(jié)束處理,如果處于休眠狀態(tài)則喚醒 */}}……} else if (512 <= i && i <= 767) { /* 鼠標數(shù)據(jù) */if (mouse_decode(&mdec, i - 512) != 0) {……if ((mdec.btn & 0x01) != 0) {if (mmx < 0) {for (j = shtctl->top - 1; j > 0; j--) {……if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {……if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {/* “X”按鈕 */if ((sht->flags & 0x10) != 0) { task = sht->task;cons_putstr0(task->cons, "\nBreak(mouse) :\n");io_cli(); task->tss.eax = (int) &(task->tss.esp0);task->tss.eip = (int) asm_end_app;io_sti();task_run(task, -1, 0); /* 為了確實執(zhí)行結(jié)束處理,如果處于休眠狀態(tài)則喚醒 */} else { ……}}break;}}}} else {……}} else {……}}} else if (768 <= i && i <= 1023) { /* 命令行窗口結(jié)束處理 */close_console(shtctl->sheets0 + (i - 768));} else if (1024 <= i && i <= 2023) {close_constask(taskctl->tasks0 + (i - 1024));}}} }- 添加的兩行代碼都是task_run(task, -1, 0);,-1和0代表不改變?nèi)蝿盏膌evel和priority。這句代碼的功能是將休眠的任務喚醒。
- 盡管在TSS中改寫了EIP和EAX以便執(zhí)行結(jié)束任務的處理,但是如果任務一直處于休眠狀態(tài)那么結(jié)束任務的處理就永遠得不到執(zhí)行。因此,需要喚醒任務,使得結(jié)束處理確實能夠被執(zhí)行。
- 之前的代碼之所以沒有這兩句代碼,強制結(jié)束應用程序也沒出什么問題的原因是:
- 命令行窗口會觸發(fā)0.5s光標定時器中斷(在命令行窗口中,不顯示光標時也會每0.5s產(chǎn)生一次定時器中斷),當產(chǎn)生定時器中斷時,定時器就會向FIFO緩沖區(qū)寫入數(shù)據(jù),于是任務就被自動喚醒了。因此,即便看上去可以強制執(zhí)行,但是其實距離應用程序真正結(jié)束還是會產(chǎn)生最大0.5s的延遲。因此,這次修改也會讓Shift+F1和“X”按鈕的速度得到提升。
- 但是ncst命令的光標定時器被刪掉了,因此不會產(chǎn)生定時器中斷,如果任務一直在休眠,那么不主動喚醒,就會一直處于休眠狀態(tài)。
- 添加的兩行代碼都是task_run(task, -1, 0);,-1和0代表不改變?nèi)蝿盏膌evel和priority。這句代碼的功能是將休眠的任務喚醒。
-
make后用VMware運行:
- 上圖先使用ncst運行color2.hrb應用程序,然后點擊“X”按鈕關(guān)閉了color2.hrb。
2. 應用程序運行時關(guān)閉命令行窗口(harib24b)
-
現(xiàn)在解決應用程序運行時無法關(guān)閉對應的命令行窗口的問題。
- 在之前(先不考慮ncst),在應用程序退出前面試法務關(guān)閉用來啟動這個程序的命令行窗口的。
-
修改HariMain:
void HariMain(void) {……struct SHEET *sht = 0, *key_win, *sht2; /*添加了sht2*/……for (;;) {……if (fifo32_status(&fifo) == 0) {……} else {……if (256 <= i && i <= 511) { /* 鍵盤數(shù)據(jù) */……} else if (512 <= i && i <= 767) { /* 鼠標數(shù)據(jù) */if (mouse_decode(&mdec, i - 512) != 0) {……if ((mdec.btn & 0x01) != 0) {if (mmx < 0) {for (j = shtctl->top - 1; j > 0; j--) {……if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {……if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {/* “X”按鈕 */if ((sht->flags & 0x10) != 0) { /* 是否是應用程序窗口 */……} else { /* 命令行窗口 */task = sht->task;sheet_updown(sht, -1); /* 暫且隱藏該圖層 */keywin_off(key_win);key_win = shtctl->sheets[shtctl->top - 1];keywin_on(key_win);io_cli();fifo32_put(&task->fifo, 4);io_sti();}}break;}}}} else {……}} else {……}}} else if (768 <= i && i <= 1023) { /* 命令行結(jié)束處理 */close_console(shtctl->sheets0 + (i - 768));} else if (1024 <= i && i <= 2023) {close_constask(taskctl->tasks0 + (i - 1024));} else if (2024 <= i && i <= 2279) { /* 只關(guān)閉命令行窗口 */sht2 = shtctl->sheets0 + (i - 2024);memman_free_4k(memman, (int) sht2->buf, 256 * 165);sheet_free(sht2);}}} }- 只修改了bootpack的兩處:
- 前一處:讓OS在用戶按下“X”按鈕時暫時將命令行窗口隱藏起來。
- 這是因為關(guān)閉有的應用程序的命令行窗口需要一定的時間,如果點了按鈕較長時間窗口還沒有關(guān)閉,用戶的體驗感會極差,先隱藏給用戶已經(jīng)關(guān)閉窗口的信息,用戶友好性極高。
- 后一處:當FIFO緩沖區(qū)接收到從console.c發(fā)送的“關(guān)閉窗口”請求數(shù)據(jù)時所進行的處理,主要是釋放指定的圖層。
- 前一處:讓OS在用戶按下“X”按鈕時暫時將命令行窗口隱藏起來。
- 只修改了bootpack的兩處:
-
修改console_task:
void console_task(struct SHEET *sheet, int memtotal) {……if (cons.sht != 0) { /*將sheet改成了cons.sht*/……}……for (;;) {io_cli();if (fifo32_status(&task->fifo) == 0) {……} else {……if (i <= 1 && cons.sht != 0) { /* 光標定時器 */……}……if (i == 3) { /* 光標OFF */if (cons.sht != 0) { /*將sheet改成了cons.sht*/boxfill8(cons.sht->buf, cons.sht->bxsize, COL8_000000,cons.cur_x, cons.cur_y, cons.cur_x + 7, cons.cur_y + 15);}cons.cur_c = -1;}……if (256 <= i && i <= 511) { /* 鍵盤數(shù)據(jù) */if (i == 8 + 256) {……} else if (i == 10 + 256) {……if (cons.sht == 0) { /*將sheet改成了cons.sht*/cmd_exit(&cons, fat);}……} else {……}}/* 重新顯示光標 */if (cons.sht != 0) { /*將sheet改成了cons.sht*/if (cons.cur_c >= 0) {boxfill8(cons.sht->buf, cons.sht->bxsize, cons.cur_c, cons.cur_x, cons.cur_y, cons.cur_x + 7, cons.cur_y + 15);}sheet_refresh(cons.sht, cons.cur_x, cons.cur_y, cons.cur_x + 8, cons.cur_y + 16);}}} }- 主要修改點是將參數(shù)sheet修改成了cons.sht。這兩個變量基本上是一致的。但是cons.sht在命令行窗口關(guān)閉后會被置為0,而sheet則不變。
-
修改hrb_api:
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax) {……struct FIFO32 *sys_fifo = (struct FIFO32 *) *((int *) 0x0fec);……} else if (edx == 15) {for (;;) {……if (i == 4) { /* 只關(guān)閉命令行窗口 */timer_cancel(cons->timer);io_cli();fifo32_put(sys_fifo, cons->sht - shtctl->sheets0 + 2024); /* 2024~2279 */cons->sht = 0; /*置0*/io_sti();}……}} else if (edx == 16) {…… }- 內(nèi)存地址0xfec中存放的是OS(task_a)用的FIFO緩沖區(qū)地址。
- 等待鍵盤輸入期間,如果從從FIFO緩沖區(qū)接受到了4這個數(shù)據(jù),則表示收到了關(guān)閉命令行窗口的信號,此時取消定時器,并發(fā)出清理圖層的消息,然后將cons->sht置為0。
-
make后用VMware運行:
- 上圖是運行color.hrb并關(guān)閉了命令行窗口。
3. 保護應用程序(1)(harib24c)
-
編寫惡意應用程序crack7.nas:
[FORMAT "WCOFF"] [INSTRSET "i486p"] [BITS 32] [FILE "crack7.nas"]GLOBAL _HariMain[SECTION .text]_HariMain:MOV AX,1005*8MOV DS,AXCMP DWORD [DS:0x0004],'Hari'JNE fin ; 不是應用程序,因此不執(zhí)行任何操作MOV ECX,[DS:0x0000] ; 讀取該應用程序數(shù)據(jù)段的大小MOV AX,2005*8MOV DS,AXcrackloop: ; 整個用123填充ADD ECX,-1MOV BYTE [DS:ECX],123CMP ECX,0JNE crackloopfin: ; 結(jié)束MOV EDX,4INT 0x40 -
make后運行試試:
- 運行應用程序lines.hrb后:
- 打開新的命令行窗口,運行crack7.hrb:
- lines圖層好像出現(xiàn)了問題!
- 運行應用程序lines.hrb后:
-
這次的crack7.hrb成功地破壞了OS,準確地說,由于無法破壞OS本身,轉(zhuǎn)而破壞運行中的應用程序。
- 運行中的應用程序存在被破壞的風險,如果不加以處理的話,用戶可能就不敢運行多個應用程序了。
-
首先,crack7將1005*8裝進自己運行的數(shù)據(jù)段寄存器DS,成功偽裝成為GDT的1005號。然后從1005號段的第4字節(jié)讀取數(shù)據(jù),判斷是否是“Hari”。
- 1005號段其實就是代表第一個打開的命令行窗口所運行的應用程序的代碼段編號。
- 回顧一下GDT編號:
- 1:OS用的數(shù)據(jù)段
- 2:OS用的代碼段
- 3~1002:多任務用的段
- 3:task_a
- 4:idle
- 5:一般就是第一個命令行窗口
- 6:一般就是第二個命令行窗口
- 1003~2002:應用程序用的代碼段
- 1003:task_a用(沒用應用程序,不使用)
- 1004:idle用(沒有應用程序,不使用)
- 1005:第一個命令行窗口的應用程序代碼段
- 1006:第二個命令行窗口的應用程序代碼段
- 2003~3002:應用程序用的數(shù)據(jù)段
- 2003:task_a用(沒用應用程序,不使用)
- 2004:idle用(沒有應用程序,不使用)
- 2005:第一個命令行窗口的應用程序數(shù)據(jù)段
- 2006:第二個命令行窗口的應用程序數(shù)據(jù)段
-
如果讀出的數(shù)據(jù)是“Hari”,那么說明應用程序正在運行的可能性很高。然后,讀取數(shù)據(jù)段開頭的四個字節(jié),即應用程序數(shù)據(jù)段的大小。
-
再然后,crack7.hrb將2005*8裝進自己運行的數(shù)據(jù)段寄存器,又成功偽裝成為GDT的2005號。然后將GDT2005號的所有數(shù)據(jù)都改成123(當然其他的數(shù)字也可以)。
- 顯然,crack7偽裝將自己偽裝成了第一個命令行窗口的應用程序,然后訪問了“自己”的代碼段和數(shù)據(jù)段,然后修改了“自己”的數(shù)據(jù)段。
- crack7的目的只是覆蓋應用程序數(shù)據(jù)段應有的內(nèi)容,使其無法正常運行。
- 對于CPU來講,應用程序訪問“自己”用的段當然是理所應當?shù)氖虑槔?#xff0c;所以不會產(chǎn)生異常。
- 讓人不由地感嘆crack7簡直tql。
-
想要防御crack7,只要禁止應用程序隨意訪問其他任務所擁有的內(nèi)存段就可以了。這樣crack7就只能攻擊自己了(笑)。
4. 保護應用程序(2)(harib24d)
-
想要防御crack7,有一個辦法是:通過改寫GDT的設(shè)置,只將正在運行的那個程序的段設(shè)置為應用程序用,其他的應用程序都暫時設(shè)置為操作系統(tǒng)用。現(xiàn)在每個任務只有兩個應用程序段,這個方法貌似可行。
- 顯然,這個方法需要修改的代碼量太大。而且,需要在每次切換任務時都需要改寫GDT設(shè)置。
-
其實CPU早就準備好了這個問題的解決方法(感謝英特爾的大叔們!),那就是LDT。
-
LDT講解
- LDT,local (segment) descriptor table,局部段號記錄表。GDT是全局段號記錄表。
- GDT中的段設(shè)置是提供給所有任務通用的,而LDT中的段設(shè)置則只是對某個應用程序有效。
- 將應用程序段設(shè)置在LDT中,其他的任務由于無法使用該LDT,也就無法破壞了。
- 和GDT一樣,LDT的容量也是64KB(可以容納8192個段),不過在此OS中只需要設(shè)置兩個段,所以只使用其中的16字節(jié),將這16字節(jié)的信息放在結(jié)構(gòu)體TASK中。
- 可以通過GDTR這個寄存器將GDT的起始內(nèi)存地址告訴CPU,而LDT的起始內(nèi)存地址則是通過在GDT中創(chuàng)建LDT段來告知CPU的。也就是說,GDT中可以設(shè)置很多個LDT(當然,不能同時使用兩個以上的LDT),這和TSS非常相似。
- 挖坑:LDT的詳細信息有時間再細看。由于書上這部分的篇幅不多且時間緊迫,因此這部分不再詳解。
-
修改bootpack.h,添加設(shè)置LDT的段屬性編號和修改TASK結(jié)構(gòu)體:
/* dsctbl.c */ #define AR_LDT 0x0082 /* mtask.c */ struct TASK {int sel, flags; int level, priority;struct FIFO32 fifo;struct TSS32 tss;struct SEGMENT_DESCRIPTOR ldt[2]; /*ldt*/struct CONSOLE *cons;int ds_base, cons_stack; }; -
修改mtask.c以便設(shè)置LDT。將LDT編號寫入tss.ldtr,這樣在創(chuàng)建TSS時就順便在GDT中設(shè)置了LDT,CPU也可以知道該任務應該使用哪個LDT了。
struct TASK *task_init(struct MEMMAN *memman) {……for (i = 0; i < MAX_TASKS; i++) {taskctl->tasks0[i].flags = 0;taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;taskctl->tasks0[i].tss.ldtr = (TASK_GDT0 + MAX_TASKS + i) * 8;set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);set_segmdesc(gdt + TASK_GDT0 + MAX_TASKS + i, 15, (int) taskctl->tasks0[i].ldt, AR_LDT);}…… }struct TASK *task_alloc(void) {……for (i = 0; i < MAX_TASKS; i++) {if (taskctl->tasks0[i].flags == 0) {……task->tss.fs = 0;task->tss.gs = 0;/*刪掉了task->tss.ldtr = 0;*/task->tss.iomap = 0x40000000;task->tss.ss0 = 0;return task;}}return 0; /* 傕偆慡晹巊梡拞 */ }- 在task_init中:
- 現(xiàn)在,GDT的31002號仍然存放的是任務用的段。10032002存放的是應用程序用的內(nèi)存段(數(shù)據(jù)段+代碼段),task->ldt[0]是數(shù)據(jù)段,task->ldt[1]是代碼段。2003~3002現(xiàn)在未使用。
- 在task_init中:
-
修改console.c,使應用程序段創(chuàng)建在LDT中:
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline) {……if (finfo != 0) {/* 找到文件 */……if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {……set_segmdesc(task->ldt + 0, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);set_segmdesc(task->ldt + 1, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);……start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));……} else {cons_putstr0(cons, ".hrb file format error.\n");}……}/* 未找到文件 */return 0; }- set_segmdesc(task->ldt + 0, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);設(shè)置代碼段。
- set_segmdesc(task->ldt + 1, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);設(shè)置數(shù)據(jù)段。
- 在start_app調(diào)用中,指定的段號是4(=0*8+4)和12(=1*8+4),這里的乘8和GDT是一樣的,+4代表該段號不是GDT中的段號,而是LDT中的段號的意思。
- 在多個任務同時運行(同時運行多個應用程序)的時候,應用程序用的代碼段號都是4,數(shù)據(jù)段號都是12,這和之前的bug一樣?其實不然,因為這里使用的是+4,指定的段號是LDT中段號,每個任務(應用程序)都有自己的專用的LDT,這樣寫是完全沒問題的。
-
make后用VMware運行crack7.hrb試試:
- 產(chǎn)生了一般保護性中斷。這是因為1005和2005號段已經(jīng)不再是應用程序用的代碼段和數(shù)據(jù)段了。
-
如果將crack7.nas中的段號從1005*8和2005*8改成4和12,也不會破壞其他應用程序的運行。這是因為,crack7.hrb本身也是應用程序,這樣運行crack7只會修改crack7自己本身。
-
不過還有一個漏洞可以利用,那就是CPU中的LLDT指令,這個指令可以改變LDTR寄存器(用于保存當前正在運行的應用程序所使用的的LDT的寄存器)的值,這樣的話就可以切換到其他應用程序的LDT,從而引發(fā)問題。其實,這是不需要擔心的,因為這個指令是OS專用指令,位于應用程序段內(nèi)的程序是無法執(zhí)行的,即便強制執(zhí)行這個指令,也會像執(zhí)行CLI指令那樣產(chǎn)生異常,進而該應用程序被強制結(jié)束。
- 終于,又一次保護了OS!Love & Peace!。
5. 優(yōu)化應用程序的大小(harib24e)
-
hello3.hrb有520字節(jié),在第21天的harib18b中hello3.hrb只有100字節(jié)。明明hello3.hrb也沒有修改啊。
- 這是因為創(chuàng)建hello3.hrb時所引用的a_nask.nas變大了。也就是說,在hello3.hrb中,除了包含向api_putchar和api_end這樣真正需要用到的函數(shù)外,還包含了api_openwin和api_linewin這些hello3.hrb根本用不到的函數(shù)。
-
將這些函數(shù)做成不同的.obj文件,將api_putchar等需要用到的函數(shù)和api_openwin等用不到的函數(shù)分開。
- 連接器(Linker,obj2bim)的功能只是決定是否將.obj文件連接上去,而不會在一個包含多個函數(shù)的obj文件中挑出需要的部分,并舍棄不需要的部分(這并不是obj2bim的功能不夠強大,一般的連接器都是這樣設(shè)計的)。
-
因此將a_nask.nas拆成很多文件:
- api001.nas:[FORMAT "WCOFF"] [INSTRSET "i486p"] [BITS 32] [FILE "api001.nas"]GLOBAL _api_putchar[SECTION .text]_api_putchar: ; void api_putchar(int c);MOV EDX,1MOV AL,[ESP+4] ; cINT 0x40RET
- api002.nas:[FORMAT "WCOFF"] [INSTRSET "i486p"] [BITS 32] [FILE "api002.nas"]GLOBAL _api_putstr0[SECTION .text]_api_putstr0: ; void api_putstr0(char *s);PUSH EBXMOV EDX,2MOV EBX,[ESP+8] ; sINT 0x40POP EBXRET
- api003.nas~api020.nas和上述類似,全都是從a_nask.nas中原封不動拆出來的,這里不再羅列。
-
由于hello3.hrb需要的.obj文件只有api001.obj和api004.obj,因此修改一下Makefile:
hello3.bim : hello3.obj api001.obj api004.obj Makefile$(OBJ2BIM) @$(RULEFILE) out:hello3.bim map:hello3.map hello3.obj api001.obj api004.obj- make以后,hello3.hrb只有112字節(jié),減少了408字節(jié)。
-
雖然說是減少了字節(jié)數(shù),如果一個一個應用程序地根據(jù)需要的API去修改Makefile文件,確實很麻煩。在此之前,只需要將a_nask.obj連接上去就行了。
-
obj2bim連接器有一個功能,如果指定的.obj文件中的函數(shù)沒有被程序所使用的,那么這個.obj文件是不會被連接的。所以將用不到的.obj文件寫進Makefile也沒問題。其實,市面上大多數(shù)的連接器都沒有這個功能,只要制定好的.obj文件就會連接進去,obj2bim比較特殊一點(笑)。
-
因此,修改Makefile文件:
OBJS_API = api001.obj api002.obj api003.obj api004.obj api005.obj api006.obj \api007.obj api008.obj api009.obj api010.obj api011.obj api012.obj \api013.obj api014.obj api015.obj api016.obj api017.obj api018.obj \api019.obj api020.obja.bim : a.obj $(OBJS_API) Makefile$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj $(OBJS_API)- 這樣,如果今后添加了api021.obj,只需要修改OBJS_API即可。
- 其余的修改和a.bim的修改差不多,因此這里不再贅述。
-
make后對比一下修改前和修改后應用程序的大小:
- 上述應用程序中,有幾個是無法正常運行的。比如hello.hrb和hello2.hrb并不是用bim2obj生成的,因此運行時會報告hrb文件格式錯誤。此外,現(xiàn)在支持了LDT,crack7.hrb也就不能破壞OS了,因此將這三個文件在harib24f中刪除。
6. 庫(harib24f)
-
將函數(shù)拆分開來,并用連接器進行連接,需要創(chuàng)建很多的.obj文件;如果不拆分,使用一個大的.obj文件(a_nask.obj),應用程序就會被無端增大。
-
要解決這個問題,需要使用庫。
- 庫,library,用途是將多個obj文件打包成一個文件。
-
要創(chuàng)建一個庫,首先需要obj文件,除此之外,還需要一個叫做庫管理器的程序。庫管理器的英文是librarian。tolset中依舊有這個庫管理器golib00.exe了。
-
創(chuàng)建庫,在Makefile中添加代碼:
GOLIB = $(TOOLPATH)golib00.exe apilib.lib : Makefile $(OBJS_API)$(GOLIB) $(OBJS_API) out:apilib.lib- 用這三行代碼便可以得到一個apilib.lib這樣一個文件。
-
可以在ojb2bim中指定剛剛生成的這個apilib.lib文件來代替一串的obj文件。從Makefile角度上來看好像沒有什么區(qū)別,不過用一個文件代替一群文件,這還是很不錯的。繼續(xù)修改Makefile:
a.bim : a.obj apilib.lib Makefile$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj apilib.lib -
再編寫一個apilib.h文件:
void api_putchar(int c); void api_putstr0(char *s); void api_putstr1(char *s, int l); void api_end(void); int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title); void api_putstrwin(int win, int x, int y, int col, int len, char *str); void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col); void api_initmalloc(void); char *api_malloc(int size); void api_free(char *addr, int size); void api_point(int win, int x, int y, int col); void api_refreshwin(int win, int x0, int y0, int x1, int y1); void api_linewin(int win, int x0, int y0, int x1, int y1, int col); void api_closewin(int win); int api_getkey(int mode); int api_alloctimer(void); void api_inittimer(int timer, int data); void api_settimer(int timer, int time); void api_freetimer(int timer); void api_beep(int tone);- 有了這個頭文件,用一句代碼就可以搞定應用程序開頭的API函數(shù)聲明了:#include "apilib.h"
- 比如,beepdown.c就可以寫成如下代碼:#include "apilib.h"void HariMain(void) {…… }
- 其他應用程序的修改和上述修改大同小異,這里不再羅列。
-
關(guān)于庫的知識
7. 整理make環(huán)境(harib24g)
-
OS、應用程序和庫的源文件都混在一起,看起來非?;靵y,因此做一下分類:
-
首先,關(guān)于OS的部分。在harib24g中創(chuàng)建了一個名為“haribote”的目錄,將OS的核心源代碼以及Makefile移動到這里。
OBJS_BOOTPACK = bootpack.obj naskfunc.obj hankaku.obj graphic.obj dsctbl.obj \int.obj fifo.obj keyboard.obj mouse.obj memory.obj sheet.obj timer.obj \mtask.obj window.obj console.obj file.objTOOLPATH = ../../z_tools/ INCPATH = ../../z_tools/haribote/MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet GAS2NASK = $(TOOLPATH)gas2nask.exe -a OBJ2BIM = $(TOOLPATH)obj2bim.exe MAKEFONT = $(TOOLPATH)makefont.exe BIN2OBJ = $(TOOLPATH)bin2obj.exe BIM2HRB = $(TOOLPATH)bim2hrb.exe RULEFILE = ../haribote.rul EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com GOLIB = $(TOOLPATH)golib00.exe COPY = copy DEL = del# 默認動作default :$(MAKE) ipl10.bin$(MAKE) haribote.sys# 文件生成規(guī)則ipl10.bin : ipl10.nas Makefile$(NASK) ipl10.nas ipl10.bin ipl10.lstasmhead.bin : asmhead.nas Makefile$(NASK) asmhead.nas asmhead.bin asmhead.lsthankaku.bin : hankaku.txt Makefile$(MAKEFONT) hankaku.txt hankaku.binhankaku.obj : hankaku.bin Makefile$(BIN2OBJ) hankaku.bin hankaku.obj _hankakubootpack.bim : $(OBJS_BOOTPACK) Makefile$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \$(OBJS_BOOTPACK) # 3MB+64KB=3136KBbootpack.hrb : bootpack.bim Makefile$(BIM2HRB) bootpack.bim bootpack.hrb 0haribote.sys : asmhead.bin bootpack.hrb Makefilecopy /B asmhead.bin+bootpack.hrb haribote.sys# 生成規(guī)則%.gas : %.c bootpack.h Makefile$(CC1) -o $*.gas $*.c%.nas : %.gas Makefile$(GAS2NASK) $*.gas $*.nas%.obj : %.nas Makefile$(NASK) $*.nas $*.obj $*.lst# 命令clean :-$(DEL) asmhead.bin-$(DEL) hankaku.bin-$(DEL) *.lst-$(DEL) *.obj-$(DEL) *.map-$(DEL) *.bim-$(DEL) *.hrbsrc_only :$(MAKE) clean-$(DEL) ipl10.bin-$(DEL) haribote.sys- haribote目錄只含有OS的核心代碼,因此能夠使用的命令只有make、make clean、make src_only。
- 主要使用make來生成OS。其余二者基本上沒用。
- 注意,make只能生成haribote.sys,不能生成磁盤映像文件。
- haribote目錄只含有OS的核心代碼,因此能夠使用的命令只有make、make clean、make src_only。
-
然后,關(guān)于庫的部分。在harib24g中創(chuàng)建了一個名為“apilib”的目錄,將庫相關(guān)的源代碼以及Makefile移動進去。
OBJS_API = api001.obj api002.obj api003.obj api004.obj api005.obj api006.obj \api007.obj api008.obj api009.obj api010.obj api011.obj api012.obj \api013.obj api014.obj api015.obj api016.obj api017.obj api018.obj \api019.obj api020.objTOOLPATH = ../../z_tools/ INCPATH = ../../z_tools/haribote/MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet GAS2NASK = $(TOOLPATH)gas2nask.exe -a OBJ2BIM = $(TOOLPATH)obj2bim.exe MAKEFONT = $(TOOLPATH)makefont.exe BIN2OBJ = $(TOOLPATH)bin2obj.exe BIM2HRB = $(TOOLPATH)bim2hrb.exe RULEFILE = ../haribote.rul EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com GOLIB = $(TOOLPATH)golib00.exe COPY = copy DEL = del# 默認default :$(MAKE) apilib.lib# 文件生成規(guī)則apilib.lib : Makefile $(OBJS_API)$(GOLIB) $(OBJS_API) out:apilib.lib# 通用規(guī)則%.obj : %.nas Makefile$(NASK) $*.nas $*.obj $*.lst# 命令clean :-$(DEL) *.lst-$(DEL) *.objsrc_only :$(MAKE) clean-$(DEL) apilib.lib- apilib目錄中,能夠使用的命令也只有make、make clean、make src_only。
- 顯然,對于庫來講,不會有命令make run。
- 注意,此文件夾是已經(jīng)默認make了的。
-
接下來,關(guān)于應用程序部分。應用程序的Makefile比較有意思,每一個應用程序都有相應的Makefile文件。這里,以a.hrb的Makefile為例:
APP = a STACK = 1k MALLOC = 0kinclude ../app_make.txt- Makefile文件只有5行,但是app_make.txt還是比較長的。
- 之所以用include,是因為所有的應用程序的Makefile都大同小異,如果將其中相同的部分改為include方式來引用就可以縮短Makefile,而且以后對Makefile修改時,也只需要修改app_make.txt文件即可應用到全部應用程序,方便省事,豈不美哉。
- app_make.txt:TOOLPATH = ../../z_tools/ INCPATH = ../../z_tools/haribote/ APILIBPATH = ../apilib/ HARIBOTEPATH = ../haribote/MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -I../ -Os -Wall -quiet GAS2NASK = $(TOOLPATH)gas2nask.exe -a OBJ2BIM = $(TOOLPATH)obj2bim.exe MAKEFONT = $(TOOLPATH)makefont.exe BIN2OBJ = $(TOOLPATH)bin2obj.exe BIM2HRB = $(TOOLPATH)bim2hrb.exe RULEFILE = ../haribote.rul EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com GOLIB = $(TOOLPATH)golib00.exe COPY = copy DEL = del# 默認動作default :$(MAKE) $(APP).hrb# 文件生成規(guī)則$(APP).bim : $(APP).obj $(APILIBPATH)apilib.lib Makefile ../app_make.txt$(OBJ2BIM) @$(RULEFILE) out:$(APP).bim map:$(APP).map stack:$(STACK) \$(APP).obj $(APILIBPATH)apilib.lib$(APP).hrb : $(APP).bim Makefile ../app_make.txt$(BIM2HRB) $(APP).bim $(APP).hrb $(MALLOC)haribote.img : ../haribote/ipl10.bin ../haribote/haribote.sys $(APP).hrb \Makefile ../app_make.txt$(EDIMG) imgin:../../z_tools/fdimg0at.tek \wbinimg src:../haribote/ipl10.bin len:512 from:0 to:0 \copy from:../haribote/haribote.sys to:@: \copy from:$(APP).hrb to:@: \imgout:haribote.img# 一般規(guī)則%.gas : %.c ../apilib.h Makefile ../app_make.txt$(CC1) -o $*.gas $*.c%.nas : %.gas Makefile ../app_make.txt$(GAS2NASK) $*.gas $*.nas%.obj : %.nas Makefile ../app_make.txt$(NASK) $*.nas $*.obj $*.lst# 命令run :$(MAKE) haribote.img$(COPY) haribote.img ..\..\z_tools\qemu\fdimage0.bin$(MAKE) -C ../../z_tools/qemufull :$(MAKE) -C $(APILIBPATH)$(MAKE) $(APP).hrbrun_full :$(MAKE) -C $(APILIBPATH)$(MAKE) -C ../haribote$(MAKE) runclean :-$(DEL) *.lst-$(DEL) *.obj-$(DEL) *.map-$(DEL) *.bim-$(DEL) haribote.imgsrc_only :$(MAKE) clean-$(DEL) $(APP).hrb
- 注意,可以使用的命令增加了。
- make可以生成a.hrb。
- make run可以生成一個磁盤文件只包含haribote.sys和a.hrb的精簡版磁盤映像,然后使用QEMU運行。
- 倘若只生成精簡版磁盤映像,而不用QEMU運行,可以在app_make.txt添加代碼:run_vmware: $(MAKE) -C $(APILIBPATH)$(MAKE) haribote.img
- 這樣,使用命令make run_vmware便可以實現(xiàn)。
- make run_vmware可以生成磁盤映像文件。
- make full:在生成應用程序(a.hrb)時可能需要引用apilib.lib,但是可能出現(xiàn)在“make”a.hrb時apilib.lib還未生成的情況。因此,這個時候應該使用make full。
- 在make full中有一句代碼$(MAKE) -C $(APILIBPATH),這句代碼的作用是“先執(zhí)行目錄apilib中的make”,如果已經(jīng)存在apilib.lib的話,這句語句將不會執(zhí)行。
- 因此,如果不放心,一直使用make full來代替make也是可以的。
- make full_run是make run的full版本。即將apilib和OS核心都make以后,再執(zhí)行原本的make run。注意,make full不會生成磁盤印象。
- 主要使用make run、make run_vmware。
- 注意:已經(jīng)執(zhí)行過了apilib目錄下的make了。
-
最后,是harib24g的Makefile:
TOOLPATH = ../z_tools/ INCPATH = ../z_tools/haribote/MAKE = $(TOOLPATH)make.exe -r EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com COPY = copy DEL = del# 默認動作default :$(MAKE) haribote.img# 文件生成規(guī)則haribote.img : haribote/ipl10.bin haribote/haribote.sys Makefile \a/a.hrb hello3/hello3.hrb hello4/hello4.hrb hello5/hello5.hrb \winhelo/winhelo.hrb winhelo2/winhelo2.hrb winhelo3/winhelo3.hrb \star1/star1.hrb stars/stars.hrb stars2/stars2.hrb \lines/lines.hrb walk/walk.hrb noodle/noodle.hrb \beepdown/beepdown.hrb color/color.hrb color2/color2.hrb$(EDIMG) imgin:../z_tools/fdimg0at.tek \wbinimg src:haribote/ipl10.bin len:512 from:0 to:0 \copy from:haribote/haribote.sys to:@: \copy from:haribote/ipl10.nas to:@: \copy from:make.bat to:@: \copy from:a/a.hrb to:@: \copy from:hello3/hello3.hrb to:@: \copy from:hello4/hello4.hrb to:@: \copy from:hello5/hello5.hrb to:@: \copy from:winhelo/winhelo.hrb to:@: \copy from:winhelo2/winhelo2.hrb to:@: \copy from:winhelo3/winhelo3.hrb to:@: \copy from:star1/star1.hrb to:@: \copy from:stars/stars.hrb to:@: \copy from:stars2/stars2.hrb to:@: \copy from:lines/lines.hrb to:@: \copy from:walk/walk.hrb to:@: \copy from:noodle/noodle.hrb to:@: \copy from:beepdown/beepdown.hrb to:@: \copy from:color/color.hrb to:@: \copy from:color2/color2.hrb to:@: \imgout:haribote.img# 命令run :$(MAKE) haribote.img$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin$(MAKE) -C ../z_tools/qemuinstall :$(MAKE) haribote.img$(IMGTOL) w a: haribote.imgfull :$(MAKE) -C haribote$(MAKE) -C apilib$(MAKE) -C a$(MAKE) -C hello3$(MAKE) -C hello4$(MAKE) -C hello5$(MAKE) -C winhelo$(MAKE) -C winhelo2$(MAKE) -C winhelo3$(MAKE) -C star1$(MAKE) -C stars$(MAKE) -C stars2$(MAKE) -C lines$(MAKE) -C walk$(MAKE) -C noodle$(MAKE) -C beepdown$(MAKE) -C color$(MAKE) -C color2$(MAKE) haribote.imgrun_full :$(MAKE) full$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin$(MAKE) -C ../z_tools/qemuinstall_full :$(MAKE) full$(IMGTOL) w a: haribote.imgrun_os :$(MAKE) -C haribote$(MAKE) runclean : # 不執(zhí)行任何操作src_only :$(MAKE) clean-$(DEL) haribote.imgclean_full :$(MAKE) -C haribote clean$(MAKE) -C apilib clean$(MAKE) -C a clean$(MAKE) -C hello3 clean$(MAKE) -C hello4 clean$(MAKE) -C hello5 clean$(MAKE) -C winhelo clean$(MAKE) -C winhelo2 clean$(MAKE) -C winhelo3 clean$(MAKE) -C star1 clean$(MAKE) -C stars clean$(MAKE) -C stars2 clean$(MAKE) -C lines clean$(MAKE) -C walk clean$(MAKE) -C noodle clean$(MAKE) -C beepdown clean$(MAKE) -C color clean$(MAKE) -C color2 cleansrc_only_full :$(MAKE) -C haribote src_only$(MAKE) -C apilib src_only$(MAKE) -C a src_only$(MAKE) -C hello3 src_only$(MAKE) -C hello4 src_only$(MAKE) -C hello5 src_only$(MAKE) -C winhelo src_only$(MAKE) -C winhelo2 src_only$(MAKE) -C winhelo3 src_only$(MAKE) -C star1 src_only$(MAKE) -C stars src_only$(MAKE) -C stars2 src_only$(MAKE) -C lines src_only$(MAKE) -C walk src_only$(MAKE) -C noodle src_only$(MAKE) -C beepdown src_only$(MAKE) -C color src_only$(MAKE) -C color2 src_only-$(DEL) haribote.imgrefresh :$(MAKE) full$(MAKE) clean_full-$(DEL) haribote.img- 可以使用很多命令:
- make:生成一個包含OS內(nèi)核以及全部應用程序的磁盤映像。
- make run:make之后使用QEMU啟動。
- make install:make后將磁盤映像文件安裝到軟盤中。
- make full:將OS內(nèi)核、apilib和應用程序全部執(zhí)行make后生成磁盤映像。
- make run_full:make full后make run。
- make install_full:make full后make install。
- make run_os: 將OS核心make后make run,當只對OS核心進行修改時可以使用這個命令。
- make clean:clean命令原本是用于清除臨時文件的,但由于這個Makefile不生成臨時文件(.map之類的),因此這個命令不執(zhí)行任何操作。
- make src_only:將生成的磁盤映像文件刪除。
- make clean_full:對OS核心、apilib和全部應用程序執(zhí)行make clean,這樣清除所有臨時文件。
- make src_only_full:對OS核心、apilib和全部應用程序執(zhí)行make src_only,這樣將清除所有的臨時文件和最終生成文件。執(zhí)行這個命令后,make和make run就無法運行了(因為默認apilib是被make了的,即文件apilib.lib是存在的)??梢允褂胢ake full代替。
- make refresh:make full后make clean_full。從執(zhí)行過make src_only_full的狀態(tài)執(zhí)行這個命令的話,就會恢復到可以直接make和make run的狀態(tài)。
- 可以使用很多命令:
-
經(jīng)過上述劃分以及修改后,make的速度將會大大提升。
- 在這個過程中,順便將某些應用程序修改了一下。比如將winhelo.hrb這個應用程序,添加了等待鍵盤輸入的代碼。
- 修改的應用程序有winhelo、winhelo2、star1、stars和stars2。
-
在harib24g下make full并用VMware運行:
- 全家桶(笑)。
8. 寫在23:20
- 現(xiàn)在是2020.5.7 23:20。
- 已經(jīng)到了既定的日期,可是還沒有完成任務,真是羞赧不堪啊。
- 預計這月10號可以完成。
- 昨日,爸媽去種花生。早上因為起床氣沒及時去幫家里種花生,讓老媽生氣了,現(xiàn)在想想自己真是混蛋,后悔極了。
- 從昨天早上七點半,到傍晚六點半,一直在種花生,可是還是沒有種完。
- 這次應該是我自初三以來第一次幫家里種花生。高中學業(yè)繁忙,大學回不了家。
- 農(nóng)民階級是最受累的階級。那種面朝黃土背朝天的滋味真的不好受。
- 出去中了一天的花生,回來后我的腰背、手臂和腿腳便酸痛得厲害。更甚的是,回來后后頸的皮膚一直如針扎般疼痛,原來是曬傷了!
- 我盡量將這一切說的云淡風輕,可是,農(nóng)活是真的不輕松:翻地、施肥、打嶺、栽種、噴藥、貼地膜、蓋土、修地邊。
- 我的手掌原本是肉色的,施肥以后就變成了黑色的,撒種以后就變成了紅色的,蓋完土以后又變成了黃色。
- 今天完成了第27天的工作,文檔行數(shù)也過了1000行,明天繼續(xù)。
總結(jié)
以上是生活随笔為你收集整理的第27天 LDT与库的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 双核旗舰处理器:德仪Omap4430、高
- 下一篇: CHD-5.3.6集群安装