Compare commits
No commits in common. "main" and "gh-pages" have entirely different histories.
58 changed files with 2118 additions and 557 deletions
25
CHANGES.md
25
CHANGES.md
|
@ -1,25 +0,0 @@
|
|||
## v0.4.0 (2025-02-04)
|
||||
|
||||
* Reimplement the necessary ELF parsing and drop the owee dependency. This makes it possible to use this library in Mirage.
|
||||
* **BREAKING**: Switch to cachet instead of requiring the whole binary in memory.
|
||||
|
||||
## v0.3.1 (2022-03-16)
|
||||
|
||||
* Update to cmdliner 1.1.0
|
||||
|
||||
## v0.3.0 (2022-01-28)
|
||||
|
||||
* Exceptions from Owee are caught in `query_abi` and `query_manifest`
|
||||
|
||||
## v0.2.0 (2021-12-16)
|
||||
|
||||
* Rename binary from solo5-elftool to osolo5-elftool so it can co-exist with
|
||||
the C binary
|
||||
* Implement `query_abi` and `osolo5-elftool query-abi`
|
||||
* Return a result error instead of an assertion exception on unknown manifest
|
||||
entry types
|
||||
* Remove noop tests
|
||||
|
||||
## v0.1.0 (2021-12-15)
|
||||
|
||||
* Initial public release
|
23
LICENSE.md
23
LICENSE.md
|
@ -1,23 +0,0 @@
|
|||
Copyright (c) 2021, Reynir Björnsson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
0
README
Normal file
0
README
Normal file
|
@ -1,8 +0,0 @@
|
|||
## Solo5-elftool - OCaml Solo5 elftool for querying solo5 manifests
|
||||
|
||||
Solo5 embeds a manifest of which devices a unikernel expects.
|
||||
Solo5-elftool can be used to read and inspect this manifest.
|
||||
|
||||
One advantage over calling out to Solo5's `solo5-elftool` is that a user of the library can read the manifest from an ELF executable held in memory; no need to write the executable to disk first!
|
||||
|
||||
|
4
bin/dune
4
bin/dune
|
@ -1,4 +0,0 @@
|
|||
(executable
|
||||
(public_name osolo5-elftool)
|
||||
(name main)
|
||||
(libraries solo5-elftool unix cachet cmdliner))
|
59
bin/main.ml
59
bin/main.ml
|
@ -1,59 +0,0 @@
|
|||
let map_binary file =
|
||||
let fd = Unix.openfile file [Unix.O_RDONLY; Unix.O_CLOEXEC] 0 in
|
||||
let stat = Unix.fstat fd in
|
||||
let map () ~pos len =
|
||||
let len = Int.min (stat.Unix.st_size - pos) len in
|
||||
let pos = Int64.of_int pos in
|
||||
let barr =
|
||||
Unix.map_file fd ~pos Bigarray.char Bigarray.c_layout false [| len |]
|
||||
in
|
||||
Bigarray.array1_of_genarray barr
|
||||
in
|
||||
at_exit (fun () -> Unix.close fd);
|
||||
Cachet.make ~map ()
|
||||
|
||||
let query_manifest file =
|
||||
map_binary file
|
||||
|> Solo5_elftool.query_manifest
|
||||
|> 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 =
|
||||
map_binary file
|
||||
|> Solo5_elftool.query_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
|
||||
Cmdliner.Arg.(required & pos 0 (some file) None &
|
||||
info ~doc ~docv:"EXECUTABLE" [])
|
||||
|
||||
let query_manifest_cmd =
|
||||
let doc = "query solo5 manifest" in
|
||||
Cmdliner.Cmd.v
|
||||
(Cmdliner.Cmd.info ~doc "query-manifest")
|
||||
Cmdliner.Term.(const query_manifest $ file)
|
||||
|
||||
let query_abi_cmd =
|
||||
let doc = "query solo5 abi" in
|
||||
Cmdliner.Cmd.v
|
||||
(Cmdliner.Cmd.info ~doc "query-abi")
|
||||
Cmdliner.Term.(const query_abi $ file)
|
||||
|
||||
let default_cmd =
|
||||
let open Cmdliner.Term in
|
||||
ret (const (fun man_format -> `Help (man_format, None)) $ Cmdliner.Arg.man_format)
|
||||
|
||||
let () =
|
||||
let cmd =
|
||||
Cmdliner.Cmd.group ~default:default_cmd
|
||||
(Cmdliner.Cmd.info "osolo5-elftool")
|
||||
[query_manifest_cmd; query_abi_cmd]
|
||||
in
|
||||
exit (Cmdliner.Cmd.eval cmd)
|
19
doc/index.html
Normal file
19
doc/index.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>index</title>
|
||||
<link rel="stylesheet" href="./odoc.support/odoc.css"/>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
||||
</head>
|
||||
<body>
|
||||
<main class="content">
|
||||
<div class="by-name">
|
||||
<h2>OCaml package documentation</h2>
|
||||
<ol>
|
||||
<li><a href="solo5-elftool/index.html">solo5-elftool</a> <span class="version">0.4.0</span></li>
|
||||
</ol>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
BIN
doc/odoc.support/fonts/KaTeX_AMS-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_AMS-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Caligraphic-Bold.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Caligraphic-Bold.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Caligraphic-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Caligraphic-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Fraktur-Bold.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Fraktur-Bold.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Fraktur-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Fraktur-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Main-Bold.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Main-Bold.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Main-BoldItalic.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Main-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Main-Italic.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Main-Italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Main-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Main-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Math-BoldItalic.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Math-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Math-Italic.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Math-Italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_SansSerif-Bold.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_SansSerif-Bold.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_SansSerif-Italic.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_SansSerif-Italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_SansSerif-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_SansSerif-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Script-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Script-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Size1-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Size1-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Size2-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Size2-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Size3-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Size3-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Size4-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Size4-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/KaTeX_Typewriter-Regular.woff2
Normal file
BIN
doc/odoc.support/fonts/KaTeX_Typewriter-Regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-mono-v14-latin-500.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-mono-v14-latin-500.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-mono-v14-latin-regular.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-mono-v14-latin-regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-500.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-500.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-500italic.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-500italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-700.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-700.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-700italic.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-700italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-italic.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-regular.woff2
Normal file
BIN
doc/odoc.support/fonts/fira-sans-v17-latin-regular.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/noticia-text-v15-latin-700.woff2
Normal file
BIN
doc/odoc.support/fonts/noticia-text-v15-latin-700.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/noticia-text-v15-latin-italic.woff2
Normal file
BIN
doc/odoc.support/fonts/noticia-text-v15-latin-italic.woff2
Normal file
Binary file not shown.
BIN
doc/odoc.support/fonts/noticia-text-v15-latin-regular.woff2
Normal file
BIN
doc/odoc.support/fonts/noticia-text-v15-latin-regular.woff2
Normal file
Binary file not shown.
634
doc/odoc.support/highlight.pack.js
Normal file
634
doc/odoc.support/highlight.pack.js
Normal file
File diff suppressed because one or more lines are too long
1
doc/odoc.support/katex.min.css
vendored
Normal file
1
doc/odoc.support/katex.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
doc/odoc.support/katex.min.js
vendored
Normal file
1
doc/odoc.support/katex.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1393
doc/odoc.support/odoc.css
Normal file
1393
doc/odoc.support/odoc.css
Normal file
File diff suppressed because it is too large
Load diff
66
doc/odoc.support/odoc_search.js
Normal file
66
doc/odoc.support/odoc_search.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
/* The browsers interpretation of the CORS origin policy prevents to run
|
||||
webworkers from javascript files fetched from the file:// protocol. This hack
|
||||
is to workaround this restriction. */
|
||||
function createWebWorker() {
|
||||
var searchs = search_urls.map((search_url) => {
|
||||
let parts = document.location.href.split("/");
|
||||
parts[parts.length - 1] = search_url;
|
||||
return '"' + parts.join("/") + '"';
|
||||
});
|
||||
blobContents = ["importScripts(" + searchs.join(",") + ");"];
|
||||
var blob = new Blob(blobContents, { type: "application/javascript" });
|
||||
var blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
var worker = new Worker(blobUrl);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
var worker;
|
||||
var waiting = 0;
|
||||
|
||||
function wait() {
|
||||
waiting = waiting + 1;
|
||||
document.querySelector(".search-snake").classList.add("search-busy");
|
||||
}
|
||||
|
||||
function stop_waiting() {
|
||||
if (waiting > 0) waiting = waiting - 1;
|
||||
else waiting = 0;
|
||||
if (waiting == 0) {
|
||||
document.querySelector(".search-snake").classList.remove("search-busy");
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector(".search-bar").addEventListener("focus", (ev) => {
|
||||
if (typeof worker == "undefined") {
|
||||
worker = createWebWorker();
|
||||
worker.onmessage = (e) => {
|
||||
stop_waiting();
|
||||
let results = e.data;
|
||||
let search_results = document.querySelector(".search-result");
|
||||
search_results.innerHTML = "";
|
||||
let f = (entry) => {
|
||||
let search_result = document.createElement("a");
|
||||
search_result.classList.add("search-entry");
|
||||
search_result.href = base_url + entry.url;
|
||||
search_result.innerHTML = entry.html;
|
||||
search_results.appendChild(search_result);
|
||||
};
|
||||
results.forEach(f);
|
||||
let search_request = document.querySelector(".search-bar").value;
|
||||
if (results.length == 0 && search_request != "") {
|
||||
let no_result = document.createElement("div");
|
||||
no_result.classList.add("search-no-result");
|
||||
no_result.innerText = "No result...";
|
||||
search_results.appendChild(no_result);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector(".search-bar").addEventListener("input", (ev) => {
|
||||
wait();
|
||||
worker.postMessage(ev.target.value);
|
||||
});
|
2
doc/solo5-elftool/Solo5_elftool/index.html
Normal file
2
doc/solo5-elftool/Solo5_elftool/index.html
Normal file
File diff suppressed because one or more lines are too long
2
doc/solo5-elftool/index.html
Normal file
2
doc/solo5-elftool/index.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>index (solo5-elftool.index)</title><meta charset="utf-8"/><link rel="stylesheet" href="../odoc.support/odoc.css"/><meta name="generator" content="odoc 2.4.4"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../odoc.support/highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="../index.html">Up</a> – solo5-elftool</nav><header class="odoc-preamble"><h1 id="solo5-elftool-index"><a href="#solo5-elftool-index" class="anchor"></a>solo5-elftool index</h1></header><nav class="odoc-toc"><ul><li><a href="#library-solo5-elftool">Library solo5-elftool</a></li></ul></nav><div class="odoc-content"><h2 id="library-solo5-elftool"><a href="#library-solo5-elftool" class="anchor"></a>Library solo5-elftool</h2><p>The entry point of this library is the module: <a href="Solo5_elftool/index.html"><code>Solo5_elftool</code></a>.</p></div></body></html>
|
|
@ -1,3 +0,0 @@
|
|||
(lang dune 2.9)
|
||||
(name solo5-elftool)
|
||||
(cram enable)
|
4
lib/dune
4
lib/dune
|
@ -1,4 +0,0 @@
|
|||
(library
|
||||
(public_name solo5-elftool)
|
||||
(name solo5_elftool)
|
||||
(libraries cachet fmt))
|
155
lib/elf.ml
155
lib/elf.ml
|
@ -1,155 +0,0 @@
|
|||
module Bstr = Cachet.Bstr
|
||||
|
||||
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 -> Cachet.get_uint16_le
|
||||
| `BE -> Cachet.get_uint16_be
|
||||
|
||||
let get_uint32 en s off =
|
||||
let get = match en with
|
||||
| `LE -> Cachet.get_int32_le
|
||||
| `BE -> Cachet.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 -> Cachet.get_int64_le
|
||||
| `BE -> Cachet.get_int64_be
|
||||
in
|
||||
match Int64.unsigned_to_int (get s off) with
|
||||
| None -> raise Elf_error
|
||||
| Some n -> n
|
||||
|
||||
let c_string seq maxlen =
|
||||
let res = Buffer.create maxlen in
|
||||
let rec scan i = function
|
||||
| Seq.Nil -> raise Elf_error
|
||||
| Seq.Cons (s, seq) ->
|
||||
match String.index_opt s '\000' with
|
||||
| None ->
|
||||
let i = i + String.length s in
|
||||
if i >= maxlen then
|
||||
raise Elf_error;
|
||||
Buffer.add_string res s;
|
||||
scan i (seq ())
|
||||
| Some l ->
|
||||
let i = i + l in
|
||||
if i >= maxlen then
|
||||
raise Elf_error;
|
||||
Buffer.add_substring res s 0 l;
|
||||
Buffer.contents res
|
||||
in
|
||||
scan 0 (seq ())
|
||||
|
||||
let read_magic c =
|
||||
if not (Cachet.get_uint8 c 0 = 0x7f &&
|
||||
String.equal (Cachet.get_string c ~len:3 1) "ELF")
|
||||
then raise Elf_error
|
||||
|
||||
let elfclass64 = 2
|
||||
|
||||
let read_identification c =
|
||||
let elf_class = Cachet.get_uint8 c 4 in
|
||||
let elf_data = Cachet.get_uint8 c 5 in
|
||||
let _elf_version = Cachet.get_uint8 c 6 in
|
||||
let _elf_osabi = Cachet.get_uint8 c 7 in
|
||||
let _elf_abiversion = Cachet.get_uint8 c 8 in
|
||||
for i = 9 to 15 do
|
||||
if Cachet.get_uint8 c i <> 0 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
|
||||
|
||||
let read_header en c =
|
||||
let e_shoff = get_uint32 en c 0x28 in
|
||||
let e_shentsize = get_uint16 en c 0x3a in
|
||||
let e_shnum = get_uint16 en c 0x3c in
|
||||
let e_shstrndx = get_uint16 en c 0x3e in
|
||||
if Sys.int_size <= 32 then
|
||||
raise Elf_error;
|
||||
{ e_shoff; e_shentsize; e_shnum; e_shstrndx }
|
||||
|
||||
let read_section en c hdr i =
|
||||
let off = hdr.e_shoff + i * hdr.e_shentsize in
|
||||
let sh_name_off = get_uint32 en c off in
|
||||
let sh_offset = get_uint64 en c (off + 24) in
|
||||
let sh_size = get_uint64 en c (off + 32) in
|
||||
{ sh_name_off; sh_offset; sh_size; sh_name = "" }
|
||||
|
||||
let read_section_name shstrndx c section =
|
||||
let off = shstrndx.sh_offset + section.sh_name_off in
|
||||
c_string (Cachet.get_seq c off) (shstrndx.sh_size - section.sh_name_off)
|
||||
|
||||
let read_sections en c hdr =
|
||||
let sections = Array.init hdr.e_shnum (read_section en c hdr) in
|
||||
let shstrndx = sections.(hdr.e_shstrndx) in
|
||||
Array.map
|
||||
(fun section -> { section with sh_name = read_section_name shstrndx c section })
|
||||
sections
|
||||
|
||||
let find_section sections name =
|
||||
Array.find_opt
|
||||
(fun section -> String.equal section.sh_name name)
|
||||
sections
|
||||
|
||||
let desc en c section ~expected_owner ~expected_type =
|
||||
let off = section.sh_offset in
|
||||
if section.sh_size < 12 then
|
||||
raise Elf_error;
|
||||
let namesz = get_uint32 en c off
|
||||
and descsz = get_uint32 en c (off + 4)
|
||||
and typ = get_uint32 en c (off + 8) in
|
||||
if typ <> expected_type ||
|
||||
String.length expected_owner + 1 <> namesz ||
|
||||
not (String.equal
|
||||
(expected_owner ^ "\000")
|
||||
(Cachet.get_string c (off+12) ~len:namesz))
|
||||
then
|
||||
None
|
||||
else
|
||||
let off = off + 12 + namesz in
|
||||
(* padding *)
|
||||
let off = off + ((4 - (off land 3)) land 3) in
|
||||
Some (Cachet.get_string c off ~len:descsz)
|
||||
|
||||
let find c section_name typ =
|
||||
let () = read_magic c in
|
||||
let en = read_identification c in
|
||||
let hdr = read_header en c in
|
||||
let sections = read_sections en c hdr in
|
||||
match find_section sections section_name with
|
||||
| None -> None
|
||||
| Some section ->
|
||||
desc en c section ~expected_owner:note_name ~expected_type:typ
|
|
@ -1,172 +0,0 @@
|
|||
type mft_type =
|
||||
| Dev_block_basic
|
||||
| Dev_net_basic
|
||||
| Reserved_first
|
||||
|
||||
type mft_entry =
|
||||
| Dev_block_basic of string
|
||||
| Dev_net_basic of string
|
||||
|
||||
type mft = {
|
||||
version : int;
|
||||
entries : mft_entry list;
|
||||
}
|
||||
|
||||
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 ->
|
||||
Fmt.pf ppf {|{@[<1>@ "name": %S,@ "type": "BLOCK_BASIC"@]@ }|} name
|
||||
| Dev_net_basic name ->
|
||||
Fmt.pf ppf {|{@[<1>@ "name": %S,@ "type": "NET_BASIC"@]@ }|} name
|
||||
|
||||
let pp_mft ppf { version; entries } =
|
||||
Fmt.pf ppf
|
||||
{|{@[<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 ()
|
||||
|
||||
let sizeof_mft_entry = 104
|
||||
let mft_max_entries = 64l
|
||||
|
||||
let parse_mft_entry s =
|
||||
(* invariant: Cstruct.length buf = sizeof_mft_entry *)
|
||||
let name_raw = String.sub s 0 68 in
|
||||
let typ = String.get_int32_le s 68 in
|
||||
let u = String.sub s 72 16 in
|
||||
let b = String.sub s 88 8 in
|
||||
let attached = String.get_uint8 s 96 <> 0 in
|
||||
let* name =
|
||||
String.index_opt name_raw '\000'
|
||||
|> Option.map (fun idx -> String.sub name_raw 0 idx)
|
||||
|> Option.to_result ~none:(`Msg "unterminated device name")
|
||||
in
|
||||
let* () = guard "non-zero mft_entry.u" (String.for_all ((=) '\000') u) in
|
||||
let* () = guard "non-zero mft_entry.b" (String.for_all ((=) '\000') b) in
|
||||
let* () = guard "non-zero mft_entry.attached" (not attached) in
|
||||
let* typ = mft_type_of_int typ in
|
||||
match typ with
|
||||
| Reserved_first ->
|
||||
let* () = guard "non-zero RESERVED_FIRST" (String.for_all ((=) '\000') name_raw) in
|
||||
Ok `Reserved_first
|
||||
| Dev_block_basic ->
|
||||
Ok (`Dev_block_basic name)
|
||||
| Dev_net_basic ->
|
||||
Ok (`Dev_net_basic name)
|
||||
|
||||
let parse_mft s =
|
||||
let* () = guard "manifest too small"
|
||||
(String.length s >= 4 + 8 + sizeof_mft_entry)
|
||||
in
|
||||
(* Solo5 defines a struct mft1_note consisting of the ELF note header
|
||||
* followed by a struct mft for reading and writing the ELF note. The note
|
||||
* header is 20 bytes long, so to get 8-byte alignment the note header is
|
||||
* padded with 4 bytes. See {[solo5/mft_abi.h]}. *)
|
||||
let version = String.get_int32_le s 4
|
||||
and entries = String.get_int32_le s 8
|
||||
in
|
||||
let* () = guard "unsupported manifest version" (version = 1l) in
|
||||
let* () = guard "zero manifest entries" (Int32.unsigned_compare entries 0l > 0) in
|
||||
(* this implicitly checks [Int32.to_int entries > 0] *)
|
||||
let* () = guard "too many manifest entries"
|
||||
(Int32.unsigned_compare entries mft_max_entries <= 0)
|
||||
in
|
||||
(* We have checked that entries interpreted unsigned is between 0 and
|
||||
* mft_max_entries, so this is safely equivalent to:
|
||||
* (Option.get (Int32.unsigned_to_int entries) *)
|
||||
let entries = Int32.to_int entries in
|
||||
let off = 12 in
|
||||
let* () = guard "unexpected note size"
|
||||
(String.length s = entries * sizeof_mft_entry + 12)
|
||||
in
|
||||
let* () =
|
||||
match parse_mft_entry (String.sub s off sizeof_mft_entry) with
|
||||
| Ok `Reserved_first -> Ok ()
|
||||
| _ -> Error (`Msg "expected RESERVED_FIRST")
|
||||
in
|
||||
let off = off + sizeof_mft_entry in
|
||||
let entries =
|
||||
Array.init (entries - 1)
|
||||
(fun i -> String.sub s (off + i * sizeof_mft_entry) sizeof_mft_entry)
|
||||
in
|
||||
let* entries =
|
||||
Array.fold_left
|
||||
(fun r s ->
|
||||
let* acc = r in
|
||||
let* mft_entry = parse_mft_entry s in
|
||||
match mft_entry with
|
||||
| `Dev_block_basic name -> Ok (Dev_block_basic name :: acc)
|
||||
| `Dev_net_basic name -> Ok (Dev_net_basic name :: acc)
|
||||
| `Reserved_first -> Error (`Msg "found RESERVED_FIRST not as first entry"))
|
||||
(Ok [])
|
||||
entries
|
||||
|> Result.map List.rev
|
||||
in
|
||||
Ok { version = Int32.to_int version; entries }
|
||||
|
||||
let parse_abi s =
|
||||
let* () = guard "abi manifest size mismatch" (String.length s = 4 * 4) in
|
||||
let target = String.get_int32_le s 0 in
|
||||
let version = String.get_int32_le s 4 in
|
||||
let reserved0 = String.get_int32_le s 8 in
|
||||
let reserved1 = String.get_int32_le s 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 query_manifest c =
|
||||
match Elf.find c Elf.section_manifest Elf.typ_mft1 with
|
||||
| None -> Error (`Msg "manifest not found")
|
||||
| Some desc -> parse_mft desc
|
||||
| exception Elf.Elf_error | exception Cachet.Out_of_bounds _ ->
|
||||
Error (`Msg "error during ELF parsing")
|
||||
|
||||
let query_abi c =
|
||||
match Elf.find c Elf.section_abi Elf.typ_abi1 with
|
||||
| None -> Error (`Msg "manifest not found")
|
||||
| Some desc -> parse_abi desc
|
||||
| exception Elf.Elf_error | exception Cachet.Out_of_bounds _ ->
|
||||
Error (`Msg "error during ELF parsing")
|
|
@ -1,43 +0,0 @@
|
|||
(** An entry in the manifest representing a device. *)
|
||||
type mft_entry =
|
||||
| Dev_block_basic of string
|
||||
| Dev_net_basic of string
|
||||
|
||||
(** The Solo5 manifest *)
|
||||
type mft = {
|
||||
version : int;
|
||||
(** [version] is at the moment always 1. *)
|
||||
entries : mft_entry list;
|
||||
(** [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 : 'fd Cachet.t -> (mft, [> `Msg of string ]) result
|
||||
(** [query_manifest cachet] is the solo5 manifest of [cachet], or an error message. *)
|
||||
|
||||
val query_abi : 'fd Cachet.t -> (abi, [> `Msg of string ]) result
|
||||
(** [query_abi cachet] is the solo5 abi of [cachet], or an error message. *)
|
|
@ -1,33 +0,0 @@
|
|||
opam-version: "2.0"
|
||||
homepage: "https://git.robur.coop/robur/ocaml-solo5-elftool"
|
||||
dev-repo: "git+https://git.robur.coop/robur/ocaml-solo5-elftool.git"
|
||||
bug-reports: "https://github.com/robur-coop/ocaml-solo5-elftool/issues"
|
||||
doc: "https://robur-coop.github.io/ocaml-solo5-elftool/doc"
|
||||
maintainer: [ "team@robur.coop" ]
|
||||
authors: [ "Reynir Björnsson <reynir@reynir.dk>" ]
|
||||
license: "BSD-2-Clause"
|
||||
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
["dune" "build" "-p" name "-j" jobs]
|
||||
]
|
||||
|
||||
depends: [
|
||||
"ocaml" {>= "4.08.0"}
|
||||
"dune" {>= "2.9"}
|
||||
"cachet"
|
||||
"fmt" {>= "0.8.7"}
|
||||
"cmdliner" {>= "1.1.0"}
|
||||
]
|
||||
|
||||
available: arch != "arm32" & arch != "x86_32"
|
||||
conflicts: [
|
||||
"result" {< "1.5"}
|
||||
]
|
||||
|
||||
synopsis: "OCaml Solo5 elftool for querying solo5 manifests"
|
||||
description: """
|
||||
OCaml Solo5 elftool is a library and executable for reading solo5 device
|
||||
manifests from solo5 ELF executables.
|
||||
"""
|
||||
x-maintenance-intent: [ "(latest)" ]
|
|
@ -1,8 +0,0 @@
|
|||
(cram
|
||||
(deps
|
||||
%{bin:osolo5-elftool}
|
||||
test_hello.hvt
|
||||
test_hello.muen
|
||||
test_hello.spt
|
||||
test_hello.virtio
|
||||
test_hello.xen))
|
|
@ -1,20 +0,0 @@
|
|||
Execute osolo5-elftool on hvt binary
|
||||
|
||||
$ osolo5-elftool query-abi test_hello.hvt
|
||||
{ "type": "solo5.abi", "target": "hvt", "version": 2
|
||||
}
|
||||
$ osolo5-elftool query-manifest test_hello.hvt
|
||||
{ "type": "solo5.manifest", "version": 1, "devices": [ ]
|
||||
}
|
||||
$ osolo5-elftool query-abi test_hello.muen
|
||||
{ "type": "solo5.abi", "target": "muen", "version": 3
|
||||
}
|
||||
$ osolo5-elftool query-abi test_hello.spt
|
||||
{ "type": "solo5.abi", "target": "spt", "version": 2
|
||||
}
|
||||
$ osolo5-elftool query-abi test_hello.virtio
|
||||
{ "type": "solo5.abi", "target": "virtio", "version": 1
|
||||
}
|
||||
$ osolo5-elftool query-abi test_hello.xen
|
||||
{ "type": "solo5.abi", "target": "xen", "version": 1
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue