-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNativeBinaryIdentity.cs
More file actions
133 lines (120 loc) · 4.51 KB
/
Copy pathNativeBinaryIdentity.cs
File metadata and controls
133 lines (120 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System.Buffers.Binary;
using System.Runtime.InteropServices;
namespace DotPython.Runtime.Native;
internal enum NativeBinaryFormat
{
Elf,
MachO,
PortableExecutable,
}
internal sealed record NativeBinaryIdentity(NativeBinaryFormat Format, Architecture Architecture)
{
internal static NativeBinaryIdentity Read(string path)
{
Span<byte> header = stackalloc byte[4096];
using var stream = new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
header.Length,
FileOptions.SequentialScan
);
var length = stream.Read(header);
var bytes = header[..length];
if (
bytes.Length >= 20
&& bytes[0] == 0x7f
&& bytes[1] == (byte)'E'
&& bytes[2] == (byte)'L'
&& bytes[3] == (byte)'F'
)
{
var littleEndian = bytes[5] == 1;
if (!littleEndian && bytes[5] != 2)
{
throw Unsupported(path, "ELF endianness is invalid.");
}
var machine = littleEndian
? BinaryPrimitives.ReadUInt16LittleEndian(bytes[18..])
: BinaryPrimitives.ReadUInt16BigEndian(bytes[18..]);
return new NativeBinaryIdentity(NativeBinaryFormat.Elf, MapElf(machine, path));
}
if (bytes.Length >= 8)
{
var magic = BinaryPrimitives.ReadUInt32LittleEndian(bytes);
if (magic is 0xfeedface or 0xfeedfacf)
{
var cpu = BinaryPrimitives.ReadUInt32LittleEndian(bytes[4..]);
return new NativeBinaryIdentity(NativeBinaryFormat.MachO, MapMachO(cpu, path));
}
}
if (bytes.Length >= 64 && bytes[0] == (byte)'M' && bytes[1] == (byte)'Z')
{
var peOffset = BinaryPrimitives.ReadUInt32LittleEndian(bytes[0x3c..]);
if (
peOffset > int.MaxValue - 6
|| peOffset + 6 > bytes.Length
|| BinaryPrimitives.ReadUInt32LittleEndian(bytes[(int)peOffset..]) != 0x00004550
)
{
throw Unsupported(path, "PE header is invalid or outside the bounded preflight.");
}
var machine = BinaryPrimitives.ReadUInt16LittleEndian(bytes[((int)peOffset + 4)..]);
return new NativeBinaryIdentity(
NativeBinaryFormat.PortableExecutable,
MapPortableExecutable(machine, path)
);
}
throw Unsupported(path, "Native binary format is not ELF, Mach-O, or PE.");
}
internal void ValidateCurrentPlatform(string path)
{
var expectedFormat =
OperatingSystem.IsWindows() ? NativeBinaryFormat.PortableExecutable
: OperatingSystem.IsMacOS() ? NativeBinaryFormat.MachO
: NativeBinaryFormat.Elf;
if (Format != expectedFormat || Architecture != RuntimeInformation.ProcessArchitecture)
{
throw new StableAbiLoadException(
"DPY8002",
StableAbiLoadPhase.Architecture,
$"Native artifact '{Path.GetFileName(path)}' targets {Format}/{Architecture}; "
+ $"the worker requires {expectedFormat}/{RuntimeInformation.ProcessArchitecture}.",
path,
artifactSha256: null,
missingSymbol: null
);
}
}
private static Architecture MapElf(ushort machine, string path) =>
machine switch
{
62 => Architecture.X64,
183 => Architecture.Arm64,
_ => throw Unsupported(path, $"ELF machine {machine} is unsupported."),
};
private static Architecture MapMachO(uint cpu, string path) =>
cpu switch
{
0x01000007 => Architecture.X64,
0x0100000c => Architecture.Arm64,
_ => throw Unsupported(path, $"Mach-O CPU {cpu} is unsupported."),
};
private static Architecture MapPortableExecutable(ushort machine, string path) =>
machine switch
{
0x8664 => Architecture.X64,
0xaa64 => Architecture.Arm64,
_ => throw Unsupported(path, $"PE machine {machine} is unsupported."),
};
private static StableAbiLoadException Unsupported(string path, string message) =>
new(
"DPY8002",
StableAbiLoadPhase.Architecture,
message,
path,
artifactSha256: null,
missingSymbol: null
);
}