Fixed version of hw2

Signed-off-by: jmug <u.g.a.mariano@gmail.com>
This commit is contained in:
Mariano Uvalle 2025-01-27 19:31:19 -08:00
parent 3308388106
commit b8fc429f4d
25 changed files with 1983 additions and 1963 deletions

View file

@ -4,32 +4,35 @@ FROM ubuntu:20.04
# Create a user # Create a user
ARG USERNAME=cis3410 ARG USERNAME=cs131
ARG USER_UID=1000 ARG USER_UID=1000
ARG USER_GID=$USER_UID ARG USER_GID=$USER_UID
ENV TZ='Asia/Shanghai' ENV TZ='Asia/Shanghai'
# !!![zjy] apt change ustc source # !!![zjy] apt change ustc source
RUN apt-get update -y\ RUN apt-get update -y \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
apt-transport-https \ apt-transport-https \
ca-certificates \ ca-certificates \
dos2unix \
tzdata \ tzdata \
&& sed -i "s@http://.*.ubuntu.com@https://mirrors.ustc.edu.cn@g" /etc/apt/sources.list \ && sed -i "s@http://.*.ubuntu.com@https://mirrors.ustc.edu.cn@g" /etc/apt/sources.list \
&& rm -rf /var/apt/cache/* && rm -rf /var/apt/cache/*
RUN groupadd --gid $USER_GID $USERNAME \ RUN groupadd --gid $USER_GID $USERNAME \
#
# [Optional] Add sudo support. Omit if you don't need to install software after connecting. # [Optional] Add sudo support. Omit if you don't need to install software after connecting.
&& apt-get update -y \ && apt-get update \
&& apt-get install -y sudo \ && apt-get install -y sudo \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME && chmod 0440 /etc/sudoers.d/$USERNAME
## Hack needs root permissions ## Hack needs root permissions
# See hack.sh # See hack.sh
COPY hack.sh /tmp/hack.sh COPY hack.sh /tmp/hack.sh
# windows compatibility
RUN dos2unix /tmp/hack.sh
RUN chmod +x /tmp/hack.sh RUN chmod +x /tmp/hack.sh
RUN /tmp/hack.sh RUN /tmp/hack.sh
@ -46,8 +49,10 @@ RUN apt-get install -y zsh
# !!![zjy] install zsh first then set user # !!![zjy] install zsh first then set user
RUN useradd --uid $USER_UID --gid $USER_GID -m $USERNAME --shell /bin/zsh RUN useradd --uid $USER_UID --gid $USER_GID -m $USERNAME --shell /bin/zsh
## Set up user environmnent ## Set up user environmnent
COPY .zshrc /home/$USERNAME/ COPY .zshrc /home/$USERNAME/
RUN dos2unix /home/$USERNAME/.zshrc
RUN chown $USERNAME /home/$USERNAME/.zshrc RUN chown $USERNAME /home/$USERNAME/.zshrc
## Run in usermode ## Run in usermode
@ -60,12 +65,13 @@ RUN touch /home/$USERNAME/.local/state/utop-history
# Configure opam/ocaml # Configure opam/ocaml
# !!![zjy] change default repo to github (SJTU repo is failed) # !!![zjy] change default repo to github (SJTU repo is failed)
RUN opam init --yes --disable-sandboxing default https://github.com/ocaml/opam-repository.git # RUN opam init --yes --disable-sandboxing default https://github.com/ocaml/opam-repository.git
RUN opam switch create 4.14.1 ocaml-base-compiler.4.14.1 RUN opam init -y --disable-sandboxing --compiler=4.14.1
# RUN opam switch create 4.14.1 ocaml-base-compiler.4.14.1
RUN opam switch 4.14.1 RUN opam switch 4.14.1
RUN opam install -y dune RUN opam install --yes dune
RUN opam install -y num RUN opam install --yes num
RUN opam install -y menhir RUN opam install --yes menhir
RUN opam install -y utop RUN opam install -y utop
RUN opam install -y ocamlformat RUN opam install -y ocamlformat
RUN opam install -y ocaml-lsp-server RUN opam install -y ocaml-lsp-server

View file

@ -20,8 +20,7 @@
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [
"ocamllabs.ocaml-platform", "ocamllabs.ocaml-platform"
"allanblanchard.ocp-indent"
] ]
} }
} }

View file

@ -1,2 +1,2 @@
profile = janestreet profile = janestreet
version = 0.26.1 version = 0.26.2

View file

@ -9,12 +9,12 @@ open X86
(* simulator machine state -------------------------------------------------- *) (* simulator machine state -------------------------------------------------- *)
let mem_bot = 0x400000L (* lowest valid address *) let mem_bot = 0x400000L (* lowest valid address *)
let mem_top = 0x410000L (* one past the last byte in memory *) let mem_top = 0x410000L (* one past the last byte in memory *)
let mem_size = Int64.to_int (Int64.sub mem_top mem_bot) let mem_size = Int64.to_int (Int64.sub mem_top mem_bot)
let nregs = 17 (* including Rip *) let nregs = 17 (* including Rip *)
let ins_size = 8L (* assume we have a 8-byte encoding *) let ins_size = 8L (* assume we have a 8-byte encoding *)
let exit_addr = 0xfdeadL (* halt when m.regs(%rip) = exit_addr *) let exit_addr = 0xfdeadL (* halt when m.regs(%rip) = exit_addr *)
(* The simulator memory maps addresses to symbolic bytes. Symbolic (* The simulator memory maps addresses to symbolic bytes. Symbolic
bytes are either actual data indicated by the Byte constructor or bytes are either actual data indicated by the Byte constructor or
@ -28,234 +28,253 @@ let exit_addr = 0xfdeadL (* halt when m.regs(%rip) = exit_addr *)
elements, which aren't valid data. elements, which aren't valid data.
For example, the two-instruction sequence: For example, the two-instruction sequence:
at&t syntax ocaml syntax at&t syntax ocaml syntax
movq %rdi, (%rsp) Movq, [~%Rdi; Ind2 Rsp] movq %rdi, (%rsp) Movq, [~%Rdi; Ind2 Rsp]
decq %rdi Decq, [~%Rdi] decq %rdi Decq, [~%Rdi]
is represented by the following elements of the mem array (starting is represented by the following elements of the mem array (starting
at address 0x400000): at address 0x400000):
0x400000 : InsB0 (Movq, [~%Rdi; Ind2 Rsp]) 0x400000 : InsB0 (Movq, [~%Rdi; Ind2 Rsp])
0x400001 : InsFrag 0x400001 : InsFrag
0x400002 : InsFrag 0x400002 : InsFrag
0x400003 : InsFrag 0x400003 : InsFrag
0x400004 : InsFrag 0x400004 : InsFrag
0x400005 : InsFrag 0x400005 : InsFrag
0x400006 : InsFrag 0x400006 : InsFrag
0x400007 : InsFrag 0x400007 : InsFrag
0x400008 : InsB0 (Decq, [~%Rdi]) 0x400008 : InsB0 (Decq, [~%Rdi])
0x40000A : InsFrag 0x40000A : InsFrag
0x40000B : InsFrag 0x40000B : InsFrag
0x40000C : InsFrag 0x40000C : InsFrag
0x40000D : InsFrag 0x40000D : InsFrag
0x40000E : InsFrag 0x40000E : InsFrag
0x40000F : InsFrag 0x40000F : InsFrag
0x400010 : InsFrag 0x400010 : InsFrag
*) *)
type sbyte = InsB0 of ins (* 1st byte of an instruction *) type sbyte =
| InsFrag (* 2nd - 8th bytes of an instruction *) | InsB0 of ins (* 1st byte of an instruction *)
| Byte of char (* non-instruction byte *) | InsFrag (* 2nd - 8th bytes of an instruction *)
| Byte of char (* non-instruction byte *)
(* memory maps addresses to symbolic bytes *) (* memory maps addresses to symbolic bytes *)
type mem = sbyte array type mem = sbyte array
(* Flags for condition codes *) (* Flags for condition codes *)
type flags = { mutable fo : bool type flags =
; mutable fs : bool { mutable fo : bool
; mutable fz : bool ; mutable fs : bool
} ; mutable fz : bool
}
(* Register files *) (* Register files *)
type regs = int64 array type regs = int64 array
(* Complete machine state *) (* Complete machine state *)
type mach = { flags : flags type mach =
; regs : regs { flags : flags
; mem : mem ; regs : regs
} ; mem : mem
}
(* simulator helper functions ----------------------------------------------- *) (* simulator helper functions ----------------------------------------------- *)
(* The index of a register in the regs array *) (* The index of a register in the regs array *)
let rind : reg -> int = function let rind : reg -> int = function
| Rip -> 16 | Rip -> 16
| Rax -> 0 | Rbx -> 1 | Rcx -> 2 | Rdx -> 3 | Rax -> 0
| Rsi -> 4 | Rdi -> 5 | Rbp -> 6 | Rsp -> 7 | Rbx -> 1
| R08 -> 8 | R09 -> 9 | R10 -> 10 | R11 -> 11 | Rcx -> 2
| R12 -> 12 | R13 -> 13 | R14 -> 14 | R15 -> 15 | Rdx -> 3
| Rsi -> 4
| Rdi -> 5
| Rbp -> 6
| Rsp -> 7
| R08 -> 8
| R09 -> 9
| R10 -> 10
| R11 -> 11
| R12 -> 12
| R13 -> 13
| R14 -> 14
| R15 -> 15
;;
(* Helper functions for reading/writing sbytes *) (* Helper functions for reading/writing sbytes *)
(* Convert an int64 to its sbyte representation *) (* Convert an int64 to its sbyte representation *)
let sbytes_of_int64 (i:int64) : sbyte list = let sbytes_of_int64 (i : int64) : sbyte list =
let open Char in let open Char in
let open Int64 in let open Int64 in
List.map (fun n -> Byte (shift_right i n |> logand 0xffL |> to_int |> chr)) List.map
[0; 8; 16; 24; 32; 40; 48; 56] (fun n -> Byte (shift_right i n |> logand 0xffL |> to_int |> chr))
[ 0; 8; 16; 24; 32; 40; 48; 56 ]
;;
(* Convert an sbyte representation to an int64 *) (* Convert an sbyte representation to an int64 *)
let int64_of_sbytes (bs:sbyte list) : int64 = let int64_of_sbytes (bs : sbyte list) : int64 =
let open Char in let open Char in
let open Int64 in let open Int64 in
let f b i = match b with let f b i =
match b with
| Byte c -> logor (shift_left i 8) (c |> code |> of_int) | Byte c -> logor (shift_left i 8) (c |> code |> of_int)
| _ -> 0L | _ -> 0L
in in
List.fold_right f bs 0L List.fold_right f bs 0L
;;
(* Convert a string to its sbyte representation *) (* Convert a string to its sbyte representation *)
let sbytes_of_string (s:string) : sbyte list = let sbytes_of_string (s : string) : sbyte list =
let rec loop acc = function let rec loop acc = function
| i when i < 0 -> acc | i when i < 0 -> acc
| i -> loop (Byte s.[i]::acc) (pred i) | i -> loop (Byte s.[i] :: acc) (pred i)
in in
loop [Byte '\x00'] @@ String.length s - 1 loop [ Byte '\x00' ] @@ (String.length s - 1)
;;
(* Serialize an instruction to sbytes *) (* Serialize an instruction to sbytes *)
let sbytes_of_ins (op, args:ins) : sbyte list = let sbytes_of_ins ((op, args) : ins) : sbyte list =
let check = function let check = function
| Imm (Lbl _) | Ind1 (Lbl _) | Ind3 (Lbl _, _) -> | Imm (Lbl _) | Ind1 (Lbl _) | Ind3 (Lbl _, _) ->
invalid_arg "sbytes_of_ins: tried to serialize a label!" invalid_arg "sbytes_of_ins: tried to serialize a label!"
| _ -> () | _ -> ()
in in
List.iter check args; List.iter check args;
[InsB0 (op, args); InsFrag; InsFrag; InsFrag; [ InsB0 (op, args); InsFrag; InsFrag; InsFrag; InsFrag; InsFrag; InsFrag; InsFrag ]
InsFrag; InsFrag; InsFrag; InsFrag] ;;
(* Serialize a data element to sbytes *) (* Serialize a data element to sbytes *)
let sbytes_of_data : data -> sbyte list = function let sbytes_of_data : data -> sbyte list = function
| Quad (Lit i) -> sbytes_of_int64 i | Quad (Lit i) -> sbytes_of_int64 i
| Asciz s -> sbytes_of_string s | Asciz s -> sbytes_of_string s
| Quad (Lbl _) -> invalid_arg "sbytes_of_data: tried to serialize a label!" | Quad (Lbl _) -> invalid_arg "sbytes_of_data: tried to serialize a label!"
;;
(* It might be useful to toggle printing of intermediate states of your (* It might be useful to toggle printing of intermediate states of your
simulator. Our implementation uses this mutable flag to turn on/off simulator. Our implementation uses this mutable flag to turn on/off
printing. For instance, you might write something like: printing. For instance, you might write something like:
[if !debug_simulator then print_endline @@ string_of_ins u; ...] [if !debug_simulator then print_endline @@ string_of_ins u; ...]
*) *)
let debug_simulator = ref false let debug_simulator = ref false
(* override some useful operators *)
(* override some useful operators *)
let ( +. ) = Int64.add let ( +. ) = Int64.add
let ( -. ) = Int64.sub let ( -. ) = Int64.sub
let ( *. ) = Int64.mul let ( *. ) = Int64.mul
let ( <. ) a b = (Int64.compare a b) < 0 let ( <. ) a b = Int64.compare a b < 0
let ( >. ) a b = (Int64.compare a b) > 0 let ( >. ) a b = Int64.compare a b > 0
let ( <=. ) a b = (Int64.compare a b) <= 0 let ( <=. ) a b = Int64.compare a b <= 0
let ( >=. ) a b = (Int64.compare a b) >= 0 let ( >=. ) a b = Int64.compare a b >= 0
(* Interpret a condition code with respect to the given flags. *) (* Interpret a condition code with respect to the given flags. *)
(* !!! Check the Specification for Help *) (* !!! Check the Specification for Help *)
let interp_cnd {fo; fs; fz} : cnd -> bool = fun x -> failwith "interp_cnd unimplemented" let interp_cnd { fo; fs; fz } : cnd -> bool = fun x -> failwith "interp_cnd unimplemented"
(* Maps an X86lite address into Some OCaml array index, (* Maps an X86lite address into Some OCaml array index,
or None if the address is not within the legal address space. *) or None if the address is not within the legal address space. *)
let map_addr (addr:quad) : int option = let map_addr (addr : quad) : int option = failwith "map_addr not implemented"
failwith "map_addr not implemented"
(* Your simulator should raise this exception if it tries to read from or (* Your simulator should raise this exception if it tries to read from or
store to an address not within the valid address space. *) store to an address not within the valid address space. *)
exception X86lite_segfault exception X86lite_segfault
(* Raise X86lite_segfault when addr is invalid. *) (* Raise X86lite_segfault when addr is invalid. *)
let map_addr_segfault (addr:quad) : int = let map_addr_segfault (addr : quad) : int = failwith "map_addr_segfault not implemented"
failwith "map_addr_segfault not implemented"
(* Simulates one step of the machine: (* Simulates one step of the machine:
- fetch the instruction at %rip - fetch the instruction at %rip
- compute the source and/or destination information from the operands - compute the source and/or destination information from the operands
- simulate the instruction semantics - simulate the instruction semantics
- update the registers and/or memory appropriately - update the registers and/or memory appropriately
- set the condition flags - set the condition flags
We provide the basic structure of step function and helper functions. We provide the basic structure of step function and helper functions.
Implement the subroutine below to complete the step function. Implement the subroutine below to complete the step function.
See step function to understand each subroutine and how they See step function to understand each subroutine and how they
are glued together. are glued together.
*) *)
let readquad (m:mach) (addr:quad) : quad = let readquad (m : mach) (addr : quad) : quad = failwith "readquad not implemented"
failwith "readquad not implemented"
let writequad (m : mach) (addr : quad) (w : quad) : unit =
let writequad (m:mach) (addr:quad) (w:quad) : unit =
failwith "writequad not implemented" failwith "writequad not implemented"
;;
let fetchins (m:mach) (addr:quad) : ins = let fetchins (m : mach) (addr : quad) : ins = failwith "fetchins not implemented"
failwith "fetchins not implemented"
(* Compute the instruction result. (* Compute the instruction result.
* NOTE: See int64_overflow.ml for the definition of the return type * NOTE: See int64_overflow.ml for the definition of the return type
* Int64_overflow.t. *) * Int64_overflow.t. *)
let interp_opcode (m: mach) (o:opcode) (args:int64 list) : Int64_overflow.t = let interp_opcode (m : mach) (o : opcode) (args : int64 list) : Int64_overflow.t =
let open Int64 in let open Int64 in
let open Int64_overflow in let open Int64_overflow in
match o, args with match o, args with
| _ -> failwith "interp_opcode not implemented" | _ -> failwith "interp_opcode not implemented"
;;
(** Update machine state with instruction results. *) (** Update machine state with instruction results. *)
let ins_writeback (m: mach) : ins -> int64 -> unit = let ins_writeback (m : mach) : ins -> int64 -> unit =
failwith "ins_writeback not implemented" failwith "ins_writeback not implemented"
;;
(* mem addr ---> mem array index *) (* mem addr ---> mem array index *)
let interp_operands (m:mach) : ins -> int64 list = let interp_operands (m : mach) : ins -> int64 list =
failwith "interp_operands not implemented" failwith "interp_operands not implemented"
;;
let validate_operands : ins -> unit = function let validate_operands : ins -> unit = function
| _ -> failwith "validate_operands not implemented" | _ -> failwith "validate_operands not implemented"
;;
let crack : ins -> ins list = function let crack : ins -> ins list = function
| _ -> failwith "crack not implemented" | _ -> failwith "crack not implemented"
;;
(* TODO: double check against spec *) (* TODO: double check against spec *)
let set_flags (m:mach) (op:opcode) (ws: quad list) (w : Int64_overflow.t) : unit = let set_flags (m : mach) (op : opcode) (ws : quad list) (w : Int64_overflow.t) : unit =
failwith "set_flags not implemented" failwith "set_flags not implemented"
;;
let step (m:mach) : unit = let step (m : mach) : unit =
(* execute an instruction *) (* execute an instruction *)
let (op, args) as ins = fetchins m m.regs.(rind Rip) in let ((op, args) as ins) = fetchins m m.regs.(rind Rip) in
validate_operands ins; validate_operands ins;
(* Some instructions involve running two or more basic instructions. (* Some instructions involve running two or more basic instructions.
* For other instructions, just return a list of one instruction. * For other instructions, just return a list of one instruction.
* See the X86lite specification for details. *) * See the X86lite specification for details. *)
let uops: ins list = crack (op,args) in let uops : ins list = crack (op, args) in
m.regs.(rind Rip) <- m.regs.(rind Rip) +. ins_size; m.regs.(rind Rip) <- m.regs.(rind Rip) +. ins_size;
List.iter List.iter
(fun (uop,_ as u) -> (fun ((uop, _) as u) ->
if !debug_simulator then print_endline @@ string_of_ins u; if !debug_simulator then print_endline @@ string_of_ins u;
let ws = interp_operands m u in let ws = interp_operands m u in
let res = interp_opcode m uop ws in let res = interp_opcode m uop ws in
ins_writeback m u @@ res.Int64_overflow.value; ins_writeback m u @@ res.Int64_overflow.value;
set_flags m op ws res set_flags m op ws res)
) uops uops
;;
(* Runs the machine until the rip register reaches a designated (* Runs the machine until the rip register reaches a designated
memory address. Returns the contents of %rax when the memory address. Returns the contents of %rax when the
machine halts. *) machine halts. *)
let run (m:mach) : int64 = let run (m : mach) : int64 =
while m.regs.(rind Rip) <> exit_addr do step m done; while m.regs.(rind Rip) <> exit_addr do
step m
done;
m.regs.(rind Rax) m.regs.(rind Rax)
;;
(* assembling and linking --------------------------------------------------- *) (* assembling and linking --------------------------------------------------- *)
(* A representation of the executable *) (* A representation of the executable *)
type exec = { entry : quad (* address of the entry point *) type exec =
; text_pos : quad (* starting address of the code *) { entry : quad (* address of the entry point *)
; data_pos : quad (* starting address of the data *) ; text_pos : quad (* starting address of the code *)
; text_seg : sbyte list (* contents of the text segment *) ; data_pos : quad (* starting address of the data *)
; data_seg : sbyte list (* contents of the data segment *) ; text_seg : sbyte list (* contents of the text segment *)
} ; data_seg : sbyte list (* contents of the data segment *)
}
(* Assemble should raise this when a label is used but not defined *) (* Assemble should raise this when a label is used but not defined *)
exception Undefined_sym of lbl exception Undefined_sym of lbl
@ -266,8 +285,8 @@ exception Redefined_sym of lbl
(* Convert an X86 program into an object file: (* Convert an X86 program into an object file:
- separate the text and data segments - separate the text and data segments
- compute the size of each segment - compute the size of each segment
Note: the size of an Asciz string section is (1 + the string length) Note: the size of an Asciz string section is (1 + the string length)
due to the null terminator due to the null terminator
- resolve the labels to concrete addresses and 'patch' the instructions to - resolve the labels to concrete addresses and 'patch' the instructions to
replace Lbl values with the corresponding Imm values. replace Lbl values with the corresponding Imm values.
@ -276,29 +295,25 @@ exception Redefined_sym of lbl
- the text segment starts at the lowest address - the text segment starts at the lowest address
- the data segment starts after the text segment - the data segment starts after the text segment
HINT: List.fold_left and List.fold_right are your friends. HINT: List.fold_left and List.fold_right are your friends.
*) *)
let is_size (is: ins list): quad = let is_size (is : ins list) : quad = failwith "is_size not implemented"
failwith "is_size not implemented" let ds_size (ds : data list) : quad = failwith "ds_size not implemented"
let assemble (p : prog) : exec = failwith "assemble unimplemented"
let ds_size (ds: data list): quad =
failwith "ds_size not implemented"
let assemble (p:prog) : exec =
failwith "assemble unimplemented"
(* Convert an object file into an executable machine state. (* Convert an object file into an executable machine state.
- allocate the mem array - allocate the mem array
- set up the memory state by writing the symbolic bytes to the - set up the memory state by writing the symbolic bytes to the
appropriate locations appropriate locations
- create the inital register state - create the inital register state
- initialize rip to the entry point address - initialize rip to the entry point address
- initializes rsp to the last word in memory - initializes rsp to the last word in memory
- the other registers are initialized to 0 - the other registers are initialized to 0
- the condition code flags start as 'false' - the condition code flags start as 'false'
Hint: The Array.make, Array.blit, and Array.of_list library functions Hint: The Array.make, Array.blit, and Array.of_list library functions
may be of use. may be of use.
*) *)
let load {entry; text_pos; data_pos; text_seg; data_seg} : mach = let load { entry; text_pos; data_pos; text_seg; data_seg } : mach =
failwith "load not implemented" failwith "load not implemented"
;;