Skip to content

Instantly share code, notes, and snippets.

@jkominek
Created May 3, 2013 19:50
Show Gist options
  • Save jkominek/5513385 to your computer and use it in GitHub Desktop.
Save jkominek/5513385 to your computer and use it in GitHub Desktop.
takes a string of assembly, feeds it to nasm, loads it into memory marked executable, and uses the ffi to turn pointers in it into procedures. obviously the assembly has to match your architecture, which has to be 32 or 64 bit x86, as well as your OS calling conventions. example assembly is for 64 bit unix.
(module nasm racket/base
(require racket/system
racket/file
racket/dict
ffi/unsafe
ffi/unsafe/alloc)
(define posix_memalign
; ((allocator free) ; can't auto free these pointers until we ensure that everything using
; the allocated block of memory holds a reference to it, lest it be
; freed prematurely
(get-ffi-obj 'posix_memalign #f
(_fun (p : (_ptr o _pointer)) _size _size
-> (ret : _int)
-> (if (= ret 0)
p
(error (format "returned ~a" ret))))));)
(define _protection (_bitmask '(PROT_NONE = 0
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4) _int))
(define _flags (_bitmask '(MAP_ANON = 4096
MAP_PRIVATE = 2) _int))
(define mprotect (get-ffi-obj 'mprotect #f
(_fun _pointer _size _protection
-> (ret : _int)
-> (if (= ret 0)
(void)
(error (format "returned ~a" ret))))))
(define (compile-asm code #:for-size [for-size #f])
(let ([out-filename (make-temporary-file "~a.asm")]
[out-bin (make-temporary-file "~a.bin")])
(with-output-to-file out-filename #:exists 'truncate
(lambda ()
(write-string code)))
(system (format "/Users/kominek/bin/nasm -o ~a -f bin ~a" out-bin out-filename))
(if for-size
(file-size out-bin)
(file->bytes out-bin))))
(define (prepend-origin code ptr)
(string-append (format "[org 0x~a]\n"
(number->string (cast ptr _pointer _uintptr) 16))
code))
(define (load-asm/mem code)
(let* ([code-size (compile-asm code #:for-size #t)]
[alloc-size (* 8192 (ceiling (/ code-size 8192)))]
[ptr (posix_memalign 8192 alloc-size)])
(bytes-copy! (make-sized-byte-string ptr alloc-size)
0
(compile-asm (prepend-origin code ptr)))
(mprotect ptr alloc-size '(PROT_READ PROT_EXEC))
ptr))
(define (load-asm/pointers code)
(let* ([map-name (make-temporary-file "~a.map")]
[map-code (string-append (format "[map symbols ~a]\n" map-name)
code)]
[ptr (load-asm/mem map-code)]
[saw-start #f]
[symbols (make-hash)])
(define pat #px"^\\s+([0-9a-fA-F]+)\\s+([0-9a-fA-F]+)\\s+(\\S+)")
(for ([l (in-list (file->lines map-name))])
(when saw-start
(let ([result (regexp-match pat l)])
(when result
(hash-set! symbols (string->symbol (cadddr result)) (cast (string->number (cadr result) 16) _uintptr _pointer)))))
(when (regexp-match? #px"Real\\s+Virtual\\s+Name" l)
(set! saw-start #t)))
symbols))
(define (load-asm/procs code types)
(let ([symbol-map (load-asm/pointers code)])
(for/hash ([(name type) (in-dict types)])
(values name (cast (hash-ref symbol-map name) _pointer type)))))
; need a preprocessor for code which
; 1) finds extern statements
; 2) extracts all of the symbols named in them
; 3) uses get-ffi-obj to try and locate those symbols
; 4) replaces the extern statements with EQU lines defining the
; symbols to make them point at the appropriate location
(define some-asm "
[bits 64]
section .text
identity:
push rbp
mov rbp, rsp
mov rax, rdi
leave
ret
fortytwo:
push rbp
mov rbp, rsp
mov rax, 42
leave
ret
")
(define procs (load-asm/procs some-asm `((identity . ,(_fun _uint64 -> _uint64)) (fortytwo . ,(_fun -> _uint64)))))
(define fortytwo (hash-ref procs 'fortytwo))
(define identity (hash-ref procs 'identity))
(identity (fortytwo))
; do i want to do some syntax for define-asm, or do i just want to make a language?
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment