手作りの温かみのあるバイナリです。
作りかた
そろそろRustで書くか、と思ってRustで書いた。
bytes というcrateが今回の用途にめちゃくちゃマッチしていた。数値をBig Endianで書き込めればいいので、こういう感じで。
pub fn write(&self, mut out: impl io::Write) -> Result<(), io::Error> { let b = &self.binary_header; let mut buf = bytes::BytesMut::new(); buf.put_slice(&b.ident); buf.put_slice(&b.major_version); buf.put_slice(&b.minor_version); let size_pos = buf.len(); buf.put_i32(-1); buf.put_slice(&b.compiler_name); buf.put_slice(&b.compiler_version); for section in self.sections.iter() { match section.header.ident { rite::markers::irep => { let h = §ion.header; let i = section.body_irep.as_ref().unwrap(); buf.put_slice(&h.ident); //.... buf.put_u16(i.pool.len() as u16); for pi in i.pool.iter() { match pi { rite::PoolItem::Str(s) => { buf.put_u8(0); // IREP_TT_STR // ... } _ => { todo!("unsupported") } } } buf.put_u16(i.syms.len() as u16); for s in i.syms.iter() { buf.put_u16(s.name.len() as u16); buf.put_slice(&s.name); buf.put_u8(0); } for _ in i.children.iter() { // ... } } rite::markers::end => { let h = §ion.header; buf.put_slice(&h.ident); buf.put_i32(h.size); } _ => { unreachable!("unknown section ident") } } } // finally write back total binsize let size = buf.len(); buf[size_pos] = ((size >> 24) & 0xff) as u8; buf[size_pos + 1] = ((size >> 16) & 0xff) as u8; buf[size_pos + 2] = ((size >> 8) & 0xff) as u8; buf[size_pos + 3] = (size & 0xff) as u8; let freeze = buf.freeze(); out.write(&freeze[..])?; out.flush()?; Ok(()) }
あとはサイズの検知の仕方とかをこなれた感じにするかなー。
できたバイナリ、compiler name が MATZ
と違っていてもちゃんと実行できる!
$ xxd /opt/ghq/github.com/udzura/mrbasm/sample.mrb 00000000: 5249 5445 3033 3030 0000 0056 4d41 534d RITE0300...VMASM 00000010: 3030 3030 4952 4550 0000 003a 3033 3030 0000IREP...:0300 00000020: 0000 002e 0001 0004 0000 0000 0000 000a ................ 00000030: 5102 002d 0100 0138 0169 0001 0000 0548 Q..-...8.i.....H 00000040: 656c 6c6f 0000 0100 0470 7574 7300 454e ello.....puts.EN 00000050: 4400 0000 0008 D..... $ ./bin/mruby /opt/ghq/github.com/udzura/mrbasm/sample.mrb Hello
コード
色々ラフですが。一旦上記に置いた。
リポジトリ名の通り、最終的にはmrubyの命令を直接記述できるアセンブリ言語を作ろうとしている。まだ何も設計できていないが、多分こういう感じ。
!binformat mruby 3.2 !begin irep main: !rootirep LOADI_2 R1 (2) MOVE R2 R1 LOADI_1 R3 (1) GT R2 R3 JMPNOT R2 else1 STRING R3 "big" SSEND R2 :puts 1 JMP end1 else1: STRING R3 "small" SSEND R2 :puts 1 end1: RETURN R2 STOP !end !begin irep sub: LOADI_2 R1 (2) MOVE R2 R1 ... !end
ただまあ、このasmは正直やろうとしていることに必要だから作っていて、ゴールでもなんでもない。作りたいのはもうちょっと別のもの。
ということでRubyKaigi お疲れ様です。みなさんと沖縄で会えるといいっすね。