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.
This commit is contained in:
Reynir Björnsson 2021-12-16 12:12:43 +01:00
parent c1863a2f5a
commit d3c7b0cd00
4 changed files with 124 additions and 12 deletions

View file

@ -1,4 +1,4 @@
(executable (executable
(public_name solo5-elftool) (public_name osolo5-elftool)
(name main) (name main)
(libraries solo5-elftool owee cstruct cmdliner)) (libraries solo5-elftool owee cstruct cmdliner))

View file

@ -4,6 +4,11 @@ let query_manifest file =
|> Result.iter (fun mft -> |> Result.iter (fun mft ->
Fmt.pr "%a\n" Solo5_elftool.pp_mft 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 file =
let doc = "Solo5 executable" in let doc = "Solo5 executable" in
Cmdliner.Arg.(required & pos 0 (some file) None & Cmdliner.Arg.(required & pos 0 (some file) None &
@ -15,5 +20,18 @@ let query_manifest_cmd =
pure query_manifest $ file, pure query_manifest $ file,
info ~doc "query-manifest") 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 () = let () =
ignore (Cmdliner.Term.eval query_manifest_cmd) ignore (Cmdliner.Term.eval_choice
default_cmd
[query_manifest_cmd; query_abi_cmd])

View file

@ -12,11 +12,33 @@ type mft = {
entries : mft_entry list; entries : mft_entry list;
} }
let mft_type_of_int : int32 -> mft_type = function type abi_target =
| 1l -> Dev_block_basic | Hvt
| 2l -> Dev_net_basic | Spt
| 1073741824l -> Reserved_first | Virtio
| _ -> assert false | 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 let pp_mft_entry ppf = function
| Dev_block_basic name -> | Dev_block_basic name ->
@ -29,6 +51,19 @@ let pp_mft ppf { version; entries } =
{|{@[<1>@ "type": "solo5.manifest",@ "version": %d,@ "devices": [@[<1>@ %a@]@ ]@]@ }|} {|{@[<1>@ "type": "solo5.manifest",@ "version": %d,@ "devices": [@[<1>@ %a@]@ ]@]@ }|}
version Fmt.(list ~sep:(append (any ",") sp) pp_mft_entry) entries 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 ( let* ) = Result.bind
let guard m b = if not b then Error (`Msg m) else Ok () 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.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.b" (Cstruct.for_all ((=) '\000') b) in
let* () = guard "non-zero mft_entry.attached" (not attached) 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 -> | Reserved_first ->
let* () = guard "non-zero RESERVED_FIRST" (Cstruct.for_all ((=) '\000') name_raw) in let* () = guard "non-zero RESERVED_FIRST" (Cstruct.for_all ((=) '\000') name_raw) in
Ok `Reserved_first Ok `Reserved_first
@ -111,9 +147,24 @@ let parse_mft buf =
in in
Ok { version = Int32.to_int version; entries } 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 ( 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 query_manifest buf =
let _header, sections = Owee_elf.read_elf buf in 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 cursor = Owee_buf.cursor body in
let descsz = let descsz =
Owee_elf_notes.read_desc_size cursor Owee_elf_notes.read_desc_size cursor
~expected_owner:mft1_note_name ~expected_owner:note_name
~expected_type:0x3154464d (* MFT1 *) ~expected_type:typ_mft1
in in
let desc = Owee_buf.Read.fixed_string cursor descsz in let desc = Owee_buf.Read.fixed_string cursor descsz in
let* () = guard "extra data" (Owee_buf.at_end cursor) in let* () = guard "extra data" (Owee_buf.at_end cursor) in
parse_mft desc 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

View file

@ -11,11 +11,37 @@ type mft = {
(** [entries] in the manifest. *) (** [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_entry : Format.formatter -> mft_entry -> unit
val pp_mft : Format.formatter -> mft -> unit val pp_mft : Format.formatter -> mft -> unit
(** Pretty-prints the manifest as JSON in a similar style as the Solo5 command (** Pretty-prints the manifest as JSON in a similar style as the Solo5 command
* line tool {[solo5-elftool query-manifest]}. *) * 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 val query_manifest : Owee_buf.t -> (mft, [> `Msg of string ]) result
(** [query_manifest buf] is the solo5 manifest of [buf], or an error message. (** [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 *)