From d3c7b0cd0063becd4856a83210e44c20ff436d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reynir=20Bj=C3=B6rnsson?= Date: Thu, 16 Dec 2021 12:12:43 +0100 Subject: [PATCH] Rename binary, implement query-abi, ... * The binary is renamed from solo5-elftool to osolo5-elftool to allow for distinguishing from the binary from solo5 * query_abi is implemented and osolo5-elftool is extended with a query-abi sub command * instead of [assert false] on unknown manifest entry types or abi targets an [Error (`Msg ...)] is returned. --- bin/dune | 2 +- bin/main.ml | 20 +++++++++- lib/solo5_elftool.ml | 86 ++++++++++++++++++++++++++++++++++++++----- lib/solo5_elftool.mli | 28 +++++++++++++- 4 files changed, 124 insertions(+), 12 deletions(-) diff --git a/bin/dune b/bin/dune index fe78246..462cfa5 100644 --- a/bin/dune +++ b/bin/dune @@ -1,4 +1,4 @@ (executable - (public_name solo5-elftool) + (public_name osolo5-elftool) (name main) (libraries solo5-elftool owee cstruct cmdliner)) diff --git a/bin/main.ml b/bin/main.ml index 74c7195..c4a2436 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -4,6 +4,11 @@ let query_manifest file = |> Result.iter (fun mft -> Fmt.pr "%a\n" Solo5_elftool.pp_mft mft) +let query_abi file = + Owee_buf.map_binary file + |> Solo5_elftool.query_abi + |> Result.iter (Fmt.pr "%a\n" Solo5_elftool.pp_abi) + let file = let doc = "Solo5 executable" in Cmdliner.Arg.(required & pos 0 (some file) None & @@ -15,5 +20,18 @@ let query_manifest_cmd = pure query_manifest $ file, info ~doc "query-manifest") +let query_abi_cmd = + let doc = "query solo5 abi" in + Cmdliner.Term.( + pure query_abi $ file, + info ~doc "query-abi") + +let default_cmd = + Cmdliner.Term.( + ret (pure (fun man_format -> `Help (man_format, None)) $ man_format), + info "osolo5-elftool") + let () = - ignore (Cmdliner.Term.eval query_manifest_cmd) + ignore (Cmdliner.Term.eval_choice + default_cmd + [query_manifest_cmd; query_abi_cmd]) diff --git a/lib/solo5_elftool.ml b/lib/solo5_elftool.ml index 9d27ec8..1fd8d9e 100644 --- a/lib/solo5_elftool.ml +++ b/lib/solo5_elftool.ml @@ -12,11 +12,33 @@ type mft = { entries : mft_entry list; } -let mft_type_of_int : int32 -> mft_type = function - | 1l -> Dev_block_basic - | 2l -> Dev_net_basic - | 1073741824l -> Reserved_first - | _ -> assert false +type abi_target = + | Hvt + | Spt + | Virtio + | Muen + | Genode + | Xen + +type abi = { + target : abi_target; + version : int32; +} + +let mft_type_of_int : int32 -> (mft_type, _) result = function + | 1l -> Ok Dev_block_basic + | 2l -> Ok Dev_net_basic + | 1073741824l -> Ok Reserved_first + | v -> Error (`Msg ("unknown manifest entry type: " ^ Int32.to_string v)) + +let abi_target_of_int : int32 -> (abi_target, _) result = function + | 1l -> Ok Hvt + | 2l -> Ok Spt + | 3l -> Ok Virtio + | 4l -> Ok Muen + | 5l -> Ok Genode + | 6l -> Ok Xen + | v -> Error (`Msg ("unknown abi target: " ^ Int32.to_string v)) let pp_mft_entry ppf = function | Dev_block_basic name -> @@ -29,6 +51,19 @@ let pp_mft ppf { version; entries } = {|{@[<1>@ "type": "solo5.manifest",@ "version": %d,@ "devices": [@[<1>@ %a@]@ ]@]@ }|} version Fmt.(list ~sep:(append (any ",") sp) pp_mft_entry) entries +let pp_abi_target ppf = function + | Hvt -> Format.fprintf ppf "hvt" + | Spt -> Format.fprintf ppf "spt" + | Virtio -> Format.fprintf ppf "virtio" + | Muen -> Format.fprintf ppf "muen" + | Genode -> Format.fprintf ppf "genode" + | Xen -> Format.fprintf ppf "xen" + +let pp_abi ppf { version; target } = + Fmt.pf ppf + {|{@[<1>@ "type": "solo5.abi",@ "target": "%a",@ "version": %lu@ @]@ }|} + pp_abi_target target version + let ( let* ) = Result.bind let guard m b = if not b then Error (`Msg m) else Ok () @@ -50,7 +85,8 @@ let parse_mft_entry buf = let* () = guard "non-zero mft_entry.u" (Cstruct.for_all ((=) '\000') u) in let* () = guard "non-zero mft_entry.b" (Cstruct.for_all ((=) '\000') b) in let* () = guard "non-zero mft_entry.attached" (not attached) in - match mft_type_of_int typ with + let* typ = mft_type_of_int typ in + match typ with | Reserved_first -> let* () = guard "non-zero RESERVED_FIRST" (Cstruct.for_all ((=) '\000') name_raw) in Ok `Reserved_first @@ -111,9 +147,24 @@ let parse_mft buf = in Ok { version = Int32.to_int version; entries } +let parse_abi buf = + let buf = Cstruct.of_string buf in + let* () = guard "abi manifest size mismatch" (Cstruct.length buf = 4 * 4) in + let target = Cstruct.LE.get_uint32 buf 0 in + let version = Cstruct.LE.get_uint32 buf 4 in + let reserved0 = Cstruct.LE.get_uint32 buf 8 in + let reserved1 = Cstruct.LE.get_uint32 buf 12 in + let* target = abi_target_of_int target in + let* () = guard "non-zero reserved0" (reserved0 = 0l) in + let* () = guard "non-zero reserved1" (reserved1 = 0l) in + (* XXX: should we check version = 1l ? *) + Ok { target; version } + let ( let* ) = Result.bind -let mft1_note_name = "Solo5" +let note_name = "Solo5" +let typ_mft1 = 0x3154464d +let typ_abi1 = 0x31494241 let query_manifest buf = let _header, sections = Owee_elf.read_elf buf in @@ -125,9 +176,26 @@ let query_manifest buf = let cursor = Owee_buf.cursor body in let descsz = Owee_elf_notes.read_desc_size cursor - ~expected_owner:mft1_note_name - ~expected_type:0x3154464d (* MFT1 *) + ~expected_owner:note_name + ~expected_type:typ_mft1 in let desc = Owee_buf.Read.fixed_string cursor descsz in let* () = guard "extra data" (Owee_buf.at_end cursor) in parse_mft desc + +let query_abi buf = + let _header, sections = Owee_elf.read_elf buf in + let* section = + Owee_elf.find_section sections ".note.solo5.abi" + |> Option.to_result ~none:(`Msg "section .note.solo5.abi not found") + in + let body = Owee_elf.section_body buf section in + let cursor = Owee_buf.cursor body in + let descsz = + Owee_elf_notes.read_desc_size cursor + ~expected_owner:note_name + ~expected_type:typ_abi1 + in + let desc = Owee_buf.Read.fixed_string cursor descsz in + let* () = guard "extra data" (Owee_buf.at_end cursor) in + parse_abi desc diff --git a/lib/solo5_elftool.mli b/lib/solo5_elftool.mli index c3e5712..33dcf74 100644 --- a/lib/solo5_elftool.mli +++ b/lib/solo5_elftool.mli @@ -11,11 +11,37 @@ type mft = { (** [entries] in the manifest. *) } +(** The known solo5 targets *) +type abi_target = + | Hvt + | Spt + | Virtio + | Muen + | Genode + | Xen + +(** abi taget and abi version *) +type abi = { + target : abi_target; (** abi target *) + version : int32; (** abi version *) +} + val pp_mft_entry : Format.formatter -> mft_entry -> unit val pp_mft : Format.formatter -> mft -> unit (** Pretty-prints the manifest as JSON in a similar style as the Solo5 command * line tool {[solo5-elftool query-manifest]}. *) +val pp_abi_target : Format.formatter -> abi_target -> unit +val pp_abi : Format.formatter -> abi -> unit +(** Pretty-prints the manifest as JSON in a similar style as the Solo5 command + * line tool {[solo5-elftool query-abi]}. *) + val query_manifest : Owee_buf.t -> (mft, [> `Msg of string ]) result (** [query_manifest buf] is the solo5 manifest of [buf], or an error message. - * @raise Owee_buf.Invalid_format If [buf] does not contain valid ELF format *) + + @raise Owee_buf.Invalid_format if [buf] is not valid ELF format *) + +val query_abi : Owee_buf.t -> (abi, [> `Msg of string ]) result +(** [query_abi buf] is the solo5 abi of [buf], or an error message. + + @raise Owee_buf.Invalid_format if [buf] is not valid ELF format *)