Drop owee dependency and work on strings
This commit is contained in:
parent
bd9e1c1b87
commit
0a04cc0bcc
4 changed files with 200 additions and 53 deletions
30
bin/main.ml
30
bin/main.ml
|
@ -1,13 +1,33 @@
|
|||
let read_binary file =
|
||||
let ic = open_in_bin file in
|
||||
let res = Buffer.create 16384 in
|
||||
let buf = Bytes.create 16384 in
|
||||
let rec loop () =
|
||||
let len = input ic buf 0 16384 in
|
||||
if len > 0 then
|
||||
let () = Buffer.add_subbytes res buf 0 len in
|
||||
loop ()
|
||||
in
|
||||
loop ();
|
||||
close_in_noerr ic;
|
||||
Buffer.contents res
|
||||
|
||||
let query_manifest file =
|
||||
Owee_buf.map_binary file
|
||||
read_binary file
|
||||
|> Solo5_elftool.query_manifest
|
||||
|> Result.iter (fun mft ->
|
||||
Fmt.pr "%a\n" Solo5_elftool.pp_mft mft)
|
||||
|> Result.fold
|
||||
~ok:(fun mft ->
|
||||
Fmt.pr "%a\n" Solo5_elftool.pp_mft mft)
|
||||
~error:(fun (`Msg e) ->
|
||||
Fmt.epr "%s\n" e)
|
||||
|
||||
let query_abi file =
|
||||
Owee_buf.map_binary file
|
||||
read_binary file
|
||||
|> Solo5_elftool.query_abi
|
||||
|> Result.iter (Fmt.pr "%a\n" Solo5_elftool.pp_abi)
|
||||
|> Result.fold
|
||||
~ok:(fun abi -> Fmt.pr "%a\n" Solo5_elftool.pp_abi abi)
|
||||
~error:(fun (`Msg e) ->
|
||||
Fmt.epr "%s\n" e)
|
||||
|
||||
let file =
|
||||
let doc = "Solo5 executable" in
|
||||
|
|
165
lib/elf.ml
Normal file
165
lib/elf.ml
Normal file
|
@ -0,0 +1,165 @@
|
|||
exception Elf_error
|
||||
|
||||
(* only the bits we care about *)
|
||||
type header = {
|
||||
e_shoff : int;
|
||||
e_shentsize : int;
|
||||
e_shnum : int;
|
||||
e_shstrndx : int;
|
||||
}
|
||||
|
||||
type section = {
|
||||
sh_offset : int;
|
||||
sh_size : int;
|
||||
sh_name_off : int;
|
||||
sh_name : string;
|
||||
}
|
||||
|
||||
let section_manifest = ".note.solo5.manifest"
|
||||
let section_abi = ".note.solo5.abi"
|
||||
let note_name = "Solo5"
|
||||
let typ_mft1 = 0x3154464d
|
||||
let typ_abi1 = 0x31494241
|
||||
|
||||
let get_uint16 = function
|
||||
| `LE -> String.get_uint16_le
|
||||
| `BE -> String.get_uint16_be
|
||||
|
||||
let get_uint32 en s off =
|
||||
let get = match en with
|
||||
| `LE -> String.get_int32_le
|
||||
| `BE -> String.get_int32_be
|
||||
in
|
||||
Int32.to_int (get s off) land 0xFFFF_FFFF
|
||||
|
||||
let get_uint64 en s off =
|
||||
let get = match en with
|
||||
| `LE -> String.get_int64_le
|
||||
| `BE -> String.get_int64_be
|
||||
in
|
||||
match Int64.unsigned_to_int (get s off) with
|
||||
| None -> raise Elf_error
|
||||
| Some n -> n
|
||||
|
||||
let c_string s off maxlen =
|
||||
let rec scan_c_string i =
|
||||
if String.length s < off + i || i = maxlen then
|
||||
raise Elf_error
|
||||
else if s.[i+off] = '\000' then
|
||||
i
|
||||
else
|
||||
scan_c_string (succ i)
|
||||
in
|
||||
String.sub s off (scan_c_string 0)
|
||||
|
||||
let read_magic s off =
|
||||
if String.length s < off + 4 then
|
||||
raise Elf_error;
|
||||
let valid =
|
||||
String.get_uint8 s off = 0x7f &&
|
||||
s.[off+1] = 'E' && s.[off+2] = 'L' && s.[off+3] = 'F'
|
||||
in
|
||||
if not valid then
|
||||
raise Elf_error;
|
||||
off+4
|
||||
|
||||
let elfclass64 = 2
|
||||
|
||||
let read_identification s off =
|
||||
if String.length s < off + 12 then
|
||||
raise Elf_error;
|
||||
let elf_class = String.get_uint8 s off in
|
||||
let elf_data = String.get_uint8 s (off+1) in
|
||||
let _elf_version = String.get_uint8 s (off+2) in
|
||||
let _elf_osabi = String.get_uint8 s (off+3) in
|
||||
let _elf_abiversion = String.get_uint8 s (off+4) in
|
||||
(* Check padding *)
|
||||
for i = off + 5 to off+11 do
|
||||
if s.[i] <> '\000' then
|
||||
raise Elf_error
|
||||
done;
|
||||
(* we only support ELFCLASS64 *)
|
||||
if elf_class <> elfclass64 then
|
||||
raise Elf_error;
|
||||
let endianness =
|
||||
match elf_data with
|
||||
| 1 -> `LE
|
||||
| 2 -> `BE
|
||||
| _ -> raise Elf_error
|
||||
in
|
||||
endianness, off+12
|
||||
|
||||
let read_header en s =
|
||||
if String.length s < 16 + 48 then
|
||||
raise Elf_error;
|
||||
let e_shoff = get_uint32 en s 0x28 in
|
||||
let e_shentsize = get_uint16 en s 0x3a in
|
||||
let e_shnum = get_uint16 en s 0x3c in
|
||||
let e_shstrndx = get_uint16 en s 0x3e in
|
||||
if Sys.int_size <= 32 then
|
||||
raise Elf_error;
|
||||
{ e_shoff; e_shentsize; e_shnum; e_shstrndx }
|
||||
|
||||
let read_section en s hdr i =
|
||||
let off = hdr.e_shoff + i * hdr.e_shentsize in
|
||||
if String.length s < off + 64 then
|
||||
raise Elf_error;
|
||||
let sh_name_off = get_uint32 en s off in
|
||||
let sh_offset = get_uint64 en s (off + 24) in
|
||||
let sh_size = get_uint64 en s (off + 32) in
|
||||
{ sh_name_off; sh_offset; sh_size; sh_name = "" }
|
||||
|
||||
let read_section_name shstrndx s section =
|
||||
let off = shstrndx.sh_offset + section.sh_name_off in
|
||||
if String.length s < off + 1 then
|
||||
raise Elf_error;
|
||||
c_string s off (shstrndx.sh_size - section.sh_name_off)
|
||||
|
||||
let read_sections en s hdr =
|
||||
let sections = Array.init hdr.e_shnum (read_section en s hdr) in
|
||||
let shstrndx = sections.(hdr.e_shstrndx) in
|
||||
Array.map
|
||||
(fun section -> { section with sh_name = read_section_name shstrndx s section })
|
||||
sections
|
||||
|
||||
let find_section sections name =
|
||||
Array.find_opt
|
||||
(fun section -> String.equal section.sh_name name)
|
||||
sections
|
||||
|
||||
let section_body s section =
|
||||
if section.sh_offset < 0 || String.length s < section.sh_offset + section.sh_size then
|
||||
raise Elf_error;
|
||||
String.sub s section.sh_offset section.sh_size
|
||||
|
||||
let desc en section_body ~expected_owner ~expected_type =
|
||||
if String.length section_body < 12 then
|
||||
raise Elf_error;
|
||||
let namesz = get_uint32 en section_body 0 in
|
||||
let descsz = get_uint32 en section_body 4
|
||||
and typ = get_uint32 en section_body 8 in
|
||||
if String.length section_body < 12 + namesz + descsz then
|
||||
raise Elf_error;
|
||||
if typ <> expected_type ||
|
||||
String.length expected_owner + 1 <> namesz ||
|
||||
not (String.equal
|
||||
(expected_owner ^ "\000")
|
||||
(String.sub section_body 12 namesz))
|
||||
then
|
||||
None
|
||||
else
|
||||
let off = 12 + namesz in
|
||||
(* padding *)
|
||||
let off = off + ((4 - (off land 3)) land 3) in
|
||||
Some (String.sub section_body off descsz)
|
||||
|
||||
let find s section_name typ =
|
||||
let off = read_magic s 0 in
|
||||
let en, _off = read_identification s off in
|
||||
let hdr = read_header en s in
|
||||
let sections = read_sections en s hdr in
|
||||
match find_section sections section_name with
|
||||
| None -> None
|
||||
| Some section ->
|
||||
let body = section_body s section in
|
||||
desc en body ~expected_owner:note_name ~expected_type:typ
|
|
@ -160,52 +160,14 @@ let parse_abi buf =
|
|||
(* XXX: should we check version = 1l ? *)
|
||||
Ok { target; version }
|
||||
|
||||
let ( let* ) = Result.bind
|
||||
|
||||
let note_name = "Solo5"
|
||||
let typ_mft1 = 0x3154464d
|
||||
let typ_abi1 = 0x31494241
|
||||
|
||||
let query_manifest_exn buf =
|
||||
let _header, sections = Owee_elf.read_elf buf in
|
||||
let* section =
|
||||
Owee_elf.find_section sections ".note.solo5.manifest"
|
||||
|> Option.to_result ~none:(`Msg "section .note.solo5.manifest 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_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_manifest buf =
|
||||
try query_manifest_exn buf with
|
||||
| Out_of_memory -> raise Out_of_memory
|
||||
| e -> Error (`Msg ("query manifest failure: " ^ Printexc.to_string e))
|
||||
|
||||
let query_abi_exn 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
|
||||
match Elf.find buf Elf.section_manifest Elf.typ_mft1 with
|
||||
| None -> Error (`Msg "manifest not found")
|
||||
| Some desc -> parse_mft desc
|
||||
(*| exception Elf.Elf_error -> Error (`Msg "error during ELF parsing")*)
|
||||
|
||||
let query_abi buf =
|
||||
try query_abi_exn buf with
|
||||
| Out_of_memory -> raise Out_of_memory
|
||||
| e -> Error (`Msg ("query abi failure: " ^ Printexc.to_string e))
|
||||
match Elf.find buf Elf.section_abi Elf.typ_abi1 with
|
||||
| None -> Error (`Msg "manifest not found")
|
||||
| Some desc -> parse_abi desc
|
||||
(*| exception Elf.Elf_error -> Error (`Msg "error during ELF parsing")*)
|
||||
|
|
|
@ -36,8 +36,8 @@ 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 : string -> (mft, [> `Msg of string ]) result
|
||||
(** [query_manifest buf] is the solo5 manifest of [buf], or an error message. *)
|
||||
|
||||
val query_abi : Owee_buf.t -> (abi, [> `Msg of string ]) result
|
||||
val query_abi : string -> (abi, [> `Msg of string ]) result
|
||||
(** [query_abi buf] is the solo5 abi of [buf], or an error message. *)
|
||||
|
|
Loading…
Reference in a new issue