• Stars
    star
    25
  • Rank 926,423 (Top 19 %)
  • Language
    Racket
  • License
    Other
  • Created over 9 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Manipulating racket bytecode

Zordoz

Build Status Coverage Status Scribble

ZORDOZ speaks to you! His chosen ones.

This is an explorer for Racket .zo files.

Tested to work on Racket v6.5 For compatibility with older versions, see the v6.1 and v6.2 and v6.3 branches of this repo. (Note that installing through raco will choose the available version, if any, matching your Racket install.)

Typed Racket users can (require zordoz/typed) for type-annotated bindings from zordoz. Also, zordoz/typed/zo-structs is a safe wrapper around Racket's compiler/zo-structs library.

Install

You have two options.

  1. Install from raco by running raco pkg install zordoz
  2. Build from source by cloning this repo and using raco: git clone https://github.com/bennn/zordoz; raco pkg install zordoz/

To run tests, do raco test zordoz. Tests are located in the test submodule of each source file.

Usage

Zordoz provides a raco zordoz command.

REPL

Activate the REPL by giving a path to a compiled file.

raco zordoz FILE.zo

Passing the -t option uses typed racket code. Beware, the typed racket version is up to 5x slower than untyped because of contracts with the compiler/zo-lib structs.

The REPL accepts the following commands:

  • alst prints all command aliases; for example, the repl treats the words 'alst' and 'aliases' the same way
  • back goes back to the previous context
  • dive ARG changes context to a new zo struct or list (other dives are not permitted)
  • find ARG searches for matches to ARG and, if successful, changes context to the list of results
  • help prints information about these commands
  • info prints data about the current context
  • jump reverts to a previously saved context
  • save marks the current context as a target for jump
  • quit exits the interpreter

The functions implementing the dive, find, and info commands are available outside the REPL. Check the guide for a summary.

Quick Search

Running:

./zordoz -f branch -f lam -f closure FILE.zo

Will count and print the number of times the zo structs branch lam and closure appear. This may take a while, depending on the size of the bytecode file. You can limit the search depth by passing a natural number with the -l flag.

See the decompilation guide for a list of all zo struct names.

Background

Racket bytecode is stored in files with a .zo extension. This tool makes it easier to explore the bytecode representation of a file, whether or not you have access to the file's source code.

Given a .zo file, we decompile the bytecode into a struct (aka, a "zo-struct") using Racket's built-in decompilation API. The REPL loads this struct as its initial context and begins accepting commands, making it easy to visualize and explore Racket bytecode.

Example

Suppose we create and compile a small racket file:

> echo -e "#lang racket/base\n(if #t (+ 1 1) 0)" > test.rkt
> raco make test.rkt

The actual bytecode is not human readable. Neither is the struct representation output by zo-parse:

> echo -e '#lang racket/base\n(require compiler/zo-parse)\n(call-with-input-file "compiled/test_rkt.zo"\n  (lambda (fd) (displayln (zo-parse fd))))' > print-test.rkt
> racket print-test.rkt
#s((compilation-top zo 0) 0 #s((prefix zo 0) 0 (#f) ()) #s((mod form 0 zo 0) test test #<module-path-index> #s((prefix zo 0) 0 (#s((module-variable zo 0) #<module-path-index> print-values 0 0 #s((function-shape zo 0) #(struct:arity-at-least 0) #f))) ()) ((0 () ()) (1 () ()) (#f () ())) ((0 #<module-path-index>) (1) (-1) (#f)) (#s((apply-values expr 0 form 0 zo 0) #s((toplevel expr 0 form 0 zo 0) 0 0 #t #t) 2)) () ((0 () ())) 0 #s((toplevel expr 0 form 0 zo 0) 0 0 #f #f) #f #t () (#s((mod form 0 zo 0) (test configure-runtime) configure-runtime #<module-path-index> #s((prefix zo 0) 0 (#s((module-variable zo 0) #<module-path-index> configure 0 0 #s((function-shape zo 0) 1 #f))) ()) ((0 () ()) (1 () ()) (#f () ())) ((0 #<module-path-index> #<module-path-index>) (1) (-1) (#f)) (#s((application expr 0 form 0 zo 0) #s((primval expr 0 form 0 zo 0) 1000) (#t))) () ((0 () ())) 1 #s((toplevel expr 0 form 0 zo 0) 0 0 #f #f) #f #t () () ())) ()))

ZORDOZ offers a more readable presentation. Below is a sample interactive session with the same small file (interspersed with commentary):

> racket zordoz.rkt compiled/test_rkt.zo 
INFO: Loading bytecode file 'compiled/test_rkt.zo'...
INFO: Parsing bytecode...
INFO: Parsing complete!
--- Welcome to the .zo shell, version 0.1 'outlands' ---
zo> info
<struct:compilation-top>
  max-let-depth : 0
  prefix        : <struct:prefix>
  code          : <struct:mod>

