Package standalone source CLI with verified runtime and notices
This commit is contained in:
+5
-103
@@ -23,109 +23,11 @@ $repository = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
||||
$utf8 = New-Object Text.UTF8Encoding($false)
|
||||
. (Join-Path $PSScriptRoot 'package-common.ps1')
|
||||
|
||||
function Require-File([string]$Path) {
|
||||
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Required file missing: $Path" }
|
||||
return (Get-Item -LiteralPath $Path).FullName
|
||||
}
|
||||
|
||||
# Read ordinary PE32+ imports without invoking a compiler or trusting PATH tools.
|
||||
# Dynamically loaded libraries and delay imports remain a separate runtime smoke gate.
|
||||
function Read-PeImports([string]$Path) {
|
||||
$stream = [IO.File]::OpenRead($Path)
|
||||
$reader = New-Object IO.BinaryReader($stream)
|
||||
try {
|
||||
if ($reader.ReadUInt16() -ne 0x5a4d) { throw "Not a PE executable: $Path" }
|
||||
$stream.Position = 0x3c
|
||||
$pe = $reader.ReadUInt32()
|
||||
$stream.Position = $pe
|
||||
if ($reader.ReadUInt32() -ne 0x4550) { throw "Invalid PE signature: $Path" }
|
||||
if ($reader.ReadUInt16() -ne 0x8664) { throw "Expected x86_64 PE input: $Path" }
|
||||
$sectionCount = $reader.ReadUInt16()
|
||||
$stream.Position = $pe + 20
|
||||
$optionalSize = $reader.ReadUInt16()
|
||||
$optional = $pe + 24
|
||||
$stream.Position = $optional
|
||||
if ($reader.ReadUInt16() -ne 0x20b) { throw "Expected PE32+ input: $Path" }
|
||||
$stream.Position = $optional + 120
|
||||
$importRva = $reader.ReadUInt32()
|
||||
$sections = @()
|
||||
for ($index = 0; $index -lt $sectionCount; $index++) {
|
||||
$stream.Position = $optional + $optionalSize + $index * 40 + 8
|
||||
$virtualSize = $reader.ReadUInt32()
|
||||
$virtualAddress = $reader.ReadUInt32()
|
||||
$rawSize = $reader.ReadUInt32()
|
||||
$rawAddress = $reader.ReadUInt32()
|
||||
$sections += [pscustomobject]@{ Start = $virtualAddress; Size = [Math]::Max($virtualSize, $rawSize); Raw = $rawAddress }
|
||||
}
|
||||
function Rva-Offset([uint32]$Rva) {
|
||||
foreach ($section in $sections) {
|
||||
if ($Rva -ge $section.Start -and $Rva -lt ($section.Start + $section.Size)) {
|
||||
return [long]($section.Raw + $Rva - $section.Start)
|
||||
}
|
||||
}
|
||||
throw "Unmapped PE RVA in $Path"
|
||||
}
|
||||
if ($importRva -eq 0) { return }
|
||||
$descriptors = Rva-Offset $importRva
|
||||
for ($index = 0; $index -lt 4096; $index++) {
|
||||
$stream.Position = $descriptors + $index * 20
|
||||
$fields = @(0..4 | ForEach-Object { $reader.ReadUInt32() })
|
||||
if (@($fields | Where-Object { $_ -ne 0 }).Count -eq 0) { return }
|
||||
$stream.Position = Rva-Offset $fields[3]
|
||||
$name = New-Object Text.StringBuilder
|
||||
for ($character = 0; $character -lt 256; $character++) {
|
||||
$value = $reader.ReadByte()
|
||||
if ($value -eq 0) { break }
|
||||
[void]$name.Append([char]$value)
|
||||
}
|
||||
if ($character -eq 256) { throw "Unterminated PE import name: $Path" }
|
||||
$name.ToString().ToLowerInvariant()
|
||||
}
|
||||
throw "Unterminated PE import table: $Path"
|
||||
} finally {
|
||||
$reader.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
# Windows system components only. Unknown imports fail, including VC redistributable DLLs.
|
||||
# Add a redistributable only with explicit provenance, license, and recursive import checks.
|
||||
$systemDlls = @(
|
||||
'kernel32.dll', 'kernelbase.dll', 'user32.dll', 'ole32.dll', 'oleaut32.dll',
|
||||
'bcrypt.dll', 'bcryptprimitives.dll', 'advapi32.dll', 'dwmapi.dll', 'gdi32.dll',
|
||||
'shell32.dll', 'imm32.dll', 'uxtheme.dll', 'ws2_32.dll', 'ntdll.dll', 'opengl32.dll',
|
||||
'ucrtbase.dll', 'msvcrt.dll', 'crypt32.dll', 'secur32.dll', 'shlwapi.dll',
|
||||
'comdlg32.dll', 'comctl32.dll', 'version.dll', 'winmm.dll', 'winhttp.dll',
|
||||
'iphlpapi.dll', 'userenv.dll', 'dbghelp.dll', 'dwrite.dll', 'd2d1.dll',
|
||||
'd3d11.dll', 'd3d12.dll', 'dxgi.dll', 'd3dcompiler_47.dll', 'propsys.dll',
|
||||
'powrprof.dll', 'setupapi.dll', 'cfgmgr32.dll', 'winspool.drv', 'wtsapi32.dll'
|
||||
)
|
||||
$inputs = [ordered]@{}
|
||||
foreach ($name in @('arcadia-studio.exe', 'arcadia-server.exe')) {
|
||||
$inputs[$name] = Require-File (Join-Path $BinaryDirectory $name)
|
||||
}
|
||||
if ($Target -eq 'x86_64-pc-windows-gnullvm') {
|
||||
if (-not $LlvmMinGWRoot) { throw 'gnullvm requires -LlvmMinGWRoot for its exact runtime and license.' }
|
||||
$inputs['libunwind.dll'] = Require-File (Join-Path $LlvmMinGWRoot 'x86_64-w64-mingw32/bin/libunwind.dll')
|
||||
$inputs['licenses/llvm-mingw-LICENSE.TXT'] = Require-File (Join-Path $LlvmMinGWRoot 'LICENSE.TXT')
|
||||
} elseif ($LlvmMinGWRoot) {
|
||||
throw 'MSVC packaging does not use LLVM-MinGW; omit -LlvmMinGWRoot.'
|
||||
}
|
||||
|
||||
$imports = [ordered]@{}
|
||||
$inspectedHashes = @{}
|
||||
foreach ($name in @($inputs.Keys | Where-Object { $_ -match '\.(exe|dll)$' })) {
|
||||
$inspectedHashes[$name] = (Get-FileHash -LiteralPath $inputs[$name] -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
$imports[$name] = @(Read-PeImports $inputs[$name] | Sort-Object -Unique)
|
||||
foreach ($dependency in $imports[$name]) {
|
||||
$isSystem = $dependency -match '^(api-ms-win-|ext-ms-win-)[a-z0-9-]+\.dll$' -or $systemDlls -contains $dependency
|
||||
if (-not $isSystem -and -not $inputs.Contains($dependency)) {
|
||||
throw "Unpackaged runtime import '$dependency' in '$name'. Resolve its provenance and dependency closure before packaging."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$binaryHashes = [ordered]@{}
|
||||
foreach ($name in @('arcadia-studio.exe', 'arcadia-server.exe')) { $binaryHashes[$name] = $inspectedHashes[$name] }
|
||||
$windowsInputs = Get-PackageWindowsInputs $BinaryDirectory $Target $LlvmMinGWRoot @('arcadia-studio.exe', 'arcadia-server.exe')
|
||||
$inputs = $windowsInputs.Inputs
|
||||
$imports = $windowsInputs.Imports
|
||||
$inspectedHashes = $windowsInputs.InspectedHashes
|
||||
$binaryHashes = $windowsInputs.BinaryHashes
|
||||
$evidence = Read-PackageBuildEvidence $BuildEvidence $Target $binaryHashes
|
||||
$documentation = Get-PackageDocumentation $repository 'windows' $Python
|
||||
foreach ($name in $documentation.Keys) { $inputs[$name] = $documentation[$name] }
|
||||
|
||||
Reference in New Issue
Block a user