Skip to content

Commit 1228235

Browse files
committed
Add pasm component
1 parent a96a2df commit 1228235

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

Writerside/boson.tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<toc-element topic="components.md">
7777
<toc-element topic="os-info.md" />
7878
<toc-element topic="cpu-info.md" />
79+
<toc-element topic="pasm.md" />
7980
<toc-element topic="globals-provider.md" />
8081
<toc-element topic="http.md" />
8182
<toc-element topic="http-body-decoder.md" />
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Machine Code Executor
2+
3+
<show-structure for="chapter" depth="2"/>
4+
5+
The PASM (PHP Assembly) provides a set of utilities for
6+
direct execution of low-level machine code in user space.
7+
8+
<note>
9+
This component already included in the <code>boson-php/runtime</code>,
10+
so no separate installation is required when using the runtime.
11+
</note>
12+
13+
14+
## Installation
15+
16+
<tldr>
17+
<p>
18+
Via <a href="https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies">Composer</a>:
19+
</p>
20+
<p>
21+
<code lang="bash">composer require boson-php/pasm</code>
22+
</p>
23+
</tldr>
24+
25+
**Requirements:**
26+
27+
* `PHP ^8.4`
28+
* `ext-ffi`
29+
30+
## Usage
31+
32+
To create an assembly executor, you must create the `Boson\Component\Pasm\Executor`
33+
object.
34+
35+
The instance contains method `compile($signature, $code)` with 2 arguments:
36+
- `string $signature` - the signature of the compiled function.
37+
- `string $code` - the code (body) executed by that compiled function.
38+
39+
This method provides the ability to compile an arbitrary set of machine codes
40+
into an executed function (`callable` type) that can be executed at any time.
41+
42+
```php
43+
$executor = new \Boson\Component\Pasm\Executor();
44+
45+
$function = $executor->compile(/* signature */, /* code */);
46+
```
47+
48+
<note>
49+
The function is directly associated with the address space in which the
50+
executable memory was allocated, and after deleting the link (see
51+
<a href="https://www.php.net/manual/en/features.gc.php">PHP GC</a>) to this
52+
function, the associated memory will also be automatically cleared.
53+
</note>
54+
55+
### AMD64 (x86_64) Example
56+
57+
Below is an example for getting information about the CPU using the
58+
[1st leaf](https://software.intel.com/content/www/us/en/develop/download/intel-64-and-ia-32-architectures-software-developers-manual-volume-2a-instruction-set-reference-a-l.html)
59+
and the `cpuid` instruction.
60+
61+
```php
62+
$executor = new \Boson\Component\Pasm\Executor();
63+
64+
//
65+
// An EAX register returned from cpuid is 32-bit, so it is
66+
// safest to explicitly specify the return type int32_t.
67+
//
68+
// You can read more about the syntax of callbacks in C/C++
69+
// in the documentation or, for example, here:
70+
// - https://www.geeksforgeeks.org/cpp/function-pointers-and-callbacks-in-cpp/
71+
//
72+
const SIGNATURE = 'int32_t(*)()';
73+
74+
//
75+
// In this case, it is machine code that can only be executed on
76+
// AMD64 (x86_64) and compatible (i.e. x86) architectures.
77+
//
78+
// To convert any assembly language to machine code,
79+
// you can use, for example:
80+
// - https://godbolt.org/
81+
//
82+
const CODE = "\xB8\x01\x00\x00\x00" // mov eax, 0x1
83+
. "\x0F\xA2" // cpuid
84+
. "\xc3" // ret
85+
86+
/**
87+
* Compiled function
88+
*
89+
* @var callable(): int<−2147483648, 2147483647> $function
90+
*/
91+
$function = $executor->compile(SIGNATURE, CODE);
92+
93+
//
94+
// Execute this function.
95+
//
96+
// After execution, the result of the EAX (CPUID leaf 1) register
97+
// will be returned, containing the following information
98+
// (according to the Intel manual):
99+
//
100+
// - Stepping (bits 3–0)
101+
// - Model (bits 7–4)
102+
// - Family (bits 11–8)
103+
// - Processor type (bits 13–12)
104+
// - Extended model (bits 19–16)
105+
// - Extended family (bits 27–20)
106+
// - Reserved (bits 31-28)
107+
//
108+
$eax = $result();
109+
110+
$stepping = $eax & 0x0F;
111+
$model = ($eax >> 4) & 0x0F;
112+
$family = ($eax >> 8) & 0x0F;
113+
$extModel = ($eax >> 16) & 0x0F;
114+
$extFamily = ($eax >> 20) & 0xFF;
115+
116+
echo "\nstepping: " . $stepping;
117+
echo "\nmodel: " . $model;
118+
echo "\nfamily: " . $family;
119+
120+
//
121+
// Other information can be obtained as follows
122+
// (exactly the same as in the Intel manual)
123+
//
124+
echo "\nfull model: " . dechex($family === 0x06 || $family === 0x0F
125+
? $model + ($extModel << 4)
126+
: $model);
127+
128+
echo "\nfull family: " . ($family === 0x0F
129+
? $family + $extFamily
130+
: $family);
131+
132+
//
133+
// The result of executing this code may be as follows:
134+
//
135+
// stepping: 5
136+
// model: 5
137+
// family: 6
138+
// full model: A5
139+
// full family: 6
140+
//
141+
```

0 commit comments

Comments
 (0)