Last active
February 17, 2024 05:07
-
-
Save m3m0r7/226e20c8115caf4a9d43b291861f978b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$handle = fopen(__DIR__ . '/HelloWorld.yarv', 'r'); | |
// see: https://github.com/ruby/ruby/blob/2f603bc4/compile.c#L11087 | |
$magic = fread($handle, 4); | |
$majorVersion = unpack('V', fread($handle, 4))[1]; | |
$minorVersion = unpack('V', fread($handle, 4))[1]; | |
$fileSize = unpack('V', fread($handle, 4))[1]; | |
$extraSize = unpack('V', fread($handle, 4))[1]; | |
$iSeqListSize = unpack('V', fread($handle, 4))[1]; | |
$globalObjectListSize = unpack('V', fread($handle, 4))[1]; | |
// see: https://github.com/ruby/ruby/blob/2f603bc4/compile.c#L11096C5-L11096C17 | |
$iSeqListOffset = unpack('V', fread($handle, 4))[1]; | |
// see: https://github.com/ruby/ruby/blob/2f603bc4/compile.c#L11097 | |
$globalObjectListOffset = unpack('V', fread($handle, 4))[1]; | |
//var_dump( | |
// $magic, $majorVersion, $minorVersion, | |
// $fileSize, $extraSize, $iSeqListSize, | |
// $globalObjectListSize, $iSeqListOffset, $globalObjectListOffset, | |
//); | |
// iseq list offset の情報を取得する | |
$iseqListOffsets = []; | |
fseek($handle, $iSeqListOffset, SEEK_SET); | |
for ($i = 0; $i < $iSeqListSize; $i++) { | |
$iseqListOffsets[] = unpack('V', fread($handle, 4))[1]; | |
} | |
// global object list offset の情報を取得する | |
$globalObjectListOffsets = []; | |
fseek($handle, $globalObjectListOffset, SEEK_SET); | |
for ($i = 0; $i < $globalObjectListSize; $i++) { | |
$globalObjectListOffsets[] = unpack('V', fread($handle, 4))[1]; | |
} | |
//var_dump($iseqListOffsets, $globalObjectListOffsets); | |
function readSmallValue(): int | |
{ | |
global $handle; | |
$offset = ftell($handle); | |
// ハミング重み | |
$ntzInt32 = function (int $x): int { | |
$x = ~$x & ($x - 1); | |
$x = ($x & 0x55555555) + ($x >> 1 & 0x55555555); | |
$x = ($x & 0x33333333) + ($x >> 2 & 0x33333333); | |
$x = ($x & 0x0F0F0F0F) + ($x >> 4 & 0x0F0F0F0F); | |
$x = ($x & 0x001F001F) + ($x >> 8 & 0x001F001F); | |
$x = ($x & 0x0000003F) + ($x >> 16 & 0x0000003F); | |
return $x; | |
}; | |
$c = unpack('C', fread($handle, 1))[1]; | |
$n = ($c & 1) | |
? 1 | |
: (0 == $c ? 9 : $ntzInt32($c) + 1); | |
$x = $c >> $n; | |
if (0x7F === $x) { | |
$x = 1; | |
} | |
for ($i = 1; $i < $n; ++$i) { | |
$x <<= 8; | |
$x |= unpack('C', fread($handle, 1))[1]; | |
} | |
fseek( | |
$handle, | |
$offset + $n, | |
SEEK_SET, | |
); | |
return $x; | |
} | |
// 0 番目のオフセットを参照 | |
fseek($handle, $iseqListOffsets[0], SEEK_SET); | |
$type = readSmallValue(); | |
$iseqSize = readSmallValue(); | |
$bytecodeOffset = readSmallValue(); | |
$bytecodeSize = readSmallValue(); | |
$mnemonic = [ | |
18 => 'putself', | |
21 => 'putstring', | |
51 => 'opt_send_without_block', | |
60 => 'leave', | |
]; | |
fseek($handle, $iseqListOffsets[0] - $bytecodeOffset, SEEK_SET); | |
class Main | |
{ | |
public function puts($string): void | |
{ | |
echo $string; | |
} | |
} | |
function loadObject(int $offset) | |
{ | |
global $handle; | |
$current = ftell($handle); | |
fseek($handle, $offset, SEEK_SET); | |
$byte = ord(fread($handle, 1)); | |
// ビットに読み込むべき種類が格納されています。 | |
$type = ($byte >> 0) & 0x1f; | |
$ret = null; | |
// 5 は文字列を読む処理 | |
if ($type === 5) { | |
// NOTE: エンコーディング手法の情報。今回は使わない。 | |
$encIndex = readSmallValue(); | |
// 文字列長の取得 | |
$len = readSmallValue(); | |
$ret = fread($handle, $len); | |
} else { | |
throw new RuntimeException("未実装です ({$type})"); | |
} | |
fseek($handle, $current, SEEK_SET); | |
return $ret; | |
} | |
// スタックの情報を管理するための変数です。 | |
$stacks = []; | |
for ($codeIndex = 0; $codeIndex < $iseqSize; $codeIndex++) { | |
$code = readSmallValue(); | |
switch ($mnemonic[$code] ?? null) { | |
case 'putself': | |
// Main コンテキストをスタックにプッシュさせます。 | |
$stacks[] = new Main(); | |
break; | |
case 'putstring': | |
// グローバルオブジェクトのオフセットの情報を取得します。 | |
$pos = readSmallValue(); | |
$objectPos = $globalObjectListOffsets[$pos]; | |
// グローバルオブジェクトを取得します | |
$stacks[] = loadObject($objectPos); | |
break; | |
case 'opt_send_without_block': | |
// NOTE: puts の実装のみ。本来は call info entry から呼び出すべきメソッドが明らかになります。 | |
$argument = array_pop($stacks); | |
$callee = array_pop($stacks); | |
// puts を呼び出し | |
$callee->puts($argument); | |
break; | |
case 'leave': | |
// NOTE: 今回の例では特に実装不要。返り値などが必要な場合に leave コマンドで操作する必要があります。 | |
break; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$handle = fopen(__DIR__ . '/HelloWorld.yarv', 'r'); | |
// see: https://github.com/ruby/ruby/blob/2f603bc4/compile.c#L11087 | |
$magic = fread($handle, 4); | |
$majorVersion = unpack('V', fread($handle, 4))[1]; | |
$minorVersion = unpack('V', fread($handle, 4))[1]; | |
$fileSize = unpack('V', fread($handle, 4))[1]; | |
$extraSize = unpack('V', fread($handle, 4))[1]; | |
$iSeqListSize = unpack('V', fread($handle, 4))[1]; | |
$globalObjectListSize = unpack('V', fread($handle, 4))[1]; | |
// see: https://github.com/ruby/ruby/blob/2f603bc4/compile.c#L11096C5-L11096C17 | |
$iSeqListOffset = unpack('V', fread($handle, 4))[1]; | |
// see: https://github.com/ruby/ruby/blob/2f603bc4/compile.c#L11097 | |
$globalObjectListOffset = unpack('V', fread($handle, 4))[1]; | |
//var_dump( | |
// $magic, $majorVersion, $minorVersion, | |
// $fileSize, $extraSize, $iSeqListSize, | |
// $globalObjectListSize, $iSeqListOffset, $globalObjectListOffset, | |
//); | |
// iseq list offset の情報を取得する | |
$iseqListOffsets = []; | |
fseek($handle, $iSeqListOffset, SEEK_SET); | |
for ($i = 0; $i < $iSeqListSize; $i++) { | |
$iseqListOffsets[] = unpack('V', fread($handle, 4))[1]; | |
} | |
// global object list offset の情報を取得する | |
$globalObjectListOffsets = []; | |
fseek($handle, $globalObjectListOffset, SEEK_SET); | |
for ($i = 0; $i < $globalObjectListSize; $i++) { | |
$globalObjectListOffsets[] = unpack('V', fread($handle, 4))[1]; | |
} | |
//var_dump($iseqListOffsets, $globalObjectListOffsets); | |
function readSmallValue(): int | |
{ | |
global $handle; | |
$offset = ftell($handle); | |
// ハミング重み | |
$ntzInt32 = function (int $x): int { | |
$x = ~$x & ($x - 1); | |
$x = ($x & 0x55555555) + ($x >> 1 & 0x55555555); | |
$x = ($x & 0x33333333) + ($x >> 2 & 0x33333333); | |
$x = ($x & 0x0F0F0F0F) + ($x >> 4 & 0x0F0F0F0F); | |
$x = ($x & 0x001F001F) + ($x >> 8 & 0x001F001F); | |
$x = ($x & 0x0000003F) + ($x >> 16 & 0x0000003F); | |
return $x; | |
}; | |
$c = unpack('C', fread($handle, 1))[1]; | |
$n = ($c & 1) | |
? 1 | |
: (0 == $c ? 9 : $ntzInt32($c) + 1); | |
$x = $c >> $n; | |
if (0x7F === $x) { | |
$x = 1; | |
} | |
for ($i = 1; $i < $n; ++$i) { | |
$x <<= 8; | |
$x |= unpack('C', fread($handle, 1))[1]; | |
} | |
fseek( | |
$handle, | |
$offset + $n, | |
SEEK_SET, | |
); | |
return $x; | |
} | |
// 0 番目のオフセットを参照 | |
fseek($handle, $iseqListOffsets[0], SEEK_SET); | |
$type = readSmallValue(); | |
$iseqSize = readSmallValue(); | |
$bytecodeOffset = readSmallValue(); | |
$bytecodeSize = readSmallValue(); | |
// Changed opcode (opt_send_without_block) from Ruby3.2 to Ruby3.3 | |
$mnemonic = [ | |
18 => 'putself', | |
21 => 'putstring', | |
53 => 'opt_send_without_block', | |
61 => 'leave', | |
]; | |
fseek($handle, $iseqListOffsets[0] - $bytecodeOffset, SEEK_SET); | |
class Main | |
{ | |
public function puts($string): void | |
{ | |
echo $string; | |
} | |
} | |
function loadObject(int $offset) | |
{ | |
global $handle; | |
$current = ftell($handle); | |
fseek($handle, $offset, SEEK_SET); | |
$byte = ord(fread($handle, 1)); | |
// ビットに読み込むべき種類が格納されています。 | |
$type = ($byte >> 0) & 0x1f; | |
$ret = null; | |
// 5 は文字列を読む処理 | |
if ($type === 5) { | |
// NOTE: エンコーディング手法の情報。今回は使わない。 | |
$encIndex = readSmallValue(); | |
// 文字列長の取得 | |
$len = readSmallValue(); | |
$ret = fread($handle, $len); | |
} else { | |
throw new RuntimeException("未実装です ({$type})"); | |
} | |
fseek($handle, $current, SEEK_SET); | |
return $ret; | |
} | |
// スタックの情報を管理するための変数です。 | |
$stacks = []; | |
for ($codeIndex = 0; $codeIndex < $iseqSize; $codeIndex++) { | |
$code = readSmallValue(); | |
switch ($mnemonic[$code] ?? null) { | |
case 'putself': | |
// Main コンテキストをスタックにプッシュさせます。 | |
$stacks[] = new Main(); | |
break; | |
case 'putstring': | |
// グローバルオブジェクトのオフセットの情報を取得します。 | |
$pos = readSmallValue(); | |
$objectPos = $globalObjectListOffsets[$pos]; | |
// グローバルオブジェクトを取得します | |
$stacks[] = loadObject($objectPos); | |
break; | |
case 'opt_send_without_block': | |
// NOTE: puts の実装のみ。本来は call info entry から呼び出すべきメソッドが明らかになります。 | |
$argument = array_pop($stacks); | |
$callee = array_pop($stacks); | |
// puts を呼び出し | |
$callee->puts($argument); | |
break; | |
case 'leave': | |
// NOTE: 今回の例では特に実装不要。返り値などが必要な場合に leave コマンドで操作する必要があります。 | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For example; implementation of the call info entry as following: