mirror of
https://github.com/GaloisInc/cryptol.git
synced 2024-11-23 22:27:25 +03:00
314 lines
5.8 KiB
Markdown
314 lines
5.8 KiB
Markdown
|
---
|
||
|
title: Cryptol FFI
|
||
|
author: Bretton
|
||
|
date: 2022-09-02
|
||
|
---
|
||
|
|
||
|
# Cryptol FFI
|
||
|
|
||
|
- Call functions written in C
|
||
|
|
||
|
---
|
||
|
|
||
|
# Why?
|
||
|
|
||
|
- Performance
|
||
|
- Use existing code in C
|
||
|
- Do things that Cryptol can't do?
|
||
|
|
||
|
---
|
||
|
|
||
|
# What does it look like?
|
||
|
|
||
|
```cry
|
||
|
foreign add : [32] -> [32] -> [32]
|
||
|
```
|
||
|
|
||
|
```c
|
||
|
uint32_t add(uint32_t x, uint32_t y) {
|
||
|
return x + y;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```
|
||
|
Main> add 1 2
|
||
|
0x00000003
|
||
|
```
|
||
|
|
||
|
---
|
||
|
|
||
|
# How do you use it?
|
||
|
|
||
|
```
|
||
|
+-------------+
|
||
|
| Example.cry | ------------------------+ +---------------Cryptol---------------+
|
||
|
+-------------+ | | Cryptol> :l Example.cry |
|
||
|
| | Loading module Cryptol |
|
||
|
+-----------+ +---------------+ +------>| Loading module Example |
|
||
|
| Example.c | =====> | Example.so | -------->| Loading dynamic library Example.so |
|
||
|
+-----------+ cc | Example.dylib | dlopen | Example> add 1 2 |
|
||
|
+---------------+ | 0x00000003 |
|
||
|
+-------------------------------------+
|
||
|
```
|
||
|
|
||
|
- Find shared library with same name as Cryptol file
|
||
|
- Dynamically load with `dlopen`
|
||
|
- Bind each `foreign` function with `dlsym`
|
||
|
- Windows not supported yet
|
||
|
- Compile it yourself (for now...)
|
||
|
- `cc -fPIC -shared Example.c -o Example.so`
|
||
|
- `cc -dynamiclib Example.c -o Example.dylib`
|
||
|
|
||
|
---
|
||
|
|
||
|
# How does it work?
|
||
|
|
||
|
- Since Cryptol is interpreted, number/types of arguments to foreign functions are not known statically
|
||
|
- `libffi` calls functions with the right calling conventions given interface at runtime
|
||
|
- Rant about `libffi` in Haskell
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## Basic types
|
||
|
|
||
|
Cryptol | C
|
||
|
--- | ---
|
||
|
Bit | uint8_t
|
||
|
[K]Bit, 0 <= K <= 8 | uint8_t
|
||
|
[K]Bit, 8 < K <= 16 | uint16_t
|
||
|
[K]Bit, 16 < K <= 32 | uint32_t
|
||
|
[K]Bit, 32 < K <= 64 | uint64_t
|
||
|
Float32 | float
|
||
|
Float64 | double
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## Multiple arguments?
|
||
|
|
||
|
Cryptol | C
|
||
|
--- | ---
|
||
|
A -> B -> ... -> Z | Z' (A', B', ...)
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## More complex types
|
||
|
|
||
|
- So far it has been pretty simple
|
||
|
- How about sequences? I guess we need arrays
|
||
|
- Who allocates the memory?
|
||
|
- Who owns the memory?
|
||
|
- How are they passed vs returned?
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## Sequences
|
||
|
|
||
|
Cryptol | C
|
||
|
--- | ---
|
||
|
[n1][n2]...[nk]T | U*
|
||
|
|
||
|
where
|
||
|
|
||
|
T | U
|
||
|
--- | ---
|
||
|
[K]Bit, 0 <= K <= 8 | uint8_t
|
||
|
[K]Bit, 8 < K <= 16 | uint16_t
|
||
|
[K]Bit, 16 < K <= 32 | uint32_t
|
||
|
[K]Bit, 32 < K <= 64 | uint64_t
|
||
|
Float32 | float
|
||
|
Float64 | double
|
||
|
|
||
|
- Cryptol allocates and owns all memory
|
||
|
- Sequence as Cryptol parameter: `const U*` C parameter
|
||
|
- Sequence as Cryptol return type: `U*` C output parameter
|
||
|
- Passed after Cryptol parameters
|
||
|
- Return type of C function is `void`
|
||
|
- Multidimensional sequences: flatten (except the [K]Bit part)
|
||
|
- How about n1, n2, ..., nk?
|
||
|
- Must be `fin`
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported ~~types~~ kinds?
|
||
|
|
||
|
## Numeric type arguments
|
||
|
|
||
|
Cryptol | C
|
||
|
--- | ---
|
||
|
\# | size_t
|
||
|
|
||
|
- Must be `fin`
|
||
|
- Type arguments are passed before any value arguments
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## Tuples and records as parameters
|
||
|
|
||
|
- Passed as separate arguments
|
||
|
|
||
|
Cryptol | C (multiple!)
|
||
|
--- | ---
|
||
|
(T1, T2, ..., Tn) | U1, U2, ..., Un
|
||
|
{s1: T1, s2: T2, ..., sn: Tn} | U1, U2, ..., Un
|
||
|
|
||
|
where
|
||
|
|
||
|
Cryptol | C
|
||
|
--- | ---
|
||
|
Ti | Ui
|
||
|
|
||
|
Note:
|
||
|
- Order of record fields is however it's written in the type in the source code
|
||
|
- Uncurried and curried Cryptol functions correspond to the same C function
|
||
|
- () corresponds to no arguments at all
|
||
|
- Can be nested
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## Tuples and records as return types
|
||
|
|
||
|
- Returned as separate output parameters
|
||
|
- This means the component types need to become pointers
|
||
|
- Except for sequences which are already pointers
|
||
|
|
||
|
Cryptol | C (multiple!)
|
||
|
--- | ---
|
||
|
(T1, T2, ..., Tn) | V1, V2, ..., Vn
|
||
|
{s1: T1, s2: T2, ..., sn: Tn} | V1, V2, ..., Vn
|
||
|
|
||
|
where
|
||
|
|
||
|
Cryptol parameter | C
|
||
|
--- | ---
|
||
|
Ti | Ui
|
||
|
|
||
|
and Vi = if Ui is a pointer type then Ui else Ui*
|
||
|
|
||
|
Note:
|
||
|
- Return type of C function is `void`
|
||
|
- () corresponds to no return value and no output arguments
|
||
|
- Can be nested
|
||
|
|
||
|
---
|
||
|
|
||
|
# Supported types
|
||
|
|
||
|
## Type synonyms
|
||
|
|
||
|
- Type synonyms are expanded before applying the previous rules
|
||
|
|
||
|
---
|
||
|
|
||
|
# Examples
|
||
|
|
||
|
```cry
|
||
|
foreign f : {n} (fin n) => [n][10] -> {a : Bit, b : [64]} -> (Float64, [n + 1][20])
|
||
|
```
|
||
|
|
||
|
```c
|
||
|
void f(size_t n, uint16_t *in0, uint8_t in1, uint64_t in2,
|
||
|
double *out0, uint32_t *out1);
|
||
|
```
|
||
|
|
||
|
- More examples in FFI tests
|
||
|
|
||
|
---
|
||
|
|
||
|
# Evaluation
|
||
|
|
||
|
- All arguments are fully evaluated
|
||
|
- Result will be fully evaluated too (of course)
|
||
|
|
||
|
---
|
||
|
|
||
|
# Questionable uses of FFI
|
||
|
|
||
|
```cry
|
||
|
foreign print : {n} (fin n) => [n][8] -> ()
|
||
|
```
|
||
|
|
||
|
```c
|
||
|
void print(size_t n, char *s) {
|
||
|
printf("%.*s\n", (int) n, s);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
```
|
||
|
Main> print "Hello world!"
|
||
|
Hello world!
|
||
|
()
|
||
|
```
|
||
|
|
||
|
```cry
|
||
|
foreign launchMissiles : ()
|
||
|
```
|
||
|
|
||
|
etc
|
||
|
|
||
|
- Cryptol functions should be pure
|
||
|
- Beware of laziness
|
||
|
|
||
|
---
|
||
|
|
||
|
# AES example
|
||
|
|
||
|
- Replacement for `SuiteB` module using AES-NI for AES functions
|
||
|
- ~5.5x faster
|
||
|
- But could actually be much faster, if not limited to 1 block
|
||
|
|
||
|
---
|
||
|
|
||
|
# Somewhat interesting(?) implementation details
|
||
|
|
||
|
- `ForeignPtr` for `dlclose`
|
||
|
- Interesting types in FFI marshalling
|
||
|
```hs
|
||
|
getMarshalBasicArg :: FFIBasicType ->
|
||
|
(forall a. FFIArg a => (GenValue Concrete -> Eval a) -> b) -> b
|
||
|
data GetRet = GetRet
|
||
|
{ getRetAsValue :: forall a. FFIRet a => IO a
|
||
|
, getRetAsOutArgs :: [SomeFFIArg] -> IO () }
|
||
|
```
|
||
|
|
||
|
---
|
||
|
|
||
|
# Future work
|
||
|
|
||
|
- Automatically generate header files from `foreign` declarations
|
||
|
- Support Windows
|
||
|
- Support Integer/Z
|
||
|
- Cryptol implementation of `foreign` functions + SAW integration
|
||
|
|
||
|
---
|
||
|
|
||
|
# Other stuff (not FFI)
|
||
|
|
||
|
- `:time` command for benchmarking evaluation of Cryptol expressions
|
||
|
- Evaluates it a bunch of times and reports the average time
|
||
|
- Import specific operators
|
||
|
- `import M ((<+>))`
|
||
|
- Fixed fixity (fixedity?) of prefix operators
|
||
|
- `-x**2` parses as `-(x**2)` not `(-x)**2`
|
||
|
|
||
|
---
|
||
|
|
||
|
# Thank you!
|
||
|
|
||
|
- Iavor, Eddy, Ryan
|
||
|
|
||
|
# Questions?
|
||
|
|
||
|
- Why is the Cryptol logo a water drop
|