Compare commits

...

No commits in common. "main" and "gh-pages" have entirely different histories.

17 changed files with 808 additions and 381 deletions

View file

@ -1,16 +0,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

View file

@ -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
View file

View 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!

View file

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

View file

@ -1,37 +0,0 @@
let query_manifest file =
Owee_buf.map_binary file
|> Solo5_elftool.query_manifest
|> 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 &
info ~doc ~docv:"EXECUTABLE" [])
let query_manifest_cmd =
let doc = "query solo5 manifest" in
Cmdliner.Term.(
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_choice
default_cmd
[query_manifest_cmd; query_abi_cmd])

2
doc/highlight.pack.js Normal file

File diff suppressed because one or more lines are too long

19
doc/index.html Normal file
View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>index</title>
<link rel="stylesheet" href="./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.3.1</span></li>
</ol>
</div>
</main>
</body>
</html>

783
doc/odoc.css Normal file
View file

@ -0,0 +1,783 @@
@charset "UTF-8";
/* Copyright (c) 2016 The odoc contributors. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
odoc 2.1.0 */
/* Fonts */
@import url('https://fonts.googleapis.com/css?family=Fira+Mono:400,500');
@import url('https://fonts.googleapis.com/css?family=Noticia+Text:400,400i,700');
@import url('https://fonts.googleapis.com/css?family=Fira+Sans:400,400i,500,500i,600,600i,700,700i');
:root,
.light:root {
--main-background: #FFFFFF;
--color: #333333;
--link-color: #2C94BD;
--anchor-hover: #555;
--anchor-color: #d5d5d5;
--xref-shadow: #cc6666;
--header-shadow: #ddd;
--by-name-version-color: #aaa;
--by-name-nav-link-color: #222;
--target-background: rgba(187, 239, 253, 0.3);
--target-shadow: rgba(187, 239, 253, 0.8);
--pre-border-color: #eee;
--code-background: #f6f8fa;
--li-code-background: #f6f8fa;
--li-code-color: #0d2b3e;
--toc-color: #1F2D3D;
--toc-before-color: #777;
--toc-background: #f6f8fa;
--toc-list-border: #ccc;
--spec-summary-border-color: #5c9cf5;
--spec-summary-background: var(--code-background);
--spec-summary-hover-background: #ebeff2;
--spec-details-after-background: rgba(0, 4, 15, 0.05);
--spec-details-after-shadow: rgba(204, 204, 204, 0.53);
}
.dark:root {
--main-background: #202020;
--code-background: #222;
--line-numbers-background: rgba(0, 0, 0, 0.125);
--navbar-background: #202020;
--color: #bebebe;
--dirname-color: #666;
--underline-color: #444;
--visited-color: #002800;
--visited-number-color: #252;
--unvisited-color: #380000;
--unvisited-number-color: #622;
--somevisited-color: #303000;
--highlight-color: #303e3f;
--line-number-color: rgba(230, 230, 230, 0.3);
--unvisited-margin-color: #622;
--border: #333;
--navbar-border: #333;
--code-color: #ccc;
--li-code-background: #373737;
--li-code-color: #999;
--toc-color: #777;
--toc-background: #252525;
--hljs-link: #999;
--hljs-keyword: #cda869;
--hljs-regexp: #f9ee98;
--hljs-title: #dcdcaa;
--hljs-type: #ac885b;
--hljs-meta: #82aaff;
--hljs-variable: #cf6a4c;
}
@media (prefers-color-scheme: dark) {
:root {
--main-background: #202020;
--code-background: #333;
--line-numbers-background: rgba(0, 0, 0, 0.125);
--navbar-background: #202020;
--meter-unvisited-color: #622;
--meter-visited-color: #252;
--meter-separator-color: black;
--color: #bebebe;
--dirname-color: #666;
--underline-color: #444;
--visited-color: #002800;
--visited-number-color: #252;
--unvisited-color: #380000;
--unvisited-number-color: #622;
--somevisited-color: #303000;
--highlight-color: #303e3f;
--line-number-color: rgba(230, 230, 230, 0.3);
--unvisited-margin-color: #622;
--border: #333;
--navbar-border: #333;
--code-color: #ccc;
--by-name-nav-link-color: var(--color);
--li-code-background: #373737;
--li-code-color: #999;
--toc-color: #777;
--toc-before-color: #777;
--toc-background: #252525;
--toc-list-border: #ccc;
--spec-summary-hover-background: #ebeff2;
--spec-details-after-background: rgba(0, 4, 15, 0.05);
--spec-details-after-shadow: rgba(204, 204, 204, 0.53);
--hljs-link: #999;
--hljs-keyword: #cda869;
--hljs-regexp: #f9ee98;
--hljs-title: #dcdcaa;
--hljs-type: #ac885b;
--hljs-meta: #82aaff;
--hljs-variable: #cf6a4c;
}
}
/* Reset a few things. */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
*, *:before, *:after {
box-sizing: border-box;
}
html {
font-size: 15px;
}
body {
text-align: left;
background: #FFFFFF;
color: var(--color);
background-color: var(--main-background);
}
body {
max-width: 100ex;
margin-left: calc(10vw + 20ex);
margin-right: 4ex;
margin-top: 20px;
margin-bottom: 50px;
font-family: "Noticia Text", Georgia, serif;
line-height: 1.5;
}
header {
margin-bottom: 30px;
}
nav {
font-family: "Fira Sans", Helvetica, Arial, sans-serif;
}
/* Basic markup elements */
b, strong {
font-weight: bold;
}
i {
font-style: italic;
}
em, i em.odd{
font-style: italic;
}
em.odd, i em {
font-style: normal;
}
sup {
vertical-align: super;
}
sub {
vertical-align: sub;
}
sup, sub {
font-size: 12px;
line-height: 0;
margin-left: 0.2ex;
}
ul, ol {
list-style-position: outside
}
ul>li {
margin-left: 22px;
}
ol>li {
margin-left: 27.2px;
}
li>*:first-child {
margin-top: 0
}
/* Text alignements, this should be forbidden. */
.left {
text-align: left;
}
.right {
text-align: right;
}
.center {
text-align: center;
}
/* Links and anchors */
a {
text-decoration: none;
color: var(--link-color);
}
a:hover {
box-shadow: 0 1px 0 0 var(--link-color);
}
/* Linked highlight */
*:target {
background-color: var(--target-background) !important;
box-shadow: 0 0px 0 1px var(--target-shadow) !important;
border-radius: 1px;
}
*:hover > a.anchor {
visibility: visible;
}
a.anchor:before {
content: "#";
}
a.anchor:hover {
box-shadow: none;
text-decoration: none;
color: var(--anchor-hover);
}
a.anchor {
visibility: hidden;
position: absolute;
/* top: 0px; */
/* margin-left: -3ex; */
margin-left: -1.3em;
font-weight: normal;
font-style: normal;
padding-right: 0.4em;
padding-left: 0.4em;
/* To remain selectable */
color: var(--anchor-color);
}
.spec > a.anchor {
margin-left: -2.3em;
padding-right: 0.9em;
}
.xref-unresolved {
color: #2C94BD;
}
.xref-unresolved:hover {
box-shadow: 0 1px 0 0 var(--xref-shadow);
}
/* Section and document divisions.
Until at least 4.03 many of the modules of the stdlib start at .h7,
we restart the sequence there like h2 */
h1, h2, h3, h4, h5, h6, .h7, .h8, .h9, .h10 {
font-family: "Fira Sans", Helvetica, Arial, sans-serif;
font-weight: 400;
padding-top: 0.1em;
line-height: 1.2;
overflow-wrap: break-word;
}
h1 {
font-weight: 500;
font-size: 2.441em;
}
h1 {
font-weight: 500;
font-size: 1.953em;
box-shadow: 0 1px 0 0 var(--header-shadow);
}
h2 {
font-size: 1.563em;
}
h3 {
font-size: 1.25em;
}
small, .font_small {
font-size: 0.8em;
}
h1 code, h1 tt {
font-size: inherit;
font-weight: inherit;
}
h2 code, h2 tt {
font-size: inherit;
font-weight: inherit;
}
h3 code, h3 tt {
font-size: inherit;
font-weight: inherit;
}
h3 code, h3 tt {
font-size: inherit;
font-weight: inherit;
}
h4 {
font-size: 1.12em;
}
/* Comment delimiters, hidden but accessible to screen readers and
selected for copy/pasting */
/* Taken from bootstrap */
/* See also https://stackoverflow.com/a/27769435/4220738 */
.comment-delim {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Preformatted and code */
tt, code, pre {
font-family: "Fira Mono", courier;
font-weight: 400;
}
pre {
padding: 0.1em;
border: 1px solid var(--pre-border-color);
border-radius: 5px;
overflow-x: auto;
}
p code,
li code {
background-color: var(--li-code-background);
color: var(--li-code-color);
border-radius: 3px;
padding: 0 0.3ex;
}
p a > code {
color: var(--link-color);
}
code {
white-space: pre-wrap;
}
/* Code blocks (e.g. Examples) */
pre code {
font-size: 0.893rem;
}
/* Code lexemes */
.keyword {
font-weight: 500;
}
.arrow { white-space: nowrap }
/* Module member specification */
.spec {
background-color: var(--spec-summary-background);
border-radius: 3px;
border-left: 4px solid var(--spec-summary-border-color);
border-right: 5px solid transparent;
padding: 0.35em 0.5em;
}
.def-doc {
margin-bottom: 10px;
}
/* Spacing between items */
div.odoc-spec,.odoc-include {
margin-bottom: 2em;
}
.spec.type .variant {
margin-left: 2ch;
}
.spec.type .variant p {
margin: 0;
font-style: italic;
}
.spec.type .record {
margin-left: 2ch;
}
.spec.type .record p {
margin: 0;
font-style: italic;
}
div.def {
margin-top: 0;
text-indent: -2ex;
padding-left: 2ex;
}
div.def+div.def-doc {
margin-left: 1ex;
margin-top: 2.5px
}
div.def-doc>*:first-child {
margin-top: 0;
}
/* Collapsible inlined include and module */
.odoc-include details {
position: relative;
}
.odoc-include.shadowed-include {
display: none;
}
.odoc-include details:after {
z-index: -100;
display: block;
content: " ";
position: absolute;
border-radius: 0 1ex 1ex 0;
right: -20px;
top: 1px;
bottom: 1px;
width: 15px;
background: var(--spec-details-after-background, rgba(0, 4, 15, 0.05));
box-shadow: 0 0px 0 1px var(--spec-details-after-shadow, rgba(204, 204, 204, 0.53));
}
.odoc-include summary {
position: relative;
margin-bottom: 1em;
cursor: pointer;
outline: none;
}
.odoc-include summary:hover {
background-color: var(--spec-summary-hover-background);
}
/* FIXME: Does not work in Firefox. */
.odoc-include summary::-webkit-details-marker {
color: #888;
transform: scaleX(-1);
position: absolute;
top: calc(50% - 5px);
height: 11px;
right: -29px;
}
/* Records and variants FIXME */
div.def table {
text-indent: 0em;
padding: 0;
margin-left: -2ex;
}
td.def {
padding-left: 2ex;
}
td.def-doc *:first-child {
margin-top: 0em;
}
/* Lists of @tags */
.at-tags { list-style-type: none; margin-left: -3ex; }
.at-tags li { padding-left: 3ex; text-indent: -3ex; }
.at-tags .at-tag { text-transform: capitalize }
/* Lists of modules */
.modules { list-style-type: none; margin-left: -3ex; }
.modules li { padding-left: 3ex; text-indent: -3ex; margin-top: 5px }
.modules .synopsis { padding-left: 1ch; }
/* Odig package index */
.packages { list-style-type: none; margin-left: -3ex; }
.packages li { padding-left: 3ex; text-indent: -3ex }
.packages li a.anchor { padding-right: 0.5ch; padding-left: 3ch; }
.packages .version { font-size: 10px; color: var(--by-name-version-color); }
.packages .synopsis { padding-left: 1ch }
.by-name nav a {
text-transform: uppercase;
font-size: 18px;
margin-right: 1ex;
color: var(--by-name-nav-link-color,);
display: inline-block;
}
.by-tag nav a {
margin-right: 1ex;
color: var(--by-name-nav-link-color);
display: inline-block;
}
.by-tag ol { list-style-type: none; }
.by-tag ol.tags li { margin-left: 1ch; display: inline-block }
.by-tag td:first-child { text-transform: uppercase; }
/* Odig package page */
.package nav {
display: inline;
font-size: 14px;
font-weight: normal;
}
.package .version {
font-size: 14px;
}
.package.info {
margin: 0;
}
.package.info td:first-child {
font-style: italic;
padding-right: 2ex;
}
.package.info ul {
list-style-type: none;
display: inline;
margin: 0;
}
.package.info li {
display: inline-block;
margin: 0;
margin-right: 1ex;
}
#info-authors li, #info-maintainers li {
display: block;
}
/* Sidebar and TOC */
.odoc-toc:before {
display: block;
content: "Contents";
text-transform: uppercase;
font-size: 1em;
margin: 1.414em 0 0.5em;
font-weight: 500;
color: var(--toc-before-color);
line-height: 1.2;
}
.odoc-toc {
position: fixed;
top: 0px;
bottom: 0px;
left: 0px;
max-width: 30ex;
min-width: 26ex;
width: 20%;
background: var(--toc-background);
overflow: auto;
color: var(--toc-color);
padding-left: 2ex;
padding-right: 2ex;
}
.odoc-toc ul li a {
font-family: "Fira Sans", sans-serif;
font-size: 0.95em;
color: var(--color);
font-weight: 400;
line-height: 1.6em;
display: block;
}
.odoc-toc ul li a:hover {
box-shadow: none;
text-decoration: underline;
}
/* First level titles */
.odoc-toc>ul>li>a {
font-weight: 500;
}
.odoc-toc li ul {
margin: 0px;
}
.odoc-toc ul {
list-style-type: none;
}
.odoc-toc ul li {
margin: 0;
}
.odoc-toc>ul>li {
margin-bottom: 0.3em;
}
.odoc-toc ul li li {
border-left: 1px solid var(--toc-list-border);
margin-left: 5px;
padding-left: 12px;
}
/* Mobile adjustements. */
@media only screen and (max-width: 95ex) {
.odoc-content {
margin: auto;
padding: 2em;
}
.odoc-toc {
position: static;
width: auto;
min-width: unset;
max-width: unset;
border: none;
padding: 0.2em 1em;
border-radius: 5px;
}
}
/* Print adjustements. */
@media print {
body {
color: black;
background: white;
}
body nav:first-child {
visibility: hidden;
}
}
/* Syntax highlighting (based on github-gist) */
.hljs {
display: block;
background: var(--code-background);
padding: 0.5em;
color: var(--color);
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag {
color: #a71d5d;
}
.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: 500;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/*---------------------------------------------------------------------------
Copyright (c) 2016 The odoc contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*/

View file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>index (solo5-elftool.index)</title><link rel="stylesheet" href="../odoc.css"/><meta charset="utf-8"/><meta name="generator" content="odoc 2.1.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../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>

View file

@ -1,2 +0,0 @@
(lang dune 2.9)
(name solo5-elftool)

View file

@ -1,4 +0,0 @@
(library
(public_name solo5-elftool)
(name solo5_elftool)
(libraries owee cstruct fmt))

View file

@ -1,211 +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 buf =
(* invariant: Cstruct.length buf = sizeof_mft_entry *)
let name_raw = Cstruct.sub buf 0 68 in
let typ = Cstruct.LE.get_uint32 buf 68 in
let u = Cstruct.sub buf 72 16 in
let b = Cstruct.sub buf 88 8 in
let attached = Cstruct.get_uint8 buf 96 <> 0 in
let* name =
Cstruct.cut ~sep:(Cstruct.create 1) name_raw
|> Option.map (fun (name, _) -> Cstruct.to_string name)
|> Option.to_result ~none:(`Msg "unterminated device name")
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.attached" (not attached) in
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
| Dev_block_basic ->
Ok (`Dev_block_basic name)
| Dev_net_basic ->
Ok (`Dev_net_basic name)
let parse_mft buf =
let buf = Cstruct.of_string buf in
let* () = guard "manifest too small"
(Cstruct.length buf >= 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 buf = Cstruct.shift buf 4 in
let version = Cstruct.LE.get_uint32 buf 0
and entries = Cstruct.LE.get_uint32 buf 4
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 buf = Cstruct.shift buf 8 in
let* () = guard "unexpected note size"
(Cstruct.length buf = entries * sizeof_mft_entry)
in
let* () =
match parse_mft_entry (Cstruct.sub buf 0 sizeof_mft_entry) with
| Ok `Reserved_first -> Ok ()
| _ -> Error (`Msg "expected RESERVED_FIRST")
in
let buf = Cstruct.shift buf sizeof_mft_entry in
let entries =
Array.init (entries - 1)
(fun i -> Cstruct.sub buf (i * sizeof_mft_entry) sizeof_mft_entry)
in
let* entries =
Array.fold_left
(fun r buf ->
let* acc = r in
let* mft_entry = parse_mft_entry buf 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 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 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
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))

View file

@ -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 : Owee_buf.t -> (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
(** [query_abi buf] is the solo5 abi of [buf], or an error message. *)

View file

@ -1,33 +0,0 @@
opam-version: "2.0"
homepage: "https://git.robur.io/robur/ocaml-solo5-elftool"
dev-repo: "git+https://git.robur.io/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"}
"owee" {>= "0.4"}
"cstruct" {>= "6.0.0"}
"fmt" {>= "0.8.7"}
"cmdliner"
]
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)" ]