The compilation-top struct is at the top of most every .zo file. Things get more interesting as we explore the structs nested inside it.

zo> dive code
zo> info
<struct:mod>
  name             : test
  srcname          : test
  self-modidx      : #<module-path-index>
  prefix           : <struct:prefix>
  provides         : 0 [] [] 1 [] [] #f [] []
  requires         : 0 #<module-path-index> 1  -1  #f 
  body             : <struct:apply-values>
  syntax-bodies    :
  unexported       : 0
  max-let-depth    : 0
  dummy            : <struct:toplevel>
  lang-info        : #f
  internal-context : #t
  flags            :
  pre-submodules   : <struct:mod>[1]
  post-submodules  : []

The mod struct represents a Racket module. This module has the name test; inferred from our filename test.rkt.

We could continue dive-ing into structs, or we can use the shell's find command to look for structs matching a name like mod or compilation-top. Let's search for branch structs. Maybe we can find the if-statement in our original code.

zo> find branch
FIND returned 0 results

Nothing. The if-statement has been optimized away. Let's try to find what it turned into by searching the body of the module.

zo> dive body
zo> info
(<struct:apply-values>)[1]

The syntax (<struct:NAME>)[LENGTH] denotes a list of zo-structs. LENGTH is the number of elements in the list--we can dive into any valid index.

zo> dive 0
zo> info
<struct:apply-values>
  proc      : <struct:expr>
  args-expr : 2

Looks like our if-statement was optimized into a constant, 2.

Happy exploring!

More Repositories

1

mechanics

(in progress) Racket port of the SICM scmutils library
Racket
31
star
2

iPoe

Interactive POetry Editor
Racket
26
star
3

forth

Forth emulator, as a Racket #lang
Racket
20
star
4

with-cache

Simple, filesystem-based caching for Racket
Racket
17
star
5

pierce-categories

Exercises from "Basic Category Theory for Computer Scientists" by Benjamin Pierce.
TeX
15
star
6

trivial

Stronger types for a few Typed Racket operators
Racket
11
star
7

little-book-of-semaphores

Racket support for implementing the little book
Racket
8
star
8

dissertation

PhD dissertation, Khoury College, Northeastern University, 2020
Racket
7
star
9

assertions

Simple assertions library for OCaml.
OCaml
6
star
10

gtp-benchmarks

Gradual Typing Performance benchmarks
Racket
5
star
11

spreadsheet

Functor for parsing and building spreadsheets
OCaml
5
star
12

g-pldi-2022

Racket
4
star
13

gtp-plot

Gradual typing performance plots
Racket
4
star
14

pacman-complete

Proof that programming language X can implement a pacman game.
Racket
4
star
15

my-favorite-resume

This is my resume. Original template pilfered from Yisong Yue.
TeX
3
star
16

racket-mutt

Racket API for the Mutt email client
Racket
3
star
17

require-typed-check

Racket's require/typed, but avoids generating contracts for typed-to-typed interaction
Racket
3
star
18

scribble-abbrevs

Scribble-to-LaTeX helper functions
Racket
3
star
19

luau-telemetry

Large-scale, anonymous, randomized logging of type errors in Luau
Racket
3
star
20

glob

Unix-style globbing in Racket
Racket
3
star
21

agile

#lang for agile software development
Racket
3
star
22

gtp-checkup

Correctness test for gradual typing
Racket
2
star
23

doubly-linked

Purely functional doubly-linked lists in OCaml
OCaml
2
star
24

way_of_analysis

Exercises from Strichartz's "Way of Analysis. By Harris Karsch & Ben Greenman
Shell
2
star
25

rational-deep-shallow

2
star
26

racket-minus-contracts

Patch to ignore contracts in your Racket runtime
Racket
2
star
27

rosette-contract

Proving away contracts with Rosette
Racket
2
star
28

gtp-measure

Benchmarking tool
Racket
2
star
29

bubbles

Unit testing in OCaml
Python
2
star
30

advent-of-code

my Advent of Code solutions (https://adventofcode.com)
Racket
2
star
31

tomato-timer

Simple tomato (pomodoro) timer
Racket
1
star
32

julia-local-registry

1
star
33

redex-models

Some models in PLT Redex
Racket
1
star
34

purity

No mutation! Enforced by this library (we'll see)
Racket
1
star
35

rackunit-abbrevs

Abbreviations for RackUnit testing
Racket
1
star
36

php-blg59

Personal website
HTML
1
star
37

gfd-oopsla-2023

Racket
1
star
38

my-favorite-.emacs.d

my favorite config files
Emacs Lisp
1
star
39

require-typed-scv

Typed Racket's `require/typed`, with soft contract verification
Racket
1
star
40

statement_of_purpose

This is my statement of purpose. It is in version control so I can see how it changes over time. It is in a public repository because I like github but not enough to pay for it.
TeX
1
star