diff --git a/src/sys.zig b/src/sys.zig index 285878683d..cb606398f4 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -4092,6 +4092,12 @@ pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDe _ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size)); } + // Seek input to beginning — the caller may have written to this fd, + // leaving the file offset at EOF. copy_file_range / sendfile / read + // all use the current offset when called with null offsets. + // Ignore errors: the fd may be non-seekable (e.g. a pipe). + _ = setFileOffset(in_handle, 0); + switch (bun.copyFile(in_handle, out_handle)) { .err => |e| return .{ .err = e }, .result => {}, diff --git a/test/bundler/bun-build-compile.test.ts b/test/bundler/bun-build-compile.test.ts index 8004a7bb78..491e3b8438 100644 --- a/test/bundler/bun-build-compile.test.ts +++ b/test/bundler/bun-build-compile.test.ts @@ -121,4 +121,71 @@ describe("Bun.build compile", () => { }); }); +describe("compiled binary validity", () => { + test("output binary has valid executable header", async () => { + using dir = tempDir("build-compile-valid-header", { + "app.js": `console.log("hello");`, + }); + + const outfile = join(dir + "", "app-out"); + const result = await Bun.build({ + entrypoints: [join(dir + "", "app.js")], + compile: { + outfile, + }, + }); + + expect(result.success).toBe(true); + + // Read the first 4 bytes and verify it's a valid executable magic number + const file = Bun.file(result.outputs[0].path); + const header = new Uint8Array(await file.slice(0, 4).arrayBuffer()); + + if (isMacOS) { + // MachO magic: 0xCFFAEDFE (little-endian) + expect(header[0]).toBe(0xcf); + expect(header[1]).toBe(0xfa); + expect(header[2]).toBe(0xed); + expect(header[3]).toBe(0xfe); + } else if (isLinux) { + // ELF magic: 0x7F 'E' 'L' 'F' + expect(header[0]).toBe(0x7f); + expect(header[1]).toBe(0x45); // 'E' + expect(header[2]).toBe(0x4c); // 'L' + expect(header[3]).toBe(0x46); // 'F' + } else if (isWindows) { + // PE magic: 'M' 'Z' + expect(header[0]).toBe(0x4d); // 'M' + expect(header[1]).toBe(0x5a); // 'Z' + } + }); + + test("compiled binary runs and produces expected output", async () => { + using dir = tempDir("build-compile-runs", { + "app.js": `console.log("compile-test-output");`, + }); + + const outfile = join(dir + "", "app-run"); + const result = await Bun.build({ + entrypoints: [join(dir + "", "app.js")], + compile: { + outfile, + }, + }); + + expect(result.success).toBe(true); + + await using proc = Bun.spawn({ + cmd: [result.outputs[0].path], + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + expect(stdout.trim()).toBe("compile-test-output"); + expect(exitCode).toBe(0); + }); +}); + // file command test works well