diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts index 7208837f4acb1..beb7ba848fd31 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts @@ -163,6 +163,48 @@ function extractAlpinefromDocker(nodeVersion: string, platform: string, arch: st return es.readArray([new File({ path: 'node', contents, stat: { mode: parseInt('755', 8) } as fs.Stats })]); } +// WSL1 binfmt_elf rejects PT_LOAD segments with p_align > PAGE_SIZE (0x1000). +// Node 24 linux-x64 ships an `lpstub` LOAD segment aligned to 2 MiB for hugepage +// remapping; clamp it so the binary still loads under WSL1. +function patchElfLoadAlign(): NodeJS.ReadWriteStream { + return es.mapSync(file => { + if (!file.contents || !Buffer.isBuffer(file.contents)) { + return file; + } + const buf = file.contents; + if (buf.length < 64) { + return file; + } + if (buf[0] !== 0x7f || buf[1] !== 0x45 || buf[2] !== 0x4c || buf[3] !== 0x46) { + return file; + } + if (buf[4] !== 2 /* ELFCLASS64 */ || buf[5] !== 1 /* ELFDATA2LSB */) { + return file; + } + const e_phoff = Number(buf.readBigUInt64LE(0x20)); + const e_phentsize = buf.readUInt16LE(0x36); + const e_phnum = buf.readUInt16LE(0x38); + if (e_phentsize !== 56) { + return file; + } + const PT_LOAD = 1; + const MAX_ALIGN = 0x1000n; + for (let i = 0; i < e_phnum; i++) { + const off = e_phoff + i * e_phentsize; + if (off + e_phentsize > buf.length) { + break; + } + if (buf.readUInt32LE(off) !== PT_LOAD) { + continue; + } + if (buf.readBigUInt64LE(off + 48) > MAX_ALIGN) { + buf.writeBigUInt64LE(MAX_ALIGN, off + 48); + } + } + return file; + }); +} + const { nodeVersion, internalNodeVersion } = getNodeVersion(); BUILD_TARGETS.forEach(({ platform, arch }) => { @@ -229,14 +271,16 @@ function nodejs(platform: string, arch: string): NodeJS.ReadWriteStream | undefi fetchUrls(`/dist/v${nodeVersion}/win-${arch}/node.exe`, { base: 'https://nodejs.org', checksumSha256 })) .pipe(rename('node.exe')); case 'darwin': - case 'linux': - return (product.nodejsRepository !== 'https://nodejs.org' ? + case 'linux': { + const downloaded = (product.nodejsRepository !== 'https://nodejs.org' ? fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName!, checksumSha256 }) : fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 }) ).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) .pipe(filter('**/node')) .pipe(util.setExecutableBit('**')) .pipe(rename('node')); + return platform === 'linux' && arch === 'x64' ? downloaded.pipe(patchElfLoadAlign()) : downloaded; + } case 'alpine': return product.nodejsRepository !== 'https://nodejs.org' ? fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName!, checksumSha256 }) diff --git a/test/sanity/src/context.ts b/test/sanity/src/context.ts index 990252bc48bd4..3fc08f3c3796a 100644 --- a/test/sanity/src/context.ts +++ b/test/sanity/src/context.ts @@ -42,7 +42,6 @@ export class TestContext { private readonly tempDirs = new Set(); private readonly wslTempDirs = new Set(); - private readonly patchedWslNodePaths = new Set(); private nextPort = 3010; private currentTestName: string | undefined; private screenshotCounter = 0; @@ -257,38 +256,6 @@ export class TestContext { return undefined; } - /** - * On WSL1, patches the Node.js binary used by the server to remove ELF note sections - * that cause Node 24 to fail to start. No-op on WSL2. - * @param wslEntryPoint The WSL path to the server entry point script. - */ - public applyWsl1Node24Workaround(wslEntryPoint: string): void { - if (this.getUbuntuWslVersion() !== 1) { - return; - } - - const wslNodePath = wslEntryPoint.replace(/\/bin\/[^/]+$/, '/node'); - if (this.patchedWslNodePaths.has(wslNodePath)) { - return; - } - - this.patchedWslNodePaths.add(wslNodePath); - this.warn(`Applying WSL1 Node 24 workaround for ${wslNodePath}`); - - const shellScript = [ - 'set -e', - `node_path='${wslNodePath}'`, - 'backup_path="${node_path}.orig"', - 'if [ -f "${backup_path}" ]; then exit 0; fi', - 'if ! command -v objcopy >/dev/null 2>&1; then apt-get update && apt-get install -y binutils; fi', - 'cp "${node_path}" "${backup_path}"', - 'objcopy --remove-section .note.ABI-tag --remove-section .note.gnu.build-id --remove-section .note.gnu.property "${backup_path}" "${node_path}"', - 'chmod +x "${node_path}"', - ].join('; '); - - this.runNoErrors('wsl', '-d', 'Ubuntu', 'sh', '-lc', shellScript); - } - /** * Ensures that the directory for the specified file path exists. */ diff --git a/test/sanity/src/wsl.test.ts b/test/sanity/src/wsl.test.ts index 0e6b56d23b8b9..65a6455fee2d8 100644 --- a/test/sanity/src/wsl.test.ts +++ b/test/sanity/src/wsl.test.ts @@ -61,7 +61,6 @@ export function setup(context: TestContext) { } const wslEntryPoint = context.toWslPath(entryPoint); - context.applyWsl1Node24Workaround(wslEntryPoint); await context.runCliApp('WSL Server', 'wsl', [ @@ -102,7 +101,6 @@ export function setup(context: TestContext) { const test = new WslUITest(context, undefined, wslWorkspaceDir, wslExtensionsDir); const wslEntryPoint = context.toWslPath(entryPoint); - context.applyWsl1Node24Workaround(wslEntryPoint); await context.runCliApp('WSL Server', 'wsl', [