Loading build-cross.sh +156 −8 Changes for build-cross.sh: 156 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ # not in the default run: # packages the FenrirOS specs as RPMs (host rpmbuild, cross toolchain) # repo those RPMs as dnf5 repository $WORK/repo/<arch> (REPO_UPLOAD) # docker container images fenriros, fenriros-devel of the repository # (linux/ppc; tarballs in $WORK/docker) # # Usage: ./build-cross.sh [step...] (default: all steps in order) # ./build-cross.sh clean Loading Loading @@ -77,6 +79,8 @@ LLVM_REF="${LLVM_REF:-llvmorg-23.1.2}" if [[ -z ${LLVM_SRC:-} && -d $SYSTEMPP_SRC/build-chroot/llvm-project/compiler-rt ]]; then LLVM_SRC="$SYSTEMPP_SRC/build-chroot/llvm-project" fi # (else the x86 build's checkout, build-chroot.sh's LLVM_SRC) [[ -n ${LLVM_SRC:-} || -d $SRCDIR/llvm-project ]] || LLVM_SRC="/build/$USER/systempp/build-chroot/llvm-project" LLVM_SRC="$(realpath -m "${LLVM_SRC:-$SRCDIR/llvm-project}")" FRESH_SRC="$(realpath -m "${FRESH_SRC:-$SYSTEMPP_SRC/../fresh}")" SYSLITE_SRC="$(realpath -m "${SYSLITE_SRC:-$SYSTEMPP_SRC/../syslite}")" Loading @@ -95,7 +99,8 @@ CROSS_PACKAGES_SET="${CROSS_PACKAGES:+1}" CROSS_PACKAGES="${CROSS_PACKAGES:-systempp fresh zlib zstd bzip2 libjson-c libbrotli libcmdplus libuuidp libnetplus libyaml libconfplus libhttppp libressl syslite sharedd adconnect storaged iscsid upsd firewalld tinyxml2 snmpdd ntpplus containerd webctl popt lua file libelf libsqlite3 rpm ca-certificates fenriros-release liblzma libxml2 libarchive glib2 curl libsolv librepo fmt toml11 libsmartcols dnf5 e2fsprogs dosfstools gptfdisk mtd-utils openssh}" libsolv librepo fmt toml11 libsmartcols dnf5 e2fsprogs dosfstools gptfdisk mtd-utils openssh llvm-project}" # the board's kernel as a package (kernel-<board>.spec, e.g. the NAS ITX's # writes its boot image into the NAND on installation) if [[ -z ${CROSS_PACKAGES_SET:-} && -f $FENRIR_FILES/rpm/specs/kernel-$BOARD.spec ]]; then Loading @@ -103,6 +108,13 @@ if [[ -z ${CROSS_PACKAGES_SET:-} && -f $FENRIR_FILES/rpm/specs/kernel-$BOARD.spe fi PREFIX=/usr # packages built and in the repository, but not in the board's root # (dnf install them when needed): the toolchain ROOTFS_EXCLUDE="${ROOTFS_EXCLUDE:-llvm-project clang lld lldb llvm-tools}" # llvm-project: the x86 build's checkout (staged as the spec's Source0) and # its native tablegen (LLVM_NATIVE_TOOL_DIR, build-chroot.sh's host-tblgen) export LLVM_SRC # (defined above: the builtins step uses it too) LLVM_NATIVE_TOOL_DIR="${LLVM_NATIVE_TOOL_DIR:-/build/$USER/systempp/build-chroot/build/llvm-host/bin}" INTERP="$PREFIX/lib/ld-linux-$PROC.so" BUILD="$WORK/build" SYSROOT="$WORK/sysroot" Loading @@ -118,6 +130,7 @@ BUILTINS="$SYSROOT$PREFIX/lib/libsystempp-builtins.a" PATH="$PATH:/usr/sbin:/sbin" msg() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; } # run <logname> <cmd...> -- run a command, output into $LOGS/<logname>.log Loading Loading @@ -226,16 +239,21 @@ step_builtins() { # gen_wrapper <out> <c|c++> -- a clang driver for the target, compiling # against the sysroot's systempp headers and linking systempp's crt objects # and libc.so (same logic as build-chroot.sh's gen_wrapper). # WRAP_CLANG/WRAP_RESDIR/WRAP_ROOT/WRAP_TFLAGS/WRAP_BUILTINS: another clang, # root and builtins than the host's and the sysroot (the native wrappers of # the target's clang package, see native_wrappers) gen_wrapper() { local out="$1" lang="$2" clang drv="" resdir clang="$(command -v "$HOST_CC")" local out="$1" lang="$2" clang drv="" resdir root tflags builtins clang="${WRAP_CLANG:-$(command -v "$HOST_CC")}" [[ $lang == c++ ]] && drv="--driver-mode=g++" resdir="$("$HOST_CC" -print-resource-dir)" local inc="$TFLAGS -fPIC -nostdinc -cxx-isystem $SYSROOT$PREFIX/include/cxx -isystem $SYSROOT$PREFIX/include -isystem $resdir/include" resdir="${WRAP_RESDIR:-$("$HOST_CC" -print-resource-dir)}" root="${WRAP_ROOT-$SYSROOT}" tflags="${WRAP_TFLAGS-$TFLAGS}" builtins="${WRAP_BUILTINS:-$BUILTINS}" local inc="$tflags -fPIC -nostdinc -cxx-isystem $root$PREFIX/include/cxx -isystem $root$PREFIX/include -isystem $resdir/include" local TFLAGS="$tflags" BUILTINS="$builtins" cat >"$out" <<EOF #!/bin/sh # generated by systempp build-cross.sh -- clang driver for systempp on $TRIPLE LIB="$SYSROOT$PREFIX/lib" LIB="$root$PREFIX/lib" mode=link pie=1 shared=0 static=0 startfiles=1 libs=1 for a in "\$@"; do case "\$a" in Loading Loading @@ -277,6 +295,31 @@ EOF chmod +x "$out" } # native_wrappers <out.tar.gz> <llvm-major> -- the target clang package's # cc/c++ (systempp-cc/-c++, as build-chroot.sh's llvm_wrappers for x86_64): # its own clang, headers and crt objects under /usr, the target's # compiler-rt builtins as /usr/lib/libsystempp-builtins.a, the mandatory # target flags (e500: soft-float, secure PLT) built in native_wrappers() { local out="$1" major="$2" d="$BUILD/native-wrappers" l rm -rf --one-file-system "${d:?}" mkdir -p "$d$PREFIX/bin" "$d$PREFIX/lib" cp "$BUILTINS" "$d$PREFIX/lib/libsystempp-builtins.a" local flags="${TFLAGS#--target=$CLANG_TARGET }" WRAP_CLANG="$PREFIX/bin/clang-$major" WRAP_RESDIR="$PREFIX/lib/clang/$major" WRAP_ROOT="" \ WRAP_TFLAGS="$flags" WRAP_BUILTINS="$PREFIX/lib/libsystempp-builtins.a" \ gen_wrapper "$d$PREFIX/bin/systempp-cc" c WRAP_CLANG="$PREFIX/bin/clang-$major" WRAP_RESDIR="$PREFIX/lib/clang/$major" WRAP_ROOT="" \ WRAP_TFLAGS="$flags" WRAP_BUILTINS="$PREFIX/lib/libsystempp-builtins.a" \ gen_wrapper "$d$PREFIX/bin/systempp-c++" c++ for l in cc clang; do ln -s systempp-cc "$d$PREFIX/bin/$l"; done for l in c++ clang++; do ln -s systempp-c++ "$d$PREFIX/bin/$l"; done for l in clang-cpp clang-cl; do ln -s "clang-$major" "$d$PREFIX/bin/$l"; done for l in ar ranlib nm strip objcopy objdump readelf; do ln -s "llvm-$l" "$d$PREFIX/bin/$l"; done tar -czf "$out" -C "$d" usr rm -rf --one-file-system "${d:?}" } step_toolchain() { [[ -f $SYSROOT$PREFIX/lib/libc.so && -f $BUILTINS ]] || die "sysroot incomplete -- run 'systempp' and 'builtins' first" msg "toolchain: $TRIPLE-cc/-c++ in $TOOLCHAIN" Loading Loading @@ -525,7 +568,8 @@ step_rootfs() { ! -name '*-debuginfo-*' 2>/dev/null | while read -r r; do printf '%s %s %s\n' "$(rpm -qp --qf '%{NAME}' "$r" 2>/dev/null)" \ "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$r" 2>/dev/null)" "$r" done | sort -k1,1 -k2,2V | awk '{ last[$1] = $3 } END { for (n in last) print last[n] }' | done | sort -k1,1 -k2,2V | awk -v ex=" $ROOTFS_EXCLUDE " '{ last[$1] = $3 } END { for (n in last) if (index(ex, " " n " ") == 0) print last[n] }' | sort >"$list" msg "rootfs: installing $(wc -l <"$list") RPMs" : >"$LOGS/rootfs.log" Loading Loading @@ -676,6 +720,14 @@ EOF cross_prebuild() { local pkg="$1" spec="$2" case "$pkg" in llvm-project) # the wrappers tarball build-chroot.sh staged is x86_64's: the # target's instead (its builtins, e500 flags) local major major="$(sed -n 's/^Version:[[:space:]]*\([0-9]*\).*/\1/p' "$spec" | head -n1)" native_wrappers "$WORK/rpmbuild/SOURCES/llvm-project-wrappers.tar.gz" "$major" echo "$spec" ;; libsqlite3) # build-chroot.sh's step_rpm: the amalgamation into /usr/local, which # libsqlite3.spec packages -- here under the sysroot Loading Loading @@ -743,6 +795,7 @@ step_packages() { CMAKE_TOOLCHAIN_FILE="$pkgtc" \ CMAKE_POLICY_VERSION_MINIMUM=3.5 \ QEMU_LD_PREFIX="$SYSROOT" QEMU_CPU=e500v2 \ LLVM_NATIVE_TOOL_DIR="$LLVM_NATIVE_TOOL_DIR" \ PKG_CONFIG_SYSROOT_DIR="$SYSROOT" \ PKG_CONFIG_LIBDIR="$SYSROOT$PREFIX/lib/pkgconfig:$SYSROOT$PREFIX/share/pkgconfig" \ rpmbuild -bb --nodeps --target "$RPM_ARCH-linux" \ Loading Loading @@ -809,12 +862,107 @@ step_repo() { msg "repo ready: $repo/$arch" } # docker: container images of the repository's RPMs, as build-chroot.sh's # 'docker' step: fenriros (DOCKER_MINIMAL and what it requires) and # fenriros-devel (every package but the board's kernel and DOCKER_DEVEL_EXCLUDE), # root tarballs in $WORK/docker and, with docker there, imported as # <DOCKER_PREFIX>fenriros[-devel]:<DOCKER_TAG> for linux/$DOCKER_ARCH (what # containerd's goArch() calls this machine: uname -m) DOCKER="${DOCKER:-docker}" DOCKER_PREFIX="${DOCKER_PREFIX:-}" DOCKER_TAG="${DOCKER_TAG:-$(date -u +%Y%m%d)}" DOCKER_ARCH="${DOCKER_ARCH:-ppc}" DOCKER_MINIMAL="${DOCKER_MINIMAL:-systempp fresh fenriros-release rpm dnf5 ca-certificates}" DOCKER_DEVEL_EXCLUDE="${DOCKER_DEVEL_EXCLUDE:-kernel-$BOARD fenriros-installer}" # newest_rpms <dir> -- "<name> <file>" of the newest build of each package newest_rpms() { local r for r in "$1"/*.rpm; do [[ -e $r ]] || continue printf '%s %s %s\n' "$(rpm -qp --qf '%{NAME}' "$r" 2>/dev/null)" \ "$(rpm -qp --qf '%{EPOCHNUM}:%{VERSION}-%{RELEASE}' "$r" 2>/dev/null)" "$r" done | sort -k1,1 -k2,2V | awk '{ last[$1] = $3 } END { for (n in last) print n, last[n] }' | sort } # rpm_closure <name-file-list> <name>... -- the files of the named packages # and everything they require (by provides and file names) rpm_closure() { local list="$1"; shift local -A file prov want req local n f c p while read -r n f; do file[$n]=$f prov[$n]=$n while read -r c; do [[ -n $c && -z ${prov[$c]:-} ]] && prov[$c]=$n; done \ < <(rpm -qp --qf '[%{PROVIDENAME}\n][%{FILENAMES}\n]' "$f" 2>/dev/null) req[$n]="$(rpm -qp --qf '[%{REQUIRENAME}\n]' "$f" 2>/dev/null)" done <"$list" local queue=("$@") while ((${#queue[@]})); do n="${queue[0]}"; queue=("${queue[@]:1}") [[ -z ${want[$n]:-} ]] || continue [[ -n ${file[$n]:-} ]] || die "docker: no $n RPM in the repository" want[$n]=1 while IFS= read -r c; do [[ -n $c && $c != rpmlib\(* && $c != \(* ]] || continue p="${prov[$c]:-}" [[ -z $p || -n ${want[$p]:-} ]] || queue+=("$p") [[ -n $p ]] || warn "docker: $n requires $c, which no RPM provides" done <<<"${req[$n]:-}" done for n in "${!want[@]}"; do echo "${file[$n]}"; done | sort } # docker_image <name> <file-list> -- root, tarball, (docker) image docker_image() { local name="$1" list="$2" root="$BUILD/docker-$1" msg "docker: $name: installing $(wc -l <"$list") RPMs" rm -rf --one-file-system "$root" mkdir -p "$root" unshare -r rpm --root "$root" --initdb # scriptlets run in the root through binfmt's qemu-$PROC if ! QEMU_CPU=e500v2 unshare -r rpm --root "$root" --ignorearch -ivh $(cat "$list") >>"$LOGS/docker-$name.log" 2>&1; then grep -E 'is needed by|conflicts with' "$LOGS/docker-$name.log" | sort -u | head -20 >&2 die "docker: $name: RPM installation failed (log: $LOGS/docker-$name.log)" fi # no RPM owns these (as build-chroot.sh's docker_image) mkdir -p "$root"/{run,tmp,root,home,var/tmp,var/log} chmod 1777 "$root/tmp" "$root/var/tmp" mkdir -p "$WORK/docker" tar -C "$root" --numeric-owner --owner=0 --group=0 -cf "$WORK/docker/$name-$DOCKER_ARCH.tar" . msg "docker: $WORK/docker/$name-$DOCKER_ARCH.tar ($(du -h "$WORK/docker/$name-$DOCKER_ARCH.tar" | cut -f1))" if command -v "$DOCKER" >/dev/null; then run docker "$DOCKER" import --platform "linux/$DOCKER_ARCH" \ --change "ENV PATH=/usr/bin:/usr/sbin:/bin:/sbin" \ --change "ENV HOME=/root" \ --change "WORKDIR /root" \ --change "CMD [\"/usr/bin/fresh\"]" \ --change "LABEL org.opencontainers.image.title=$name" \ --change "LABEL org.opencontainers.image.version=$DOCKER_TAG" \ "$WORK/docker/$name-$DOCKER_ARCH.tar" "${DOCKER_PREFIX}${name}:$DOCKER_TAG-$DOCKER_ARCH" msg "docker: ${DOCKER_PREFIX}${name}:$DOCKER_TAG-$DOCKER_ARCH (linux/$DOCKER_ARCH)" fi } step_docker() { local pk="$WORK/repo/$RPM_ARCH/Packages" all="$BUILD/docker-all" [[ -d $pk ]] || die "no repository -- run the 'repo' step first" mkdir -p "$LOGS" : >"$LOGS/docker-fenriros.log"; : >"$LOGS/docker-fenriros-devel.log" newest_rpms "$pk" >"$all" # shellcheck disable=SC2086 rpm_closure "$all" $DOCKER_MINIMAL >"$BUILD/docker-minimal" docker_image fenriros "$BUILD/docker-minimal" awk -v ex=" $DOCKER_DEVEL_EXCLUDE " 'index(ex, " " $1 " ") == 0 { print $2 }' "$all" >"$BUILD/docker-devel" docker_image fenriros-devel "$BUILD/docker-devel" } steps=("$@") ((${#steps[@]})) || steps=("${ALL_STEPS[@]}") for s in "${steps[@]}"; do case "$s" in headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer|packages|repo) headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer|packages|repo|docker) check_deps; mkdir -p "$WORK" "$BUILD"; "step_$s" ;; clean) step_clean ;; -h|--help) sed -n '2,/^$/s/^# \{0,1\}//p' "$0"; exit 0 ;; Loading src/linux/include/sched.h +20 −0 Changes for src/linux/include/sched.h: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -72,6 +72,8 @@ extern "C" { } cpu_set_t; int __sched_cpucount (size_t setsize, const cpu_set_t *setp); cpu_set_t *__sched_cpualloc (size_t count); void __sched_cpufree (cpu_set_t *set); int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t* mask); int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask); Loading Loading @@ -121,3 +123,21 @@ extern "C" { #define CPU_ISSET(cpu, set) \ ((((set)->__bits[(cpu) / __CPU_BITS_PER_WORD]) >> ((cpu) % __CPU_BITS_PER_WORD)) & 1UL) #define CPU_COUNT(set) __sched_cpucount(sizeof(cpu_set_t), (set)) /* Dynamically sized sets (glibc): CPU_ALLOC(count) for `count` CPUs, * CPU_ALLOC_SIZE(count) bytes, and the *_S variants taking that size. */ #define CPU_ALLOC_SIZE(count) \ ((((size_t)(count) + __CPU_BITS_PER_WORD - 1) / __CPU_BITS_PER_WORD) * sizeof(unsigned long)) #define CPU_ALLOC(count) __sched_cpualloc(count) #define CPU_FREE(set) __sched_cpufree(set) #define CPU_ZERO_S(size, set) __builtin_memset((set), 0, (size)) #define CPU_SET_S(cpu, size, set) \ ((size_t)(cpu) / 8 < (size_t)(size) \ ? ((set)->__bits[(cpu) / __CPU_BITS_PER_WORD] |= (1UL << ((cpu) % __CPU_BITS_PER_WORD))) : 0) #define CPU_CLR_S(cpu, size, set) \ ((size_t)(cpu) / 8 < (size_t)(size) \ ? ((set)->__bits[(cpu) / __CPU_BITS_PER_WORD] &= ~(1UL << ((cpu) % __CPU_BITS_PER_WORD))) : 0) #define CPU_ISSET_S(cpu, size, set) \ ((size_t)(cpu) / 8 < (size_t)(size) \ ? ((((set)->__bits[(cpu) / __CPU_BITS_PER_WORD]) >> ((cpu) % __CPU_BITS_PER_WORD)) & 1UL) : 0) #define CPU_COUNT_S(size, set) __sched_cpucount((size), (set)) src/linux/sched.cpp +20 −1 Changes for src/linux/sched.cpp: 20 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ *******************************************************************************/ #include <sched.h> #include <stdlib.h> #include <bits/syscall.h> #include <bits/sysbits.h> Loading @@ -44,6 +45,17 @@ extern "C" { return count; } /* CPU_ALLOC()/CPU_FREE(): sets for more than CPU_SETSIZE CPUs, sized * with CPU_ALLOC_SIZE() (as glibc: whole longs, zeroed). LLVM's * computeHostNumPhysicalCores() uses them on every Linux but x86. */ cpu_set_t *__sched_cpualloc (size_t count){ return static_cast<cpu_set_t *>(calloc(1, CPU_ALLOC_SIZE(count))); } void __sched_cpufree (cpu_set_t *set){ free(set); } /* Real syscall wrapper -- was a stub that always returned -1, * meaning sched_yield() (and std::this_thread::yield() built on * it) never actually yielded at all. */ Loading Loading @@ -92,7 +104,14 @@ extern "C" { return syscall_ret(syscall3(__NR_sched_setaffinity, pid, cpusetsize, (unsigned long)mask)); } /* The system call returns the bytes of the kernel's mask it copied; * the libc function returns 0 and zeroes the rest of the caller's set * (as glibc and musl) -- callers test == 0 (LLVM's core count). */ int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask){ return syscall_ret(syscall3(__NR_sched_getaffinity, pid, cpusetsize, (unsigned long)mask)); long r = syscall_ret(syscall3(__NR_sched_getaffinity, pid, cpusetsize, (unsigned long)mask)); if (r < 0) return -1; if ((size_t)r < cpusetsize) __builtin_memset((char *)mask + r, 0, cpusetsize - (size_t)r); return 0; } }; src/test/e2e/CMakeLists.txt +20 −0 Changes for src/test/e2e/CMakeLists.txt: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -3787,3 +3787,23 @@ target_link_libraries(e2e_to_string_wide PRIVATE add_dependencies(e2e_to_string_wide crti Scrt1 crtbeginS crtn systempp) add_test(NAME e2e_to_string_wide COMMAND e2e_to_string_wide) add_executable(e2e_cpu_alloc cpu_alloc_test.cpp) set_target_properties(e2e_cpu_alloc PROPERTIES OUTPUT_NAME "cpu_alloc_test") target_compile_options(e2e_cpu_alloc PRIVATE -fPIE) target_link_options(e2e_cpu_alloc PRIVATE -nostdlib -pie -Wl,--hash-style=sysv -Wl,--dynamic-linker,$<TARGET_FILE_DIR:systempp>/ld-linux-${CMAKE_SYSTEM_PROCESSOR}.so -Wl,-rpath,$<TARGET_FILE_DIR:systempp> ) target_link_libraries(e2e_cpu_alloc PRIVATE $<TARGET_OBJECTS:crti> $<TARGET_OBJECTS:Scrt1> $<TARGET_OBJECTS:crtbeginS> $<TARGET_OBJECTS:crtn> systempp ) add_dependencies(e2e_cpu_alloc crti Scrt1 crtbeginS crtn systempp) add_test(NAME e2e_cpu_alloc COMMAND e2e_cpu_alloc) src/test/e2e/cpu_alloc_test.cpp 0 → 100644 +27 −0 Changes for src/test/e2e/cpu_alloc_test.cpp: 27 added lines, 0 removed lines. Original line number Diff line number Diff line /* CPU_ALLOC()/CPU_ALLOC_SIZE()/CPU_FREE() and the *_S macros (were missing: * LLVM's computeHostNumPhysicalCores() needs them off x86). */ #include <sched.h> #include <stdio.h> #define CHECK(c, m) do { if (!(c)) { printf("FAIL: %s\n", m); return 1; } } while (0) int main() { CHECK(CPU_ALLOC_SIZE(2048) == 2048 / 8, "CPU_ALLOC_SIZE(2048) is 256 bytes"); CHECK(CPU_ALLOC_SIZE(1) == sizeof(unsigned long), "CPU_ALLOC_SIZE(1) is one long"); cpu_set_t *s = CPU_ALLOC(2048); CHECK(s, "CPU_ALLOC returns memory"); size_t sz = CPU_ALLOC_SIZE(2048); CHECK(CPU_COUNT_S(sz, s) == 0, "CPU_ALLOC zeroes the set"); CPU_SET_S(2000, sz, s); CPU_SET_S(3, sz, s); CPU_SET_S(4096, sz, s); /* beyond the set: ignored */ CHECK(CPU_ISSET_S(2000, sz, s) && CPU_ISSET_S(3, sz, s), "CPU_SET_S/CPU_ISSET_S"); CHECK(!CPU_ISSET_S(4096, sz, s), "CPU_ISSET_S beyond the set is 0"); CHECK(CPU_COUNT_S(sz, s) == 2, "CPU_COUNT_S counts the whole set"); CHECK(sched_getaffinity(0, sz, s) == 0 && CPU_COUNT_S(sz, s) >= 1, "sched_getaffinity with a CPU_ALLOC set"); CPU_ZERO_S(sz, s); CHECK(CPU_COUNT_S(sz, s) == 0, "CPU_ZERO_S"); CPU_FREE(s); printf("PASS\n"); return 0; } Loading
build-cross.sh +156 −8 Changes for build-cross.sh: 156 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ # not in the default run: # packages the FenrirOS specs as RPMs (host rpmbuild, cross toolchain) # repo those RPMs as dnf5 repository $WORK/repo/<arch> (REPO_UPLOAD) # docker container images fenriros, fenriros-devel of the repository # (linux/ppc; tarballs in $WORK/docker) # # Usage: ./build-cross.sh [step...] (default: all steps in order) # ./build-cross.sh clean Loading Loading @@ -77,6 +79,8 @@ LLVM_REF="${LLVM_REF:-llvmorg-23.1.2}" if [[ -z ${LLVM_SRC:-} && -d $SYSTEMPP_SRC/build-chroot/llvm-project/compiler-rt ]]; then LLVM_SRC="$SYSTEMPP_SRC/build-chroot/llvm-project" fi # (else the x86 build's checkout, build-chroot.sh's LLVM_SRC) [[ -n ${LLVM_SRC:-} || -d $SRCDIR/llvm-project ]] || LLVM_SRC="/build/$USER/systempp/build-chroot/llvm-project" LLVM_SRC="$(realpath -m "${LLVM_SRC:-$SRCDIR/llvm-project}")" FRESH_SRC="$(realpath -m "${FRESH_SRC:-$SYSTEMPP_SRC/../fresh}")" SYSLITE_SRC="$(realpath -m "${SYSLITE_SRC:-$SYSTEMPP_SRC/../syslite}")" Loading @@ -95,7 +99,8 @@ CROSS_PACKAGES_SET="${CROSS_PACKAGES:+1}" CROSS_PACKAGES="${CROSS_PACKAGES:-systempp fresh zlib zstd bzip2 libjson-c libbrotli libcmdplus libuuidp libnetplus libyaml libconfplus libhttppp libressl syslite sharedd adconnect storaged iscsid upsd firewalld tinyxml2 snmpdd ntpplus containerd webctl popt lua file libelf libsqlite3 rpm ca-certificates fenriros-release liblzma libxml2 libarchive glib2 curl libsolv librepo fmt toml11 libsmartcols dnf5 e2fsprogs dosfstools gptfdisk mtd-utils openssh}" libsolv librepo fmt toml11 libsmartcols dnf5 e2fsprogs dosfstools gptfdisk mtd-utils openssh llvm-project}" # the board's kernel as a package (kernel-<board>.spec, e.g. the NAS ITX's # writes its boot image into the NAND on installation) if [[ -z ${CROSS_PACKAGES_SET:-} && -f $FENRIR_FILES/rpm/specs/kernel-$BOARD.spec ]]; then Loading @@ -103,6 +108,13 @@ if [[ -z ${CROSS_PACKAGES_SET:-} && -f $FENRIR_FILES/rpm/specs/kernel-$BOARD.spe fi PREFIX=/usr # packages built and in the repository, but not in the board's root # (dnf install them when needed): the toolchain ROOTFS_EXCLUDE="${ROOTFS_EXCLUDE:-llvm-project clang lld lldb llvm-tools}" # llvm-project: the x86 build's checkout (staged as the spec's Source0) and # its native tablegen (LLVM_NATIVE_TOOL_DIR, build-chroot.sh's host-tblgen) export LLVM_SRC # (defined above: the builtins step uses it too) LLVM_NATIVE_TOOL_DIR="${LLVM_NATIVE_TOOL_DIR:-/build/$USER/systempp/build-chroot/build/llvm-host/bin}" INTERP="$PREFIX/lib/ld-linux-$PROC.so" BUILD="$WORK/build" SYSROOT="$WORK/sysroot" Loading @@ -118,6 +130,7 @@ BUILTINS="$SYSROOT$PREFIX/lib/libsystempp-builtins.a" PATH="$PATH:/usr/sbin:/sbin" msg() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; } # run <logname> <cmd...> -- run a command, output into $LOGS/<logname>.log Loading Loading @@ -226,16 +239,21 @@ step_builtins() { # gen_wrapper <out> <c|c++> -- a clang driver for the target, compiling # against the sysroot's systempp headers and linking systempp's crt objects # and libc.so (same logic as build-chroot.sh's gen_wrapper). # WRAP_CLANG/WRAP_RESDIR/WRAP_ROOT/WRAP_TFLAGS/WRAP_BUILTINS: another clang, # root and builtins than the host's and the sysroot (the native wrappers of # the target's clang package, see native_wrappers) gen_wrapper() { local out="$1" lang="$2" clang drv="" resdir clang="$(command -v "$HOST_CC")" local out="$1" lang="$2" clang drv="" resdir root tflags builtins clang="${WRAP_CLANG:-$(command -v "$HOST_CC")}" [[ $lang == c++ ]] && drv="--driver-mode=g++" resdir="$("$HOST_CC" -print-resource-dir)" local inc="$TFLAGS -fPIC -nostdinc -cxx-isystem $SYSROOT$PREFIX/include/cxx -isystem $SYSROOT$PREFIX/include -isystem $resdir/include" resdir="${WRAP_RESDIR:-$("$HOST_CC" -print-resource-dir)}" root="${WRAP_ROOT-$SYSROOT}" tflags="${WRAP_TFLAGS-$TFLAGS}" builtins="${WRAP_BUILTINS:-$BUILTINS}" local inc="$tflags -fPIC -nostdinc -cxx-isystem $root$PREFIX/include/cxx -isystem $root$PREFIX/include -isystem $resdir/include" local TFLAGS="$tflags" BUILTINS="$builtins" cat >"$out" <<EOF #!/bin/sh # generated by systempp build-cross.sh -- clang driver for systempp on $TRIPLE LIB="$SYSROOT$PREFIX/lib" LIB="$root$PREFIX/lib" mode=link pie=1 shared=0 static=0 startfiles=1 libs=1 for a in "\$@"; do case "\$a" in Loading Loading @@ -277,6 +295,31 @@ EOF chmod +x "$out" } # native_wrappers <out.tar.gz> <llvm-major> -- the target clang package's # cc/c++ (systempp-cc/-c++, as build-chroot.sh's llvm_wrappers for x86_64): # its own clang, headers and crt objects under /usr, the target's # compiler-rt builtins as /usr/lib/libsystempp-builtins.a, the mandatory # target flags (e500: soft-float, secure PLT) built in native_wrappers() { local out="$1" major="$2" d="$BUILD/native-wrappers" l rm -rf --one-file-system "${d:?}" mkdir -p "$d$PREFIX/bin" "$d$PREFIX/lib" cp "$BUILTINS" "$d$PREFIX/lib/libsystempp-builtins.a" local flags="${TFLAGS#--target=$CLANG_TARGET }" WRAP_CLANG="$PREFIX/bin/clang-$major" WRAP_RESDIR="$PREFIX/lib/clang/$major" WRAP_ROOT="" \ WRAP_TFLAGS="$flags" WRAP_BUILTINS="$PREFIX/lib/libsystempp-builtins.a" \ gen_wrapper "$d$PREFIX/bin/systempp-cc" c WRAP_CLANG="$PREFIX/bin/clang-$major" WRAP_RESDIR="$PREFIX/lib/clang/$major" WRAP_ROOT="" \ WRAP_TFLAGS="$flags" WRAP_BUILTINS="$PREFIX/lib/libsystempp-builtins.a" \ gen_wrapper "$d$PREFIX/bin/systempp-c++" c++ for l in cc clang; do ln -s systempp-cc "$d$PREFIX/bin/$l"; done for l in c++ clang++; do ln -s systempp-c++ "$d$PREFIX/bin/$l"; done for l in clang-cpp clang-cl; do ln -s "clang-$major" "$d$PREFIX/bin/$l"; done for l in ar ranlib nm strip objcopy objdump readelf; do ln -s "llvm-$l" "$d$PREFIX/bin/$l"; done tar -czf "$out" -C "$d" usr rm -rf --one-file-system "${d:?}" } step_toolchain() { [[ -f $SYSROOT$PREFIX/lib/libc.so && -f $BUILTINS ]] || die "sysroot incomplete -- run 'systempp' and 'builtins' first" msg "toolchain: $TRIPLE-cc/-c++ in $TOOLCHAIN" Loading Loading @@ -525,7 +568,8 @@ step_rootfs() { ! -name '*-debuginfo-*' 2>/dev/null | while read -r r; do printf '%s %s %s\n' "$(rpm -qp --qf '%{NAME}' "$r" 2>/dev/null)" \ "$(rpm -qp --qf '%{VERSION}-%{RELEASE}' "$r" 2>/dev/null)" "$r" done | sort -k1,1 -k2,2V | awk '{ last[$1] = $3 } END { for (n in last) print last[n] }' | done | sort -k1,1 -k2,2V | awk -v ex=" $ROOTFS_EXCLUDE " '{ last[$1] = $3 } END { for (n in last) if (index(ex, " " n " ") == 0) print last[n] }' | sort >"$list" msg "rootfs: installing $(wc -l <"$list") RPMs" : >"$LOGS/rootfs.log" Loading Loading @@ -676,6 +720,14 @@ EOF cross_prebuild() { local pkg="$1" spec="$2" case "$pkg" in llvm-project) # the wrappers tarball build-chroot.sh staged is x86_64's: the # target's instead (its builtins, e500 flags) local major major="$(sed -n 's/^Version:[[:space:]]*\([0-9]*\).*/\1/p' "$spec" | head -n1)" native_wrappers "$WORK/rpmbuild/SOURCES/llvm-project-wrappers.tar.gz" "$major" echo "$spec" ;; libsqlite3) # build-chroot.sh's step_rpm: the amalgamation into /usr/local, which # libsqlite3.spec packages -- here under the sysroot Loading Loading @@ -743,6 +795,7 @@ step_packages() { CMAKE_TOOLCHAIN_FILE="$pkgtc" \ CMAKE_POLICY_VERSION_MINIMUM=3.5 \ QEMU_LD_PREFIX="$SYSROOT" QEMU_CPU=e500v2 \ LLVM_NATIVE_TOOL_DIR="$LLVM_NATIVE_TOOL_DIR" \ PKG_CONFIG_SYSROOT_DIR="$SYSROOT" \ PKG_CONFIG_LIBDIR="$SYSROOT$PREFIX/lib/pkgconfig:$SYSROOT$PREFIX/share/pkgconfig" \ rpmbuild -bb --nodeps --target "$RPM_ARCH-linux" \ Loading Loading @@ -809,12 +862,107 @@ step_repo() { msg "repo ready: $repo/$arch" } # docker: container images of the repository's RPMs, as build-chroot.sh's # 'docker' step: fenriros (DOCKER_MINIMAL and what it requires) and # fenriros-devel (every package but the board's kernel and DOCKER_DEVEL_EXCLUDE), # root tarballs in $WORK/docker and, with docker there, imported as # <DOCKER_PREFIX>fenriros[-devel]:<DOCKER_TAG> for linux/$DOCKER_ARCH (what # containerd's goArch() calls this machine: uname -m) DOCKER="${DOCKER:-docker}" DOCKER_PREFIX="${DOCKER_PREFIX:-}" DOCKER_TAG="${DOCKER_TAG:-$(date -u +%Y%m%d)}" DOCKER_ARCH="${DOCKER_ARCH:-ppc}" DOCKER_MINIMAL="${DOCKER_MINIMAL:-systempp fresh fenriros-release rpm dnf5 ca-certificates}" DOCKER_DEVEL_EXCLUDE="${DOCKER_DEVEL_EXCLUDE:-kernel-$BOARD fenriros-installer}" # newest_rpms <dir> -- "<name> <file>" of the newest build of each package newest_rpms() { local r for r in "$1"/*.rpm; do [[ -e $r ]] || continue printf '%s %s %s\n' "$(rpm -qp --qf '%{NAME}' "$r" 2>/dev/null)" \ "$(rpm -qp --qf '%{EPOCHNUM}:%{VERSION}-%{RELEASE}' "$r" 2>/dev/null)" "$r" done | sort -k1,1 -k2,2V | awk '{ last[$1] = $3 } END { for (n in last) print n, last[n] }' | sort } # rpm_closure <name-file-list> <name>... -- the files of the named packages # and everything they require (by provides and file names) rpm_closure() { local list="$1"; shift local -A file prov want req local n f c p while read -r n f; do file[$n]=$f prov[$n]=$n while read -r c; do [[ -n $c && -z ${prov[$c]:-} ]] && prov[$c]=$n; done \ < <(rpm -qp --qf '[%{PROVIDENAME}\n][%{FILENAMES}\n]' "$f" 2>/dev/null) req[$n]="$(rpm -qp --qf '[%{REQUIRENAME}\n]' "$f" 2>/dev/null)" done <"$list" local queue=("$@") while ((${#queue[@]})); do n="${queue[0]}"; queue=("${queue[@]:1}") [[ -z ${want[$n]:-} ]] || continue [[ -n ${file[$n]:-} ]] || die "docker: no $n RPM in the repository" want[$n]=1 while IFS= read -r c; do [[ -n $c && $c != rpmlib\(* && $c != \(* ]] || continue p="${prov[$c]:-}" [[ -z $p || -n ${want[$p]:-} ]] || queue+=("$p") [[ -n $p ]] || warn "docker: $n requires $c, which no RPM provides" done <<<"${req[$n]:-}" done for n in "${!want[@]}"; do echo "${file[$n]}"; done | sort } # docker_image <name> <file-list> -- root, tarball, (docker) image docker_image() { local name="$1" list="$2" root="$BUILD/docker-$1" msg "docker: $name: installing $(wc -l <"$list") RPMs" rm -rf --one-file-system "$root" mkdir -p "$root" unshare -r rpm --root "$root" --initdb # scriptlets run in the root through binfmt's qemu-$PROC if ! QEMU_CPU=e500v2 unshare -r rpm --root "$root" --ignorearch -ivh $(cat "$list") >>"$LOGS/docker-$name.log" 2>&1; then grep -E 'is needed by|conflicts with' "$LOGS/docker-$name.log" | sort -u | head -20 >&2 die "docker: $name: RPM installation failed (log: $LOGS/docker-$name.log)" fi # no RPM owns these (as build-chroot.sh's docker_image) mkdir -p "$root"/{run,tmp,root,home,var/tmp,var/log} chmod 1777 "$root/tmp" "$root/var/tmp" mkdir -p "$WORK/docker" tar -C "$root" --numeric-owner --owner=0 --group=0 -cf "$WORK/docker/$name-$DOCKER_ARCH.tar" . msg "docker: $WORK/docker/$name-$DOCKER_ARCH.tar ($(du -h "$WORK/docker/$name-$DOCKER_ARCH.tar" | cut -f1))" if command -v "$DOCKER" >/dev/null; then run docker "$DOCKER" import --platform "linux/$DOCKER_ARCH" \ --change "ENV PATH=/usr/bin:/usr/sbin:/bin:/sbin" \ --change "ENV HOME=/root" \ --change "WORKDIR /root" \ --change "CMD [\"/usr/bin/fresh\"]" \ --change "LABEL org.opencontainers.image.title=$name" \ --change "LABEL org.opencontainers.image.version=$DOCKER_TAG" \ "$WORK/docker/$name-$DOCKER_ARCH.tar" "${DOCKER_PREFIX}${name}:$DOCKER_TAG-$DOCKER_ARCH" msg "docker: ${DOCKER_PREFIX}${name}:$DOCKER_TAG-$DOCKER_ARCH (linux/$DOCKER_ARCH)" fi } step_docker() { local pk="$WORK/repo/$RPM_ARCH/Packages" all="$BUILD/docker-all" [[ -d $pk ]] || die "no repository -- run the 'repo' step first" mkdir -p "$LOGS" : >"$LOGS/docker-fenriros.log"; : >"$LOGS/docker-fenriros-devel.log" newest_rpms "$pk" >"$all" # shellcheck disable=SC2086 rpm_closure "$all" $DOCKER_MINIMAL >"$BUILD/docker-minimal" docker_image fenriros "$BUILD/docker-minimal" awk -v ex=" $DOCKER_DEVEL_EXCLUDE " 'index(ex, " " $1 " ") == 0 { print $2 }' "$all" >"$BUILD/docker-devel" docker_image fenriros-devel "$BUILD/docker-devel" } steps=("$@") ((${#steps[@]})) || steps=("${ALL_STEPS[@]}") for s in "${steps[@]}"; do case "$s" in headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer|packages|repo) headers|systempp|builtins|toolchain|fresh|syslite|kernel|rootfs|image|installer|packages|repo|docker) check_deps; mkdir -p "$WORK" "$BUILD"; "step_$s" ;; clean) step_clean ;; -h|--help) sed -n '2,/^$/s/^# \{0,1\}//p' "$0"; exit 0 ;; Loading
src/linux/include/sched.h +20 −0 Changes for src/linux/include/sched.h: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -72,6 +72,8 @@ extern "C" { } cpu_set_t; int __sched_cpucount (size_t setsize, const cpu_set_t *setp); cpu_set_t *__sched_cpualloc (size_t count); void __sched_cpufree (cpu_set_t *set); int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t* mask); int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask); Loading Loading @@ -121,3 +123,21 @@ extern "C" { #define CPU_ISSET(cpu, set) \ ((((set)->__bits[(cpu) / __CPU_BITS_PER_WORD]) >> ((cpu) % __CPU_BITS_PER_WORD)) & 1UL) #define CPU_COUNT(set) __sched_cpucount(sizeof(cpu_set_t), (set)) /* Dynamically sized sets (glibc): CPU_ALLOC(count) for `count` CPUs, * CPU_ALLOC_SIZE(count) bytes, and the *_S variants taking that size. */ #define CPU_ALLOC_SIZE(count) \ ((((size_t)(count) + __CPU_BITS_PER_WORD - 1) / __CPU_BITS_PER_WORD) * sizeof(unsigned long)) #define CPU_ALLOC(count) __sched_cpualloc(count) #define CPU_FREE(set) __sched_cpufree(set) #define CPU_ZERO_S(size, set) __builtin_memset((set), 0, (size)) #define CPU_SET_S(cpu, size, set) \ ((size_t)(cpu) / 8 < (size_t)(size) \ ? ((set)->__bits[(cpu) / __CPU_BITS_PER_WORD] |= (1UL << ((cpu) % __CPU_BITS_PER_WORD))) : 0) #define CPU_CLR_S(cpu, size, set) \ ((size_t)(cpu) / 8 < (size_t)(size) \ ? ((set)->__bits[(cpu) / __CPU_BITS_PER_WORD] &= ~(1UL << ((cpu) % __CPU_BITS_PER_WORD))) : 0) #define CPU_ISSET_S(cpu, size, set) \ ((size_t)(cpu) / 8 < (size_t)(size) \ ? ((((set)->__bits[(cpu) / __CPU_BITS_PER_WORD]) >> ((cpu) % __CPU_BITS_PER_WORD)) & 1UL) : 0) #define CPU_COUNT_S(size, set) __sched_cpucount((size), (set))
src/linux/sched.cpp +20 −1 Changes for src/linux/sched.cpp: 20 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ *******************************************************************************/ #include <sched.h> #include <stdlib.h> #include <bits/syscall.h> #include <bits/sysbits.h> Loading @@ -44,6 +45,17 @@ extern "C" { return count; } /* CPU_ALLOC()/CPU_FREE(): sets for more than CPU_SETSIZE CPUs, sized * with CPU_ALLOC_SIZE() (as glibc: whole longs, zeroed). LLVM's * computeHostNumPhysicalCores() uses them on every Linux but x86. */ cpu_set_t *__sched_cpualloc (size_t count){ return static_cast<cpu_set_t *>(calloc(1, CPU_ALLOC_SIZE(count))); } void __sched_cpufree (cpu_set_t *set){ free(set); } /* Real syscall wrapper -- was a stub that always returned -1, * meaning sched_yield() (and std::this_thread::yield() built on * it) never actually yielded at all. */ Loading Loading @@ -92,7 +104,14 @@ extern "C" { return syscall_ret(syscall3(__NR_sched_setaffinity, pid, cpusetsize, (unsigned long)mask)); } /* The system call returns the bytes of the kernel's mask it copied; * the libc function returns 0 and zeroes the rest of the caller's set * (as glibc and musl) -- callers test == 0 (LLVM's core count). */ int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask){ return syscall_ret(syscall3(__NR_sched_getaffinity, pid, cpusetsize, (unsigned long)mask)); long r = syscall_ret(syscall3(__NR_sched_getaffinity, pid, cpusetsize, (unsigned long)mask)); if (r < 0) return -1; if ((size_t)r < cpusetsize) __builtin_memset((char *)mask + r, 0, cpusetsize - (size_t)r); return 0; } };
src/test/e2e/CMakeLists.txt +20 −0 Changes for src/test/e2e/CMakeLists.txt: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -3787,3 +3787,23 @@ target_link_libraries(e2e_to_string_wide PRIVATE add_dependencies(e2e_to_string_wide crti Scrt1 crtbeginS crtn systempp) add_test(NAME e2e_to_string_wide COMMAND e2e_to_string_wide) add_executable(e2e_cpu_alloc cpu_alloc_test.cpp) set_target_properties(e2e_cpu_alloc PROPERTIES OUTPUT_NAME "cpu_alloc_test") target_compile_options(e2e_cpu_alloc PRIVATE -fPIE) target_link_options(e2e_cpu_alloc PRIVATE -nostdlib -pie -Wl,--hash-style=sysv -Wl,--dynamic-linker,$<TARGET_FILE_DIR:systempp>/ld-linux-${CMAKE_SYSTEM_PROCESSOR}.so -Wl,-rpath,$<TARGET_FILE_DIR:systempp> ) target_link_libraries(e2e_cpu_alloc PRIVATE $<TARGET_OBJECTS:crti> $<TARGET_OBJECTS:Scrt1> $<TARGET_OBJECTS:crtbeginS> $<TARGET_OBJECTS:crtn> systempp ) add_dependencies(e2e_cpu_alloc crti Scrt1 crtbeginS crtn systempp) add_test(NAME e2e_cpu_alloc COMMAND e2e_cpu_alloc)
src/test/e2e/cpu_alloc_test.cpp 0 → 100644 +27 −0 Changes for src/test/e2e/cpu_alloc_test.cpp: 27 added lines, 0 removed lines. Original line number Diff line number Diff line /* CPU_ALLOC()/CPU_ALLOC_SIZE()/CPU_FREE() and the *_S macros (were missing: * LLVM's computeHostNumPhysicalCores() needs them off x86). */ #include <sched.h> #include <stdio.h> #define CHECK(c, m) do { if (!(c)) { printf("FAIL: %s\n", m); return 1; } } while (0) int main() { CHECK(CPU_ALLOC_SIZE(2048) == 2048 / 8, "CPU_ALLOC_SIZE(2048) is 256 bytes"); CHECK(CPU_ALLOC_SIZE(1) == sizeof(unsigned long), "CPU_ALLOC_SIZE(1) is one long"); cpu_set_t *s = CPU_ALLOC(2048); CHECK(s, "CPU_ALLOC returns memory"); size_t sz = CPU_ALLOC_SIZE(2048); CHECK(CPU_COUNT_S(sz, s) == 0, "CPU_ALLOC zeroes the set"); CPU_SET_S(2000, sz, s); CPU_SET_S(3, sz, s); CPU_SET_S(4096, sz, s); /* beyond the set: ignored */ CHECK(CPU_ISSET_S(2000, sz, s) && CPU_ISSET_S(3, sz, s), "CPU_SET_S/CPU_ISSET_S"); CHECK(!CPU_ISSET_S(4096, sz, s), "CPU_ISSET_S beyond the set is 0"); CHECK(CPU_COUNT_S(sz, s) == 2, "CPU_COUNT_S counts the whole set"); CHECK(sched_getaffinity(0, sz, s) == 0 && CPU_COUNT_S(sz, s) >= 1, "sched_getaffinity with a CPU_ALLOC set"); CPU_ZERO_S(sz, s); CHECK(CPU_COUNT_S(sz, s) == 0, "CPU_ZERO_S"); CPU_FREE(s); printf("PASS\n"); return 0; }