第27天 LDT与库
第27天 LDT與庫(kù)
2020.5.7
1. 先來(lái)修復(fù)bug(harib24a)
-
在harib23j中有一個(gè)bug,就是用ncst運(yùn)行的應(yīng)用程序,使用Shift+F1和點(diǎn)擊“X”按鈕都無(wú)法關(guān)閉窗口。
- 其實(shí)這個(gè)bug在很久之前就有了,只是一直沒(méi)有發(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); /* 為了確實(shí)執(zhí)行結(jié)束處理,如果處于休眠狀態(tài)則喚醒 */}}……} else if (512 <= i && i <= 767) { /* 鼠標(biāo)數(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); /* 為了確實(shí)執(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)蝿?wù)的level和priority。這句代碼的功能是將休眠的任務(wù)喚醒。
- 盡管在TSS中改寫了EIP和EAX以便執(zhí)行結(jié)束任務(wù)的處理,但是如果任務(wù)一直處于休眠狀態(tài)那么結(jié)束任務(wù)的處理就永遠(yuǎn)得不到執(zhí)行。因此,需要喚醒任務(wù),使得結(jié)束處理確實(shí)能夠被執(zhí)行。
- 之前的代碼之所以沒(méi)有這兩句代碼,強(qiáng)制結(jié)束應(yīng)用程序也沒(méi)出什么問(wèn)題的原因是:
- 命令行窗口會(huì)觸發(fā)0.5s光標(biāo)定時(shí)器中斷(在命令行窗口中,不顯示光標(biāo)時(shí)也會(huì)每0.5s產(chǎn)生一次定時(shí)器中斷),當(dāng)產(chǎn)生定時(shí)器中斷時(shí),定時(shí)器就會(huì)向FIFO緩沖區(qū)寫入數(shù)據(jù),于是任務(wù)就被自動(dòng)喚醒了。因此,即便看上去可以強(qiáng)制執(zhí)行,但是其實(shí)距離應(yīng)用程序真正結(jié)束還是會(huì)產(chǎn)生最大0.5s的延遲。因此,這次修改也會(huì)讓Shift+F1和“X”按鈕的速度得到提升。
- 但是ncst命令的光標(biāo)定時(shí)器被刪掉了,因此不會(huì)產(chǎn)生定時(shí)器中斷,如果任務(wù)一直在休眠,那么不主動(dòng)喚醒,就會(huì)一直處于休眠狀態(tài)。
- 添加的兩行代碼都是task_run(task, -1, 0);,-1和0代表不改變?nèi)蝿?wù)的level和priority。這句代碼的功能是將休眠的任務(wù)喚醒。
-
make后用VMware運(yùn)行:
- 上圖先使用ncst運(yùn)行color2.hrb應(yīng)用程序,然后點(diǎn)擊“X”按鈕關(guān)閉了color2.hrb。
2. 應(yīng)用程序運(yùn)行時(shí)關(guān)閉命令行窗口(harib24b)
-
現(xiàn)在解決應(yīng)用程序運(yùn)行時(shí)無(wú)法關(guān)閉對(duì)應(yīng)的命令行窗口的問(wèn)題。
- 在之前(先不考慮ncst),在應(yīng)用程序退出前面試法務(wù)關(guān)閉用來(lái)啟動(dòng)這個(gè)程序的命令行窗口的。
-
修改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) { /* 鼠標(biāo)數(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) { /* 是否是應(yīng)用程序窗口 */……} 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”按鈕時(shí)暫時(shí)將命令行窗口隱藏起來(lái)。
- 這是因?yàn)殛P(guān)閉有的應(yīng)用程序的命令行窗口需要一定的時(shí)間,如果點(diǎn)了按鈕較長(zhǎng)時(shí)間窗口還沒(méi)有關(guān)閉,用戶的體驗(yàn)感會(huì)極差,先隱藏給用戶已經(jīng)關(guān)閉窗口的信息,用戶友好性極高。
- 后一處:當(dāng)FIFO緩沖區(qū)接收到從console.c發(fā)送的“關(guān)閉窗口”請(qǐng)求數(shù)據(jù)時(shí)所進(jìn)行的處理,主要是釋放指定的圖層。
- 前一處:讓OS在用戶按下“X”按鈕時(shí)暫時(shí)將命令行窗口隱藏起來(lái)。
- 只修改了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) { /* 光標(biāo)定時(shí)器 */……}……if (i == 3) { /* 光標(biāo)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 {……}}/* 重新顯示光標(biāo) */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);}}} }- 主要修改點(diǎn)是將參數(shù)sheet修改成了cons.sht。這兩個(gè)變量基本上是一致的。但是cons.sht在命令行窗口關(guān)閉后會(huì)被置為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這個(gè)數(shù)據(jù),則表示收到了關(guān)閉命令行窗口的信號(hào),此時(shí)取消定時(shí)器,并發(fā)出清理圖層的消息,然后將cons->sht置為0。
-
make后用VMware運(yùn)行:
- 上圖是運(yùn)行color.hrb并關(guān)閉了命令行窗口。
3. 保護(hù)應(yīng)用程序(1)(harib24c)
-
編寫惡意應(yīng)用程序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 ; 不是應(yīng)用程序,因此不執(zhí)行任何操作MOV ECX,[DS:0x0000] ; 讀取該應(yīng)用程序數(shù)據(jù)段的大小MOV AX,2005*8MOV DS,AXcrackloop: ; 整個(gè)用123填充ADD ECX,-1MOV BYTE [DS:ECX],123CMP ECX,0JNE crackloopfin: ; 結(jié)束MOV EDX,4INT 0x40 -
make后運(yùn)行試試:
- 運(yùn)行應(yīng)用程序lines.hrb后:
- 打開(kāi)新的命令行窗口,運(yùn)行crack7.hrb:
- lines圖層好像出現(xiàn)了問(wèn)題!
- 運(yùn)行應(yīng)用程序lines.hrb后:
-
這次的crack7.hrb成功地破壞了OS,準(zhǔn)確地說(shuō),由于無(wú)法破壞OS本身,轉(zhuǎn)而破壞運(yùn)行中的應(yīng)用程序。
- 運(yùn)行中的應(yīng)用程序存在被破壞的風(fēng)險(xiǎn),如果不加以處理的話,用戶可能就不敢運(yùn)行多個(gè)應(yīng)用程序了。
-
首先,crack7將1005*8裝進(jìn)自己運(yùn)行的數(shù)據(jù)段寄存器DS,成功偽裝成為GDT的1005號(hào)。然后從1005號(hào)段的第4字節(jié)讀取數(shù)據(jù),判斷是否是“Hari”。
- 1005號(hào)段其實(shí)就是代表第一個(gè)打開(kāi)的命令行窗口所運(yùn)行的應(yīng)用程序的代碼段編號(hào)。
- 回顧一下GDT編號(hào):
- 1:OS用的數(shù)據(jù)段
- 2:OS用的代碼段
- 3~1002:多任務(wù)用的段
- 3:task_a
- 4:idle
- 5:一般就是第一個(gè)命令行窗口
- 6:一般就是第二個(gè)命令行窗口
- 1003~2002:應(yīng)用程序用的代碼段
- 1003:task_a用(沒(méi)用應(yīng)用程序,不使用)
- 1004:idle用(沒(méi)有應(yīng)用程序,不使用)
- 1005:第一個(gè)命令行窗口的應(yīng)用程序代碼段
- 1006:第二個(gè)命令行窗口的應(yīng)用程序代碼段
- 2003~3002:應(yīng)用程序用的數(shù)據(jù)段
- 2003:task_a用(沒(méi)用應(yīng)用程序,不使用)
- 2004:idle用(沒(méi)有應(yīng)用程序,不使用)
- 2005:第一個(gè)命令行窗口的應(yīng)用程序數(shù)據(jù)段
- 2006:第二個(gè)命令行窗口的應(yīng)用程序數(shù)據(jù)段
-
如果讀出的數(shù)據(jù)是“Hari”,那么說(shuō)明應(yīng)用程序正在運(yùn)行的可能性很高。然后,讀取數(shù)據(jù)段開(kāi)頭的四個(gè)字節(jié),即應(yīng)用程序數(shù)據(jù)段的大小。
-
再然后,crack7.hrb將2005*8裝進(jìn)自己運(yùn)行的數(shù)據(jù)段寄存器,又成功偽裝成為GDT的2005號(hào)。然后將GDT2005號(hào)的所有數(shù)據(jù)都改成123(當(dāng)然其他的數(shù)字也可以)。
- 顯然,crack7偽裝將自己偽裝成了第一個(gè)命令行窗口的應(yīng)用程序,然后訪問(wèn)了“自己”的代碼段和數(shù)據(jù)段,然后修改了“自己”的數(shù)據(jù)段。
- crack7的目的只是覆蓋應(yīng)用程序數(shù)據(jù)段應(yīng)有的內(nèi)容,使其無(wú)法正常運(yùn)行。
- 對(duì)于CPU來(lái)講,應(yīng)用程序訪問(wèn)“自己”用的段當(dāng)然是理所應(yīng)當(dāng)?shù)氖虑槔?#xff0c;所以不會(huì)產(chǎn)生異常。
- 讓人不由地感嘆crack7簡(jiǎn)直tql。
-
想要防御crack7,只要禁止應(yīng)用程序隨意訪問(wèn)其他任務(wù)所擁有的內(nèi)存段就可以了。這樣crack7就只能攻擊自己了(笑)。
4. 保護(hù)應(yīng)用程序(2)(harib24d)
-
想要防御crack7,有一個(gè)辦法是:通過(guò)改寫GDT的設(shè)置,只將正在運(yùn)行的那個(gè)程序的段設(shè)置為應(yīng)用程序用,其他的應(yīng)用程序都暫時(shí)設(shè)置為操作系統(tǒng)用。現(xiàn)在每個(gè)任務(wù)只有兩個(gè)應(yīng)用程序段,這個(gè)方法貌似可行。
- 顯然,這個(gè)方法需要修改的代碼量太大。而且,需要在每次切換任務(wù)時(shí)都需要改寫GDT設(shè)置。
-
其實(shí)CPU早就準(zhǔn)備好了這個(gè)問(wèn)題的解決方法(感謝英特爾的大叔們!),那就是LDT。
-
LDT講解
- LDT,local (segment) descriptor table,局部段號(hào)記錄表。GDT是全局段號(hào)記錄表。
- GDT中的段設(shè)置是提供給所有任務(wù)通用的,而LDT中的段設(shè)置則只是對(duì)某個(gè)應(yīng)用程序有效。
- 將應(yīng)用程序段設(shè)置在LDT中,其他的任務(wù)由于無(wú)法使用該LDT,也就無(wú)法破壞了。
- 和GDT一樣,LDT的容量也是64KB(可以容納8192個(gè)段),不過(guò)在此OS中只需要設(shè)置兩個(gè)段,所以只使用其中的16字節(jié),將這16字節(jié)的信息放在結(jié)構(gòu)體TASK中。
- 可以通過(guò)GDTR這個(gè)寄存器將GDT的起始內(nèi)存地址告訴CPU,而LDT的起始內(nèi)存地址則是通過(guò)在GDT中創(chuàng)建LDT段來(lái)告知CPU的。也就是說(shuō),GDT中可以設(shè)置很多個(gè)LDT(當(dāng)然,不能同時(shí)使用兩個(gè)以上的LDT),這和TSS非常相似。
- 挖坑:LDT的詳細(xì)信息有時(shí)間再細(xì)看。由于書上這部分的篇幅不多且時(shí)間緊迫,因此這部分不再詳解。
-
修改bootpack.h,添加設(shè)置LDT的段屬性編號(hào)和修改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編號(hào)寫入tss.ldtr,這樣在創(chuàng)建TSS時(shí)就順便在GDT中設(shè)置了LDT,CPU也可以知道該任務(wù)應(yīng)該使用哪個(gè)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號(hào)仍然存放的是任務(wù)用的段。10032002存放的是應(yīng)用程序用的內(nèi)存段(數(shù)據(jù)段+代碼段),task->ldt[0]是數(shù)據(jù)段,task->ldt[1]是代碼段。2003~3002現(xiàn)在未使用。
- 在task_init中:
-
修改console.c,使應(yīng)用程序段創(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)用中,指定的段號(hào)是4(=0*8+4)和12(=1*8+4),這里的乘8和GDT是一樣的,+4代表該段號(hào)不是GDT中的段號(hào),而是LDT中的段號(hào)的意思。
- 在多個(gè)任務(wù)同時(shí)運(yùn)行(同時(shí)運(yùn)行多個(gè)應(yīng)用程序)的時(shí)候,應(yīng)用程序用的代碼段號(hào)都是4,數(shù)據(jù)段號(hào)都是12,這和之前的bug一樣?其實(shí)不然,因?yàn)檫@里使用的是+4,指定的段號(hào)是LDT中段號(hào),每個(gè)任務(wù)(應(yīng)用程序)都有自己的專用的LDT,這樣寫是完全沒(méi)問(wèn)題的。
-
make后用VMware運(yùn)行crack7.hrb試試:
- 產(chǎn)生了一般保護(hù)性中斷。這是因?yàn)?005和2005號(hào)段已經(jīng)不再是應(yīng)用程序用的代碼段和數(shù)據(jù)段了。
-
如果將crack7.nas中的段號(hào)從1005*8和2005*8改成4和12,也不會(huì)破壞其他應(yīng)用程序的運(yùn)行。這是因?yàn)?#xff0c;crack7.hrb本身也是應(yīng)用程序,這樣運(yùn)行crack7只會(huì)修改crack7自己本身。
-
不過(guò)還有一個(gè)漏洞可以利用,那就是CPU中的LLDT指令,這個(gè)指令可以改變LDTR寄存器(用于保存當(dāng)前正在運(yùn)行的應(yīng)用程序所使用的的LDT的寄存器)的值,這樣的話就可以切換到其他應(yīng)用程序的LDT,從而引發(fā)問(wèn)題。其實(shí),這是不需要擔(dān)心的,因?yàn)檫@個(gè)指令是OS專用指令,位于應(yīng)用程序段內(nèi)的程序是無(wú)法執(zhí)行的,即便強(qiáng)制執(zhí)行這個(gè)指令,也會(huì)像執(zhí)行CLI指令那樣產(chǎn)生異常,進(jìn)而該應(yīng)用程序被強(qiáng)制結(jié)束。
- 終于,又一次保護(hù)了OS!Love & Peace!。
5. 優(yōu)化應(yīng)用程序的大小(harib24e)
-
hello3.hrb有520字節(jié),在第21天的harib18b中hello3.hrb只有100字節(jié)。明明hello3.hrb也沒(méi)有修改啊。
- 這是因?yàn)閯?chuàng)建hello3.hrb時(shí)所引用的a_nask.nas變大了。也就是說(shuō),在hello3.hrb中,除了包含向api_putchar和api_end這樣真正需要用到的函數(shù)外,還包含了api_openwin和api_linewin這些hello3.hrb根本用不到的函數(shù)。
-
將這些函數(shù)做成不同的.obj文件,將api_putchar等需要用到的函數(shù)和api_openwin等用不到的函數(shù)分開(kāi)。
- 連接器(Linker,obj2bim)的功能只是決定是否將.obj文件連接上去,而不會(huì)在一個(gè)包含多個(gè)函數(shù)的obj文件中挑出需要的部分,并舍棄不需要的部分(這并不是obj2bim的功能不夠強(qiáng)大,一般的連接器都是這樣設(shè)計(jì)的)。
-
因此將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中原封不動(dòng)拆出來(lái)的,這里不再羅列。
-
由于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é)。
-
雖然說(shuō)是減少了字節(jié)數(shù),如果一個(gè)一個(gè)應(yīng)用程序地根據(jù)需要的API去修改Makefile文件,確實(shí)很麻煩。在此之前,只需要將a_nask.obj連接上去就行了。
-
obj2bim連接器有一個(gè)功能,如果指定的.obj文件中的函數(shù)沒(méi)有被程序所使用的,那么這個(gè).obj文件是不會(huì)被連接的。所以將用不到的.obj文件寫進(jìn)Makefile也沒(méi)問(wèn)題。其實(shí),市面上大多數(shù)的連接器都沒(méi)有這個(gè)功能,只要制定好的.obj文件就會(huì)連接進(jìn)去,obj2bim比較特殊一點(diǎ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.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后對(duì)比一下修改前和修改后應(yīng)用程序的大小:
- 上述應(yīng)用程序中,有幾個(gè)是無(wú)法正常運(yùn)行的。比如hello.hrb和hello2.hrb并不是用bim2obj生成的,因此運(yùn)行時(shí)會(huì)報(bào)告hrb文件格式錯(cuò)誤。此外,現(xiàn)在支持了LDT,crack7.hrb也就不能破壞OS了,因此將這三個(gè)文件在harib24f中刪除。
6. 庫(kù)(harib24f)
-
將函數(shù)拆分開(kāi)來(lái),并用連接器進(jìn)行連接,需要?jiǎng)?chuàng)建很多的.obj文件;如果不拆分,使用一個(gè)大的.obj文件(a_nask.obj),應(yīng)用程序就會(huì)被無(wú)端增大。
-
要解決這個(gè)問(wèn)題,需要使用庫(kù)。
- 庫(kù),library,用途是將多個(gè)obj文件打包成一個(gè)文件。
-
要?jiǎng)?chuàng)建一個(gè)庫(kù),首先需要obj文件,除此之外,還需要一個(gè)叫做庫(kù)管理器的程序。庫(kù)管理器的英文是librarian。tolset中依舊有這個(gè)庫(kù)管理器golib00.exe了。
-
創(chuàng)建庫(kù),在Makefile中添加代碼:
GOLIB = $(TOOLPATH)golib00.exe apilib.lib : Makefile $(OBJS_API)$(GOLIB) $(OBJS_API) out:apilib.lib- 用這三行代碼便可以得到一個(gè)apilib.lib這樣一個(gè)文件。
-
可以在ojb2bim中指定剛剛生成的這個(gè)apilib.lib文件來(lái)代替一串的obj文件。從Makefile角度上來(lái)看好像沒(méi)有什么區(qū)別,不過(guò)用一個(gè)文件代替一群文件,這還是很不錯(cuò)的。繼續(xù)修改Makefile:
a.bim : a.obj apilib.lib Makefile$(OBJ2BIM) @$(RULEFILE) out:a.bim map:a.map a.obj apilib.lib -
再編寫一個(gè)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);- 有了這個(gè)頭文件,用一句代碼就可以搞定應(yīng)用程序開(kāi)頭的API函數(shù)聲明了:#include "apilib.h"
- 比如,beepdown.c就可以寫成如下代碼:#include "apilib.h"void HariMain(void) {…… }
- 其他應(yīng)用程序的修改和上述修改大同小異,這里不再羅列。
-
關(guān)于庫(kù)的知識(shí)
7. 整理make環(huán)境(harib24g)
-
OS、應(yīng)用程序和庫(kù)的源文件都混在一起,看起來(lái)非常混亂,因此做一下分類:
-
首先,關(guān)于OS的部分。在harib24g中創(chuàng)建了一個(gè)名為“haribote”的目錄,將OS的核心源代碼以及Makefile移動(dòng)到這里。
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# 默認(rèn)動(dòng)作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來(lái)生成OS。其余二者基本上沒(méi)用。
- 注意,make只能生成haribote.sys,不能生成磁盤映像文件。
- haribote目錄只含有OS的核心代碼,因此能夠使用的命令只有make、make clean、make src_only。
-
然后,關(guān)于庫(kù)的部分。在harib24g中創(chuàng)建了一個(gè)名為“apilib”的目錄,將庫(kù)相關(guān)的源代碼以及Makefile移動(dòng)進(jìn)去。
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# 默認(rèn)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。
- 顯然,對(duì)于庫(kù)來(lái)講,不會(huì)有命令make run。
- 注意,此文件夾是已經(jīng)默認(rèn)make了的。
-
接下來(lái),關(guān)于應(yīng)用程序部分。應(yīng)用程序的Makefile比較有意思,每一個(gè)應(yīng)用程序都有相應(yīng)的Makefile文件。這里,以a.hrb的Makefile為例:
APP = a STACK = 1k MALLOC = 0kinclude ../app_make.txt- Makefile文件只有5行,但是app_make.txt還是比較長(zhǎng)的。
- 之所以用include,是因?yàn)樗械膽?yīng)用程序的Makefile都大同小異,如果將其中相同的部分改為include方式來(lái)引用就可以縮短Makefile,而且以后對(duì)Makefile修改時(shí),也只需要修改app_make.txt文件即可應(yīng)用到全部應(yīng)用程序,方便省事,豈不美哉。
- 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# 默認(rèn)動(dòng)作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可以生成一個(gè)磁盤文件只包含haribote.sys和a.hrb的精簡(jiǎn)版磁盤映像,然后使用QEMU運(yùn)行。
- 倘若只生成精簡(jiǎn)版磁盤映像,而不用QEMU運(yùn)行,可以在app_make.txt添加代碼:run_vmware: $(MAKE) -C $(APILIBPATH)$(MAKE) haribote.img
- 這樣,使用命令make run_vmware便可以實(shí)現(xiàn)。
- make run_vmware可以生成磁盤映像文件。
- make full:在生成應(yīng)用程序(a.hrb)時(shí)可能需要引用apilib.lib,但是可能出現(xiàn)在“make”a.hrb時(shí)apilib.lib還未生成的情況。因此,這個(gè)時(shí)候應(yīng)該使用make full。
- 在make full中有一句代碼$(MAKE) -C $(APILIBPATH),這句代碼的作用是“先執(zhí)行目錄apilib中的make”,如果已經(jīng)存在apilib.lib的話,這句語(yǔ)句將不會(huì)執(zhí)行。
- 因此,如果不放心,一直使用make full來(lái)代替make也是可以的。
- make full_run是make run的full版本。即將apilib和OS核心都make以后,再執(zhí)行原本的make run。注意,make full不會(huì)生成磁盤印象。
- 主要使用make run、make run_vmware。
- 注意:已經(jīng)執(zhí)行過(guò)了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# 默認(rèn)動(dòng)作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:生成一個(gè)包含OS內(nèi)核以及全部應(yīng)用程序的磁盤映像。
- make run:make之后使用QEMU啟動(dòng)。
- make install:make后將磁盤映像文件安裝到軟盤中。
- make full:將OS內(nèi)核、apilib和應(yīng)用程序全部執(zhí)行make后生成磁盤映像。
- make run_full:make full后make run。
- make install_full:make full后make install。
- make run_os: 將OS核心make后make run,當(dāng)只對(duì)OS核心進(jìn)行修改時(shí)可以使用這個(gè)命令。
- make clean:clean命令原本是用于清除臨時(shí)文件的,但由于這個(gè)Makefile不生成臨時(shí)文件(.map之類的),因此這個(gè)命令不執(zhí)行任何操作。
- make src_only:將生成的磁盤映像文件刪除。
- make clean_full:對(duì)OS核心、apilib和全部應(yīng)用程序執(zhí)行make clean,這樣清除所有臨時(shí)文件。
- make src_only_full:對(duì)OS核心、apilib和全部應(yīng)用程序執(zhí)行make src_only,這樣將清除所有的臨時(shí)文件和最終生成文件。執(zhí)行這個(gè)命令后,make和make run就無(wú)法運(yùn)行了(因?yàn)槟J(rèn)apilib是被make了的,即文件apilib.lib是存在的)。可以使用make full代替。
- make refresh:make full后make clean_full。從執(zhí)行過(guò)make src_only_full的狀態(tài)執(zhí)行這個(gè)命令的話,就會(huì)恢復(fù)到可以直接make和make run的狀態(tài)。
- 可以使用很多命令:
-
經(jīng)過(guò)上述劃分以及修改后,make的速度將會(huì)大大提升。
- 在這個(gè)過(guò)程中,順便將某些應(yīng)用程序修改了一下。比如將winhelo.hrb這個(gè)應(yīng)用程序,添加了等待鍵盤輸入的代碼。
- 修改的應(yīng)用程序有winhelo、winhelo2、star1、stars和stars2。
-
在harib24g下make full并用VMware運(yùn)行:
- 全家桶(笑)。
8. 寫在23:20
- 現(xiàn)在是2020.5.7 23:20。
- 已經(jīng)到了既定的日期,可是還沒(méi)有完成任務(wù),真是羞赧不堪啊。
- 預(yù)計(jì)這月10號(hào)可以完成。
- 昨日,爸媽去種花生。早上因?yàn)槠鸫矚鉀](méi)及時(shí)去幫家里種花生,讓老媽生氣了,現(xiàn)在想想自己真是混蛋,后悔極了。
- 從昨天早上七點(diǎn)半,到傍晚六點(diǎn)半,一直在種花生,可是還是沒(méi)有種完。
- 這次應(yīng)該是我自初三以來(lái)第一次幫家里種花生。高中學(xué)業(yè)繁忙,大學(xué)回不了家。
- 農(nóng)民階級(jí)是最受累的階級(jí)。那種面朝黃土背朝天的滋味真的不好受。
- 出去中了一天的花生,回來(lái)后我的腰背、手臂和腿腳便酸痛得厲害。更甚的是,回來(lái)后后頸的皮膚一直如針扎般疼痛,原來(lái)是曬傷了!
- 我盡量將這一切說(shuō)的云淡風(fēng)輕,可是,農(nóng)活是真的不輕松:翻地、施肥、打嶺、栽種、噴藥、貼地膜、蓋土、修地邊。
- 我的手掌原本是肉色的,施肥以后就變成了黑色的,撒種以后就變成了紅色的,蓋完土以后又變成了黃色。
- 今天完成了第27天的工作,文檔行數(shù)也過(guò)了1000行,明天繼續(xù)。
總結(jié)
以上是生活随笔為你收集整理的第27天 LDT与库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 双核旗舰处理器:德仪Omap4430、高
- 下一篇: CHD-5.3.6集群安装