Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/bun-usockets/src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,10 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_controllen = CMSG_LEN(sizeof(int));
/* CMSG_SPACE, not CMSG_LEN: FreeBSD sets MSG_CTRUNC and
* discards the descriptor when the buffer can't hold the
* aligned trailing padding (20 vs 24 bytes there). */
msg.msg_controllen = sizeof(cmsg_buf);
msg.msg_control = cmsg_buf;

length = bsd_recvmsg(us_poll_fd(&s->p), &msg, recv_flags);
Expand Down
8 changes: 6 additions & 2 deletions scripts/build/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1374,10 +1374,14 @@ export const linkerFlags: Flag[] = [
desc: "FreeBSD 13+ clang defaults to PIE; opt out (matches Linux, avoids -fPIC rebuild of WebKit/deps)",
},
{
// No `-z stack-size`: unlike Linux, FreeBSD's exec uses PT_GNU_STACK's
// size as the main-thread stack reservation, while libthr reports the
// main thread's stack as RLIMIT_STACK — so a value here smaller than the
// rlimit makes WTF::StackBounds overshoot and stack-overflow guards never
// fire (the process dies with SIGILL once the real stack is exhausted).
flag: [
"-Wl,-O2",
"-Wl,--as-needed",
"-Wl,-z,stack-size=12800000",
"-Wl,-z,lazy",
"-Wl,-z,norelro",
"-Wl,--gdb-index",
Expand All @@ -1386,7 +1390,7 @@ export const linkerFlags: Flag[] = [
"-Wl,--build-id=sha1",
],
when: c => c.freebsd,
desc: "FreeBSD linker tuning (same as Linux ELF)",
desc: "FreeBSD linker tuning (same as Linux ELF minus stack-size)",
},
{
// rust-lang/llvm-project doesn't enable `LLVM_ENABLE_ZLIB` (or `_ZSTD`) for
Expand Down
22 changes: 16 additions & 6 deletions scripts/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import { normalize as normalizeWindows } from "node:path/win32";

export const isWindows = process.platform === "win32";
export const isMacOS = process.platform === "darwin";
export const isLinux = process.platform === "linux";
export const isPosix = isMacOS || isLinux;
// Node built for Termux/bionic reports "android"; CI models that as linux + abi=android.
export const isAndroid = process.platform === "android";
export const isLinux = process.platform === "linux" || isAndroid;
export const isFreeBSD = process.platform === "freebsd";
export const isPosix = isMacOS || isLinux || isFreeBSD;

export const isArm64 = process.arch === "arm64";
export const isX64 = process.arch === "x64";
Expand Down Expand Up @@ -1538,23 +1541,26 @@ export function parseNumber(value) {

/**
* @param {string} string
* @returns {"darwin" | "linux" | "windows"}
* @returns {"darwin" | "linux" | "windows" | "freebsd"}
*/
export function parseOs(string) {
if (/darwin|apple|mac/i.test(string)) {
return "darwin";
}
if (/linux/i.test(string)) {
if (/linux|android/i.test(string)) {
return "linux";
}
if (/freebsd/i.test(string)) {
return "freebsd";
}
if (/win/i.test(string)) {
return "windows";
}
throw new Error(`Unsupported operating system: ${string}`);
}

/**
* @returns {"darwin" | "linux" | "windows"}
* @returns {"darwin" | "linux" | "windows" | "freebsd"}
*/
export function getOs() {
return parseOs(process.platform);
Expand Down Expand Up @@ -1604,13 +1610,17 @@ export function getKernel() {
}

/**
* @returns {"musl" | "gnu" | undefined}
* @returns {"musl" | "gnu" | "android" | undefined}
*/
export function getAbi() {
if (!isLinux) {
return;
}

if (isAndroid || existsSync("/system/bin/linker64")) {
return "android";
}

if (existsSync("/etc/alpine-release")) {
return "musl";
}
Expand Down
14 changes: 14 additions & 0 deletions src/bun_core/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2413,10 +2413,24 @@ pub mod ffi {
// (thin non-null pointer to a `#[repr(C)]` struct); the type encodes
// the only pointer-validity precondition, so `safe fn` discharges the
// link-time proof and the call needs no `unsafe` block.
#[cfg(not(target_os = "freebsd"))]
unsafe extern "C" {
#[link_name = "uname"]
safe fn libc_uname(buf: &mut libc::utsname) -> core::ffi::c_int;
}
// FreeBSD's exported `uname` symbol is a compat entry that fills
// 32-byte fields; `struct utsname` has 256-byte fields and the
// header's `uname()` is an inline over `__xuname(SYS_NMLN, buf)`.
#[cfg(target_os = "freebsd")]
fn libc_uname(buf: &mut libc::utsname) -> core::ffi::c_int {
unsafe extern "C" {
safe fn __xuname(
nmln: core::ffi::c_int,
buf: &mut libc::utsname,
) -> core::ffi::c_int;
}
__xuname(256, buf)
}
let mut u: libc::utsname = zeroed();
let _ = libc_uname(&mut u);
u
Expand Down
11 changes: 11 additions & 0 deletions src/io/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,17 @@ impl Poll {
return;
}
let poll = pollable.poll();
// FreeBSD reports a changelist entry it could not apply with
// `flags == EV_ERROR` and the errno in `data`. ENOENT/EBADF is the
// `Cancel` for a oneshot knote that already fired (or an fd that was
// already closed): kernel state already matches, nothing to deliver.
#[cfg(target_os = "freebsd")]
if (event.flags & libc::EV_ERROR) != 0
&& (event.data == libc::ENOENT as _ || event.data == libc::EBADF as _)
{
log!("cancel({}) already gone = {}", event.ident, event.data);
return;
}
// CYCLEBREAK: owner (ReadFile/WriteFile) is T6; dispatch via link-time
// `extern "Rust"` defined in `bun_runtime::dispatch`. The
// container_of(io_poll) recovery happens there.
Expand Down
23 changes: 17 additions & 6 deletions src/runtime/api/bun/Terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,8 +877,8 @@ pub type OpenPtyFn = unsafe extern "C" fn(
winp: *const Winsize,
) -> c_int;

/// Dynamic loading of openpty on Linux (it's in libutil which may not be linked)
#[cfg(any(target_os = "linux", target_os = "android"))]
/// Dynamic loading of openpty on Linux/FreeBSD (it's in libutil which may not be linked)
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
mod lib_util {
use super::*;
use bun_core::ZStr;
Expand All @@ -898,12 +898,18 @@ mod lib_util {
}
LOADED.store(true, Relaxed);

// Try libutil.so first (most common), then libutil.so.1
// Try libutil.so first (most common), then the versioned soname
#[cfg(not(target_os = "freebsd"))]
const LIB_NAMES: [&ZStr; 3] = [
bun_core::zstr!("libutil.so"),
bun_core::zstr!("libutil.so.1"),
bun_core::zstr!("libc.so.6"),
];
#[cfg(target_os = "freebsd")]
const LIB_NAMES: [&ZStr; 2] = [
bun_core::zstr!("libutil.so.9"),
bun_core::zstr!("libutil.so"),
];
for lib_name in LIB_NAMES {
if let Some(h) = sys::dlopen(lib_name, sys::RTLD::LAZY) {
HANDLE.store(h, Relaxed);
Expand Down Expand Up @@ -940,14 +946,19 @@ fn get_open_pty_fn() -> Option<OpenPtyFn> {
return Some(openpty);
}

// On Linux, openpty is in libutil, which may not be linked
// On Linux/FreeBSD, openpty is in libutil, which may not be linked
// Load it dynamically via dlopen
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
{
return lib_util::get_open_pty();
}

#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "android")))]
#[cfg(not(any(
target_os = "macos",
target_os = "linux",
target_os = "android",
target_os = "freebsd"
)))]
None
}

Expand Down
Loading
Loading