-
Notifications
You must be signed in to change notification settings - Fork 273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds traps for memory and division operations #840
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,18 @@ let assumptions blk = | |
| Bil.Int _ -> assns, (neg assns) :: assms | ||
| _ -> failwith "Not in TCF") |> snd | ||
|
||
|
||
module TrapPageFault(Machine : Primus.Machine.S) = struct | ||
module Code = Primus.Linker.Make(Machine) | ||
let exec = | ||
Code.unlink (`symbol Primus.Interpreter.pagefault_handler) | ||
end | ||
|
||
module DoNothing(Machine : Primus.Machine.S) = struct | ||
let exec = Machine.return () | ||
end | ||
|
||
|
||
module Main(Machine : Primus.Machine.S) = struct | ||
open Machine.Syntax | ||
module Eval = Primus.Interpreter.Make(Machine) | ||
|
@@ -157,20 +169,30 @@ module Main(Machine : Primus.Machine.S) = struct | |
| _ -> Machine.return () | ||
|
||
|
||
let map addr = | ||
Mem.allocate ~generator:Primus.Generator.Random.Seeded.byte addr 1 | ||
let default_page_size = 4096 | ||
let default_generator = Primus.Generator.Random.Seeded.byte | ||
|
||
let map_page already_mapped addr = | ||
let rec map len = | ||
let last = Addr.nsucc addr (len - 1) in | ||
already_mapped last >>= function | ||
| true -> map (len / 2) | ||
| false -> | ||
Mem.allocate ~generator:default_generator addr len in | ||
map default_page_size | ||
|
||
let trap () = | ||
Linker.link ~name:Primus.Interpreter.pagefault_handler | ||
(module TrapPageFault) | ||
|
||
let make_loadable value = | ||
let addr = Primus.Value.to_word value in | ||
Mem.is_mapped addr >>= function | ||
| false -> map addr | ||
| true -> Machine.return () | ||
let pagefault x = | ||
Mem.is_mapped x >>= function | ||
| false -> map_page Mem.is_mapped x >>= trap | ||
| true -> | ||
Mem.is_writable x >>= function | ||
| false -> map_page Mem.is_writable x >>= trap | ||
| true -> Machine.return () | ||
|
||
let make_writable value = | ||
let addr = Primus.Value.to_word value in | ||
Mem.is_writable addr >>= function | ||
| false -> map addr | ||
| true -> Machine.return () | ||
|
||
let mark_visited blk = | ||
Machine.Global.update state ~f:(fun t -> { | ||
|
@@ -190,10 +212,19 @@ module Main(Machine : Primus.Machine.S) = struct | |
Machine.Seq.iter ~f:(fun var -> | ||
Env.add var (Primus.Generator.static 0)) | ||
|
||
let ignore_division_by_zero = | ||
Linker.link ~name:Primus.Interpreter.division_by_zero_handler | ||
(module DoNothing) | ||
|
||
let ignore_unresolved_names = | ||
Linker.link ~name:Primus.Linker.unresolved_handler | ||
(module DoNothing) | ||
|
||
let init () = Machine.sequence [ | ||
setup_vars; | ||
Primus.Interpreter.loading >>> make_loadable; | ||
Primus.Interpreter.storing >>> make_writable; | ||
ignore_division_by_zero; | ||
ignore_unresolved_names; | ||
Primus.Interpreter.pagefault >>> pagefault; | ||
Primus.Interpreter.leave_pos >>> step; | ||
Primus.Interpreter.leave_blk >>> mark_visited; | ||
] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I understand what's going on. Let me summarize and correct me if I got this wrong: A page fault condition occurs when trying to access a page of memory that hasn't been mapped yet. This is handled by allocating a new page if the requested range is writable. This is what the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm testing these changes on our application. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything works great. 💯 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Roughly correct, a pagefault occurs when the memory subsystem can't satisfy the request, either because of insufficient permissions or because of the missing page. Since this is a promiscuous mode, we're going to fix both cases and prevent the segmentation fault from occurring. In the let pagefault x =
Mem.is_mapped x >>= function
| false -> map_page Mem.is_mapped x >>= trap
| true ->
Mem.is_writable x >>= function
| false -> map_page Mem.is_writable x >>= trap
| true -> Machine.return () we first check whether the page is mapped, if it is not, we map up to 4K of memory, trying not to override an existing mapping, i.e., if the new page is overlapping with an existent one, then we half the size of the new page and try again until, at the worst case, we end up with the 1 byte page mapping (this scenario could occur when 1 byte is requested just before the already mapped page, quite a common case, so a better algorithm could be devised). After the mapping we trap the pagefault by registering a handler. The handler is invoked by the memory subsystem. Since there is nothing else to do, it just unlinks itself, so that it doesn't trap the consequent pagefaults. If the page is mapped, but we still hit the pagefault, then we try to remap this region with writable permissions. (Note, the |
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in the string, should be: "__primus_pagefault_handler"