<contributed by HexRabbit
>
https://hackmd.io/hFGdxT1JRPa8SbdofdNgQw
;
)#define OP(name) OP_##name
##
串接前後字串driver.c: as_exec的主邏輯設計
opcode.c: 定義各個opcode(除call,ret,helt,jmp類)的實做
vm.c: opcode(call,ret,helt,jmp類)的實做,虛擬機器初始化,暫存器定義
as.c: 語法解析,另存成可運行的結構(存於vm_env內)
elf.c: 產生elf檔案用
–
vm_value: 儲存char *
或int
的結構
vm_operand: 儲存運算子
vm_inst: 儲存運算符
vm_seg_info: 資料串列
vm_regs: 儲存pc,sp,from(目前pc在ret後的上一個inst),to(目前branch的第一個inst)
vm_env: 紀錄目前虛擬機狀態
vm_opcode_impl: 儲存opcode實做
vm_handler: function pointer,可用來模擬OOP中的function
static void and_impl(VM_HANDLER_ARGS)
{
- if (VM_T(op1) == INT && VM_T(op1) == INT) {
+ if (VM_T(op1) == INT && VM_T(op2) == INT) {
VM_INT(result) = VM_INT(op1) & VM_INT(op2);
} else {
UNIMPL;
}
}
static void or_impl(VM_HANDLER_ARGS)
{
- if (VM_T(op1) == INT && VM_T(op1) == INT) {
+ if (VM_T(op1) == INT && VM_T(op2) == INT) {
VM_INT(result) = VM_INT(op1) | VM_INT(op2);
} else {
UNIMPL;
}
}
issue
if 判斷式中重複檢查了兩次 VM_T(op1) == INT,應將 op1 改為 op2。
f.write('''/* File opcode.h was generated by scripts/gen_opcode.py. */
#ifndef OPCODE_H
#define OPCODE_H
+ #include "vm.h"
/* opcode listing */
enum {\n''')
issue
由 python 腳本生成的 opcode.h 使用到外部定義的 type vm_env 而未引入 vm.h 可能導致編譯錯誤,將其引入即可。
請提交 pull request,謝謝
搞定!
觀察程式碼時發現 JMP 類與 CALL 在呼叫時不會檢查參數的型別是 CONST 還是 TEMP,預設使用者將填入#X
(TEMP 型別),並直接取出參數(value.id
)作為 pc 的目標,不僅使得以$X
呼叫時會出錯外(value.id
!= 在 cpool 中的 index),使用#X
呼叫卻完全不理會 stack 中存的值,僅僅只是作為一個符號來使用也相當的不符合程式寫作的邏輯。
重新修改為支援#
從stack取值,以$
使用常數操作
+#define VM_CALL() \
+ do { \
+ vm_push(env, env->r.pc); \
+ size_t n = vm_get_op_value(env, &OPCODE.op1)->value.vint; \
+ GOTO(n); \
+ } while (0)
+
+#define VM_JMP() \
+ do { \
+ size_t n = vm_get_op_value(env, &OPCODE.op1)->value.vint; \
+ GOTO(n); \
} while (0)
修改 make_from_operand()
+ case ':':;
+ int label = vm_find_label(env, &data[1]);
+ if (~label) {
+ op.type = CONST;
+ op.value.id = vm_add_const(env, INT, &label);
+ break;
+ } else {
+ printf("Error: specified label name '%s' not found.\n", data);
+ free(line);
+ exit(-1);
+ break;
+ }
修改 assemble_from_fd()
while (getline(&line, &size, fp) != -1) {
if (line[0] == ';' || line[0] == '\n')
continue;
/* Remove trailing newline feed */
line[strcspn(line, "\r\n")] = 0;
+ if (line[strlen(line) - 1] == ':') {
+ line[strlen(line) - 1] = 0;
+ vm_make_label(env, line, insts_count);
+ } else
+ insts_count++;
+ }
+
+ fseek(fp, 0, SEEK_SET);
+
+ while (getline(&line, &size, fp) != -1) {
+ if (line[0] == ';' || line[0] == '\n' || line[strlen(line) - 2] == ':')
+ continue;
+ /* Remove trailing newline feed */
+ line[strcspn(line, "\r\n")] = 0;
assemble_line(env, line);
}
free(line);
採用 djb2 作為 hash function,並暫時選擇 49157 作為 hash table size。
add $5 $0 #0 ; init to 5
jz #0 $8
add $0 $0 #1
add $1 $0 #2
add #1 #2 #2
sub #2 #1 #1
sub #0 $1 #0
jnz #0 $4
print #1
與一般撰寫程式差不多,很直覺。
add $5 $0 #2 ; init to 5
jmp $14
jz #2 $13 ; if 0
sub #2 $1 #3
jz #3 $12 ; if 1
sub #2 $1 #2
call $2
add #2 $1 #2
sub #2 $2 #2
call $2
add #2 $2 #2
ret
add #0 $1 #0 ; add 1 to val
ret
call $2
print #0
jmp 到後面然後再 call 回來的作法實在不是很直觀,就算我以前有寫一點 shellcode 的經驗還是花了些思考時間在上面。
使用此方式若數字過大的話會 overflow 到 heap 上。