From 1b3e94a20d8e82d059ce7db380b85c12dea971bc Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Sat, 27 Jun 2026 09:19:26 +0300 Subject: [PATCH] Red Bear OS relibc baseline From release 0.1.0 pre-patched archive. This includes all Red Bear modifications previously maintained as patches in local/patches/relibc/. --- .editorconfig | 7 + .gitignore | 11 + .gitlab-ci.yml | 73 + .gitmodules | 7 + CONTRIBUTING.md | 123 + Cargo.lock | 752 ++ Cargo.toml | 148 + LICENSE | 21 + Makefile | 226 + README.md | 173 + build.rs | 30 + cbindgen.globdefs.toml | 24 + check.sh | 168 + config.mk | 73 + dlmalloc-rs/.github/workflows/main.yml | 117 + dlmalloc-rs/.gitignore | 3 + dlmalloc-rs/Cargo.toml | 70 + dlmalloc-rs/LICENSE-APACHE | 201 + dlmalloc-rs/LICENSE-MIT | 25 + dlmalloc-rs/README.md | 40 + dlmalloc-rs/fuzz/.gitignore | 2 + dlmalloc-rs/fuzz/Cargo.toml | 19 + dlmalloc-rs/fuzz/fuzz_targets/alloc.rs | 8 + dlmalloc-rs/fuzz/src/lib.rs | 108 + dlmalloc-rs/src/dlmalloc.c | 6280 +++++++++++++++++ dlmalloc-rs/src/dlmalloc.rs | 1900 +++++ dlmalloc-rs/src/dummy.rs | 42 + dlmalloc-rs/src/global.rs | 56 + dlmalloc-rs/src/lib.rs | 230 + dlmalloc-rs/src/unix.rs | 131 + dlmalloc-rs/src/wasm.rs | 76 + dlmalloc-rs/src/windows.rs | 88 + dlmalloc-rs/src/xous.rs | 117 + dlmalloc-rs/tests/global.rs | 32 + dlmalloc-rs/tests/smoke.rs | 36 + fmt.sh | 3 + generic-rt/Cargo.toml | 7 + generic-rt/src/lib.rs | 133 + include/alloca.h | 6 + include/complex.h | 1 + include/features.h | 95 + include/fenv.h | 3 + include/iso646.h | 22 + include/machine/endian.h | 17 + include/math.h | 113 + include/memory.h | 1 + include/netinet/in_systm.h | 10 + include/paths.h | 6 + include/setjmp.h | 81 + include/stdarg.h | 10 + include/stdatomic.h | 243 + include/stdbool.h | 19 + include/stddef.h | 18 + include/stdint.h | 377 + include/stdio_ext.h | 6 + include/stdnoreturn.h | 19 + include/sys/param.h | 36 + include/sys/poll.h | 1 + include/sys/queue.h | 846 +++ include/sys/redox.h | 22 + include/sys/sysmacros.h | 16 + include/sys/user.h | 13 + include/sysexits.h | 21 + include/syslog.h | 1 + ld_so/Cargo.toml | 12 + ld_so/ld_script/aarch64-unknown-linux-gnu.ld | 263 + ld_so/ld_script/aarch64-unknown-redox.ld | 255 + ld_so/ld_script/i586-unknown-redox.ld | 256 + ld_so/ld_script/i686-unknown-redox.ld | 256 + ld_so/ld_script/riscv64gc-unknown-redox.ld | 247 + ld_so/ld_script/x86_64-unknown-linux-gnu.ld | 259 + ld_so/ld_script/x86_64-unknown-redox.ld | 256 + ld_so/src/lib.rs | 136 + openlibm/.github/dependabot.yml | 7 + openlibm/.github/workflows/ci.yml | 93 + .../.github/workflows/cross-loongarch64.yml | 54 + openlibm/.github/workflows/cross.yml | 53 + openlibm/.gitignore | 13 + openlibm/.mailmap | 61 + openlibm/CMakeLists.txt | 576 ++ openlibm/LICENSE.md | 115 + openlibm/Make.inc | 197 + openlibm/Makefile | 140 + openlibm/README.md | 71 + openlibm/aarch64/Make.files | 1 + openlibm/aarch64/fenv.c | 51 + openlibm/amd64/Make.files | 7 + openlibm/amd64/bsd_asm.h | 110 + openlibm/amd64/bsd_fpu.h | 218 + openlibm/amd64/bsd_ieeefp.h | 272 + openlibm/amd64/e_fmod.S | 56 + openlibm/amd64/e_fmodf.S | 26 + openlibm/amd64/e_fmodl.S | 52 + openlibm/amd64/e_remainder.S | 30 + openlibm/amd64/e_remainderf.S | 29 + openlibm/amd64/e_remainderl.S | 35 + openlibm/amd64/e_sqrt.S | 40 + openlibm/amd64/e_sqrtf.S | 39 + openlibm/amd64/e_sqrtl.S | 46 + openlibm/amd64/fenv.c | 162 + openlibm/amd64/s_llrint.S | 12 + openlibm/amd64/s_llrintf.S | 12 + openlibm/amd64/s_llrintl.S | 45 + openlibm/amd64/s_logbl.S | 29 + openlibm/amd64/s_lrint.S | 44 + openlibm/amd64/s_lrintf.S | 44 + openlibm/amd64/s_lrintl.S | 45 + openlibm/amd64/s_remquo.S | 76 + openlibm/amd64/s_remquof.S | 76 + openlibm/amd64/s_remquol.S | 81 + openlibm/amd64/s_rintl.S | 26 + openlibm/amd64/s_scalbn.S | 55 + openlibm/amd64/s_scalbnf.S | 55 + openlibm/amd64/s_scalbnl.S | 40 + openlibm/arm/Make.files | 1 + openlibm/arm/fenv.c | 52 + openlibm/bsdsrc/Make.files | 1 + openlibm/bsdsrc/b_exp.c | 172 + openlibm/bsdsrc/b_log.c | 466 ++ openlibm/bsdsrc/b_tgamma.c | 319 + openlibm/bsdsrc/mathimpl.h | 70 + openlibm/docs/CNAME | 1 + openlibm/docs/images/arrow-down.png | Bin 0 -> 216 bytes openlibm/docs/images/octocat-small.png | Bin 0 -> 357 bytes openlibm/docs/index.html | 94 + openlibm/docs/javascripts/scale.fix.js | 20 + openlibm/docs/params.json | 1 + openlibm/docs/stylesheets/pygment_trac.css | 69 + openlibm/docs/stylesheets/styles.css | 423 ++ openlibm/i387/Make.files | 21 + openlibm/i387/bsd_asm.h | 118 + openlibm/i387/bsd_ieeefp.h | 265 + openlibm/i387/bsd_npx.h | 160 + openlibm/i387/e_exp.S | 76 + openlibm/i387/e_fmod.S | 25 + openlibm/i387/e_log.S | 21 + openlibm/i387/e_log10.S | 21 + openlibm/i387/e_log10f.S | 22 + openlibm/i387/e_logf.S | 21 + openlibm/i387/e_remainder.S | 25 + openlibm/i387/e_remainderf.S | 26 + openlibm/i387/e_remainderl.S | 25 + openlibm/i387/e_sqrt.S | 37 + openlibm/i387/e_sqrtf.S | 21 + openlibm/i387/e_sqrtl.S | 19 + openlibm/i387/fenv.c | 226 + openlibm/i387/invtrig.c | 86 + openlibm/i387/osx_asm.h | 408 ++ openlibm/i387/s_ceil.S | 34 + openlibm/i387/s_ceilf.S | 36 + openlibm/i387/s_ceill.S | 34 + openlibm/i387/s_copysign.S | 25 + openlibm/i387/s_copysignf.S | 26 + openlibm/i387/s_copysignl.S | 24 + openlibm/i387/s_cos.S | 33 + openlibm/i387/s_floor.S | 35 + openlibm/i387/s_floorf.S | 36 + openlibm/i387/s_floorl.S | 34 + openlibm/i387/s_llrint.S | 43 + openlibm/i387/s_llrintf.S | 43 + openlibm/i387/s_llrintl.S | 42 + openlibm/i387/s_logb.S | 21 + openlibm/i387/s_logbf.S | 22 + openlibm/i387/s_logbl.S | 20 + openlibm/i387/s_lrint.S | 42 + openlibm/i387/s_lrintf.S | 42 + openlibm/i387/s_lrintl.S | 41 + openlibm/i387/s_remquo.S | 69 + openlibm/i387/s_remquof.S | 69 + openlibm/i387/s_remquol.S | 69 + openlibm/i387/s_rint.S | 20 + openlibm/i387/s_rintf.S | 21 + openlibm/i387/s_rintl.S | 19 + openlibm/i387/s_scalbn.S | 23 + openlibm/i387/s_scalbnf.S | 26 + openlibm/i387/s_scalbnl.S | 26 + openlibm/i387/s_sin.S | 33 + openlibm/i387/s_tan.S | 35 + openlibm/i387/s_trunc.S | 33 + openlibm/i387/s_truncf.S | 33 + openlibm/i387/s_truncl.S | 33 + openlibm/include/openlibm.h | 8 + openlibm/include/openlibm_complex.h | 179 + openlibm/include/openlibm_defs.h | 14 + openlibm/include/openlibm_fenv.h | 27 + openlibm/include/openlibm_fenv_aarch64.h | 247 + openlibm/include/openlibm_fenv_amd64.h | 223 + openlibm/include/openlibm_fenv_arm.h | 227 + openlibm/include/openlibm_fenv_i387.h | 260 + openlibm/include/openlibm_fenv_loongarch64.h | 226 + openlibm/include/openlibm_fenv_mips.h | 278 + openlibm/include/openlibm_fenv_powerpc.h | 281 + openlibm/include/openlibm_fenv_riscv.h | 261 + openlibm/include/openlibm_fenv_s390.h | 237 + openlibm/include/openlibm_math.h | 493 ++ openlibm/ld128/Make.files | 13 + openlibm/ld128/e_acoshl.c | 58 + openlibm/ld128/e_atanhl.c | 65 + openlibm/ld128/e_coshl.c | 105 + openlibm/ld128/e_expl.c | 145 + openlibm/ld128/e_fmodl.c | 129 + openlibm/ld128/e_hypotl.c | 122 + openlibm/ld128/e_lgammal_r.c | 1037 +++ openlibm/ld128/e_log10l.c | 255 + openlibm/ld128/e_log2l.c | 248 + openlibm/ld128/e_logl.c | 283 + openlibm/ld128/e_powl.c | 439 ++ openlibm/ld128/e_rem_pio2l.h | 144 + openlibm/ld128/e_sinhl.c | 104 + openlibm/ld128/e_tgammal.c | 39 + openlibm/ld128/invtrig.c | 100 + openlibm/ld128/invtrig.h | 113 + openlibm/ld128/k_cosl.c | 61 + openlibm/ld128/k_sinl.c | 59 + openlibm/ld128/k_tanl.c | 120 + openlibm/ld128/s_asinhl.c | 69 + openlibm/ld128/s_ceill.c | 69 + openlibm/ld128/s_erfl.c | 926 +++ openlibm/ld128/s_exp2l.c | 431 ++ openlibm/ld128/s_expm1l.c | 162 + openlibm/ld128/s_floorl.c | 71 + openlibm/ld128/s_log1pl.c | 247 + openlibm/ld128/s_modfl.c | 73 + openlibm/ld128/s_nanl.c | 46 + openlibm/ld128/s_nextafterl.c | 72 + openlibm/ld128/s_nexttoward.c | 85 + openlibm/ld128/s_nexttowardf.c | 65 + openlibm/ld128/s_remquol.c | 168 + openlibm/ld128/s_tanhl.c | 105 + openlibm/ld128/s_truncl.c | 72 + openlibm/ld80/Make.files | 13 + openlibm/ld80/e_acoshl.c | 57 + openlibm/ld80/e_atanhl.c | 60 + openlibm/ld80/e_coshl.c | 83 + openlibm/ld80/e_expl.c | 131 + openlibm/ld80/e_fmodl.c | 142 + openlibm/ld80/e_hypotl.c | 122 + openlibm/ld80/e_lgammal_r.c | 425 ++ openlibm/ld80/e_log10l.c | 205 + openlibm/ld80/e_log2l.c | 199 + openlibm/ld80/e_logl.c | 190 + openlibm/ld80/e_powl.c | 615 ++ openlibm/ld80/e_rem_pio2l.h | 152 + openlibm/ld80/e_sinhl.c | 76 + openlibm/ld80/e_tgammal.c | 313 + openlibm/ld80/invtrig.c | 82 + openlibm/ld80/invtrig.h | 114 + openlibm/ld80/k_cosl.c | 78 + openlibm/ld80/k_sinl.c | 62 + openlibm/ld80/k_tanl.c | 125 + openlibm/ld80/s_asinhl.c | 54 + openlibm/ld80/s_ceill.c | 78 + openlibm/ld80/s_erfl.c | 430 ++ openlibm/ld80/s_exp2l.c | 295 + openlibm/ld80/s_expm1l.c | 138 + openlibm/ld80/s_floorl.c | 80 + openlibm/ld80/s_log1pl.c | 191 + openlibm/ld80/s_modfl.c | 69 + openlibm/ld80/s_nanl.c | 45 + openlibm/ld80/s_nextafterl.c | 90 + openlibm/ld80/s_nexttoward.c | 86 + openlibm/ld80/s_nexttowardf.c | 67 + openlibm/ld80/s_remquol.c | 166 + openlibm/ld80/s_tanhl.c | 79 + openlibm/ld80/s_truncl.c | 72 + openlibm/loongarch64/Make.files | 1 + openlibm/loongarch64/fenv.c | 27 + openlibm/mips/Make.files | 1 + openlibm/mips/fenv-softfloat.h | 184 + openlibm/mips/fenv.c | 67 + openlibm/openlibm.pc.in | 6 + openlibm/powerpc/Make.files | 1 + openlibm/powerpc/fenv.c | 47 + openlibm/riscv64/Make.files | 1 + openlibm/riscv64/fenv.c | 63 + openlibm/s390/Make.files | 1 + openlibm/s390/fenv.c | 50 + openlibm/src/Make.files | 78 + openlibm/src/aarch64_fpmath.h | 60 + openlibm/src/amd64_fpmath.h | 55 + openlibm/src/bsd_cdefs.h | 102 + openlibm/src/cdefs-compat.h | 105 + openlibm/src/common.c | 7 + openlibm/src/e_acos.c | 111 + openlibm/src/e_acosf.c | 78 + openlibm/src/e_acosh.c | 68 + openlibm/src/e_acoshf.c | 49 + openlibm/src/e_acosl.c | 87 + openlibm/src/e_asin.c | 117 + openlibm/src/e_asinf.c | 66 + openlibm/src/e_asinl.c | 77 + openlibm/src/e_atan2.c | 129 + openlibm/src/e_atan2f.c | 97 + openlibm/src/e_atan2l.c | 120 + openlibm/src/e_atanh.c | 68 + openlibm/src/e_atanhf.c | 46 + openlibm/src/e_cosh.c | 85 + openlibm/src/e_coshf.c | 60 + openlibm/src/e_exp.c | 171 + openlibm/src/e_expf.c | 97 + openlibm/src/e_fmod.c | 133 + openlibm/src/e_fmodf.c | 105 + openlibm/src/e_fmodl.c | 150 + openlibm/src/e_hypot.c | 131 + openlibm/src/e_hypotf.c | 84 + openlibm/src/e_hypotl.c | 124 + openlibm/src/e_j0.c | 388 + openlibm/src/e_j0f.c | 337 + openlibm/src/e_j1.c | 383 + openlibm/src/e_j1f.c | 332 + openlibm/src/e_jn.c | 271 + openlibm/src/e_jnf.c | 200 + openlibm/src/e_lgamma.c | 36 + openlibm/src/e_lgamma_r.c | 298 + openlibm/src/e_lgammaf.c | 37 + openlibm/src/e_lgammaf_r.c | 231 + openlibm/src/e_lgammal.c | 15 + openlibm/src/e_log.c | 146 + openlibm/src/e_log10.c | 93 + openlibm/src/e_log10f.c | 75 + openlibm/src/e_log2.c | 116 + openlibm/src/e_log2f.c | 85 + openlibm/src/e_logf.c | 89 + openlibm/src/e_pow.c | 317 + openlibm/src/e_powf.c | 253 + openlibm/src/e_rem_pio2.c | 183 + openlibm/src/e_rem_pio2f.c | 81 + openlibm/src/e_remainder.c | 79 + openlibm/src/e_remainderf.c | 66 + openlibm/src/e_remainderl.c | 39 + openlibm/src/e_sinh.c | 79 + openlibm/src/e_sinhf.c | 57 + openlibm/src/e_sqrt.c | 451 ++ openlibm/src/e_sqrtf.c | 86 + openlibm/src/e_sqrtl.c | 162 + openlibm/src/fpmath.h | 124 + openlibm/src/i386_fpmath.h | 54 + openlibm/src/k_cos.c | 80 + openlibm/src/k_cosf.c | 48 + openlibm/src/k_exp.c | 108 + openlibm/src/k_expf.c | 87 + openlibm/src/k_log.h | 100 + openlibm/src/k_logf.h | 39 + openlibm/src/k_rem_pio2.c | 444 ++ openlibm/src/k_sin.c | 71 + openlibm/src/k_sinf.c | 48 + openlibm/src/k_tan.c | 134 + openlibm/src/k_tanf.c | 68 + openlibm/src/loongarch64_fpmath.h | 56 + openlibm/src/math_private.h | 375 + openlibm/src/math_private_openbsd.h | 218 + openlibm/src/mips_fpmath.h | 57 + openlibm/src/polevll.c | 104 + openlibm/src/powerpc_fpmath.h | 49 + openlibm/src/riscv_fpmath.h | 58 + openlibm/src/s390_fpmath.h | 51 + openlibm/src/s_asinh.c | 62 + openlibm/src/s_asinhf.c | 49 + openlibm/src/s_atan.c | 124 + openlibm/src/s_atanf.c | 93 + openlibm/src/s_atanl.c | 85 + openlibm/src/s_cabs.c | 32 + openlibm/src/s_cabsf.c | 25 + openlibm/src/s_cabsl.c | 26 + openlibm/src/s_cacos.c | 67 + openlibm/src/s_cacosf.c | 60 + openlibm/src/s_cacosh.c | 62 + openlibm/src/s_cacoshf.c | 55 + openlibm/src/s_cacoshl.c | 56 + openlibm/src/s_cacosl.c | 63 + openlibm/src/s_carg.c | 40 + openlibm/src/s_cargf.c | 40 + openlibm/src/s_cargl.c | 40 + openlibm/src/s_casin.c | 136 + openlibm/src/s_casinf.c | 132 + openlibm/src/s_casinh.c | 62 + openlibm/src/s_casinhf.c | 55 + openlibm/src/s_casinhl.c | 56 + openlibm/src/s_casinl.c | 130 + openlibm/src/s_catan.c | 133 + openlibm/src/s_catanf.c | 124 + openlibm/src/s_catanh.c | 62 + openlibm/src/s_catanhf.c | 55 + openlibm/src/s_catanhl.c | 56 + openlibm/src/s_catanl.c | 127 + openlibm/src/s_cbrt.c | 118 + openlibm/src/s_cbrtf.c | 74 + openlibm/src/s_cbrtl.c | 161 + openlibm/src/s_ccos.c | 91 + openlibm/src/s_ccosf.c | 84 + openlibm/src/s_ccosh.c | 155 + openlibm/src/s_ccoshf.c | 104 + openlibm/src/s_ccoshl.c | 59 + openlibm/src/s_ccosl.c | 82 + openlibm/src/s_ceil.c | 77 + openlibm/src/s_ceilf.c | 53 + openlibm/src/s_ceill.c | 102 + openlibm/src/s_cexp.c | 89 + openlibm/src/s_cexpf.c | 89 + openlibm/src/s_cexpl.c | 69 + openlibm/src/s_cimag.c | 37 + openlibm/src/s_cimagf.c | 37 + openlibm/src/s_cimagl.c | 37 + openlibm/src/s_clog.c | 79 + openlibm/src/s_clogf.c | 72 + openlibm/src/s_clogl.c | 73 + openlibm/src/s_conj.c | 38 + openlibm/src/s_conjf.c | 38 + openlibm/src/s_conjl.c | 38 + openlibm/src/s_copysign.c | 34 + openlibm/src/s_copysignf.c | 37 + openlibm/src/s_copysignl.c | 43 + openlibm/src/s_cos.c | 89 + openlibm/src/s_cosf.c | 85 + openlibm/src/s_cosl.c | 90 + openlibm/src/s_cpow.c | 78 + openlibm/src/s_cpowf.c | 73 + openlibm/src/s_cpowl.c | 74 + openlibm/src/s_cproj.c | 47 + openlibm/src/s_cprojf.c | 43 + openlibm/src/s_cprojl.c | 43 + openlibm/src/s_creal.c | 37 + openlibm/src/s_crealf.c | 37 + openlibm/src/s_creall.c | 37 + openlibm/src/s_csin.c | 93 + openlibm/src/s_csinf.c | 85 + openlibm/src/s_csinh.c | 157 + openlibm/src/s_csinhf.c | 105 + openlibm/src/s_csinhl.c | 58 + openlibm/src/s_csinl.c | 84 + openlibm/src/s_csqrt.c | 114 + openlibm/src/s_csqrtf.c | 90 + openlibm/src/s_csqrtl.c | 109 + openlibm/src/s_ctan.c | 159 + openlibm/src/s_ctanf.c | 148 + openlibm/src/s_ctanh.c | 144 + openlibm/src/s_ctanhf.c | 84 + openlibm/src/s_ctanhl.c | 60 + openlibm/src/s_ctanl.c | 157 + openlibm/src/s_erf.c | 307 + openlibm/src/s_erff.c | 184 + openlibm/src/s_exp2.c | 396 ++ openlibm/src/s_exp2f.c | 136 + openlibm/src/s_expm1.c | 221 + openlibm/src/s_expm1f.c | 123 + openlibm/src/s_fabs.c | 28 + openlibm/src/s_fabsf.c | 34 + openlibm/src/s_fabsl.c | 43 + openlibm/src/s_fdim.c | 48 + openlibm/src/s_floor.c | 78 + openlibm/src/s_floorf.c | 62 + openlibm/src/s_floorl.c | 102 + openlibm/src/s_fma.c | 284 + openlibm/src/s_fmaf.c | 69 + openlibm/src/s_fmal.c | 269 + openlibm/src/s_fmax.c | 54 + openlibm/src/s_fmaxf.c | 54 + openlibm/src/s_fmaxl.c | 56 + openlibm/src/s_fmin.c | 54 + openlibm/src/s_fminf.c | 54 + openlibm/src/s_fminl.c | 56 + openlibm/src/s_fpclassify.c | 98 + openlibm/src/s_frexp.c | 56 + openlibm/src/s_frexpf.c | 44 + openlibm/src/s_frexpl.c | 63 + openlibm/src/s_ilogb.c | 49 + openlibm/src/s_ilogbf.c | 41 + openlibm/src/s_ilogbl.c | 54 + openlibm/src/s_isfinite.c | 61 + openlibm/src/s_isinf.c | 66 + openlibm/src/s_isnan.c | 67 + openlibm/src/s_isnormal.c | 61 + openlibm/src/s_llrint.c | 9 + openlibm/src/s_llrintf.c | 9 + openlibm/src/s_llrintl.c | 9 + openlibm/src/s_llround.c | 11 + openlibm/src/s_llroundf.c | 11 + openlibm/src/s_llroundl.c | 11 + openlibm/src/s_log1p.c | 179 + openlibm/src/s_log1pf.c | 114 + openlibm/src/s_logb.c | 49 + openlibm/src/s_logbf.c | 41 + openlibm/src/s_logbl.c | 52 + openlibm/src/s_lrint.c | 61 + openlibm/src/s_lrintf.c | 9 + openlibm/src/s_lrintl.c | 9 + openlibm/src/s_lround.c | 69 + openlibm/src/s_lroundf.c | 11 + openlibm/src/s_lroundl.c | 11 + openlibm/src/s_modf.c | 76 + openlibm/src/s_modff.c | 58 + openlibm/src/s_modfl.c | 101 + openlibm/src/s_nan.c | 124 + openlibm/src/s_nearbyint.c | 56 + openlibm/src/s_nextafter.c | 83 + openlibm/src/s_nextafterf.c | 67 + openlibm/src/s_nextafterl.c | 80 + openlibm/src/s_nexttoward.c | 72 + openlibm/src/s_nexttowardf.c | 61 + openlibm/src/s_remquo.c | 159 + openlibm/src/s_remquof.c | 123 + openlibm/src/s_remquol.c | 178 + openlibm/src/s_rint.c | 92 + openlibm/src/s_rintf.c | 53 + openlibm/src/s_rintl.c | 116 + openlibm/src/s_round.c | 55 + openlibm/src/s_roundf.c | 53 + openlibm/src/s_roundl.c | 53 + openlibm/src/s_scalbln.c | 80 + openlibm/src/s_scalbn.c | 66 + openlibm/src/s_scalbnf.c | 57 + openlibm/src/s_scalbnl.c | 70 + openlibm/src/s_signbit.c | 61 + openlibm/src/s_signgam.c | 7 + openlibm/src/s_sin.c | 89 + openlibm/src/s_sincos.c | 159 + openlibm/src/s_sincosf.c | 172 + openlibm/src/s_sincosl.c | 31 + openlibm/src/s_sinf.c | 85 + openlibm/src/s_sinl.c | 88 + openlibm/src/s_tan.c | 83 + openlibm/src/s_tanf.c | 72 + openlibm/src/s_tanh.c | 83 + openlibm/src/s_tanhf.c | 56 + openlibm/src/s_tanl.c | 90 + openlibm/src/s_tgammaf.c | 45 + openlibm/src/s_trunc.c | 67 + openlibm/src/s_truncf.c | 54 + openlibm/src/s_truncl.c | 69 + openlibm/src/types-compat.h | 13 + openlibm/src/w_cabs.c | 25 + openlibm/src/w_cabsf.c | 19 + openlibm/src/w_cabsl.c | 22 + openlibm/test/.gitignore | 9 + openlibm/test/Makefile | 37 + openlibm/test/inf_torture.c | 76 + openlibm/test/libm-bench.cpp | 144 + openlibm/test/libm-test-ulps.h | 254 + openlibm/test/libm-test.c | 4537 ++++++++++++ openlibm/test/test-211.c | 12 + openlibm/test/test-double.c | 34 + openlibm/test/test-float.c | 34 + openlibm/wasm32/Make.files | 0 openlibm/wasm32/assert.h | 1 + openlibm/wasm32/float.h | 33 + openlibm/wasm32/limits.h | 9 + openlibm/wasm32/stdint.h | 43 + redox-ioctl/Cargo.toml | 13 + redox-ioctl/src/drm.rs | 319 + redox-ioctl/src/ioctl_data.rs | 169 + redox-ioctl/src/lib.rs | 11 + redox-rt/Cargo.toml | 27 + redox-rt/src/arch/aarch64.rs | 485 ++ redox-rt/src/arch/i686.rs | 411 ++ redox-rt/src/arch/mod.rs | 27 + redox-rt/src/arch/riscv64.rs | 654 ++ redox-rt/src/arch/x86_64.rs | 528 ++ redox-rt/src/lib.rs | 313 + redox-rt/src/proc.rs | 1190 ++++ redox-rt/src/signal.rs | 1015 +++ redox-rt/src/sync.rs | 68 + redox-rt/src/sys.rs | 563 ++ redox-rt/src/thread.rs | 70 + renamesyms.sh | 58 + rust-toolchain.toml | 3 + rustfmt.toml | 21 + src/c/stdlib.c | 13 + src/c_str.rs | 322 + src/c_vec.rs | 294 + src/crt0/Cargo.toml | 12 + src/crt0/src/lib.rs | 105 + src/crti/Cargo.toml | 8 + src/crti/src/lib.rs | 3 + src/crtn/Cargo.toml | 8 + src/crtn/src/lib.rs | 3 + src/cxa.rs | 103 + src/db.rs | 51 + src/error.rs | 90 + src/fs.rs | 156 + src/header/_aio/cbindgen.toml | 9 + src/header/_aio/mod.rs | 75 + src/header/_fenv/cbindgen.toml | 9 + src/header/_fenv/mod.rs | 85 + src/header/_paths/cbindgen.toml | 5 + src/header/_paths/linux.rs | 29 + src/header/_paths/mod.rs | 11 + src/header/_paths/redox.rs | 13 + src/header/_template/cbindgen.toml | 9 + src/header/_template/mod.rs | 10 + src/header/arch_aarch64_user/cbindgen.toml | 7 + src/header/arch_aarch64_user/mod.rs | 30 + src/header/arch_riscv64_user/cbindgen.toml | 7 + src/header/arch_riscv64_user/mod.rs | 39 + src/header/arch_x64_user/cbindgen.toml | 7 + src/header/arch_x64_user/mod.rs | 83 + src/header/arpa_inet/cbindgen.toml | 31 + src/header/arpa_inet/mod.rs | 447 ++ src/header/assert/cbindgen.toml | 28 + src/header/assert/mod.rs | 24 + src/header/bits_arpainet/cbindgen.toml | 19 + src/header/bits_arpainet/mod.rs | 25 + src/header/bits_iovec/cbindgen.toml | 18 + src/header/bits_iovec/mod.rs | 38 + src/header/bits_locale-t/cbindgen.toml | 29 + src/header/bits_locale-t/mod.rs | 1 + src/header/bits_pthread/cbindgen.toml | 53 + src/header/bits_pthread/mod.rs | 123 + src/header/bits_safamily-t/cbindgen.toml | 21 + src/header/bits_safamily-t/mod.rs | 3 + src/header/bits_sigset-t/cbindgen.toml | 18 + src/header/bits_sigset-t/mod.rs | 4 + src/header/bits_socklen-t/cbindgen.toml | 12 + src/header/bits_socklen-t/mod.rs | 1 + src/header/bits_timespec/cbindgen.toml | 25 + src/header/bits_timespec/mod.rs | 64 + src/header/cpio/cbindgen.toml | 16 + src/header/cpio/mod.rs | 48 + src/header/crypt/argon2.rs | 16 + src/header/crypt/blowfish.rs | 90 + src/header/crypt/cbindgen.toml | 9 + src/header/crypt/md5.rs | 146 + src/header/crypt/mod.rs | 121 + src/header/crypt/pbkdf2.rs | 64 + src/header/crypt/scrypt.rs | 113 + src/header/crypt/sha.rs | 141 + src/header/ctype/cbindgen.toml | 21 + src/header/ctype/mod.rs | 205 + src/header/dirent/cbindgen.toml | 20 + src/header/dirent/mod.rs | 399 ++ src/header/dl-tls/cbindgen.toml | 9 + src/header/dl-tls/mod.rs | 102 + src/header/dlfcn/cbindgen.toml | 12 + src/header/dlfcn/mod.rs | 197 + src/header/elf/cbindgen.toml | 46 + src/header/elf/mod.rs | 996 +++ src/header/endian/cbindgen.toml | 12 + src/header/endian/mod.rs | 77 + src/header/err/cbindgen.toml | 5 + src/header/err/mod.rs | 204 + src/header/errno/cbindgen.toml | 17 + src/header/errno/mod.rs | 344 + src/header/fcntl/cbindgen.toml | 9 + src/header/fcntl/linux.rs | 31 + src/header/fcntl/mod.rs | 142 + src/header/fcntl/redox.rs | 40 + src/header/float/cbindgen.toml | 62 + src/header/float/mod.rs | 20 + src/header/fnmatch/cbindgen.toml | 11 + src/header/fnmatch/mod.rs | 159 + src/header/getopt/cbindgen.toml | 9 + src/header/getopt/mod.rs | 245 + src/header/glob/cbindgen.toml | 16 + src/header/glob/mod.rs | 401 ++ src/header/grp/cbindgen.toml | 17 + src/header/grp/mod.rs | 558 ++ src/header/ifaddrs/cbindgen.toml | 17 + src/header/ifaddrs/mod.rs | 44 + src/header/inttypes/cbindgen.toml | 213 + src/header/inttypes/mod.rs | 103 + src/header/langinfo/cbindgen.toml | 10 + src/header/langinfo/mod.rs | 225 + src/header/libgen/cbindgen.toml | 12 + src/header/libgen/mod.rs | 55 + src/header/limits/cbindgen.toml | 12 + src/header/limits/mod.rs | 131 + src/header/locale/cbindgen.toml | 16 + src/header/locale/constants.rs | 16 + src/header/locale/data.rs | 392 + src/header/locale/mod.rs | 182 + src/header/malloc/cbindgen.toml | 30 + src/header/malloc/mod.rs | 48 + src/header/math/cbindgen.toml | 57 + src/header/math/mod.rs | 867 +++ src/header/mod.rs | 149 + src/header/monetary/cbindgen.toml | 21 + src/header/monetary/mod.rs | 102 + src/header/monetary/strfmon.rs | 326 + src/header/net_if/cbindgen.toml | 12 + src/header/net_if/mod.rs | 119 + src/header/netdb/cbindgen.toml | 22 + src/header/netdb/dns/answer.rs | 21 + src/header/netdb/dns/mod.rs | 166 + src/header/netdb/dns/query.rs | 18 + src/header/netdb/host.rs | 132 + src/header/netdb/linux.rs | 19 + src/header/netdb/lookup.rs | 371 + src/header/netdb/mod.rs | 1158 +++ src/header/netdb/redox.rs | 16 + src/header/netinet_in/cbindgen.toml | 123 + src/header/netinet_in/mod.rs | 209 + src/header/netinet_ip/cbindgen.toml | 9 + src/header/netinet_ip/mod.rs | 3 + src/header/netinet_tcp/cbindgen.toml | 12 + src/header/netinet_tcp/mod.rs | 16 + src/header/poll/cbindgen.toml | 15 + src/header/poll/mod.rs | 191 + src/header/pthread/attr.rs | 228 + src/header/pthread/barrier.rs | 104 + src/header/pthread/cbindgen.toml | 13 + src/header/pthread/cond.rs | 132 + src/header/pthread/mod.rs | 352 + src/header/pthread/mutex.rs | 217 + src/header/pthread/once.rs | 20 + src/header/pthread/rwlock.rs | 156 + src/header/pthread/spin.rs | 75 + src/header/pthread/tls.rs | 130 + src/header/pty/cbindgen.toml | 13 + src/header/pty/linux.rs | 58 + src/header/pty/mod.rs | 136 + src/header/pty/redox.rs | 33 + src/header/pwd/cbindgen.toml | 13 + src/header/pwd/linux.rs | 15 + src/header/pwd/mod.rs | 330 + src/header/pwd/redox.rs | 15 + src/header/regex/cbindgen.toml | 13 + src/header/regex/mod.rs | 172 + src/header/sched/cbindgen.toml | 22 + src/header/sched/mod.rs | 76 + src/header/semaphore/cbindgen.toml | 15 + src/header/semaphore/mod.rs | 115 + src/header/setjmp/impl/README.md | 6 + src/header/setjmp/impl/aarch64/longjmp.s | 24 + src/header/setjmp/impl/aarch64/setjmp.s | 24 + src/header/setjmp/impl/aarch64/sigsetjmp.s | 21 + src/header/setjmp/impl/arm/longjmp.s | 43 + src/header/setjmp/impl/arm/setjmp.s | 45 + src/header/setjmp/impl/i386/longjmp.s | 16 + src/header/setjmp/impl/i386/setjmp.s | 23 + src/header/setjmp/impl/i386/sigsetjmp.s | 26 + src/header/setjmp/impl/m68k/longjmp.s | 14 + src/header/setjmp/impl/m68k/setjmp.s | 18 + src/header/setjmp/impl/microblaze/longjmp.s | 29 + src/header/setjmp/impl/microblaze/setjmp.s | 32 + src/header/setjmp/impl/mips/longjmp.S | 40 + src/header/setjmp/impl/mips/setjmp.S | 39 + src/header/setjmp/impl/mips64/longjmp.S | 37 + src/header/setjmp/impl/mips64/setjmp.S | 34 + src/header/setjmp/impl/mipsn32/longjmp.S | 36 + src/header/setjmp/impl/mipsn32/setjmp.S | 34 + src/header/setjmp/impl/or1k/longjmp.s | 25 + src/header/setjmp/impl/or1k/setjmp.s | 27 + src/header/setjmp/impl/powerpc/longjmp.S | 69 + src/header/setjmp/impl/powerpc/setjmp.S | 63 + src/header/setjmp/impl/powerpc64/longjmp.s | 81 + src/header/setjmp/impl/powerpc64/setjmp.s | 89 + src/header/setjmp/impl/riscv64/longjmp.S | 41 + src/header/setjmp/impl/riscv64/setjmp.S | 40 + src/header/setjmp/impl/riscv64/sigsetjmp.S | 23 + src/header/setjmp/impl/s390x/longjmp.s | 23 + src/header/setjmp/impl/s390x/setjmp.s | 25 + src/header/setjmp/impl/sh/longjmp.S | 28 + src/header/setjmp/impl/sh/setjmp.S | 32 + src/header/setjmp/impl/x32/longjmp.s | 22 + src/header/setjmp/impl/x32/setjmp.s | 22 + src/header/setjmp/impl/x86_64/longjmp.s | 22 + src/header/setjmp/impl/x86_64/setjmp.s | 22 + src/header/setjmp/impl/x86_64/sigsetjmp.s | 24 + src/header/setjmp/mod.rs | 33 + src/header/sgtty/cbindgen.toml | 12 + src/header/sgtty/mod.rs | 9 + src/header/shadow/cbindgen.toml | 12 + src/header/shadow/mod.rs | 275 + src/header/signal/cbindgen.toml | 45 + src/header/signal/linux.rs | 179 + src/header/signal/mod.rs | 587 ++ src/header/signal/redox.rs | 197 + src/header/signal/signalfd.rs | 103 + src/header/stdio/cbindgen.toml | 32 + src/header/stdio/constants.rs | 50 + src/header/stdio/default.rs | 61 + src/header/stdio/ext.rs | 41 + src/header/stdio/getdelim.rs | 145 + src/header/stdio/helpers.rs | 97 + src/header/stdio/mod.rs | 1607 +++++ src/header/stdio/open_memstream.rs | 124 + src/header/stdio/printf.rs | 1447 ++++ src/header/stdio/reader.rs | 180 + src/header/stdio/scanf.rs | 514 ++ src/header/stdlib/cbindgen.toml | 25 + src/header/stdlib/mod.rs | 1705 +++++ src/header/stdlib/rand48.rs | 141 + src/header/stdlib/random.rs | 118 + src/header/stdlib/sort.rs | 245 + src/header/string/cbindgen.toml | 25 + src/header/string/mod.rs | 670 ++ src/header/strings/cbindgen.toml | 22 + src/header/strings/mod.rs | 157 + src/header/sys_auxv/cbindgen.toml | 8 + src/header/sys_auxv/mod.rs | 13 + src/header/sys_epoll/cbindgen.toml | 9 + src/header/sys_epoll/linux.rs | 20 + src/header/sys_epoll/mod.rs | 136 + src/header/sys_epoll/redox.rs | 20 + src/header/sys_eventfd/mod.rs | 8 + src/header/sys_file/cbindgen.toml | 8 + src/header/sys_file/mod.rs | 23 + src/header/sys_ioctl/cbindgen.toml | 24 + src/header/sys_ioctl/linux.rs | 201 + src/header/sys_ioctl/mod.rs | 40 + src/header/sys_ioctl/redox/drm.rs | 135 + src/header/sys_ioctl/redox/mod.rs | 196 + src/header/sys_mman/cbindgen.toml | 21 + src/header/sys_mman/linux.rs | 17 + src/header/sys_mman/mod.rs | 195 + src/header/sys_mman/redox.rs | 14 + src/header/sys_procfs/cbindgen.toml | 7 + src/header/sys_procfs/mod.rs | 77 + src/header/sys_ptrace/cbindgen.toml | 7 + src/header/sys_ptrace/mod.rs | 35 + src/header/sys_random/cbindgen.toml | 9 + src/header/sys_random/mod.rs | 29 + src/header/sys_resource/cbindgen.toml | 19 + src/header/sys_resource/mod.rs | 124 + src/header/sys_select/cbindgen.toml | 33 + src/header/sys_select/mod.rs | 228 + src/header/sys_signalfd/cbindgen.toml | 24 + src/header/sys_signalfd/mod.rs | 14 + src/header/sys_socket/cbindgen.toml | 45 + src/header/sys_socket/constants.rs | 80 + src/header/sys_socket/mod.rs | 445 ++ src/header/sys_stat/cbindgen.toml | 33 + src/header/sys_stat/mod.rs | 242 + src/header/sys_statvfs/cbindgen.toml | 13 + src/header/sys_statvfs/mod.rs | 58 + src/header/sys_syscall/aarch64.rs | 303 + src/header/sys_syscall/cbindgen.toml | 8 + src/header/sys_syscall/mod.rs | 5 + src/header/sys_syscall/x86_64.rs | 356 + src/header/sys_syslog/cbindgen.toml | 11 + src/header/sys_syslog/linux.rs | 74 + src/header/sys_syslog/logger.rs | 274 + src/header/sys_syslog/mod.rs | 146 + src/header/sys_syslog/redox.rs | 20 + src/header/sys_time/cbindgen.toml | 36 + src/header/sys_time/mod.rs | 145 + src/header/sys_timeb/cbindgen.toml | 9 + src/header/sys_timeb/mod.rs | 64 + src/header/sys_timerfd/cbindgen.toml | 23 + src/header/sys_timerfd/mod.rs | 89 + src/header/sys_times/cbindgen.toml | 9 + src/header/sys_times/mod.rs | 21 + src/header/sys_types/cbindgen.toml | 24 + src/header/sys_types/mod.rs | 8 + src/header/sys_types_internal/cbindgen.toml | 45 + src/header/sys_types_internal/mod.rs | 74 + src/header/sys_uio/cbindgen.toml | 18 + src/header/sys_uio/mod.rs | 92 + src/header/sys_un/cbindgen.toml | 19 + src/header/sys_un/mod.rs | 21 + src/header/sys_utsname/cbindgen.toml | 11 + src/header/sys_utsname/mod.rs | 34 + src/header/sys_wait/cbindgen.toml | 20 + src/header/sys_wait/mod.rs | 173 + src/header/tar/cbindgen.toml | 17 + src/header/tar/mod.rs | 150 + src/header/termios/cbindgen.toml | 16 + src/header/termios/linux.rs | 117 + src/header/termios/mod.rs | 248 + src/header/termios/redox.rs | 111 + src/header/time/cbindgen.toml | 17 + src/header/time/constants.rs | 33 + src/header/time/linux.rs | 12 + src/header/time/mod.rs | 929 +++ src/header/time/redox.rs | 4 + src/header/time/strftime.rs | 304 + src/header/time/strptime.rs | 593 ++ src/header/unistd/brk.rs | 58 + src/header/unistd/cbindgen.toml | 26 + src/header/unistd/getopt.rs | 40 + src/header/unistd/getpass.rs | 71 + src/header/unistd/mod.rs | 1324 ++++ src/header/unistd/pathconf.rs | 80 + src/header/unistd/syscall.rs | 14 + src/header/unistd/sysconf.rs | 18 + src/header/unistd/sysconf/linux.rs | 390 + src/header/unistd/sysconf/redox.rs | 147 + src/header/utime/cbindgen.toml | 9 + src/header/utime/mod.rs | 47 + src/header/utmp/cbindgen.toml | 9 + src/header/utmp/mod.rs | 40 + src/header/wchar/cbindgen.toml | 45 + src/header/wchar/mod.rs | 1095 +++ src/header/wchar/utf8.rs | 107 + src/header/wchar/wprintf.rs | 12 + src/header/wchar/wscanf.rs | 13 + src/header/wctype/alpha.rs | 193 + src/header/wctype/casecmp.rs | 414 ++ src/header/wctype/cbindgen.toml | 9 + src/header/wctype/mod.rs | 232 + src/header/wctype/punct.rs | 166 + src/io/buffered.rs | 1123 +++ src/io/cursor.rs | 724 ++ src/io/error.rs | 313 + src/io/impls.rs | 298 + src/io/mod.rs | 2266 ++++++ src/io/prelude.rs | 23 + src/iter.rs | 206 + src/ld_so/access.rs | 10 + src/ld_so/callbacks.rs | 39 + src/ld_so/debug.rs | 138 + src/ld_so/dso.rs | 1310 ++++ src/ld_so/linker.rs | 1229 ++++ src/ld_so/mod.rs | 212 + src/ld_so/start.rs | 440 ++ src/ld_so/tcb.rs | 386 + src/lib.rs | 117 + src/macros.rs | 585 ++ src/out.rs | 231 + src/platform/allocator/mod.rs | 121 + src/platform/allocator/sys.rs | 129 + src/platform/auxv_defs.rs | 60 + src/platform/linux/epoll.rs | 45 + src/platform/linux/mod.rs | 859 +++ src/platform/linux/ptrace.rs | 16 + src/platform/linux/signal.rs | 205 + src/platform/linux/socket.rs | 152 + src/platform/logger.rs | 293 + src/platform/mod.rs | 421 ++ src/platform/pal/epoll.rs | 23 + src/platform/pal/mod.rs | 459 ++ src/platform/pal/ptrace.rs | 15 + src/platform/pal/signal.rs | 62 + src/platform/pal/socket.rs | 101 + src/platform/redox/epoll.rs | 176 + src/platform/redox/event.rs | 82 + src/platform/redox/exec.rs | 618 ++ src/platform/redox/extra.rs | 38 + src/platform/redox/libcscheme.rs | 52 + src/platform/redox/libredox.rs | 634 ++ src/platform/redox/mod.rs | 1763 +++++ src/platform/redox/path.rs | 522 ++ src/platform/redox/ptrace.rs | 290 + src/platform/redox/signal.rs | 362 + src/platform/redox/socket.rs | 1215 ++++ src/platform/redox/timer.rs | 103 + src/platform/rlb.rs | 106 + src/platform/types.rs | 161 + src/pthread/mod.rs | 440 ++ src/raw_cell.rs | 50 + src/start.rs | 259 + src/sync/barrier.rs | 85 + src/sync/barrier.rs.unused | 91 + src/sync/cond.rs | 134 + src/sync/mod.rs | 247 + src/sync/mutex.rs | 142 + src/sync/once.rs | 124 + src/sync/pthread_mutex.rs | 259 + src/sync/reentrant_mutex.rs | 27 + src/sync/rwlock.rs | 318 + src/sync/semaphore.rs | 85 + src/sync/waitval.rs | 42 + stripcore.sh | 17 + tests/.gitignore | 4 + tests/Cargo.lock | 5 + tests/Cargo.toml | 9 + tests/Makefile | 183 + tests/Makefile.run.mk | 33 + tests/Makefile.tests.mk | 358 + tests/alloca.c | 14 + tests/args.c | 12 + tests/arpainet.c | 25 + tests/assert.c | 19 + tests/constructor.c | 22 + tests/crypt/blowfish.c | 26 + tests/crypt/md5.c | 38 + tests/crypt/pbkdf2.c | 25 + tests/crypt/scrypt.c | 25 + tests/crypt/sha256.c | 67 + tests/crypt/sha512.c | 72 + tests/ctype.c | 334 + tests/destructor.c | 22 + tests/dirent/fdopendir.c | 156 + tests/dirent/main.c | 43 + tests/dirent/posix_getdents.c | 53 + tests/dirent/scandir.c | 31 + tests/dlfcn.c | 122 + tests/dlopen_scopes.c | 38 + tests/endian.c | 75 + tests/err.c | 61 + tests/errno.c | 680 ++ tests/error.c | 60 + tests/example_dir/1-never-gonna-give-you-up | 0 tests/example_dir/2-never-gonna-let-you-down | 0 tests/example_dir/3-never-gonna-run-around | 0 tests/example_dir/4-and-desert-you | 0 tests/example_dir/5-never-gonna-make-you-cry | 0 tests/example_dir/6-never-gonna-say-goodbye | 0 tests/example_dir/7-never-gonna-tell-a-lie | 0 tests/example_dir/8-and-hurt-you | 0 tests/expected/bins_dynamic/alloca.stderr | 0 tests/expected/bins_dynamic/alloca.stdout | 1 + tests/expected/bins_dynamic/args.stderr | 0 tests/expected/bins_dynamic/args.stdout | 1 + tests/expected/bins_dynamic/arpainet.stderr | 0 tests/expected/bins_dynamic/arpainet.stdout | 0 tests/expected/bins_dynamic/assert.stderr | 0 tests/expected/bins_dynamic/assert.stdout | 2 + .../expected/bins_dynamic/constructor.stderr | 0 .../expected/bins_dynamic/constructor.stdout | 6 + .../bins_dynamic/crypt/blowfish.stderr | 0 .../bins_dynamic/crypt/blowfish.stdout | 0 tests/expected/bins_dynamic/crypt/md5.stderr | 0 tests/expected/bins_dynamic/crypt/md5.stdout | 1 + .../expected/bins_dynamic/crypt/pbkdf2.stderr | 0 .../expected/bins_dynamic/crypt/pbkdf2.stdout | 0 .../expected/bins_dynamic/crypt/scrypt.stderr | 0 .../expected/bins_dynamic/crypt/scrypt.stdout | 0 .../expected/bins_dynamic/crypt/sha256.stderr | 0 .../expected/bins_dynamic/crypt/sha256.stdout | 0 .../expected/bins_dynamic/crypt/sha512.stderr | 0 .../expected/bins_dynamic/crypt/sha512.stdout | 0 tests/expected/bins_dynamic/ctype.stderr | 0 tests/expected/bins_dynamic/ctype.stdout | 1 + tests/expected/bins_dynamic/destructor.stderr | 0 tests/expected/bins_dynamic/destructor.stdout | 6 + .../bins_dynamic/dirent/fdopendir.stderr | 0 .../bins_dynamic/dirent/fdopendir.stdout | 0 .../bins_dynamic/dirent/scandir.stderr | 0 .../bins_dynamic/dirent/scandir.stdout | 7 + tests/expected/bins_dynamic/dlfcn.stderr | 0 tests/expected/bins_dynamic/dlfcn.stdout | 6 + .../bins_dynamic/dlopen_scopes.stderr | 0 .../bins_dynamic/dlopen_scopes.stdout | 0 tests/expected/bins_dynamic/err.stderr | 6 + tests/expected/bins_dynamic/err.stdout | 2 + tests/expected/bins_dynamic/errno.stderr | 0 tests/expected/bins_dynamic/errno.stdout | 136 + tests/expected/bins_dynamic/error.stderr | 1 + tests/expected/bins_dynamic/error.stdout | 4 + .../expected/bins_dynamic/fcntl/create.stderr | 0 .../expected/bins_dynamic/fcntl/create.stdout | 0 .../expected/bins_dynamic/fcntl/fcntl.stderr | 0 .../expected/bins_dynamic/fcntl/fcntl.stdout | 1 + tests/expected/bins_dynamic/fcntl/open.stderr | 0 tests/expected/bins_dynamic/fcntl/open.stdout | 0 .../bins_dynamic/fcntl/posix_fallocate.stderr | 0 .../bins_dynamic/fcntl/posix_fallocate.stdout | 0 tests/expected/bins_dynamic/fnmatch.stderr | 0 tests/expected/bins_dynamic/fnmatch.stdout | 32 + tests/expected/bins_dynamic/futimens.stderr | 0 tests/expected/bins_dynamic/futimens.stdout | 0 tests/expected/bins_dynamic/iso646.stderr | 0 tests/expected/bins_dynamic/iso646.stdout | 33 + tests/expected/bins_dynamic/libgen.stderr | 0 tests/expected/bins_dynamic/libgen.stdout | 21 + .../bins_dynamic/locale/duplocale.stderr | 0 .../bins_dynamic/locale/duplocale.stdout | 0 .../bins_dynamic/locale/newlocale.stderr | 0 .../bins_dynamic/locale/newlocale.stdout | 0 .../bins_dynamic/locale/setlocale.stderr | 0 .../bins_dynamic/locale/setlocale.stdout | 1 + .../bins_dynamic/malloc/usable_size.stderr | 0 .../bins_dynamic/malloc/usable_size.stdout | 2 + tests/expected/bins_dynamic/math.stderr | 0 tests/expected/bins_dynamic/math.stdout | 1 + tests/expected/bins_dynamic/mkfifo.stderr | 0 tests/expected/bins_dynamic/mkfifo.stdout | 0 tests/expected/bins_dynamic/mknod.stderr | 0 tests/expected/bins_dynamic/mknod.stdout | 0 tests/expected/bins_dynamic/mknodat.stderr | 0 tests/expected/bins_dynamic/mknodat.stdout | 0 .../bins_dynamic/netdb/getaddrinfo.stderr | 0 .../bins_dynamic/netdb/getaddrinfo.stdout | 1 + tests/expected/bins_dynamic/ptrace.stderr | 4 + tests/expected/bins_dynamic/ptrace.stdout | 5 + .../expected/bins_dynamic/pty/forkpty.stderr | 0 .../expected/bins_dynamic/pty/forkpty.stdout | 0 tests/expected/bins_dynamic/regex.stderr | 0 tests/expected/bins_dynamic/regex.stdout | 3 + tests/expected/bins_dynamic/select.stderr | 0 tests/expected/bins_dynamic/select.stdout | 8 + tests/expected/bins_dynamic/setjmp.stderr | 0 tests/expected/bins_dynamic/setjmp.stdout | 2 + tests/expected/bins_dynamic/sigaction.stderr | 0 tests/expected/bins_dynamic/sigaction.stdout | 5 + .../expected/bins_dynamic/sigaltstack.stderr | 0 .../expected/bins_dynamic/sigaltstack.stdout | 101 + tests/expected/bins_dynamic/signal.stderr | 0 tests/expected/bins_dynamic/signal.stdout | 3 + tests/expected/bins_dynamic/sigsetjmp.stderr | 0 tests/expected/bins_dynamic/sigsetjmp.stdout | 2 + tests/expected/bins_dynamic/stdio/all.stderr | 0 tests/expected/bins_dynamic/stdio/all.stdout | 4 + .../expected/bins_dynamic/stdio/buffer.stderr | 0 .../expected/bins_dynamic/stdio/buffer.stdout | 5 + .../bins_dynamic/stdio/dprintf.stderr | 0 .../bins_dynamic/stdio/dprintf.stdout | 2 + .../expected/bins_dynamic/stdio/fgets.stderr | 0 .../expected/bins_dynamic/stdio/fgets.stdout | 31 + .../expected/bins_dynamic/stdio/fputs.stderr | 0 .../expected/bins_dynamic/stdio/fputs.stdout | 0 .../expected/bins_dynamic/stdio/fread.stderr | 0 .../expected/bins_dynamic/stdio/fread.stdout | 32 + .../bins_dynamic/stdio/freopen.stderr | 0 .../bins_dynamic/stdio/freopen.stdout | 3 + .../expected/bins_dynamic/stdio/fscanf.stderr | 0 .../expected/bins_dynamic/stdio/fscanf.stdout | 14 + .../bins_dynamic/stdio/fscanf_offby1.stderr | 0 .../bins_dynamic/stdio/fscanf_offby1.stdout | 1 + .../expected/bins_dynamic/stdio/fseek.stderr | 0 .../expected/bins_dynamic/stdio/fseek.stdout | 2 + .../expected/bins_dynamic/stdio/fwrite.stderr | 0 .../expected/bins_dynamic/stdio/fwrite.stdout | 0 .../bins_dynamic/stdio/getc_unget.stderr | 0 .../bins_dynamic/stdio/getc_unget.stdout | 1 + .../bins_dynamic/stdio/getline.stderr | 0 .../bins_dynamic/stdio/getline.stdout | 41 + .../expected/bins_dynamic/stdio/mutex.stderr | 0 .../expected/bins_dynamic/stdio/mutex.stdout | 0 .../expected/bins_dynamic/stdio/popen.stderr | 0 .../expected/bins_dynamic/stdio/popen.stdout | 8 + .../expected/bins_dynamic/stdio/printf.stderr | 0 .../expected/bins_dynamic/stdio/printf.stdout | 78 + .../bins_dynamic/stdio/printf_neg_pad.stderr | 0 .../bins_dynamic/stdio/printf_neg_pad.stdout | 2 + .../stdio/printf_space_pad.stderr | 0 .../stdio/printf_space_pad.stdout | 1 + .../bins_dynamic/stdio/putc_unlocked.stderr | 0 .../bins_dynamic/stdio/putc_unlocked.stdout | 0 .../expected/bins_dynamic/stdio/rename.stderr | 0 .../expected/bins_dynamic/stdio/rename.stdout | 0 .../bins_dynamic/stdio/renameat.stderr | 0 .../bins_dynamic/stdio/renameat.stdout | 0 .../expected/bins_dynamic/stdio/scanf.stderr | 0 .../expected/bins_dynamic/stdio/scanf.stdout | 16 + .../bins_dynamic/stdio/setvbuf.stderr | 0 .../bins_dynamic/stdio/setvbuf.stdout | 4 + .../bins_dynamic/stdio/sprintf.stderr | 0 .../bins_dynamic/stdio/sprintf.stdout | 1 + .../bins_dynamic/stdio/ungetc_ftell.stderr | 0 .../bins_dynamic/stdio/ungetc_ftell.stdout | 28 + .../bins_dynamic/stdio/ungetc_multiple.stderr | 0 .../bins_dynamic/stdio/ungetc_multiple.stdout | 1 + .../expected/bins_dynamic/stdlib/a64l.stderr | 0 .../expected/bins_dynamic/stdlib/a64l.stdout | 42 + .../expected/bins_dynamic/stdlib/alloc.stderr | 0 .../expected/bins_dynamic/stdlib/alloc.stdout | 30 + .../expected/bins_dynamic/stdlib/atof.stderr | 0 .../expected/bins_dynamic/stdlib/atof.stdout | 2 + .../expected/bins_dynamic/stdlib/atoi.stderr | 0 .../expected/bins_dynamic/stdlib/atoi.stdout | 6 + tests/expected/bins_dynamic/stdlib/div.stderr | 0 tests/expected/bins_dynamic/stdlib/div.stdout | 0 tests/expected/bins_dynamic/stdlib/env.stderr | 0 tests/expected/bins_dynamic/stdlib/env.stdout | 8 + .../bins_dynamic/stdlib/getsubopt.stderr | 0 .../bins_dynamic/stdlib/getsubopt.stdout | 0 .../bins_dynamic/stdlib/mkostemps.stderr | 0 .../bins_dynamic/stdlib/mkostemps.stdout | 3 + .../bins_dynamic/stdlib/ptsname.stderr | 0 .../bins_dynamic/stdlib/ptsname.stdout | 0 .../expected/bins_dynamic/stdlib/qsort.stderr | 0 .../expected/bins_dynamic/stdlib/qsort.stdout | 2 + .../expected/bins_dynamic/stdlib/rand.stderr | 0 .../expected/bins_dynamic/stdlib/rand.stdout | 8 + .../bins_dynamic/stdlib/rand48.stderr | 0 .../bins_dynamic/stdlib/rand48.stdout | 14 + .../bins_dynamic/stdlib/random.stderr | 0 .../bins_dynamic/stdlib/random.stdout | 1011 +++ .../bins_dynamic/stdlib/strtod.stderr | 0 .../bins_dynamic/stdlib/strtod.stdout | 196 + .../bins_dynamic/stdlib/strtol.stderr | 0 .../bins_dynamic/stdlib/strtol.stdout | 26 + .../bins_dynamic/stdlib/strtoul.stderr | 0 .../bins_dynamic/stdlib/strtoul.stdout | 26 + .../bins_dynamic/stdlib/system.stderr | 0 .../bins_dynamic/stdlib/system.stdout | 2 + tests/expected/bins_dynamic/string/mem.stderr | 0 tests/expected/bins_dynamic/string/mem.stdout | 5 + .../bins_dynamic/string/memcpy.stderr | 0 .../bins_dynamic/string/memcpy.stdout | 0 .../bins_dynamic/string/memmem.stderr | 0 .../bins_dynamic/string/memmem.stdout | 0 .../bins_dynamic/string/stpcpy.stderr | 0 .../bins_dynamic/string/stpcpy.stdout | 0 .../bins_dynamic/string/stpncpy.stderr | 0 .../bins_dynamic/string/stpncpy.stdout | 0 .../bins_dynamic/string/strcat.stderr | 0 .../bins_dynamic/string/strcat.stdout | 2 + .../bins_dynamic/string/strchr.stderr | 0 .../bins_dynamic/string/strchr.stdout | 4 + .../bins_dynamic/string/strchrnul.stderr | 0 .../bins_dynamic/string/strchrnul.stdout | 0 .../bins_dynamic/string/strcpy.stderr | 0 .../bins_dynamic/string/strcpy.stdout | 7 + .../bins_dynamic/string/strcspn.stderr | 0 .../bins_dynamic/string/strcspn.stdout | 2 + .../bins_dynamic/string/strlen.stderr | 0 .../bins_dynamic/string/strlen.stdout | 9 + .../bins_dynamic/string/strncmp.stderr | 0 .../bins_dynamic/string/strncmp.stdout | 6 + .../bins_dynamic/string/strpbrk.stderr | 0 .../bins_dynamic/string/strpbrk.stdout | 3 + .../bins_dynamic/string/strrchr.stderr | 0 .../bins_dynamic/string/strrchr.stdout | 1 + .../bins_dynamic/string/strsep.stderr | 0 .../bins_dynamic/string/strsep.stdout | 0 .../bins_dynamic/string/strsignal.stderr | 0 .../bins_dynamic/string/strsignal.stdout | 1 + .../bins_dynamic/string/strspn.stderr | 0 .../bins_dynamic/string/strspn.stdout | 3 + .../bins_dynamic/string/strstr.stderr | 0 .../bins_dynamic/string/strstr.stdout | 5 + .../bins_dynamic/string/strtok.stderr | 0 .../bins_dynamic/string/strtok.stdout | 1 + .../bins_dynamic/string/strtok_r.stderr | 0 .../bins_dynamic/string/strtok_r.stdout | 1 + tests/expected/bins_dynamic/strings.stderr | 0 tests/expected/bins_dynamic/strings.stdout | 0 .../bins_dynamic/sys_epoll/epollet.stdout | 5 + tests/expected/bins_dynamic/sys_mman.stderr | 0 tests/expected/bins_dynamic/sys_mman.stdout | 9 + .../bins_dynamic/sys_socket/recv.stdout | 2 + .../bins_dynamic/sys_socket/recvfrom.stdout | 2 + .../sys_socket/unixpeername.stdout | 3 + .../bins_dynamic/sys_socket/unixrecv.stdout | 2 + .../sys_socket/unixrecvfrom.stdout | 2 + .../sys_socket/unixsocketpair.stdout | 6 + .../bins_dynamic/sys_stat/chmod.stderr | 0 .../bins_dynamic/sys_stat/chmod.stdout | 0 .../bins_dynamic/sys_stat/fstatat.stderr | 0 .../bins_dynamic/sys_stat/fstatat.stdout | 0 .../bins_dynamic/sys_stat/lstat.stderr | 0 .../bins_dynamic/sys_stat/lstat.stdout | 0 .../bins_dynamic/sys_syslog/syslog.stderr | 13 + .../bins_dynamic/sys_syslog/syslog.stdout | 0 tests/expected/bins_dynamic/termios.stderr | 0 tests/expected/bins_dynamic/termios.stdout | 0 .../expected/bins_dynamic/time/asctime.stderr | 0 .../expected/bins_dynamic/time/asctime.stdout | 5 + .../bins_dynamic/time/constants.stderr | 0 .../bins_dynamic/time/constants.stdout | 2 + .../expected/bins_dynamic/time/gmtime.stderr | 0 .../expected/bins_dynamic/time/gmtime.stdout | 168 + .../bins_dynamic/time/localtime.stderr | 0 .../bins_dynamic/time/localtime.stdout | 9 + .../bins_dynamic/time/localtime_r.stderr | 0 .../bins_dynamic/time/localtime_r.stdout | 0 .../expected/bins_dynamic/time/macros.stderr | 0 .../expected/bins_dynamic/time/macros.stdout | 0 .../expected/bins_dynamic/time/mktime.stderr | 0 .../expected/bins_dynamic/time/mktime.stdout | 6 + .../bins_dynamic/time/strftime.stderr | 0 .../bins_dynamic/time/strftime.stdout | 14 + .../bins_dynamic/time/strptime.stderr | 0 .../bins_dynamic/time/strptime.stdout | 0 tests/expected/bins_dynamic/time/time.stderr | 0 tests/expected/bins_dynamic/time/time.stdout | 0 .../expected/bins_dynamic/time/timegm.stderr | 0 .../expected/bins_dynamic/time/timegm.stdout | 0 tests/expected/bins_dynamic/time/tzset.stderr | 0 tests/expected/bins_dynamic/time/tzset.stdout | 1 + tests/expected/bins_dynamic/tls.stderr | 0 tests/expected/bins_dynamic/tls.stdout | 2 + .../bins_dynamic/unistd/access.stderr | 0 .../bins_dynamic/unistd/access.stdout | 0 .../expected/bins_dynamic/unistd/alarm.stderr | 0 .../expected/bins_dynamic/unistd/alarm.stdout | 4 + tests/expected/bins_dynamic/unistd/brk.stderr | 0 tests/expected/bins_dynamic/unistd/brk.stdout | 0 .../bins_dynamic/unistd/confstr.stderr | 0 .../bins_dynamic/unistd/confstr.stdout | 0 .../bins_dynamic/unistd/constants.stderr | 0 .../bins_dynamic/unistd/constants.stdout | 16 + tests/expected/bins_dynamic/unistd/dup.stderr | 0 tests/expected/bins_dynamic/unistd/dup.stdout | 1 + .../expected/bins_dynamic/unistd/exec.stderr | 0 .../expected/bins_dynamic/unistd/exec.stdout | 1 + .../bins_dynamic/unistd/fchdir.stderr | 0 .../bins_dynamic/unistd/fchdir.stdout | 0 .../expected/bins_dynamic/unistd/fork.stderr | 0 .../expected/bins_dynamic/unistd/fork.stdout | 3 + .../expected/bins_dynamic/unistd/fsync.stderr | 0 .../expected/bins_dynamic/unistd/fsync.stdout | 0 .../bins_dynamic/unistd/ftruncate.stderr | 0 .../bins_dynamic/unistd/ftruncate.stdout | 0 .../bins_dynamic/unistd/getopt.stderr | 0 .../bins_dynamic/unistd/getopt.stdout | 42 + .../bins_dynamic/unistd/getopt_long.stderr | 0 .../bins_dynamic/unistd/getopt_long.stdout | 20 + .../expected/bins_dynamic/unistd/pipe.stderr | 0 .../expected/bins_dynamic/unistd/pipe.stdout | 1 + .../bins_dynamic/unistd/readlinkat.stderr | 0 .../bins_dynamic/unistd/readlinkat.stdout | 0 .../expected/bins_dynamic/unistd/rmdir.stderr | 0 .../expected/bins_dynamic/unistd/rmdir.stdout | 0 .../expected/bins_dynamic/unistd/sleep.stderr | 0 .../expected/bins_dynamic/unistd/sleep.stdout | 1 + .../expected/bins_dynamic/unistd/swab.stderr | 0 .../expected/bins_dynamic/unistd/swab.stdout | 0 .../expected/bins_dynamic/unistd/write.stderr | 0 .../expected/bins_dynamic/unistd/write.stdout | 1 + .../expected/bins_dynamic/wchar/fgetwc.stderr | 0 .../expected/bins_dynamic/wchar/fgetwc.stdout | 3 + .../expected/bins_dynamic/wchar/fwide.stderr | 0 .../expected/bins_dynamic/wchar/fwide.stdout | 4 + .../bins_dynamic/wchar/mbrtowc.stderr | 0 .../bins_dynamic/wchar/mbrtowc.stdout | 2 + .../bins_dynamic/wchar/mbsrtowcs.stderr | 0 .../bins_dynamic/wchar/mbsrtowcs.stdout | 1 + .../wchar/printf-on-wchars.stderr | 0 .../wchar/printf-on-wchars.stdout | 4 + .../bins_dynamic/wchar/putwchar.stderr | 0 .../bins_dynamic/wchar/putwchar.stdout | 1 + .../bins_dynamic/wchar/ungetwc.stderr | 0 .../bins_dynamic/wchar/ungetwc.stdout | 0 .../expected/bins_dynamic/wchar/wcpcpy.stderr | 0 .../expected/bins_dynamic/wchar/wcpcpy.stdout | 0 .../bins_dynamic/wchar/wcpncpy.stderr | 0 .../bins_dynamic/wchar/wcpncpy.stdout | 0 .../bins_dynamic/wchar/wcrtomb.stderr | 0 .../bins_dynamic/wchar/wcrtomb.stdout | 2 + .../bins_dynamic/wchar/wcscasecmp.stderr | 0 .../bins_dynamic/wchar/wcscasecmp.stdout | 4 + .../expected/bins_dynamic/wchar/wcschr.stderr | 0 .../expected/bins_dynamic/wchar/wcschr.stdout | 0 .../bins_dynamic/wchar/wcscspn.stderr | 0 .../bins_dynamic/wchar/wcscspn.stdout | 0 .../expected/bins_dynamic/wchar/wcsdup.stderr | 0 .../expected/bins_dynamic/wchar/wcsdup.stdout | 0 .../bins_dynamic/wchar/wcsncasecmp.stderr | 0 .../bins_dynamic/wchar/wcsncasecmp.stdout | 5 + .../bins_dynamic/wchar/wcsnlen.stderr | 0 .../bins_dynamic/wchar/wcsnlen.stdout | 0 .../bins_dynamic/wchar/wcsnrtombs.stderr | 0 .../bins_dynamic/wchar/wcsnrtombs.stdout | 0 .../bins_dynamic/wchar/wcsrchr.stderr | 0 .../bins_dynamic/wchar/wcsrchr.stdout | 0 .../bins_dynamic/wchar/wcsrtombs.stderr | 0 .../bins_dynamic/wchar/wcsrtombs.stdout | 0 .../expected/bins_dynamic/wchar/wcsstr.stderr | 0 .../expected/bins_dynamic/wchar/wcsstr.stdout | 0 .../expected/bins_dynamic/wchar/wcstod.stderr | 0 .../expected/bins_dynamic/wchar/wcstod.stdout | 6 + .../bins_dynamic/wchar/wcstoimax.stderr | 0 .../bins_dynamic/wchar/wcstoimax.stdout | 6 + .../expected/bins_dynamic/wchar/wcstok.stderr | 0 .../expected/bins_dynamic/wchar/wcstok.stdout | 12 + .../expected/bins_dynamic/wchar/wcstol.stderr | 0 .../expected/bins_dynamic/wchar/wcstol.stdout | 1 + .../bins_dynamic/wchar/wcstoumax.stderr | 0 .../bins_dynamic/wchar/wcstoumax.stdout | 3 + .../bins_dynamic/wchar/wcswidth.stderr | 0 .../bins_dynamic/wchar/wcswidth.stdout | 1 + .../bins_dynamic/wchar/wprintf.stderr | 0 .../bins_dynamic/wchar/wprintf.stdout | 67 + .../expected/bins_dynamic/wchar/wscanf.stderr | 0 .../expected/bins_dynamic/wchar/wscanf.stdout | Bin 0 -> 4414 bytes .../bins_dynamic/wctype/towlower.stderr | 0 .../bins_dynamic/wctype/towlower.stdout | 2 + .../bins_dynamic/wctype/towupper.stderr | 0 .../bins_dynamic/wctype/towupper.stdout | 2 + tests/expected/bins_static/alloca.stderr | 0 tests/expected/bins_static/alloca.stdout | 1 + tests/expected/bins_static/args.stderr | 0 tests/expected/bins_static/args.stdout | 1 + tests/expected/bins_static/arpainet.stderr | 0 tests/expected/bins_static/arpainet.stdout | 0 tests/expected/bins_static/assert.stderr | 0 tests/expected/bins_static/assert.stdout | 2 + tests/expected/bins_static/constructor.stderr | 0 tests/expected/bins_static/constructor.stdout | 6 + .../bins_static/crypt/blowfish.stderr | 0 .../bins_static/crypt/blowfish.stdout | 0 tests/expected/bins_static/crypt/md5.stderr | 0 tests/expected/bins_static/crypt/md5.stdout | 1 + .../expected/bins_static/crypt/pbkdf2.stderr | 0 .../expected/bins_static/crypt/pbkdf2.stdout | 0 .../expected/bins_static/crypt/scrypt.stderr | 0 .../expected/bins_static/crypt/scrypt.stdout | 0 .../expected/bins_static/crypt/sha256.stderr | 0 .../expected/bins_static/crypt/sha256.stdout | 0 .../expected/bins_static/crypt/sha512.stderr | 0 .../expected/bins_static/crypt/sha512.stdout | 0 tests/expected/bins_static/ctype.stderr | 0 tests/expected/bins_static/ctype.stdout | 1 + tests/expected/bins_static/destructor.stderr | 0 tests/expected/bins_static/destructor.stdout | 6 + .../bins_static/dirent/fdopendir.stderr | 0 .../bins_static/dirent/fdopendir.stdout | 0 .../bins_static/dirent/scandir.stderr | 0 .../bins_static/dirent/scandir.stdout | 7 + tests/expected/bins_static/err.stderr | 6 + tests/expected/bins_static/err.stdout | 2 + tests/expected/bins_static/errno.stderr | 0 tests/expected/bins_static/errno.stdout | 136 + tests/expected/bins_static/error.stderr | 1 + tests/expected/bins_static/error.stdout | 4 + .../expected/bins_static/fcntl/create.stderr | 0 .../expected/bins_static/fcntl/create.stdout | 0 tests/expected/bins_static/fcntl/fcntl.stderr | 0 tests/expected/bins_static/fcntl/fcntl.stdout | 1 + tests/expected/bins_static/fcntl/open.stderr | 0 tests/expected/bins_static/fcntl/open.stdout | 0 .../bins_static/fcntl/posix_fallocate.stderr | 0 .../bins_static/fcntl/posix_fallocate.stdout | 0 tests/expected/bins_static/fnmatch.stderr | 0 tests/expected/bins_static/fnmatch.stdout | 32 + tests/expected/bins_static/futimens.stderr | 0 tests/expected/bins_static/futimens.stdout | 0 tests/expected/bins_static/iso646.stderr | 0 tests/expected/bins_static/iso646.stdout | 33 + tests/expected/bins_static/libgen.stderr | 0 tests/expected/bins_static/libgen.stdout | 21 + .../bins_static/locale/duplocale.stderr | 0 .../bins_static/locale/duplocale.stdout | 0 .../bins_static/locale/newlocale.stderr | 0 .../bins_static/locale/newlocale.stdout | 0 .../bins_static/locale/setlocale.stderr | 0 .../bins_static/locale/setlocale.stdout | 1 + .../bins_static/malloc/usable_size.stderr | 0 .../bins_static/malloc/usable_size.stdout | 2 + tests/expected/bins_static/math.stderr | 0 tests/expected/bins_static/math.stdout | 1 + tests/expected/bins_static/mkfifo.stderr | 0 tests/expected/bins_static/mkfifo.stdout | 0 tests/expected/bins_static/mknod.stderr | 0 tests/expected/bins_static/mknod.stdout | 0 tests/expected/bins_static/mknodat.stderr | 0 tests/expected/bins_static/mknodat.stdout | 0 tests/expected/bins_static/ptrace.stderr | 4 + tests/expected/bins_static/ptrace.stdout | 5 + tests/expected/bins_static/pty/forkpty.stderr | 0 tests/expected/bins_static/pty/forkpty.stdout | 0 tests/expected/bins_static/regex.stderr | 0 tests/expected/bins_static/regex.stdout | 3 + tests/expected/bins_static/select.stderr | 0 tests/expected/bins_static/select.stdout | 8 + tests/expected/bins_static/setjmp.stderr | 0 tests/expected/bins_static/setjmp.stdout | 2 + tests/expected/bins_static/sigaction.stderr | 0 tests/expected/bins_static/sigaction.stdout | 5 + tests/expected/bins_static/sigaltstack.stderr | 0 tests/expected/bins_static/sigaltstack.stdout | 101 + tests/expected/bins_static/signal.stderr | 0 tests/expected/bins_static/signal.stdout | 3 + tests/expected/bins_static/sigsetjmp.stderr | 0 tests/expected/bins_static/sigsetjmp.stdout | 2 + tests/expected/bins_static/stdio/all.stderr | 0 tests/expected/bins_static/stdio/all.stdout | 4 + .../expected/bins_static/stdio/buffer.stderr | 0 .../expected/bins_static/stdio/buffer.stdout | 5 + .../expected/bins_static/stdio/dprintf.stderr | 0 .../expected/bins_static/stdio/dprintf.stdout | 2 + tests/expected/bins_static/stdio/fgets.stderr | 0 tests/expected/bins_static/stdio/fgets.stdout | 31 + tests/expected/bins_static/stdio/fputs.stderr | 0 tests/expected/bins_static/stdio/fputs.stdout | 0 tests/expected/bins_static/stdio/fread.stderr | 0 tests/expected/bins_static/stdio/fread.stdout | 32 + .../expected/bins_static/stdio/freopen.stderr | 0 .../expected/bins_static/stdio/freopen.stdout | 3 + .../expected/bins_static/stdio/fscanf.stderr | 0 .../expected/bins_static/stdio/fscanf.stdout | 14 + .../bins_static/stdio/fscanf_offby1.stderr | 0 .../bins_static/stdio/fscanf_offby1.stdout | 1 + tests/expected/bins_static/stdio/fseek.stderr | 0 tests/expected/bins_static/stdio/fseek.stdout | 2 + .../expected/bins_static/stdio/fwrite.stderr | 0 .../expected/bins_static/stdio/fwrite.stdout | 0 .../bins_static/stdio/getc_unget.stderr | 0 .../bins_static/stdio/getc_unget.stdout | 1 + .../expected/bins_static/stdio/getline.stderr | 0 .../expected/bins_static/stdio/getline.stdout | 41 + tests/expected/bins_static/stdio/mutex.stderr | 0 tests/expected/bins_static/stdio/mutex.stdout | 0 tests/expected/bins_static/stdio/popen.stderr | 0 tests/expected/bins_static/stdio/popen.stdout | 8 + .../expected/bins_static/stdio/printf.stderr | 0 .../expected/bins_static/stdio/printf.stdout | 78 + .../bins_static/stdio/printf_neg_pad.stderr | 0 .../bins_static/stdio/printf_neg_pad.stdout | 2 + .../bins_static/stdio/printf_space_pad.stderr | 0 .../bins_static/stdio/printf_space_pad.stdout | 1 + .../bins_static/stdio/putc_unlocked.stderr | 0 .../bins_static/stdio/putc_unlocked.stdout | 0 .../expected/bins_static/stdio/rename.stderr | 0 .../expected/bins_static/stdio/rename.stdout | 0 .../bins_static/stdio/renameat.stderr | 0 .../bins_static/stdio/renameat.stdout | 0 tests/expected/bins_static/stdio/scanf.stderr | 0 tests/expected/bins_static/stdio/scanf.stdout | 16 + .../expected/bins_static/stdio/setvbuf.stderr | 0 .../expected/bins_static/stdio/setvbuf.stdout | 4 + .../expected/bins_static/stdio/sprintf.stderr | 0 .../expected/bins_static/stdio/sprintf.stdout | 1 + .../bins_static/stdio/ungetc_ftell.stderr | 0 .../bins_static/stdio/ungetc_ftell.stdout | 28 + .../bins_static/stdio/ungetc_multiple.stderr | 0 .../bins_static/stdio/ungetc_multiple.stdout | 1 + tests/expected/bins_static/stdlib/a64l.stderr | 0 tests/expected/bins_static/stdlib/a64l.stdout | 42 + .../expected/bins_static/stdlib/alloc.stderr | 0 .../expected/bins_static/stdlib/alloc.stdout | 30 + tests/expected/bins_static/stdlib/atof.stderr | 0 tests/expected/bins_static/stdlib/atof.stdout | 2 + tests/expected/bins_static/stdlib/atoi.stderr | 0 tests/expected/bins_static/stdlib/atoi.stdout | 6 + tests/expected/bins_static/stdlib/div.stderr | 0 tests/expected/bins_static/stdlib/div.stdout | 0 tests/expected/bins_static/stdlib/env.stderr | 0 tests/expected/bins_static/stdlib/env.stdout | 8 + .../bins_static/stdlib/getsubopt.stderr | 0 .../bins_static/stdlib/getsubopt.stdout | 0 .../bins_static/stdlib/mkostemps.stderr | 0 .../bins_static/stdlib/mkostemps.stdout | 3 + .../bins_static/stdlib/ptsname.stderr | 0 .../bins_static/stdlib/ptsname.stdout | 0 .../expected/bins_static/stdlib/qsort.stderr | 0 .../expected/bins_static/stdlib/qsort.stdout | 2 + tests/expected/bins_static/stdlib/rand.stderr | 0 tests/expected/bins_static/stdlib/rand.stdout | 8 + .../expected/bins_static/stdlib/rand48.stderr | 0 .../expected/bins_static/stdlib/rand48.stdout | 14 + .../expected/bins_static/stdlib/random.stderr | 0 .../expected/bins_static/stdlib/random.stdout | 1011 +++ .../expected/bins_static/stdlib/strtod.stderr | 0 .../expected/bins_static/stdlib/strtod.stdout | 196 + .../expected/bins_static/stdlib/strtol.stderr | 0 .../expected/bins_static/stdlib/strtol.stdout | 26 + .../bins_static/stdlib/strtoul.stderr | 0 .../bins_static/stdlib/strtoul.stdout | 26 + .../expected/bins_static/stdlib/system.stderr | 0 .../expected/bins_static/stdlib/system.stdout | 2 + tests/expected/bins_static/string/mem.stderr | 0 tests/expected/bins_static/string/mem.stdout | 5 + .../expected/bins_static/string/memcpy.stderr | 0 .../expected/bins_static/string/memcpy.stdout | 0 .../expected/bins_static/string/memmem.stderr | 0 .../expected/bins_static/string/memmem.stdout | 0 .../expected/bins_static/string/stpcpy.stderr | 0 .../expected/bins_static/string/stpcpy.stdout | 0 .../bins_static/string/stpncpy.stderr | 0 .../bins_static/string/stpncpy.stdout | 0 .../expected/bins_static/string/strcat.stderr | 0 .../expected/bins_static/string/strcat.stdout | 2 + .../expected/bins_static/string/strchr.stderr | 0 .../expected/bins_static/string/strchr.stdout | 4 + .../bins_static/string/strchrnul.stderr | 0 .../bins_static/string/strchrnul.stdout | 0 .../expected/bins_static/string/strcpy.stderr | 0 .../expected/bins_static/string/strcpy.stdout | 7 + .../bins_static/string/strcspn.stderr | 0 .../bins_static/string/strcspn.stdout | 2 + .../expected/bins_static/string/strlen.stderr | 0 .../expected/bins_static/string/strlen.stdout | 9 + .../bins_static/string/strncmp.stderr | 0 .../bins_static/string/strncmp.stdout | 6 + .../bins_static/string/strpbrk.stderr | 0 .../bins_static/string/strpbrk.stdout | 3 + .../bins_static/string/strrchr.stderr | 0 .../bins_static/string/strrchr.stdout | 1 + .../expected/bins_static/string/strsep.stderr | 0 .../expected/bins_static/string/strsep.stdout | 0 .../bins_static/string/strsignal.stderr | 0 .../bins_static/string/strsignal.stdout | 1 + .../expected/bins_static/string/strspn.stderr | 0 .../expected/bins_static/string/strspn.stdout | 3 + .../expected/bins_static/string/strstr.stderr | 0 .../expected/bins_static/string/strstr.stdout | 5 + .../expected/bins_static/string/strtok.stderr | 0 .../expected/bins_static/string/strtok.stdout | 1 + .../bins_static/string/strtok_r.stderr | 0 .../bins_static/string/strtok_r.stdout | 1 + tests/expected/bins_static/strings.stderr | 0 tests/expected/bins_static/strings.stdout | 0 .../bins_static/sys_epoll/epollet.stdout | 5 + tests/expected/bins_static/sys_mman.stderr | 0 tests/expected/bins_static/sys_mman.stdout | 9 + .../bins_static/sys_socket/recv.stdout | 2 + .../bins_static/sys_socket/recvfrom.stdout | 2 + .../sys_socket/unixpeername.stdout | 3 + .../bins_static/sys_socket/unixrecv.stdout | 2 + .../sys_socket/unixrecvfrom.stdout | 2 + .../sys_socket/unixsocketpair.stdout | 6 + .../bins_static/sys_stat/chmod.stderr | 0 .../bins_static/sys_stat/chmod.stdout | 0 .../bins_static/sys_stat/fstatat.stderr | 0 .../bins_static/sys_stat/fstatat.stdout | 0 .../bins_static/sys_stat/lstat.stderr | 0 .../bins_static/sys_stat/lstat.stdout | 0 .../bins_static/sys_syslog/syslog.stderr | 13 + .../bins_static/sys_syslog/syslog.stdout | 0 tests/expected/bins_static/termios.stderr | 0 tests/expected/bins_static/termios.stdout | 0 .../expected/bins_static/time/asctime.stderr | 0 .../expected/bins_static/time/asctime.stdout | 5 + .../bins_static/time/constants.stderr | 0 .../bins_static/time/constants.stdout | 2 + tests/expected/bins_static/time/gmtime.stderr | 0 tests/expected/bins_static/time/gmtime.stdout | 168 + .../bins_static/time/localtime.stderr | 0 .../bins_static/time/localtime.stdout | 9 + .../bins_static/time/localtime_r.stderr | 0 .../bins_static/time/localtime_r.stdout | 0 tests/expected/bins_static/time/macros.stderr | 0 tests/expected/bins_static/time/macros.stdout | 0 tests/expected/bins_static/time/mktime.stderr | 0 tests/expected/bins_static/time/mktime.stdout | 6 + .../expected/bins_static/time/strftime.stderr | 0 .../expected/bins_static/time/strftime.stdout | 14 + .../expected/bins_static/time/strptime.stderr | 0 .../expected/bins_static/time/strptime.stdout | 0 tests/expected/bins_static/time/time.stderr | 0 tests/expected/bins_static/time/time.stdout | 0 tests/expected/bins_static/time/timegm.stderr | 0 tests/expected/bins_static/time/timegm.stdout | 0 tests/expected/bins_static/time/tzset.stderr | 0 tests/expected/bins_static/time/tzset.stdout | 1 + tests/expected/bins_static/tls.stderr | 0 tests/expected/bins_static/tls.stdout | 2 + .../expected/bins_static/unistd/access.stderr | 0 .../expected/bins_static/unistd/access.stdout | 0 .../expected/bins_static/unistd/alarm.stderr | 0 .../expected/bins_static/unistd/alarm.stdout | 4 + tests/expected/bins_static/unistd/brk.stderr | 0 tests/expected/bins_static/unistd/brk.stdout | 0 .../bins_static/unistd/confstr.stderr | 0 .../bins_static/unistd/confstr.stdout | 0 .../bins_static/unistd/constants.stderr | 0 .../bins_static/unistd/constants.stdout | 16 + tests/expected/bins_static/unistd/dup.stderr | 0 tests/expected/bins_static/unistd/dup.stdout | 1 + tests/expected/bins_static/unistd/exec.stderr | 0 tests/expected/bins_static/unistd/exec.stdout | 1 + .../expected/bins_static/unistd/fchdir.stderr | 0 .../expected/bins_static/unistd/fchdir.stdout | 0 tests/expected/bins_static/unistd/fork.stderr | 0 tests/expected/bins_static/unistd/fork.stdout | 3 + .../expected/bins_static/unistd/fsync.stderr | 0 .../expected/bins_static/unistd/fsync.stdout | 0 .../bins_static/unistd/ftruncate.stderr | 0 .../bins_static/unistd/ftruncate.stdout | 0 .../expected/bins_static/unistd/getopt.stderr | 0 .../expected/bins_static/unistd/getopt.stdout | 42 + .../bins_static/unistd/getopt_long.stderr | 0 .../bins_static/unistd/getopt_long.stdout | 20 + tests/expected/bins_static/unistd/pipe.stderr | 0 tests/expected/bins_static/unistd/pipe.stdout | 1 + .../bins_static/unistd/readlinkat.stderr | 0 .../bins_static/unistd/readlinkat.stdout | 0 .../expected/bins_static/unistd/rmdir.stderr | 0 .../expected/bins_static/unistd/rmdir.stdout | 0 .../expected/bins_static/unistd/sleep.stderr | 0 .../expected/bins_static/unistd/sleep.stdout | 1 + tests/expected/bins_static/unistd/swab.stderr | 0 tests/expected/bins_static/unistd/swab.stdout | 0 .../expected/bins_static/unistd/write.stderr | 0 .../expected/bins_static/unistd/write.stdout | 1 + .../expected/bins_static/wchar/fgetwc.stderr | 0 .../expected/bins_static/wchar/fgetwc.stdout | 3 + tests/expected/bins_static/wchar/fwide.stderr | 0 tests/expected/bins_static/wchar/fwide.stdout | 4 + .../expected/bins_static/wchar/mbrtowc.stderr | 0 .../expected/bins_static/wchar/mbrtowc.stdout | 2 + .../bins_static/wchar/mbsrtowcs.stderr | 0 .../bins_static/wchar/mbsrtowcs.stdout | 1 + .../bins_static/wchar/printf-on-wchars.stderr | 0 .../bins_static/wchar/printf-on-wchars.stdout | 4 + .../bins_static/wchar/putwchar.stderr | 0 .../bins_static/wchar/putwchar.stdout | 1 + .../expected/bins_static/wchar/ungetwc.stderr | 0 .../expected/bins_static/wchar/ungetwc.stdout | 0 .../expected/bins_static/wchar/wcpcpy.stderr | 0 .../expected/bins_static/wchar/wcpcpy.stdout | 0 .../expected/bins_static/wchar/wcpncpy.stderr | 0 .../expected/bins_static/wchar/wcpncpy.stdout | 0 .../expected/bins_static/wchar/wcrtomb.stderr | 0 .../expected/bins_static/wchar/wcrtomb.stdout | 2 + .../bins_static/wchar/wcscasecmp.stderr | 0 .../bins_static/wchar/wcscasecmp.stdout | 4 + .../expected/bins_static/wchar/wcschr.stderr | 0 .../expected/bins_static/wchar/wcschr.stdout | 0 .../expected/bins_static/wchar/wcscspn.stderr | 0 .../expected/bins_static/wchar/wcscspn.stdout | 0 .../expected/bins_static/wchar/wcsdup.stderr | 0 .../expected/bins_static/wchar/wcsdup.stdout | 0 .../bins_static/wchar/wcsncasecmp.stderr | 0 .../bins_static/wchar/wcsncasecmp.stdout | 5 + .../expected/bins_static/wchar/wcsnlen.stderr | 0 .../expected/bins_static/wchar/wcsnlen.stdout | 0 .../bins_static/wchar/wcsnrtombs.stderr | 0 .../bins_static/wchar/wcsnrtombs.stdout | 0 .../expected/bins_static/wchar/wcsrchr.stderr | 0 .../expected/bins_static/wchar/wcsrchr.stdout | 0 .../bins_static/wchar/wcsrtombs.stderr | 0 .../bins_static/wchar/wcsrtombs.stdout | 0 .../expected/bins_static/wchar/wcsstr.stderr | 0 .../expected/bins_static/wchar/wcsstr.stdout | 0 .../expected/bins_static/wchar/wcstod.stderr | 0 .../expected/bins_static/wchar/wcstod.stdout | 6 + .../bins_static/wchar/wcstoimax.stderr | 0 .../bins_static/wchar/wcstoimax.stdout | 6 + .../expected/bins_static/wchar/wcstok.stderr | 0 .../expected/bins_static/wchar/wcstok.stdout | 12 + .../expected/bins_static/wchar/wcstol.stderr | 0 .../expected/bins_static/wchar/wcstol.stdout | 1 + .../bins_static/wchar/wcstoumax.stderr | 0 .../bins_static/wchar/wcstoumax.stdout | 3 + .../bins_static/wchar/wcswidth.stderr | 0 .../bins_static/wchar/wcswidth.stdout | 1 + .../expected/bins_static/wchar/wprintf.stderr | 0 .../expected/bins_static/wchar/wprintf.stdout | 67 + .../expected/bins_static/wchar/wscanf.stderr | 0 .../expected/bins_static/wchar/wscanf.stdout | Bin 0 -> 4414 bytes .../bins_static/wctype/towlower.stderr | 0 .../bins_static/wctype/towlower.stdout | 2 + .../bins_static/wctype/towupper.stderr | 0 .../bins_static/wctype/towupper.stdout | 2 + tests/expected/endian.stderr | 0 tests/expected/endian.stdout | 0 tests/expected/features.stderr | 0 tests/expected/features.stdout | 0 tests/expected/glob.stderr | 0 tests/expected/glob.stdout | 70 + tests/fcntl/create.c | 18 + tests/fcntl/fcntl.c | 31 + tests/fcntl/open.c | 296 + tests/fcntl/posix_fallocate.c | 217 + tests/features.c | 50 + tests/fnmatch.c | 48 + tests/futimens.c | 100 + tests/glob.c | 85 + tests/grp/getgrgid_r.c | 42 + tests/grp/getgrnam_r.c | 42 + tests/grp/getgrouplist.c | 15 + tests/grp/getgroups.c | 25 + tests/grp/gr_iter.c | 10 + tests/includes.c | 91 + tests/iso646.c | 92 + tests/kill-waitpid.c | 36 + tests/libfoo.c | 3 + tests/libfoobar.c | 4 + tests/libgen.c | 127 + tests/limits.c | 48 + tests/locale/duplocale.c | 39 + tests/locale/newlocale.c | 18 + tests/locale/setlocale.c | 15 + tests/malloc/usable_size.c | 31 + tests/math.c | 10 + tests/mkfifo.c | 46 + tests/mknod.c | 50 + tests/mknodat.c | 62 + tests/net/if.c | 43 + tests/netdb/getaddrinfo.c | 49 + tests/netdb/getaddrinfo_null.c | 44 + tests/netdb/netdb.c | 280 + tests/psignal.c | 22 + tests/pthread/barrier.c | 124 + tests/pthread/cleanup.c | 65 + tests/pthread/common.h | 24 + tests/pthread/customstack.c | 81 + tests/pthread/exit.c | 31 + tests/pthread/extjoin.c | 68 + tests/pthread/main.c | 38 + tests/pthread/mutex_recursive.c | 108 + tests/pthread/once.c | 100 + tests/pthread/rwlock_randtest.c | 64 + tests/pthread/rwlock_trylock.c | 58 + tests/pthread/thread_fork.c | 51 + tests/pthread/timedwait.c | 86 + tests/pthread/timeout.c | 91 + tests/pthread/tls.c | 99 + tests/ptrace.c | 85 + tests/pty/forkpty.c | 38 + tests/pwd.c | 105 + tests/regex.c | 34 + tests/sa_restart.c | 53 + tests/select.c | 83 + tests/setjmp.c | 14 + tests/sharedlib.c | 10 + tests/sigaction.c | 70 + tests/sigaltstack.c | 84 + tests/sigchld.c | 113 + tests/signal.c | 26 + tests/signals/kill-child.c | 96 + tests/signals/kill-group.c | 61 + tests/signals/kill-invalid.c | 22 + tests/signals/kill-permission.c | 20 + tests/signals/kill-self.c | 56 + tests/signals/kill0-self.c | 16 + tests/signals/killpg-child.c | 82 + tests/signals/killpg-esrch.c | 19 + tests/signals/killpg-invalid.c | 23 + tests/signals/killpg-self.c | 57 + tests/signals/killpg0-self.c | 25 + tests/signals/pthread_kill-child.c | 46 + tests/signals/pthread_kill-invalid.c | 23 + tests/signals/pthread_kill-self.c | 94 + tests/signals/pthread_kill0-self.c | 21 + tests/signals/raise-compliance.c | 48 + tests/signals/sigaddset-add.c | 39 + tests/signals/sigdelset-delete.c | 36 + tests/signals/sigismember-invalid.c | 21 + tests/signals/sigismember-valid.c | 42 + tests/signals/signal-h-2.c | 32 + tests/signals/signal-h.c | 232 + tests/signals/signal-handle_return.c | 35 + tests/signals/signal-handler.c | 33 + tests/signals/signal-handler2.c | 37 + tests/signals/signal-ignore.c | 45 + tests/signals/signal-invalid.c | 25 + tests/signals/signal-uncatchable.c | 25 + tests/signals/signals_list.h | 79 + tests/signals/sigpause-error.c | 101 + tests/signals/sigpause-invalid.c | 56 + tests/signals/sigpause-pause.c | 75 + tests/signals/sigpause-revert.c | 108 + tests/signals/sigpause-suspend.c | 94 + tests/signals/sigprocmask-10.c | 68 + tests/signals/sigprocmask-11.c | 20 + tests/signals/sigprocmask-3.c | 88 + tests/signals/sigprocmask-4.c | 42 + tests/signals/sigprocmask-5.c | 49 + tests/signals/sigprocmask-6.c | 46 + tests/signals/sigprocmask-7.c | 45 + tests/signals/sigprocmask-8.c | 51 + tests/signals/sigprocmask-9.c | 28 + tests/signals/sigprocmask-blocksingle.c | 64 + tests/signals/sigrelse-1.c | 69 + tests/signals/sigrelse-2.c | 12 + tests/signals/sigrelse-3.c | 14 + tests/signals/sigset-1.c | 46 + tests/signals/sigset-10.c | 25 + tests/signals/sigset-2.c | 52 + tests/signals/sigset-3.c | 43 + tests/signals/sigset-4.c | 54 + tests/signals/sigset-5.c | 70 + tests/signals/sigset-9.c | 43 + tests/sigqueue.c | 157 + tests/sigsetjmp.c | 15 + tests/src/main.rs | 315 + tests/stdio/all.c | 29 + tests/stdio/buffer.c | 12 + tests/stdio/ctermid.c | 22 + tests/stdio/dprintf.c | 23 + tests/stdio/fgets.c | 24 + tests/stdio/fputs.c | 19 + tests/stdio/fputs.out | 1 + tests/stdio/fread.c | 29 + tests/stdio/fread.in | 1 + tests/stdio/freopen.c | 39 + tests/stdio/fscanf.c | 13 + tests/stdio/fscanf_offby1.c | 8 + tests/stdio/fseek.c | 20 + tests/stdio/fwrite.c | 27 + tests/stdio/fwrite.out | Bin 0 -> 13 bytes tests/stdio/getc_unget.c | 15 + tests/stdio/getline.c | 168 + tests/stdio/getline.in | 7 + tests/stdio/mutex.c | 47 + tests/stdio/popen.c | 19 + tests/stdio/printf.c | 108 + tests/stdio/printf_neg_pad.c | 5 + tests/stdio/printf_space_pad.c | 4 + tests/stdio/putc_unlocked.c | 23 + tests/stdio/rename.c | 139 + tests/stdio/renameat.c | 225 + tests/stdio/scanf.c | 83 + tests/stdio/setvbuf.c | 15 + tests/stdio/sprintf.c | 34 + tests/stdio/stdio.in | 30 + tests/stdio/tempnam.c | 127 + tests/stdio/tmpnam.c | 41 + tests/stdio/ungetc_ftell.c | 37 + tests/stdio/ungetc_multiple.c | 27 + tests/stdlib/a64l.c | 53 + tests/stdlib/alloc.c | 341 + tests/stdlib/atof.c | 12 + tests/stdlib/atoi.c | 13 + tests/stdlib/bsearch.c | 56 + tests/stdlib/div.c | 23 + tests/stdlib/env.c | 45 + tests/stdlib/getsubopt.c | 55 + tests/stdlib/mkostemps.c | 28 + tests/stdlib/mktemp.c | 16 + tests/stdlib/ptsname.c | 89 + tests/stdlib/qsort.c | 33 + tests/stdlib/rand.c | 70 + tests/stdlib/rand48.c | 137 + tests/stdlib/random.c | 100 + tests/stdlib/realpath.c | 19 + tests/stdlib/strtod.c | 81 + tests/stdlib/strtol.c | 46 + tests/stdlib/strtoul.c | 46 + tests/stdlib/system.c | 18 + tests/string/mem.c | 48 + tests/string/memcpy.c | 53 + tests/string/memmem.c | 31 + tests/string/stpcpy.c | 26 + tests/string/stpncpy.c | 40 + tests/string/strcat.c | 12 + tests/string/strchr.c | 11 + tests/string/strchrnul.c | 31 + tests/string/strcpy.c | 29 + tests/string/strcspn.c | 10 + tests/string/strlen.c | 76 + tests/string/strncmp.c | 13 + tests/string/strpbrk.c | 20 + tests/string/strrchr.c | 25 + tests/string/strsep.c | 68 + tests/string/strsignal.c | 27 + tests/string/strspn.c | 13 + tests/string/strstr.c | 12 + tests/string/strtok.c | 17 + tests/string/strtok_r.c | 18 + tests/strings.c | 47 + tests/sys_epoll/epoll.c | 180 + tests/sys_epoll/epollet.c | 46 + tests/sys_mman/fmap.c | 104 + tests/sys_mman/mmap.c | 52 + tests/sys_resource/constants.c | 27 + tests/sys_resource/getrusage.c | 39 + tests/sys_resource/rlimit_roundtrip.c | 80 + tests/sys_socket/getpeername.c | 77 + tests/sys_socket/recv.c | 50 + tests/sys_socket/recvfrom.c | 56 + tests/sys_socket/shutdown.c | 85 + tests/sys_socket/unixpeername.c | 77 + tests/sys_socket/unixrecv.c | 65 + tests/sys_socket/unixrecvearly.c | 66 + tests/sys_socket/unixrecvfrom.c | 50 + tests/sys_socket/unixsocketpair.c | 59 + tests/sys_socket/unixwrite.c | 56 + tests/sys_stat/chmod.c | 252 + tests/sys_stat/fstatat.c | 356 + tests/sys_stat/lstat.c | 101 + tests/sys_stat/stat.c | 31 + tests/sys_statvfs/statvfs.c | 42 + tests/sys_syslog/syslog.c | 46 + tests/sys_utsname/uname.c | 19 + tests/termios.c | 57 + tests/test_helpers.h | 132 + tests/time/asctime.c | 39 + tests/time/constants.c | 12 + tests/time/gettimeofday.c | 14 + tests/time/gmtime.c | 113 + tests/time/localtime.c | 31 + tests/time/localtime_r.c | 124 + tests/time/macros.c | 38 + tests/time/mktime.c | 60 + tests/time/strftime.c | 167 + tests/time/strptime.c | 129 + tests/time/time.c | 44 + tests/time/timegm.c | 49 + tests/time/times.c | 17 + tests/time/tzset.c | 16 + tests/tls.c | 10 + tests/unistd/access.c | 17 + tests/unistd/alarm.c | 80 + tests/unistd/brk.c | 26 + tests/unistd/chdir.c | 27 + tests/unistd/confstr.c | 34 + tests/unistd/constants.c | 123 + tests/unistd/dup.c | 46 + tests/unistd/exec.c | 12 + tests/unistd/fchdir.c | 20 + tests/unistd/fork.c | 30 + tests/unistd/fsync.c | 20 + tests/unistd/ftruncate.c | 24 + tests/unistd/getcwd.c | 24 + tests/unistd/gethostname.c | 15 + tests/unistd/getid.c | 16 + tests/unistd/getopt.c | 71 + tests/unistd/getopt_long.c | 66 + tests/unistd/getpagesize.c | 11 + tests/unistd/getpass.c | 39 + tests/unistd/getpass.exp | 18 + tests/unistd/isatty.c | 26 + tests/unistd/link.c | 49 + tests/unistd/pathconf.c | 35 + tests/unistd/pipe.c | 76 + tests/unistd/readlinkat.c | 146 + tests/unistd/rmdir.c | 15 + tests/unistd/setid.c | 28 + tests/unistd/sleep.c | 43 + tests/unistd/swab.c | 53 + tests/unistd/sysconf.c | 32 + tests/unistd/write.c | 8 + tests/waitid.c | 50 + tests/waitpid.c | 38 + tests/waitpid_multiple.c | 58 + tests/wchar/fgetwc.c | 19 + tests/wchar/fgetwc.in | 3 + tests/wchar/fwide.c | 60 + tests/wchar/mbrtowc.c | 31 + tests/wchar/mbsrtowcs.c | 25 + tests/wchar/printf-on-wchars.c | 22 + tests/wchar/putwchar.c | 15 + tests/wchar/ungetwc.c | 28 + tests/wchar/ungetwc.in | 1 + tests/wchar/wcpcpy.c | 16 + tests/wchar/wcpncpy.c | 37 + tests/wchar/wcrtomb.c | 30 + tests/wchar/wcscasecmp.c | 11 + tests/wchar/wcschr.c | 13 + tests/wchar/wcscspn.c | 16 + tests/wchar/wcsdup.c | 16 + tests/wchar/wcsncasecmp.c | 12 + tests/wchar/wcsnlen.c | 9 + tests/wchar/wcsnrtombs.c | 18 + tests/wchar/wcsrchr.c | 24 + tests/wchar/wcsrtombs.c | 23 + tests/wchar/wcsstr.c | 27 + tests/wchar/wcstod.c | 20 + tests/wchar/wcstoimax.c | 13 + tests/wchar/wcstok.c | 14 + tests/wchar/wcstol.c | 17 + tests/wchar/wcstoumax.c | 15 + tests/wchar/wcswidth.c | 10 + tests/wchar/wprintf.c | 93 + tests/wchar/wscanf.c | 102 + tests/wctype/towlower.c | 13 + tests/wctype/towupper.c | 13 + 2014 files changed, 155365 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .gitmodules create mode 100644 CONTRIBUTING.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 build.rs create mode 100644 cbindgen.globdefs.toml create mode 100755 check.sh create mode 100644 config.mk create mode 100644 dlmalloc-rs/.github/workflows/main.yml create mode 100644 dlmalloc-rs/.gitignore create mode 100644 dlmalloc-rs/Cargo.toml create mode 100644 dlmalloc-rs/LICENSE-APACHE create mode 100644 dlmalloc-rs/LICENSE-MIT create mode 100644 dlmalloc-rs/README.md create mode 100644 dlmalloc-rs/fuzz/.gitignore create mode 100644 dlmalloc-rs/fuzz/Cargo.toml create mode 100644 dlmalloc-rs/fuzz/fuzz_targets/alloc.rs create mode 100644 dlmalloc-rs/fuzz/src/lib.rs create mode 100644 dlmalloc-rs/src/dlmalloc.c create mode 100644 dlmalloc-rs/src/dlmalloc.rs create mode 100644 dlmalloc-rs/src/dummy.rs create mode 100644 dlmalloc-rs/src/global.rs create mode 100644 dlmalloc-rs/src/lib.rs create mode 100644 dlmalloc-rs/src/unix.rs create mode 100644 dlmalloc-rs/src/wasm.rs create mode 100644 dlmalloc-rs/src/windows.rs create mode 100644 dlmalloc-rs/src/xous.rs create mode 100644 dlmalloc-rs/tests/global.rs create mode 100644 dlmalloc-rs/tests/smoke.rs create mode 100755 fmt.sh create mode 100644 generic-rt/Cargo.toml create mode 100644 generic-rt/src/lib.rs create mode 100644 include/alloca.h create mode 100644 include/complex.h create mode 100644 include/features.h create mode 100644 include/fenv.h create mode 100644 include/iso646.h create mode 100644 include/machine/endian.h create mode 100644 include/math.h create mode 100644 include/memory.h create mode 100644 include/netinet/in_systm.h create mode 100644 include/paths.h create mode 100644 include/setjmp.h create mode 100644 include/stdarg.h create mode 100644 include/stdatomic.h create mode 100644 include/stdbool.h create mode 100644 include/stddef.h create mode 100644 include/stdint.h create mode 100644 include/stdio_ext.h create mode 100644 include/stdnoreturn.h create mode 100644 include/sys/param.h create mode 100644 include/sys/poll.h create mode 100644 include/sys/queue.h create mode 100644 include/sys/redox.h create mode 100644 include/sys/sysmacros.h create mode 100644 include/sys/user.h create mode 100644 include/sysexits.h create mode 100644 include/syslog.h create mode 100644 ld_so/Cargo.toml create mode 100644 ld_so/ld_script/aarch64-unknown-linux-gnu.ld create mode 100644 ld_so/ld_script/aarch64-unknown-redox.ld create mode 100644 ld_so/ld_script/i586-unknown-redox.ld create mode 100644 ld_so/ld_script/i686-unknown-redox.ld create mode 100644 ld_so/ld_script/riscv64gc-unknown-redox.ld create mode 100644 ld_so/ld_script/x86_64-unknown-linux-gnu.ld create mode 100644 ld_so/ld_script/x86_64-unknown-redox.ld create mode 100644 ld_so/src/lib.rs create mode 100644 openlibm/.github/dependabot.yml create mode 100644 openlibm/.github/workflows/ci.yml create mode 100644 openlibm/.github/workflows/cross-loongarch64.yml create mode 100644 openlibm/.github/workflows/cross.yml create mode 100644 openlibm/.gitignore create mode 100644 openlibm/.mailmap create mode 100755 openlibm/CMakeLists.txt create mode 100644 openlibm/LICENSE.md create mode 100644 openlibm/Make.inc create mode 100644 openlibm/Makefile create mode 100644 openlibm/README.md create mode 100644 openlibm/aarch64/Make.files create mode 100644 openlibm/aarch64/fenv.c create mode 100644 openlibm/amd64/Make.files create mode 100644 openlibm/amd64/bsd_asm.h create mode 100644 openlibm/amd64/bsd_fpu.h create mode 100644 openlibm/amd64/bsd_ieeefp.h create mode 100644 openlibm/amd64/e_fmod.S create mode 100644 openlibm/amd64/e_fmodf.S create mode 100644 openlibm/amd64/e_fmodl.S create mode 100644 openlibm/amd64/e_remainder.S create mode 100644 openlibm/amd64/e_remainderf.S create mode 100644 openlibm/amd64/e_remainderl.S create mode 100644 openlibm/amd64/e_sqrt.S create mode 100644 openlibm/amd64/e_sqrtf.S create mode 100644 openlibm/amd64/e_sqrtl.S create mode 100644 openlibm/amd64/fenv.c create mode 100644 openlibm/amd64/s_llrint.S create mode 100644 openlibm/amd64/s_llrintf.S create mode 100644 openlibm/amd64/s_llrintl.S create mode 100644 openlibm/amd64/s_logbl.S create mode 100644 openlibm/amd64/s_lrint.S create mode 100644 openlibm/amd64/s_lrintf.S create mode 100644 openlibm/amd64/s_lrintl.S create mode 100644 openlibm/amd64/s_remquo.S create mode 100644 openlibm/amd64/s_remquof.S create mode 100644 openlibm/amd64/s_remquol.S create mode 100644 openlibm/amd64/s_rintl.S create mode 100644 openlibm/amd64/s_scalbn.S create mode 100644 openlibm/amd64/s_scalbnf.S create mode 100644 openlibm/amd64/s_scalbnl.S create mode 100644 openlibm/arm/Make.files create mode 100644 openlibm/arm/fenv.c create mode 100644 openlibm/bsdsrc/Make.files create mode 100644 openlibm/bsdsrc/b_exp.c create mode 100644 openlibm/bsdsrc/b_log.c create mode 100644 openlibm/bsdsrc/b_tgamma.c create mode 100644 openlibm/bsdsrc/mathimpl.h create mode 100644 openlibm/docs/CNAME create mode 100644 openlibm/docs/images/arrow-down.png create mode 100644 openlibm/docs/images/octocat-small.png create mode 100644 openlibm/docs/index.html create mode 100644 openlibm/docs/javascripts/scale.fix.js create mode 100644 openlibm/docs/params.json create mode 100644 openlibm/docs/stylesheets/pygment_trac.css create mode 100644 openlibm/docs/stylesheets/styles.css create mode 100644 openlibm/i387/Make.files create mode 100644 openlibm/i387/bsd_asm.h create mode 100644 openlibm/i387/bsd_ieeefp.h create mode 100644 openlibm/i387/bsd_npx.h create mode 100644 openlibm/i387/e_exp.S create mode 100644 openlibm/i387/e_fmod.S create mode 100644 openlibm/i387/e_log.S create mode 100644 openlibm/i387/e_log10.S create mode 100644 openlibm/i387/e_log10f.S create mode 100644 openlibm/i387/e_logf.S create mode 100644 openlibm/i387/e_remainder.S create mode 100644 openlibm/i387/e_remainderf.S create mode 100644 openlibm/i387/e_remainderl.S create mode 100644 openlibm/i387/e_sqrt.S create mode 100644 openlibm/i387/e_sqrtf.S create mode 100644 openlibm/i387/e_sqrtl.S create mode 100644 openlibm/i387/fenv.c create mode 100644 openlibm/i387/invtrig.c create mode 100644 openlibm/i387/osx_asm.h create mode 100644 openlibm/i387/s_ceil.S create mode 100644 openlibm/i387/s_ceilf.S create mode 100644 openlibm/i387/s_ceill.S create mode 100644 openlibm/i387/s_copysign.S create mode 100644 openlibm/i387/s_copysignf.S create mode 100644 openlibm/i387/s_copysignl.S create mode 100644 openlibm/i387/s_cos.S create mode 100644 openlibm/i387/s_floor.S create mode 100644 openlibm/i387/s_floorf.S create mode 100644 openlibm/i387/s_floorl.S create mode 100644 openlibm/i387/s_llrint.S create mode 100644 openlibm/i387/s_llrintf.S create mode 100644 openlibm/i387/s_llrintl.S create mode 100644 openlibm/i387/s_logb.S create mode 100644 openlibm/i387/s_logbf.S create mode 100644 openlibm/i387/s_logbl.S create mode 100644 openlibm/i387/s_lrint.S create mode 100644 openlibm/i387/s_lrintf.S create mode 100644 openlibm/i387/s_lrintl.S create mode 100644 openlibm/i387/s_remquo.S create mode 100644 openlibm/i387/s_remquof.S create mode 100644 openlibm/i387/s_remquol.S create mode 100644 openlibm/i387/s_rint.S create mode 100644 openlibm/i387/s_rintf.S create mode 100644 openlibm/i387/s_rintl.S create mode 100644 openlibm/i387/s_scalbn.S create mode 100644 openlibm/i387/s_scalbnf.S create mode 100644 openlibm/i387/s_scalbnl.S create mode 100644 openlibm/i387/s_sin.S create mode 100644 openlibm/i387/s_tan.S create mode 100644 openlibm/i387/s_trunc.S create mode 100644 openlibm/i387/s_truncf.S create mode 100644 openlibm/i387/s_truncl.S create mode 100644 openlibm/include/openlibm.h create mode 100644 openlibm/include/openlibm_complex.h create mode 100644 openlibm/include/openlibm_defs.h create mode 100644 openlibm/include/openlibm_fenv.h create mode 100644 openlibm/include/openlibm_fenv_aarch64.h create mode 100644 openlibm/include/openlibm_fenv_amd64.h create mode 100644 openlibm/include/openlibm_fenv_arm.h create mode 100644 openlibm/include/openlibm_fenv_i387.h create mode 100644 openlibm/include/openlibm_fenv_loongarch64.h create mode 100644 openlibm/include/openlibm_fenv_mips.h create mode 100644 openlibm/include/openlibm_fenv_powerpc.h create mode 100644 openlibm/include/openlibm_fenv_riscv.h create mode 100644 openlibm/include/openlibm_fenv_s390.h create mode 100644 openlibm/include/openlibm_math.h create mode 100644 openlibm/ld128/Make.files create mode 100644 openlibm/ld128/e_acoshl.c create mode 100644 openlibm/ld128/e_atanhl.c create mode 100644 openlibm/ld128/e_coshl.c create mode 100644 openlibm/ld128/e_expl.c create mode 100644 openlibm/ld128/e_fmodl.c create mode 100644 openlibm/ld128/e_hypotl.c create mode 100644 openlibm/ld128/e_lgammal_r.c create mode 100644 openlibm/ld128/e_log10l.c create mode 100644 openlibm/ld128/e_log2l.c create mode 100644 openlibm/ld128/e_logl.c create mode 100644 openlibm/ld128/e_powl.c create mode 100644 openlibm/ld128/e_rem_pio2l.h create mode 100644 openlibm/ld128/e_sinhl.c create mode 100644 openlibm/ld128/e_tgammal.c create mode 100644 openlibm/ld128/invtrig.c create mode 100644 openlibm/ld128/invtrig.h create mode 100644 openlibm/ld128/k_cosl.c create mode 100644 openlibm/ld128/k_sinl.c create mode 100644 openlibm/ld128/k_tanl.c create mode 100644 openlibm/ld128/s_asinhl.c create mode 100644 openlibm/ld128/s_ceill.c create mode 100644 openlibm/ld128/s_erfl.c create mode 100644 openlibm/ld128/s_exp2l.c create mode 100644 openlibm/ld128/s_expm1l.c create mode 100644 openlibm/ld128/s_floorl.c create mode 100644 openlibm/ld128/s_log1pl.c create mode 100644 openlibm/ld128/s_modfl.c create mode 100644 openlibm/ld128/s_nanl.c create mode 100644 openlibm/ld128/s_nextafterl.c create mode 100644 openlibm/ld128/s_nexttoward.c create mode 100644 openlibm/ld128/s_nexttowardf.c create mode 100644 openlibm/ld128/s_remquol.c create mode 100644 openlibm/ld128/s_tanhl.c create mode 100644 openlibm/ld128/s_truncl.c create mode 100644 openlibm/ld80/Make.files create mode 100644 openlibm/ld80/e_acoshl.c create mode 100644 openlibm/ld80/e_atanhl.c create mode 100644 openlibm/ld80/e_coshl.c create mode 100644 openlibm/ld80/e_expl.c create mode 100644 openlibm/ld80/e_fmodl.c create mode 100644 openlibm/ld80/e_hypotl.c create mode 100644 openlibm/ld80/e_lgammal_r.c create mode 100644 openlibm/ld80/e_log10l.c create mode 100644 openlibm/ld80/e_log2l.c create mode 100644 openlibm/ld80/e_logl.c create mode 100644 openlibm/ld80/e_powl.c create mode 100644 openlibm/ld80/e_rem_pio2l.h create mode 100644 openlibm/ld80/e_sinhl.c create mode 100644 openlibm/ld80/e_tgammal.c create mode 100644 openlibm/ld80/invtrig.c create mode 100644 openlibm/ld80/invtrig.h create mode 100644 openlibm/ld80/k_cosl.c create mode 100644 openlibm/ld80/k_sinl.c create mode 100644 openlibm/ld80/k_tanl.c create mode 100644 openlibm/ld80/s_asinhl.c create mode 100644 openlibm/ld80/s_ceill.c create mode 100644 openlibm/ld80/s_erfl.c create mode 100644 openlibm/ld80/s_exp2l.c create mode 100644 openlibm/ld80/s_expm1l.c create mode 100644 openlibm/ld80/s_floorl.c create mode 100644 openlibm/ld80/s_log1pl.c create mode 100644 openlibm/ld80/s_modfl.c create mode 100644 openlibm/ld80/s_nanl.c create mode 100644 openlibm/ld80/s_nextafterl.c create mode 100644 openlibm/ld80/s_nexttoward.c create mode 100644 openlibm/ld80/s_nexttowardf.c create mode 100644 openlibm/ld80/s_remquol.c create mode 100644 openlibm/ld80/s_tanhl.c create mode 100644 openlibm/ld80/s_truncl.c create mode 100644 openlibm/loongarch64/Make.files create mode 100644 openlibm/loongarch64/fenv.c create mode 100644 openlibm/mips/Make.files create mode 100644 openlibm/mips/fenv-softfloat.h create mode 100644 openlibm/mips/fenv.c create mode 100644 openlibm/openlibm.pc.in create mode 100644 openlibm/powerpc/Make.files create mode 100644 openlibm/powerpc/fenv.c create mode 100644 openlibm/riscv64/Make.files create mode 100644 openlibm/riscv64/fenv.c create mode 100644 openlibm/s390/Make.files create mode 100644 openlibm/s390/fenv.c create mode 100644 openlibm/src/Make.files create mode 100644 openlibm/src/aarch64_fpmath.h create mode 100644 openlibm/src/amd64_fpmath.h create mode 100644 openlibm/src/bsd_cdefs.h create mode 100644 openlibm/src/cdefs-compat.h create mode 100644 openlibm/src/common.c create mode 100644 openlibm/src/e_acos.c create mode 100644 openlibm/src/e_acosf.c create mode 100644 openlibm/src/e_acosh.c create mode 100644 openlibm/src/e_acoshf.c create mode 100644 openlibm/src/e_acosl.c create mode 100644 openlibm/src/e_asin.c create mode 100644 openlibm/src/e_asinf.c create mode 100644 openlibm/src/e_asinl.c create mode 100644 openlibm/src/e_atan2.c create mode 100644 openlibm/src/e_atan2f.c create mode 100644 openlibm/src/e_atan2l.c create mode 100644 openlibm/src/e_atanh.c create mode 100644 openlibm/src/e_atanhf.c create mode 100644 openlibm/src/e_cosh.c create mode 100644 openlibm/src/e_coshf.c create mode 100644 openlibm/src/e_exp.c create mode 100644 openlibm/src/e_expf.c create mode 100644 openlibm/src/e_fmod.c create mode 100644 openlibm/src/e_fmodf.c create mode 100644 openlibm/src/e_fmodl.c create mode 100644 openlibm/src/e_hypot.c create mode 100644 openlibm/src/e_hypotf.c create mode 100644 openlibm/src/e_hypotl.c create mode 100644 openlibm/src/e_j0.c create mode 100644 openlibm/src/e_j0f.c create mode 100644 openlibm/src/e_j1.c create mode 100644 openlibm/src/e_j1f.c create mode 100644 openlibm/src/e_jn.c create mode 100644 openlibm/src/e_jnf.c create mode 100644 openlibm/src/e_lgamma.c create mode 100644 openlibm/src/e_lgamma_r.c create mode 100644 openlibm/src/e_lgammaf.c create mode 100644 openlibm/src/e_lgammaf_r.c create mode 100644 openlibm/src/e_lgammal.c create mode 100644 openlibm/src/e_log.c create mode 100644 openlibm/src/e_log10.c create mode 100644 openlibm/src/e_log10f.c create mode 100644 openlibm/src/e_log2.c create mode 100644 openlibm/src/e_log2f.c create mode 100644 openlibm/src/e_logf.c create mode 100644 openlibm/src/e_pow.c create mode 100644 openlibm/src/e_powf.c create mode 100644 openlibm/src/e_rem_pio2.c create mode 100644 openlibm/src/e_rem_pio2f.c create mode 100644 openlibm/src/e_remainder.c create mode 100644 openlibm/src/e_remainderf.c create mode 100644 openlibm/src/e_remainderl.c create mode 100644 openlibm/src/e_sinh.c create mode 100644 openlibm/src/e_sinhf.c create mode 100644 openlibm/src/e_sqrt.c create mode 100644 openlibm/src/e_sqrtf.c create mode 100644 openlibm/src/e_sqrtl.c create mode 100644 openlibm/src/fpmath.h create mode 100644 openlibm/src/i386_fpmath.h create mode 100644 openlibm/src/k_cos.c create mode 100644 openlibm/src/k_cosf.c create mode 100644 openlibm/src/k_exp.c create mode 100644 openlibm/src/k_expf.c create mode 100644 openlibm/src/k_log.h create mode 100644 openlibm/src/k_logf.h create mode 100644 openlibm/src/k_rem_pio2.c create mode 100644 openlibm/src/k_sin.c create mode 100644 openlibm/src/k_sinf.c create mode 100644 openlibm/src/k_tan.c create mode 100644 openlibm/src/k_tanf.c create mode 100644 openlibm/src/loongarch64_fpmath.h create mode 100644 openlibm/src/math_private.h create mode 100644 openlibm/src/math_private_openbsd.h create mode 100644 openlibm/src/mips_fpmath.h create mode 100644 openlibm/src/polevll.c create mode 100644 openlibm/src/powerpc_fpmath.h create mode 100644 openlibm/src/riscv_fpmath.h create mode 100644 openlibm/src/s390_fpmath.h create mode 100644 openlibm/src/s_asinh.c create mode 100644 openlibm/src/s_asinhf.c create mode 100644 openlibm/src/s_atan.c create mode 100644 openlibm/src/s_atanf.c create mode 100644 openlibm/src/s_atanl.c create mode 100644 openlibm/src/s_cabs.c create mode 100644 openlibm/src/s_cabsf.c create mode 100644 openlibm/src/s_cabsl.c create mode 100644 openlibm/src/s_cacos.c create mode 100644 openlibm/src/s_cacosf.c create mode 100644 openlibm/src/s_cacosh.c create mode 100644 openlibm/src/s_cacoshf.c create mode 100644 openlibm/src/s_cacoshl.c create mode 100644 openlibm/src/s_cacosl.c create mode 100644 openlibm/src/s_carg.c create mode 100644 openlibm/src/s_cargf.c create mode 100644 openlibm/src/s_cargl.c create mode 100644 openlibm/src/s_casin.c create mode 100644 openlibm/src/s_casinf.c create mode 100644 openlibm/src/s_casinh.c create mode 100644 openlibm/src/s_casinhf.c create mode 100644 openlibm/src/s_casinhl.c create mode 100644 openlibm/src/s_casinl.c create mode 100644 openlibm/src/s_catan.c create mode 100644 openlibm/src/s_catanf.c create mode 100644 openlibm/src/s_catanh.c create mode 100644 openlibm/src/s_catanhf.c create mode 100644 openlibm/src/s_catanhl.c create mode 100644 openlibm/src/s_catanl.c create mode 100644 openlibm/src/s_cbrt.c create mode 100644 openlibm/src/s_cbrtf.c create mode 100644 openlibm/src/s_cbrtl.c create mode 100644 openlibm/src/s_ccos.c create mode 100644 openlibm/src/s_ccosf.c create mode 100644 openlibm/src/s_ccosh.c create mode 100644 openlibm/src/s_ccoshf.c create mode 100644 openlibm/src/s_ccoshl.c create mode 100644 openlibm/src/s_ccosl.c create mode 100644 openlibm/src/s_ceil.c create mode 100644 openlibm/src/s_ceilf.c create mode 100644 openlibm/src/s_ceill.c create mode 100644 openlibm/src/s_cexp.c create mode 100644 openlibm/src/s_cexpf.c create mode 100644 openlibm/src/s_cexpl.c create mode 100644 openlibm/src/s_cimag.c create mode 100644 openlibm/src/s_cimagf.c create mode 100644 openlibm/src/s_cimagl.c create mode 100644 openlibm/src/s_clog.c create mode 100644 openlibm/src/s_clogf.c create mode 100644 openlibm/src/s_clogl.c create mode 100644 openlibm/src/s_conj.c create mode 100644 openlibm/src/s_conjf.c create mode 100644 openlibm/src/s_conjl.c create mode 100644 openlibm/src/s_copysign.c create mode 100644 openlibm/src/s_copysignf.c create mode 100644 openlibm/src/s_copysignl.c create mode 100644 openlibm/src/s_cos.c create mode 100644 openlibm/src/s_cosf.c create mode 100644 openlibm/src/s_cosl.c create mode 100644 openlibm/src/s_cpow.c create mode 100644 openlibm/src/s_cpowf.c create mode 100644 openlibm/src/s_cpowl.c create mode 100644 openlibm/src/s_cproj.c create mode 100644 openlibm/src/s_cprojf.c create mode 100644 openlibm/src/s_cprojl.c create mode 100644 openlibm/src/s_creal.c create mode 100644 openlibm/src/s_crealf.c create mode 100644 openlibm/src/s_creall.c create mode 100644 openlibm/src/s_csin.c create mode 100644 openlibm/src/s_csinf.c create mode 100644 openlibm/src/s_csinh.c create mode 100644 openlibm/src/s_csinhf.c create mode 100644 openlibm/src/s_csinhl.c create mode 100644 openlibm/src/s_csinl.c create mode 100644 openlibm/src/s_csqrt.c create mode 100644 openlibm/src/s_csqrtf.c create mode 100644 openlibm/src/s_csqrtl.c create mode 100644 openlibm/src/s_ctan.c create mode 100644 openlibm/src/s_ctanf.c create mode 100644 openlibm/src/s_ctanh.c create mode 100644 openlibm/src/s_ctanhf.c create mode 100644 openlibm/src/s_ctanhl.c create mode 100644 openlibm/src/s_ctanl.c create mode 100644 openlibm/src/s_erf.c create mode 100644 openlibm/src/s_erff.c create mode 100644 openlibm/src/s_exp2.c create mode 100644 openlibm/src/s_exp2f.c create mode 100644 openlibm/src/s_expm1.c create mode 100644 openlibm/src/s_expm1f.c create mode 100644 openlibm/src/s_fabs.c create mode 100644 openlibm/src/s_fabsf.c create mode 100644 openlibm/src/s_fabsl.c create mode 100644 openlibm/src/s_fdim.c create mode 100644 openlibm/src/s_floor.c create mode 100644 openlibm/src/s_floorf.c create mode 100644 openlibm/src/s_floorl.c create mode 100644 openlibm/src/s_fma.c create mode 100644 openlibm/src/s_fmaf.c create mode 100644 openlibm/src/s_fmal.c create mode 100644 openlibm/src/s_fmax.c create mode 100644 openlibm/src/s_fmaxf.c create mode 100644 openlibm/src/s_fmaxl.c create mode 100644 openlibm/src/s_fmin.c create mode 100644 openlibm/src/s_fminf.c create mode 100644 openlibm/src/s_fminl.c create mode 100644 openlibm/src/s_fpclassify.c create mode 100644 openlibm/src/s_frexp.c create mode 100644 openlibm/src/s_frexpf.c create mode 100644 openlibm/src/s_frexpl.c create mode 100644 openlibm/src/s_ilogb.c create mode 100644 openlibm/src/s_ilogbf.c create mode 100644 openlibm/src/s_ilogbl.c create mode 100644 openlibm/src/s_isfinite.c create mode 100644 openlibm/src/s_isinf.c create mode 100644 openlibm/src/s_isnan.c create mode 100644 openlibm/src/s_isnormal.c create mode 100644 openlibm/src/s_llrint.c create mode 100644 openlibm/src/s_llrintf.c create mode 100644 openlibm/src/s_llrintl.c create mode 100644 openlibm/src/s_llround.c create mode 100644 openlibm/src/s_llroundf.c create mode 100644 openlibm/src/s_llroundl.c create mode 100644 openlibm/src/s_log1p.c create mode 100644 openlibm/src/s_log1pf.c create mode 100644 openlibm/src/s_logb.c create mode 100644 openlibm/src/s_logbf.c create mode 100644 openlibm/src/s_logbl.c create mode 100644 openlibm/src/s_lrint.c create mode 100644 openlibm/src/s_lrintf.c create mode 100644 openlibm/src/s_lrintl.c create mode 100644 openlibm/src/s_lround.c create mode 100644 openlibm/src/s_lroundf.c create mode 100644 openlibm/src/s_lroundl.c create mode 100644 openlibm/src/s_modf.c create mode 100644 openlibm/src/s_modff.c create mode 100644 openlibm/src/s_modfl.c create mode 100644 openlibm/src/s_nan.c create mode 100644 openlibm/src/s_nearbyint.c create mode 100644 openlibm/src/s_nextafter.c create mode 100644 openlibm/src/s_nextafterf.c create mode 100644 openlibm/src/s_nextafterl.c create mode 100644 openlibm/src/s_nexttoward.c create mode 100644 openlibm/src/s_nexttowardf.c create mode 100644 openlibm/src/s_remquo.c create mode 100644 openlibm/src/s_remquof.c create mode 100644 openlibm/src/s_remquol.c create mode 100644 openlibm/src/s_rint.c create mode 100644 openlibm/src/s_rintf.c create mode 100644 openlibm/src/s_rintl.c create mode 100644 openlibm/src/s_round.c create mode 100644 openlibm/src/s_roundf.c create mode 100644 openlibm/src/s_roundl.c create mode 100644 openlibm/src/s_scalbln.c create mode 100644 openlibm/src/s_scalbn.c create mode 100644 openlibm/src/s_scalbnf.c create mode 100644 openlibm/src/s_scalbnl.c create mode 100644 openlibm/src/s_signbit.c create mode 100644 openlibm/src/s_signgam.c create mode 100644 openlibm/src/s_sin.c create mode 100644 openlibm/src/s_sincos.c create mode 100644 openlibm/src/s_sincosf.c create mode 100644 openlibm/src/s_sincosl.c create mode 100644 openlibm/src/s_sinf.c create mode 100644 openlibm/src/s_sinl.c create mode 100644 openlibm/src/s_tan.c create mode 100644 openlibm/src/s_tanf.c create mode 100644 openlibm/src/s_tanh.c create mode 100644 openlibm/src/s_tanhf.c create mode 100644 openlibm/src/s_tanl.c create mode 100644 openlibm/src/s_tgammaf.c create mode 100644 openlibm/src/s_trunc.c create mode 100644 openlibm/src/s_truncf.c create mode 100644 openlibm/src/s_truncl.c create mode 100644 openlibm/src/types-compat.h create mode 100644 openlibm/src/w_cabs.c create mode 100644 openlibm/src/w_cabsf.c create mode 100644 openlibm/src/w_cabsl.c create mode 100644 openlibm/test/.gitignore create mode 100644 openlibm/test/Makefile create mode 100644 openlibm/test/inf_torture.c create mode 100644 openlibm/test/libm-bench.cpp create mode 100644 openlibm/test/libm-test-ulps.h create mode 100644 openlibm/test/libm-test.c create mode 100644 openlibm/test/test-211.c create mode 100644 openlibm/test/test-double.c create mode 100644 openlibm/test/test-float.c create mode 100644 openlibm/wasm32/Make.files create mode 100644 openlibm/wasm32/assert.h create mode 100644 openlibm/wasm32/float.h create mode 100644 openlibm/wasm32/limits.h create mode 100644 openlibm/wasm32/stdint.h create mode 100644 redox-ioctl/Cargo.toml create mode 100644 redox-ioctl/src/drm.rs create mode 100644 redox-ioctl/src/ioctl_data.rs create mode 100644 redox-ioctl/src/lib.rs create mode 100644 redox-rt/Cargo.toml create mode 100644 redox-rt/src/arch/aarch64.rs create mode 100644 redox-rt/src/arch/i686.rs create mode 100644 redox-rt/src/arch/mod.rs create mode 100644 redox-rt/src/arch/riscv64.rs create mode 100644 redox-rt/src/arch/x86_64.rs create mode 100644 redox-rt/src/lib.rs create mode 100644 redox-rt/src/proc.rs create mode 100644 redox-rt/src/signal.rs create mode 100644 redox-rt/src/sync.rs create mode 100644 redox-rt/src/sys.rs create mode 100644 redox-rt/src/thread.rs create mode 100755 renamesyms.sh create mode 100644 rust-toolchain.toml create mode 100644 rustfmt.toml create mode 100644 src/c/stdlib.c create mode 100644 src/c_str.rs create mode 100644 src/c_vec.rs create mode 100644 src/crt0/Cargo.toml create mode 100644 src/crt0/src/lib.rs create mode 100644 src/crti/Cargo.toml create mode 100644 src/crti/src/lib.rs create mode 100644 src/crtn/Cargo.toml create mode 100644 src/crtn/src/lib.rs create mode 100644 src/cxa.rs create mode 100644 src/db.rs create mode 100644 src/error.rs create mode 100644 src/fs.rs create mode 100644 src/header/_aio/cbindgen.toml create mode 100644 src/header/_aio/mod.rs create mode 100644 src/header/_fenv/cbindgen.toml create mode 100644 src/header/_fenv/mod.rs create mode 100644 src/header/_paths/cbindgen.toml create mode 100644 src/header/_paths/linux.rs create mode 100644 src/header/_paths/mod.rs create mode 100644 src/header/_paths/redox.rs create mode 100644 src/header/_template/cbindgen.toml create mode 100644 src/header/_template/mod.rs create mode 100644 src/header/arch_aarch64_user/cbindgen.toml create mode 100644 src/header/arch_aarch64_user/mod.rs create mode 100644 src/header/arch_riscv64_user/cbindgen.toml create mode 100644 src/header/arch_riscv64_user/mod.rs create mode 100644 src/header/arch_x64_user/cbindgen.toml create mode 100644 src/header/arch_x64_user/mod.rs create mode 100644 src/header/arpa_inet/cbindgen.toml create mode 100644 src/header/arpa_inet/mod.rs create mode 100644 src/header/assert/cbindgen.toml create mode 100644 src/header/assert/mod.rs create mode 100644 src/header/bits_arpainet/cbindgen.toml create mode 100644 src/header/bits_arpainet/mod.rs create mode 100644 src/header/bits_iovec/cbindgen.toml create mode 100644 src/header/bits_iovec/mod.rs create mode 100644 src/header/bits_locale-t/cbindgen.toml create mode 100644 src/header/bits_locale-t/mod.rs create mode 100644 src/header/bits_pthread/cbindgen.toml create mode 100644 src/header/bits_pthread/mod.rs create mode 100644 src/header/bits_safamily-t/cbindgen.toml create mode 100644 src/header/bits_safamily-t/mod.rs create mode 100644 src/header/bits_sigset-t/cbindgen.toml create mode 100644 src/header/bits_sigset-t/mod.rs create mode 100644 src/header/bits_socklen-t/cbindgen.toml create mode 100644 src/header/bits_socklen-t/mod.rs create mode 100644 src/header/bits_timespec/cbindgen.toml create mode 100644 src/header/bits_timespec/mod.rs create mode 100644 src/header/cpio/cbindgen.toml create mode 100644 src/header/cpio/mod.rs create mode 100644 src/header/crypt/argon2.rs create mode 100644 src/header/crypt/blowfish.rs create mode 100644 src/header/crypt/cbindgen.toml create mode 100644 src/header/crypt/md5.rs create mode 100644 src/header/crypt/mod.rs create mode 100644 src/header/crypt/pbkdf2.rs create mode 100644 src/header/crypt/scrypt.rs create mode 100644 src/header/crypt/sha.rs create mode 100644 src/header/ctype/cbindgen.toml create mode 100644 src/header/ctype/mod.rs create mode 100644 src/header/dirent/cbindgen.toml create mode 100644 src/header/dirent/mod.rs create mode 100644 src/header/dl-tls/cbindgen.toml create mode 100644 src/header/dl-tls/mod.rs create mode 100644 src/header/dlfcn/cbindgen.toml create mode 100644 src/header/dlfcn/mod.rs create mode 100644 src/header/elf/cbindgen.toml create mode 100644 src/header/elf/mod.rs create mode 100644 src/header/endian/cbindgen.toml create mode 100644 src/header/endian/mod.rs create mode 100644 src/header/err/cbindgen.toml create mode 100644 src/header/err/mod.rs create mode 100644 src/header/errno/cbindgen.toml create mode 100644 src/header/errno/mod.rs create mode 100644 src/header/fcntl/cbindgen.toml create mode 100644 src/header/fcntl/linux.rs create mode 100644 src/header/fcntl/mod.rs create mode 100644 src/header/fcntl/redox.rs create mode 100644 src/header/float/cbindgen.toml create mode 100644 src/header/float/mod.rs create mode 100644 src/header/fnmatch/cbindgen.toml create mode 100644 src/header/fnmatch/mod.rs create mode 100644 src/header/getopt/cbindgen.toml create mode 100644 src/header/getopt/mod.rs create mode 100644 src/header/glob/cbindgen.toml create mode 100644 src/header/glob/mod.rs create mode 100644 src/header/grp/cbindgen.toml create mode 100644 src/header/grp/mod.rs create mode 100644 src/header/ifaddrs/cbindgen.toml create mode 100644 src/header/ifaddrs/mod.rs create mode 100644 src/header/inttypes/cbindgen.toml create mode 100644 src/header/inttypes/mod.rs create mode 100644 src/header/langinfo/cbindgen.toml create mode 100644 src/header/langinfo/mod.rs create mode 100644 src/header/libgen/cbindgen.toml create mode 100644 src/header/libgen/mod.rs create mode 100644 src/header/limits/cbindgen.toml create mode 100644 src/header/limits/mod.rs create mode 100644 src/header/locale/cbindgen.toml create mode 100644 src/header/locale/constants.rs create mode 100644 src/header/locale/data.rs create mode 100644 src/header/locale/mod.rs create mode 100644 src/header/malloc/cbindgen.toml create mode 100644 src/header/malloc/mod.rs create mode 100644 src/header/math/cbindgen.toml create mode 100644 src/header/math/mod.rs create mode 100644 src/header/mod.rs create mode 100644 src/header/monetary/cbindgen.toml create mode 100644 src/header/monetary/mod.rs create mode 100644 src/header/monetary/strfmon.rs create mode 100644 src/header/net_if/cbindgen.toml create mode 100644 src/header/net_if/mod.rs create mode 100644 src/header/netdb/cbindgen.toml create mode 100644 src/header/netdb/dns/answer.rs create mode 100644 src/header/netdb/dns/mod.rs create mode 100644 src/header/netdb/dns/query.rs create mode 100644 src/header/netdb/host.rs create mode 100644 src/header/netdb/linux.rs create mode 100644 src/header/netdb/lookup.rs create mode 100644 src/header/netdb/mod.rs create mode 100644 src/header/netdb/redox.rs create mode 100644 src/header/netinet_in/cbindgen.toml create mode 100644 src/header/netinet_in/mod.rs create mode 100644 src/header/netinet_ip/cbindgen.toml create mode 100644 src/header/netinet_ip/mod.rs create mode 100644 src/header/netinet_tcp/cbindgen.toml create mode 100644 src/header/netinet_tcp/mod.rs create mode 100644 src/header/poll/cbindgen.toml create mode 100644 src/header/poll/mod.rs create mode 100644 src/header/pthread/attr.rs create mode 100644 src/header/pthread/barrier.rs create mode 100644 src/header/pthread/cbindgen.toml create mode 100644 src/header/pthread/cond.rs create mode 100644 src/header/pthread/mod.rs create mode 100644 src/header/pthread/mutex.rs create mode 100644 src/header/pthread/once.rs create mode 100644 src/header/pthread/rwlock.rs create mode 100644 src/header/pthread/spin.rs create mode 100644 src/header/pthread/tls.rs create mode 100644 src/header/pty/cbindgen.toml create mode 100644 src/header/pty/linux.rs create mode 100644 src/header/pty/mod.rs create mode 100644 src/header/pty/redox.rs create mode 100644 src/header/pwd/cbindgen.toml create mode 100644 src/header/pwd/linux.rs create mode 100644 src/header/pwd/mod.rs create mode 100644 src/header/pwd/redox.rs create mode 100644 src/header/regex/cbindgen.toml create mode 100644 src/header/regex/mod.rs create mode 100644 src/header/sched/cbindgen.toml create mode 100644 src/header/sched/mod.rs create mode 100644 src/header/semaphore/cbindgen.toml create mode 100644 src/header/semaphore/mod.rs create mode 100644 src/header/setjmp/impl/README.md create mode 100644 src/header/setjmp/impl/aarch64/longjmp.s create mode 100644 src/header/setjmp/impl/aarch64/setjmp.s create mode 100644 src/header/setjmp/impl/aarch64/sigsetjmp.s create mode 100644 src/header/setjmp/impl/arm/longjmp.s create mode 100644 src/header/setjmp/impl/arm/setjmp.s create mode 100644 src/header/setjmp/impl/i386/longjmp.s create mode 100644 src/header/setjmp/impl/i386/setjmp.s create mode 100644 src/header/setjmp/impl/i386/sigsetjmp.s create mode 100644 src/header/setjmp/impl/m68k/longjmp.s create mode 100644 src/header/setjmp/impl/m68k/setjmp.s create mode 100644 src/header/setjmp/impl/microblaze/longjmp.s create mode 100644 src/header/setjmp/impl/microblaze/setjmp.s create mode 100644 src/header/setjmp/impl/mips/longjmp.S create mode 100644 src/header/setjmp/impl/mips/setjmp.S create mode 100644 src/header/setjmp/impl/mips64/longjmp.S create mode 100644 src/header/setjmp/impl/mips64/setjmp.S create mode 100644 src/header/setjmp/impl/mipsn32/longjmp.S create mode 100644 src/header/setjmp/impl/mipsn32/setjmp.S create mode 100644 src/header/setjmp/impl/or1k/longjmp.s create mode 100644 src/header/setjmp/impl/or1k/setjmp.s create mode 100644 src/header/setjmp/impl/powerpc/longjmp.S create mode 100644 src/header/setjmp/impl/powerpc/setjmp.S create mode 100644 src/header/setjmp/impl/powerpc64/longjmp.s create mode 100644 src/header/setjmp/impl/powerpc64/setjmp.s create mode 100644 src/header/setjmp/impl/riscv64/longjmp.S create mode 100644 src/header/setjmp/impl/riscv64/setjmp.S create mode 100644 src/header/setjmp/impl/riscv64/sigsetjmp.S create mode 100644 src/header/setjmp/impl/s390x/longjmp.s create mode 100644 src/header/setjmp/impl/s390x/setjmp.s create mode 100644 src/header/setjmp/impl/sh/longjmp.S create mode 100644 src/header/setjmp/impl/sh/setjmp.S create mode 100644 src/header/setjmp/impl/x32/longjmp.s create mode 100644 src/header/setjmp/impl/x32/setjmp.s create mode 100644 src/header/setjmp/impl/x86_64/longjmp.s create mode 100644 src/header/setjmp/impl/x86_64/setjmp.s create mode 100644 src/header/setjmp/impl/x86_64/sigsetjmp.s create mode 100644 src/header/setjmp/mod.rs create mode 100644 src/header/sgtty/cbindgen.toml create mode 100644 src/header/sgtty/mod.rs create mode 100644 src/header/shadow/cbindgen.toml create mode 100644 src/header/shadow/mod.rs create mode 100644 src/header/signal/cbindgen.toml create mode 100644 src/header/signal/linux.rs create mode 100644 src/header/signal/mod.rs create mode 100644 src/header/signal/redox.rs create mode 100644 src/header/signal/signalfd.rs create mode 100644 src/header/stdio/cbindgen.toml create mode 100644 src/header/stdio/constants.rs create mode 100644 src/header/stdio/default.rs create mode 100644 src/header/stdio/ext.rs create mode 100644 src/header/stdio/getdelim.rs create mode 100644 src/header/stdio/helpers.rs create mode 100644 src/header/stdio/mod.rs create mode 100644 src/header/stdio/open_memstream.rs create mode 100644 src/header/stdio/printf.rs create mode 100644 src/header/stdio/reader.rs create mode 100644 src/header/stdio/scanf.rs create mode 100644 src/header/stdlib/cbindgen.toml create mode 100644 src/header/stdlib/mod.rs create mode 100644 src/header/stdlib/rand48.rs create mode 100644 src/header/stdlib/random.rs create mode 100644 src/header/stdlib/sort.rs create mode 100644 src/header/string/cbindgen.toml create mode 100644 src/header/string/mod.rs create mode 100644 src/header/strings/cbindgen.toml create mode 100644 src/header/strings/mod.rs create mode 100644 src/header/sys_auxv/cbindgen.toml create mode 100644 src/header/sys_auxv/mod.rs create mode 100644 src/header/sys_epoll/cbindgen.toml create mode 100644 src/header/sys_epoll/linux.rs create mode 100644 src/header/sys_epoll/mod.rs create mode 100644 src/header/sys_epoll/redox.rs create mode 100644 src/header/sys_eventfd/mod.rs create mode 100644 src/header/sys_file/cbindgen.toml create mode 100644 src/header/sys_file/mod.rs create mode 100644 src/header/sys_ioctl/cbindgen.toml create mode 100644 src/header/sys_ioctl/linux.rs create mode 100644 src/header/sys_ioctl/mod.rs create mode 100644 src/header/sys_ioctl/redox/drm.rs create mode 100644 src/header/sys_ioctl/redox/mod.rs create mode 100644 src/header/sys_mman/cbindgen.toml create mode 100644 src/header/sys_mman/linux.rs create mode 100644 src/header/sys_mman/mod.rs create mode 100644 src/header/sys_mman/redox.rs create mode 100644 src/header/sys_procfs/cbindgen.toml create mode 100644 src/header/sys_procfs/mod.rs create mode 100644 src/header/sys_ptrace/cbindgen.toml create mode 100644 src/header/sys_ptrace/mod.rs create mode 100644 src/header/sys_random/cbindgen.toml create mode 100644 src/header/sys_random/mod.rs create mode 100644 src/header/sys_resource/cbindgen.toml create mode 100644 src/header/sys_resource/mod.rs create mode 100644 src/header/sys_select/cbindgen.toml create mode 100644 src/header/sys_select/mod.rs create mode 100644 src/header/sys_signalfd/cbindgen.toml create mode 100644 src/header/sys_signalfd/mod.rs create mode 100644 src/header/sys_socket/cbindgen.toml create mode 100644 src/header/sys_socket/constants.rs create mode 100644 src/header/sys_socket/mod.rs create mode 100644 src/header/sys_stat/cbindgen.toml create mode 100644 src/header/sys_stat/mod.rs create mode 100644 src/header/sys_statvfs/cbindgen.toml create mode 100644 src/header/sys_statvfs/mod.rs create mode 100644 src/header/sys_syscall/aarch64.rs create mode 100644 src/header/sys_syscall/cbindgen.toml create mode 100644 src/header/sys_syscall/mod.rs create mode 100644 src/header/sys_syscall/x86_64.rs create mode 100644 src/header/sys_syslog/cbindgen.toml create mode 100644 src/header/sys_syslog/linux.rs create mode 100644 src/header/sys_syslog/logger.rs create mode 100644 src/header/sys_syslog/mod.rs create mode 100644 src/header/sys_syslog/redox.rs create mode 100644 src/header/sys_time/cbindgen.toml create mode 100644 src/header/sys_time/mod.rs create mode 100644 src/header/sys_timeb/cbindgen.toml create mode 100644 src/header/sys_timeb/mod.rs create mode 100644 src/header/sys_timerfd/cbindgen.toml create mode 100644 src/header/sys_timerfd/mod.rs create mode 100644 src/header/sys_times/cbindgen.toml create mode 100644 src/header/sys_times/mod.rs create mode 100644 src/header/sys_types/cbindgen.toml create mode 100644 src/header/sys_types/mod.rs create mode 100644 src/header/sys_types_internal/cbindgen.toml create mode 100644 src/header/sys_types_internal/mod.rs create mode 100644 src/header/sys_uio/cbindgen.toml create mode 100644 src/header/sys_uio/mod.rs create mode 100644 src/header/sys_un/cbindgen.toml create mode 100644 src/header/sys_un/mod.rs create mode 100644 src/header/sys_utsname/cbindgen.toml create mode 100644 src/header/sys_utsname/mod.rs create mode 100644 src/header/sys_wait/cbindgen.toml create mode 100644 src/header/sys_wait/mod.rs create mode 100644 src/header/tar/cbindgen.toml create mode 100644 src/header/tar/mod.rs create mode 100644 src/header/termios/cbindgen.toml create mode 100644 src/header/termios/linux.rs create mode 100644 src/header/termios/mod.rs create mode 100644 src/header/termios/redox.rs create mode 100644 src/header/time/cbindgen.toml create mode 100644 src/header/time/constants.rs create mode 100644 src/header/time/linux.rs create mode 100644 src/header/time/mod.rs create mode 100644 src/header/time/redox.rs create mode 100644 src/header/time/strftime.rs create mode 100644 src/header/time/strptime.rs create mode 100644 src/header/unistd/brk.rs create mode 100644 src/header/unistd/cbindgen.toml create mode 100644 src/header/unistd/getopt.rs create mode 100644 src/header/unistd/getpass.rs create mode 100644 src/header/unistd/mod.rs create mode 100644 src/header/unistd/pathconf.rs create mode 100644 src/header/unistd/syscall.rs create mode 100644 src/header/unistd/sysconf.rs create mode 100644 src/header/unistd/sysconf/linux.rs create mode 100644 src/header/unistd/sysconf/redox.rs create mode 100644 src/header/utime/cbindgen.toml create mode 100644 src/header/utime/mod.rs create mode 100644 src/header/utmp/cbindgen.toml create mode 100644 src/header/utmp/mod.rs create mode 100644 src/header/wchar/cbindgen.toml create mode 100644 src/header/wchar/mod.rs create mode 100644 src/header/wchar/utf8.rs create mode 100644 src/header/wchar/wprintf.rs create mode 100644 src/header/wchar/wscanf.rs create mode 100644 src/header/wctype/alpha.rs create mode 100644 src/header/wctype/casecmp.rs create mode 100644 src/header/wctype/cbindgen.toml create mode 100644 src/header/wctype/mod.rs create mode 100644 src/header/wctype/punct.rs create mode 100644 src/io/buffered.rs create mode 100644 src/io/cursor.rs create mode 100644 src/io/error.rs create mode 100644 src/io/impls.rs create mode 100644 src/io/mod.rs create mode 100644 src/io/prelude.rs create mode 100644 src/iter.rs create mode 100644 src/ld_so/access.rs create mode 100644 src/ld_so/callbacks.rs create mode 100644 src/ld_so/debug.rs create mode 100644 src/ld_so/dso.rs create mode 100644 src/ld_so/linker.rs create mode 100644 src/ld_so/mod.rs create mode 100644 src/ld_so/start.rs create mode 100644 src/ld_so/tcb.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/out.rs create mode 100644 src/platform/allocator/mod.rs create mode 100644 src/platform/allocator/sys.rs create mode 100644 src/platform/auxv_defs.rs create mode 100644 src/platform/linux/epoll.rs create mode 100644 src/platform/linux/mod.rs create mode 100644 src/platform/linux/ptrace.rs create mode 100644 src/platform/linux/signal.rs create mode 100644 src/platform/linux/socket.rs create mode 100644 src/platform/logger.rs create mode 100644 src/platform/mod.rs create mode 100644 src/platform/pal/epoll.rs create mode 100644 src/platform/pal/mod.rs create mode 100644 src/platform/pal/ptrace.rs create mode 100644 src/platform/pal/signal.rs create mode 100644 src/platform/pal/socket.rs create mode 100644 src/platform/redox/epoll.rs create mode 100644 src/platform/redox/event.rs create mode 100644 src/platform/redox/exec.rs create mode 100644 src/platform/redox/extra.rs create mode 100644 src/platform/redox/libcscheme.rs create mode 100644 src/platform/redox/libredox.rs create mode 100644 src/platform/redox/mod.rs create mode 100644 src/platform/redox/path.rs create mode 100644 src/platform/redox/ptrace.rs create mode 100644 src/platform/redox/signal.rs create mode 100644 src/platform/redox/socket.rs create mode 100644 src/platform/redox/timer.rs create mode 100644 src/platform/rlb.rs create mode 100644 src/platform/types.rs create mode 100644 src/pthread/mod.rs create mode 100644 src/raw_cell.rs create mode 100644 src/start.rs create mode 100644 src/sync/barrier.rs create mode 100644 src/sync/barrier.rs.unused create mode 100644 src/sync/cond.rs create mode 100644 src/sync/mod.rs create mode 100644 src/sync/mutex.rs create mode 100644 src/sync/once.rs create mode 100644 src/sync/pthread_mutex.rs create mode 100644 src/sync/reentrant_mutex.rs create mode 100644 src/sync/rwlock.rs create mode 100644 src/sync/semaphore.rs create mode 100644 src/sync/waitval.rs create mode 100755 stripcore.sh create mode 100644 tests/.gitignore create mode 100644 tests/Cargo.lock create mode 100644 tests/Cargo.toml create mode 100644 tests/Makefile create mode 100644 tests/Makefile.run.mk create mode 100644 tests/Makefile.tests.mk create mode 100644 tests/alloca.c create mode 100644 tests/args.c create mode 100644 tests/arpainet.c create mode 100644 tests/assert.c create mode 100644 tests/constructor.c create mode 100644 tests/crypt/blowfish.c create mode 100644 tests/crypt/md5.c create mode 100644 tests/crypt/pbkdf2.c create mode 100644 tests/crypt/scrypt.c create mode 100644 tests/crypt/sha256.c create mode 100644 tests/crypt/sha512.c create mode 100644 tests/ctype.c create mode 100644 tests/destructor.c create mode 100644 tests/dirent/fdopendir.c create mode 100644 tests/dirent/main.c create mode 100644 tests/dirent/posix_getdents.c create mode 100644 tests/dirent/scandir.c create mode 100644 tests/dlfcn.c create mode 100644 tests/dlopen_scopes.c create mode 100644 tests/endian.c create mode 100644 tests/err.c create mode 100644 tests/errno.c create mode 100644 tests/error.c create mode 100644 tests/example_dir/1-never-gonna-give-you-up create mode 100644 tests/example_dir/2-never-gonna-let-you-down create mode 100644 tests/example_dir/3-never-gonna-run-around create mode 100644 tests/example_dir/4-and-desert-you create mode 100644 tests/example_dir/5-never-gonna-make-you-cry create mode 100644 tests/example_dir/6-never-gonna-say-goodbye create mode 100644 tests/example_dir/7-never-gonna-tell-a-lie create mode 100644 tests/example_dir/8-and-hurt-you create mode 100644 tests/expected/bins_dynamic/alloca.stderr create mode 100644 tests/expected/bins_dynamic/alloca.stdout create mode 100644 tests/expected/bins_dynamic/args.stderr create mode 100644 tests/expected/bins_dynamic/args.stdout create mode 100644 tests/expected/bins_dynamic/arpainet.stderr create mode 100644 tests/expected/bins_dynamic/arpainet.stdout create mode 100644 tests/expected/bins_dynamic/assert.stderr create mode 100644 tests/expected/bins_dynamic/assert.stdout create mode 100644 tests/expected/bins_dynamic/constructor.stderr create mode 100644 tests/expected/bins_dynamic/constructor.stdout create mode 100644 tests/expected/bins_dynamic/crypt/blowfish.stderr create mode 100644 tests/expected/bins_dynamic/crypt/blowfish.stdout create mode 100644 tests/expected/bins_dynamic/crypt/md5.stderr create mode 100644 tests/expected/bins_dynamic/crypt/md5.stdout create mode 100644 tests/expected/bins_dynamic/crypt/pbkdf2.stderr create mode 100644 tests/expected/bins_dynamic/crypt/pbkdf2.stdout create mode 100644 tests/expected/bins_dynamic/crypt/scrypt.stderr create mode 100644 tests/expected/bins_dynamic/crypt/scrypt.stdout create mode 100644 tests/expected/bins_dynamic/crypt/sha256.stderr create mode 100644 tests/expected/bins_dynamic/crypt/sha256.stdout create mode 100644 tests/expected/bins_dynamic/crypt/sha512.stderr create mode 100644 tests/expected/bins_dynamic/crypt/sha512.stdout create mode 100644 tests/expected/bins_dynamic/ctype.stderr create mode 100644 tests/expected/bins_dynamic/ctype.stdout create mode 100644 tests/expected/bins_dynamic/destructor.stderr create mode 100644 tests/expected/bins_dynamic/destructor.stdout create mode 100644 tests/expected/bins_dynamic/dirent/fdopendir.stderr create mode 100644 tests/expected/bins_dynamic/dirent/fdopendir.stdout create mode 100644 tests/expected/bins_dynamic/dirent/scandir.stderr create mode 100644 tests/expected/bins_dynamic/dirent/scandir.stdout create mode 100644 tests/expected/bins_dynamic/dlfcn.stderr create mode 100644 tests/expected/bins_dynamic/dlfcn.stdout create mode 100644 tests/expected/bins_dynamic/dlopen_scopes.stderr create mode 100644 tests/expected/bins_dynamic/dlopen_scopes.stdout create mode 100644 tests/expected/bins_dynamic/err.stderr create mode 100644 tests/expected/bins_dynamic/err.stdout create mode 100644 tests/expected/bins_dynamic/errno.stderr create mode 100644 tests/expected/bins_dynamic/errno.stdout create mode 100644 tests/expected/bins_dynamic/error.stderr create mode 100644 tests/expected/bins_dynamic/error.stdout create mode 100644 tests/expected/bins_dynamic/fcntl/create.stderr create mode 100644 tests/expected/bins_dynamic/fcntl/create.stdout create mode 100644 tests/expected/bins_dynamic/fcntl/fcntl.stderr create mode 100644 tests/expected/bins_dynamic/fcntl/fcntl.stdout create mode 100644 tests/expected/bins_dynamic/fcntl/open.stderr create mode 100644 tests/expected/bins_dynamic/fcntl/open.stdout create mode 100644 tests/expected/bins_dynamic/fcntl/posix_fallocate.stderr create mode 100644 tests/expected/bins_dynamic/fcntl/posix_fallocate.stdout create mode 100644 tests/expected/bins_dynamic/fnmatch.stderr create mode 100644 tests/expected/bins_dynamic/fnmatch.stdout create mode 100644 tests/expected/bins_dynamic/futimens.stderr create mode 100644 tests/expected/bins_dynamic/futimens.stdout create mode 100644 tests/expected/bins_dynamic/iso646.stderr create mode 100644 tests/expected/bins_dynamic/iso646.stdout create mode 100644 tests/expected/bins_dynamic/libgen.stderr create mode 100644 tests/expected/bins_dynamic/libgen.stdout create mode 100644 tests/expected/bins_dynamic/locale/duplocale.stderr create mode 100644 tests/expected/bins_dynamic/locale/duplocale.stdout create mode 100644 tests/expected/bins_dynamic/locale/newlocale.stderr create mode 100644 tests/expected/bins_dynamic/locale/newlocale.stdout create mode 100644 tests/expected/bins_dynamic/locale/setlocale.stderr create mode 100644 tests/expected/bins_dynamic/locale/setlocale.stdout create mode 100644 tests/expected/bins_dynamic/malloc/usable_size.stderr create mode 100644 tests/expected/bins_dynamic/malloc/usable_size.stdout create mode 100644 tests/expected/bins_dynamic/math.stderr create mode 100644 tests/expected/bins_dynamic/math.stdout create mode 100644 tests/expected/bins_dynamic/mkfifo.stderr create mode 100644 tests/expected/bins_dynamic/mkfifo.stdout create mode 100644 tests/expected/bins_dynamic/mknod.stderr create mode 100644 tests/expected/bins_dynamic/mknod.stdout create mode 100644 tests/expected/bins_dynamic/mknodat.stderr create mode 100644 tests/expected/bins_dynamic/mknodat.stdout create mode 100644 tests/expected/bins_dynamic/netdb/getaddrinfo.stderr create mode 100644 tests/expected/bins_dynamic/netdb/getaddrinfo.stdout create mode 100644 tests/expected/bins_dynamic/ptrace.stderr create mode 100644 tests/expected/bins_dynamic/ptrace.stdout create mode 100644 tests/expected/bins_dynamic/pty/forkpty.stderr create mode 100644 tests/expected/bins_dynamic/pty/forkpty.stdout create mode 100644 tests/expected/bins_dynamic/regex.stderr create mode 100644 tests/expected/bins_dynamic/regex.stdout create mode 100644 tests/expected/bins_dynamic/select.stderr create mode 100644 tests/expected/bins_dynamic/select.stdout create mode 100644 tests/expected/bins_dynamic/setjmp.stderr create mode 100644 tests/expected/bins_dynamic/setjmp.stdout create mode 100644 tests/expected/bins_dynamic/sigaction.stderr create mode 100644 tests/expected/bins_dynamic/sigaction.stdout create mode 100644 tests/expected/bins_dynamic/sigaltstack.stderr create mode 100644 tests/expected/bins_dynamic/sigaltstack.stdout create mode 100644 tests/expected/bins_dynamic/signal.stderr create mode 100644 tests/expected/bins_dynamic/signal.stdout create mode 100644 tests/expected/bins_dynamic/sigsetjmp.stderr create mode 100644 tests/expected/bins_dynamic/sigsetjmp.stdout create mode 100644 tests/expected/bins_dynamic/stdio/all.stderr create mode 100644 tests/expected/bins_dynamic/stdio/all.stdout create mode 100644 tests/expected/bins_dynamic/stdio/buffer.stderr create mode 100644 tests/expected/bins_dynamic/stdio/buffer.stdout create mode 100644 tests/expected/bins_dynamic/stdio/dprintf.stderr create mode 100644 tests/expected/bins_dynamic/stdio/dprintf.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fgets.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fgets.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fputs.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fputs.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fread.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fread.stdout create mode 100644 tests/expected/bins_dynamic/stdio/freopen.stderr create mode 100644 tests/expected/bins_dynamic/stdio/freopen.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fscanf.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fscanf.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fscanf_offby1.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fscanf_offby1.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fseek.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fseek.stdout create mode 100644 tests/expected/bins_dynamic/stdio/fwrite.stderr create mode 100644 tests/expected/bins_dynamic/stdio/fwrite.stdout create mode 100644 tests/expected/bins_dynamic/stdio/getc_unget.stderr create mode 100644 tests/expected/bins_dynamic/stdio/getc_unget.stdout create mode 100644 tests/expected/bins_dynamic/stdio/getline.stderr create mode 100644 tests/expected/bins_dynamic/stdio/getline.stdout create mode 100644 tests/expected/bins_dynamic/stdio/mutex.stderr create mode 100644 tests/expected/bins_dynamic/stdio/mutex.stdout create mode 100644 tests/expected/bins_dynamic/stdio/popen.stderr create mode 100644 tests/expected/bins_dynamic/stdio/popen.stdout create mode 100644 tests/expected/bins_dynamic/stdio/printf.stderr create mode 100644 tests/expected/bins_dynamic/stdio/printf.stdout create mode 100644 tests/expected/bins_dynamic/stdio/printf_neg_pad.stderr create mode 100644 tests/expected/bins_dynamic/stdio/printf_neg_pad.stdout create mode 100644 tests/expected/bins_dynamic/stdio/printf_space_pad.stderr create mode 100644 tests/expected/bins_dynamic/stdio/printf_space_pad.stdout create mode 100644 tests/expected/bins_dynamic/stdio/putc_unlocked.stderr create mode 100644 tests/expected/bins_dynamic/stdio/putc_unlocked.stdout create mode 100644 tests/expected/bins_dynamic/stdio/rename.stderr create mode 100644 tests/expected/bins_dynamic/stdio/rename.stdout create mode 100644 tests/expected/bins_dynamic/stdio/renameat.stderr create mode 100644 tests/expected/bins_dynamic/stdio/renameat.stdout create mode 100644 tests/expected/bins_dynamic/stdio/scanf.stderr create mode 100644 tests/expected/bins_dynamic/stdio/scanf.stdout create mode 100644 tests/expected/bins_dynamic/stdio/setvbuf.stderr create mode 100644 tests/expected/bins_dynamic/stdio/setvbuf.stdout create mode 100644 tests/expected/bins_dynamic/stdio/sprintf.stderr create mode 100644 tests/expected/bins_dynamic/stdio/sprintf.stdout create mode 100644 tests/expected/bins_dynamic/stdio/ungetc_ftell.stderr create mode 100644 tests/expected/bins_dynamic/stdio/ungetc_ftell.stdout create mode 100644 tests/expected/bins_dynamic/stdio/ungetc_multiple.stderr create mode 100644 tests/expected/bins_dynamic/stdio/ungetc_multiple.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/a64l.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/a64l.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/alloc.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/alloc.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/atof.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/atof.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/atoi.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/atoi.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/div.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/div.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/env.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/env.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/getsubopt.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/getsubopt.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/mkostemps.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/mkostemps.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/ptsname.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/ptsname.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/qsort.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/qsort.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/rand.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/rand.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/rand48.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/rand48.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/random.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/random.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/strtod.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/strtod.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/strtol.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/strtol.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/strtoul.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/strtoul.stdout create mode 100644 tests/expected/bins_dynamic/stdlib/system.stderr create mode 100644 tests/expected/bins_dynamic/stdlib/system.stdout create mode 100644 tests/expected/bins_dynamic/string/mem.stderr create mode 100644 tests/expected/bins_dynamic/string/mem.stdout create mode 100644 tests/expected/bins_dynamic/string/memcpy.stderr create mode 100644 tests/expected/bins_dynamic/string/memcpy.stdout create mode 100644 tests/expected/bins_dynamic/string/memmem.stderr create mode 100644 tests/expected/bins_dynamic/string/memmem.stdout create mode 100644 tests/expected/bins_dynamic/string/stpcpy.stderr create mode 100644 tests/expected/bins_dynamic/string/stpcpy.stdout create mode 100644 tests/expected/bins_dynamic/string/stpncpy.stderr create mode 100644 tests/expected/bins_dynamic/string/stpncpy.stdout create mode 100644 tests/expected/bins_dynamic/string/strcat.stderr create mode 100644 tests/expected/bins_dynamic/string/strcat.stdout create mode 100644 tests/expected/bins_dynamic/string/strchr.stderr create mode 100644 tests/expected/bins_dynamic/string/strchr.stdout create mode 100644 tests/expected/bins_dynamic/string/strchrnul.stderr create mode 100644 tests/expected/bins_dynamic/string/strchrnul.stdout create mode 100644 tests/expected/bins_dynamic/string/strcpy.stderr create mode 100644 tests/expected/bins_dynamic/string/strcpy.stdout create mode 100644 tests/expected/bins_dynamic/string/strcspn.stderr create mode 100644 tests/expected/bins_dynamic/string/strcspn.stdout create mode 100644 tests/expected/bins_dynamic/string/strlen.stderr create mode 100644 tests/expected/bins_dynamic/string/strlen.stdout create mode 100644 tests/expected/bins_dynamic/string/strncmp.stderr create mode 100644 tests/expected/bins_dynamic/string/strncmp.stdout create mode 100644 tests/expected/bins_dynamic/string/strpbrk.stderr create mode 100644 tests/expected/bins_dynamic/string/strpbrk.stdout create mode 100644 tests/expected/bins_dynamic/string/strrchr.stderr create mode 100644 tests/expected/bins_dynamic/string/strrchr.stdout create mode 100644 tests/expected/bins_dynamic/string/strsep.stderr create mode 100644 tests/expected/bins_dynamic/string/strsep.stdout create mode 100644 tests/expected/bins_dynamic/string/strsignal.stderr create mode 100644 tests/expected/bins_dynamic/string/strsignal.stdout create mode 100644 tests/expected/bins_dynamic/string/strspn.stderr create mode 100644 tests/expected/bins_dynamic/string/strspn.stdout create mode 100644 tests/expected/bins_dynamic/string/strstr.stderr create mode 100644 tests/expected/bins_dynamic/string/strstr.stdout create mode 100644 tests/expected/bins_dynamic/string/strtok.stderr create mode 100644 tests/expected/bins_dynamic/string/strtok.stdout create mode 100644 tests/expected/bins_dynamic/string/strtok_r.stderr create mode 100644 tests/expected/bins_dynamic/string/strtok_r.stdout create mode 100644 tests/expected/bins_dynamic/strings.stderr create mode 100644 tests/expected/bins_dynamic/strings.stdout create mode 100644 tests/expected/bins_dynamic/sys_epoll/epollet.stdout create mode 100644 tests/expected/bins_dynamic/sys_mman.stderr create mode 100644 tests/expected/bins_dynamic/sys_mman.stdout create mode 100644 tests/expected/bins_dynamic/sys_socket/recv.stdout create mode 100644 tests/expected/bins_dynamic/sys_socket/recvfrom.stdout create mode 100644 tests/expected/bins_dynamic/sys_socket/unixpeername.stdout create mode 100644 tests/expected/bins_dynamic/sys_socket/unixrecv.stdout create mode 100644 tests/expected/bins_dynamic/sys_socket/unixrecvfrom.stdout create mode 100644 tests/expected/bins_dynamic/sys_socket/unixsocketpair.stdout create mode 100644 tests/expected/bins_dynamic/sys_stat/chmod.stderr create mode 100644 tests/expected/bins_dynamic/sys_stat/chmod.stdout create mode 100644 tests/expected/bins_dynamic/sys_stat/fstatat.stderr create mode 100644 tests/expected/bins_dynamic/sys_stat/fstatat.stdout create mode 100644 tests/expected/bins_dynamic/sys_stat/lstat.stderr create mode 100644 tests/expected/bins_dynamic/sys_stat/lstat.stdout create mode 100644 tests/expected/bins_dynamic/sys_syslog/syslog.stderr create mode 100644 tests/expected/bins_dynamic/sys_syslog/syslog.stdout create mode 100644 tests/expected/bins_dynamic/termios.stderr create mode 100644 tests/expected/bins_dynamic/termios.stdout create mode 100644 tests/expected/bins_dynamic/time/asctime.stderr create mode 100644 tests/expected/bins_dynamic/time/asctime.stdout create mode 100644 tests/expected/bins_dynamic/time/constants.stderr create mode 100644 tests/expected/bins_dynamic/time/constants.stdout create mode 100644 tests/expected/bins_dynamic/time/gmtime.stderr create mode 100644 tests/expected/bins_dynamic/time/gmtime.stdout create mode 100644 tests/expected/bins_dynamic/time/localtime.stderr create mode 100644 tests/expected/bins_dynamic/time/localtime.stdout create mode 100644 tests/expected/bins_dynamic/time/localtime_r.stderr create mode 100644 tests/expected/bins_dynamic/time/localtime_r.stdout create mode 100644 tests/expected/bins_dynamic/time/macros.stderr create mode 100644 tests/expected/bins_dynamic/time/macros.stdout create mode 100644 tests/expected/bins_dynamic/time/mktime.stderr create mode 100644 tests/expected/bins_dynamic/time/mktime.stdout create mode 100644 tests/expected/bins_dynamic/time/strftime.stderr create mode 100644 tests/expected/bins_dynamic/time/strftime.stdout create mode 100644 tests/expected/bins_dynamic/time/strptime.stderr create mode 100644 tests/expected/bins_dynamic/time/strptime.stdout create mode 100644 tests/expected/bins_dynamic/time/time.stderr create mode 100644 tests/expected/bins_dynamic/time/time.stdout create mode 100644 tests/expected/bins_dynamic/time/timegm.stderr create mode 100644 tests/expected/bins_dynamic/time/timegm.stdout create mode 100644 tests/expected/bins_dynamic/time/tzset.stderr create mode 100644 tests/expected/bins_dynamic/time/tzset.stdout create mode 100644 tests/expected/bins_dynamic/tls.stderr create mode 100644 tests/expected/bins_dynamic/tls.stdout create mode 100644 tests/expected/bins_dynamic/unistd/access.stderr create mode 100644 tests/expected/bins_dynamic/unistd/access.stdout create mode 100644 tests/expected/bins_dynamic/unistd/alarm.stderr create mode 100644 tests/expected/bins_dynamic/unistd/alarm.stdout create mode 100644 tests/expected/bins_dynamic/unistd/brk.stderr create mode 100644 tests/expected/bins_dynamic/unistd/brk.stdout create mode 100644 tests/expected/bins_dynamic/unistd/confstr.stderr create mode 100644 tests/expected/bins_dynamic/unistd/confstr.stdout create mode 100644 tests/expected/bins_dynamic/unistd/constants.stderr create mode 100644 tests/expected/bins_dynamic/unistd/constants.stdout create mode 100644 tests/expected/bins_dynamic/unistd/dup.stderr create mode 100644 tests/expected/bins_dynamic/unistd/dup.stdout create mode 100644 tests/expected/bins_dynamic/unistd/exec.stderr create mode 100644 tests/expected/bins_dynamic/unistd/exec.stdout create mode 100644 tests/expected/bins_dynamic/unistd/fchdir.stderr create mode 100644 tests/expected/bins_dynamic/unistd/fchdir.stdout create mode 100644 tests/expected/bins_dynamic/unistd/fork.stderr create mode 100644 tests/expected/bins_dynamic/unistd/fork.stdout create mode 100644 tests/expected/bins_dynamic/unistd/fsync.stderr create mode 100644 tests/expected/bins_dynamic/unistd/fsync.stdout create mode 100644 tests/expected/bins_dynamic/unistd/ftruncate.stderr create mode 100644 tests/expected/bins_dynamic/unistd/ftruncate.stdout create mode 100644 tests/expected/bins_dynamic/unistd/getopt.stderr create mode 100644 tests/expected/bins_dynamic/unistd/getopt.stdout create mode 100644 tests/expected/bins_dynamic/unistd/getopt_long.stderr create mode 100644 tests/expected/bins_dynamic/unistd/getopt_long.stdout create mode 100644 tests/expected/bins_dynamic/unistd/pipe.stderr create mode 100644 tests/expected/bins_dynamic/unistd/pipe.stdout create mode 100644 tests/expected/bins_dynamic/unistd/readlinkat.stderr create mode 100644 tests/expected/bins_dynamic/unistd/readlinkat.stdout create mode 100644 tests/expected/bins_dynamic/unistd/rmdir.stderr create mode 100644 tests/expected/bins_dynamic/unistd/rmdir.stdout create mode 100644 tests/expected/bins_dynamic/unistd/sleep.stderr create mode 100644 tests/expected/bins_dynamic/unistd/sleep.stdout create mode 100644 tests/expected/bins_dynamic/unistd/swab.stderr create mode 100644 tests/expected/bins_dynamic/unistd/swab.stdout create mode 100644 tests/expected/bins_dynamic/unistd/write.stderr create mode 100644 tests/expected/bins_dynamic/unistd/write.stdout create mode 100644 tests/expected/bins_dynamic/wchar/fgetwc.stderr create mode 100644 tests/expected/bins_dynamic/wchar/fgetwc.stdout create mode 100644 tests/expected/bins_dynamic/wchar/fwide.stderr create mode 100644 tests/expected/bins_dynamic/wchar/fwide.stdout create mode 100644 tests/expected/bins_dynamic/wchar/mbrtowc.stderr create mode 100644 tests/expected/bins_dynamic/wchar/mbrtowc.stdout create mode 100644 tests/expected/bins_dynamic/wchar/mbsrtowcs.stderr create mode 100644 tests/expected/bins_dynamic/wchar/mbsrtowcs.stdout create mode 100644 tests/expected/bins_dynamic/wchar/printf-on-wchars.stderr create mode 100644 tests/expected/bins_dynamic/wchar/printf-on-wchars.stdout create mode 100644 tests/expected/bins_dynamic/wchar/putwchar.stderr create mode 100644 tests/expected/bins_dynamic/wchar/putwchar.stdout create mode 100644 tests/expected/bins_dynamic/wchar/ungetwc.stderr create mode 100644 tests/expected/bins_dynamic/wchar/ungetwc.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcpcpy.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcpcpy.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcpncpy.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcpncpy.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcrtomb.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcrtomb.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcscasecmp.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcscasecmp.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcschr.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcschr.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcscspn.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcscspn.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsdup.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsdup.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsncasecmp.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsncasecmp.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsnlen.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsnlen.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsnrtombs.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsnrtombs.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsrchr.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsrchr.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsrtombs.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsrtombs.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcsstr.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcsstr.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcstod.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcstod.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcstoimax.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcstoimax.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcstok.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcstok.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcstol.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcstol.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcstoumax.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcstoumax.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wcswidth.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wcswidth.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wprintf.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wprintf.stdout create mode 100644 tests/expected/bins_dynamic/wchar/wscanf.stderr create mode 100644 tests/expected/bins_dynamic/wchar/wscanf.stdout create mode 100644 tests/expected/bins_dynamic/wctype/towlower.stderr create mode 100644 tests/expected/bins_dynamic/wctype/towlower.stdout create mode 100644 tests/expected/bins_dynamic/wctype/towupper.stderr create mode 100644 tests/expected/bins_dynamic/wctype/towupper.stdout create mode 100644 tests/expected/bins_static/alloca.stderr create mode 100644 tests/expected/bins_static/alloca.stdout create mode 100644 tests/expected/bins_static/args.stderr create mode 100644 tests/expected/bins_static/args.stdout create mode 100644 tests/expected/bins_static/arpainet.stderr create mode 100644 tests/expected/bins_static/arpainet.stdout create mode 100644 tests/expected/bins_static/assert.stderr create mode 100644 tests/expected/bins_static/assert.stdout create mode 100644 tests/expected/bins_static/constructor.stderr create mode 100644 tests/expected/bins_static/constructor.stdout create mode 100644 tests/expected/bins_static/crypt/blowfish.stderr create mode 100644 tests/expected/bins_static/crypt/blowfish.stdout create mode 100644 tests/expected/bins_static/crypt/md5.stderr create mode 100644 tests/expected/bins_static/crypt/md5.stdout create mode 100644 tests/expected/bins_static/crypt/pbkdf2.stderr create mode 100644 tests/expected/bins_static/crypt/pbkdf2.stdout create mode 100644 tests/expected/bins_static/crypt/scrypt.stderr create mode 100644 tests/expected/bins_static/crypt/scrypt.stdout create mode 100644 tests/expected/bins_static/crypt/sha256.stderr create mode 100644 tests/expected/bins_static/crypt/sha256.stdout create mode 100644 tests/expected/bins_static/crypt/sha512.stderr create mode 100644 tests/expected/bins_static/crypt/sha512.stdout create mode 100644 tests/expected/bins_static/ctype.stderr create mode 100644 tests/expected/bins_static/ctype.stdout create mode 100644 tests/expected/bins_static/destructor.stderr create mode 100644 tests/expected/bins_static/destructor.stdout create mode 100644 tests/expected/bins_static/dirent/fdopendir.stderr create mode 100644 tests/expected/bins_static/dirent/fdopendir.stdout create mode 100644 tests/expected/bins_static/dirent/scandir.stderr create mode 100644 tests/expected/bins_static/dirent/scandir.stdout create mode 100644 tests/expected/bins_static/err.stderr create mode 100644 tests/expected/bins_static/err.stdout create mode 100644 tests/expected/bins_static/errno.stderr create mode 100644 tests/expected/bins_static/errno.stdout create mode 100644 tests/expected/bins_static/error.stderr create mode 100644 tests/expected/bins_static/error.stdout create mode 100644 tests/expected/bins_static/fcntl/create.stderr create mode 100644 tests/expected/bins_static/fcntl/create.stdout create mode 100644 tests/expected/bins_static/fcntl/fcntl.stderr create mode 100644 tests/expected/bins_static/fcntl/fcntl.stdout create mode 100644 tests/expected/bins_static/fcntl/open.stderr create mode 100644 tests/expected/bins_static/fcntl/open.stdout create mode 100644 tests/expected/bins_static/fcntl/posix_fallocate.stderr create mode 100644 tests/expected/bins_static/fcntl/posix_fallocate.stdout create mode 100644 tests/expected/bins_static/fnmatch.stderr create mode 100644 tests/expected/bins_static/fnmatch.stdout create mode 100644 tests/expected/bins_static/futimens.stderr create mode 100644 tests/expected/bins_static/futimens.stdout create mode 100644 tests/expected/bins_static/iso646.stderr create mode 100644 tests/expected/bins_static/iso646.stdout create mode 100644 tests/expected/bins_static/libgen.stderr create mode 100644 tests/expected/bins_static/libgen.stdout create mode 100644 tests/expected/bins_static/locale/duplocale.stderr create mode 100644 tests/expected/bins_static/locale/duplocale.stdout create mode 100644 tests/expected/bins_static/locale/newlocale.stderr create mode 100644 tests/expected/bins_static/locale/newlocale.stdout create mode 100644 tests/expected/bins_static/locale/setlocale.stderr create mode 100644 tests/expected/bins_static/locale/setlocale.stdout create mode 100644 tests/expected/bins_static/malloc/usable_size.stderr create mode 100644 tests/expected/bins_static/malloc/usable_size.stdout create mode 100644 tests/expected/bins_static/math.stderr create mode 100644 tests/expected/bins_static/math.stdout create mode 100644 tests/expected/bins_static/mkfifo.stderr create mode 100644 tests/expected/bins_static/mkfifo.stdout create mode 100644 tests/expected/bins_static/mknod.stderr create mode 100644 tests/expected/bins_static/mknod.stdout create mode 100644 tests/expected/bins_static/mknodat.stderr create mode 100644 tests/expected/bins_static/mknodat.stdout create mode 100644 tests/expected/bins_static/ptrace.stderr create mode 100644 tests/expected/bins_static/ptrace.stdout create mode 100644 tests/expected/bins_static/pty/forkpty.stderr create mode 100644 tests/expected/bins_static/pty/forkpty.stdout create mode 100644 tests/expected/bins_static/regex.stderr create mode 100644 tests/expected/bins_static/regex.stdout create mode 100644 tests/expected/bins_static/select.stderr create mode 100644 tests/expected/bins_static/select.stdout create mode 100644 tests/expected/bins_static/setjmp.stderr create mode 100644 tests/expected/bins_static/setjmp.stdout create mode 100644 tests/expected/bins_static/sigaction.stderr create mode 100644 tests/expected/bins_static/sigaction.stdout create mode 100644 tests/expected/bins_static/sigaltstack.stderr create mode 100644 tests/expected/bins_static/sigaltstack.stdout create mode 100644 tests/expected/bins_static/signal.stderr create mode 100644 tests/expected/bins_static/signal.stdout create mode 100644 tests/expected/bins_static/sigsetjmp.stderr create mode 100644 tests/expected/bins_static/sigsetjmp.stdout create mode 100644 tests/expected/bins_static/stdio/all.stderr create mode 100644 tests/expected/bins_static/stdio/all.stdout create mode 100644 tests/expected/bins_static/stdio/buffer.stderr create mode 100644 tests/expected/bins_static/stdio/buffer.stdout create mode 100644 tests/expected/bins_static/stdio/dprintf.stderr create mode 100644 tests/expected/bins_static/stdio/dprintf.stdout create mode 100644 tests/expected/bins_static/stdio/fgets.stderr create mode 100644 tests/expected/bins_static/stdio/fgets.stdout create mode 100644 tests/expected/bins_static/stdio/fputs.stderr create mode 100644 tests/expected/bins_static/stdio/fputs.stdout create mode 100644 tests/expected/bins_static/stdio/fread.stderr create mode 100644 tests/expected/bins_static/stdio/fread.stdout create mode 100644 tests/expected/bins_static/stdio/freopen.stderr create mode 100644 tests/expected/bins_static/stdio/freopen.stdout create mode 100644 tests/expected/bins_static/stdio/fscanf.stderr create mode 100644 tests/expected/bins_static/stdio/fscanf.stdout create mode 100644 tests/expected/bins_static/stdio/fscanf_offby1.stderr create mode 100644 tests/expected/bins_static/stdio/fscanf_offby1.stdout create mode 100644 tests/expected/bins_static/stdio/fseek.stderr create mode 100644 tests/expected/bins_static/stdio/fseek.stdout create mode 100644 tests/expected/bins_static/stdio/fwrite.stderr create mode 100644 tests/expected/bins_static/stdio/fwrite.stdout create mode 100644 tests/expected/bins_static/stdio/getc_unget.stderr create mode 100644 tests/expected/bins_static/stdio/getc_unget.stdout create mode 100644 tests/expected/bins_static/stdio/getline.stderr create mode 100644 tests/expected/bins_static/stdio/getline.stdout create mode 100644 tests/expected/bins_static/stdio/mutex.stderr create mode 100644 tests/expected/bins_static/stdio/mutex.stdout create mode 100644 tests/expected/bins_static/stdio/popen.stderr create mode 100644 tests/expected/bins_static/stdio/popen.stdout create mode 100644 tests/expected/bins_static/stdio/printf.stderr create mode 100644 tests/expected/bins_static/stdio/printf.stdout create mode 100644 tests/expected/bins_static/stdio/printf_neg_pad.stderr create mode 100644 tests/expected/bins_static/stdio/printf_neg_pad.stdout create mode 100644 tests/expected/bins_static/stdio/printf_space_pad.stderr create mode 100644 tests/expected/bins_static/stdio/printf_space_pad.stdout create mode 100644 tests/expected/bins_static/stdio/putc_unlocked.stderr create mode 100644 tests/expected/bins_static/stdio/putc_unlocked.stdout create mode 100644 tests/expected/bins_static/stdio/rename.stderr create mode 100644 tests/expected/bins_static/stdio/rename.stdout create mode 100644 tests/expected/bins_static/stdio/renameat.stderr create mode 100644 tests/expected/bins_static/stdio/renameat.stdout create mode 100644 tests/expected/bins_static/stdio/scanf.stderr create mode 100644 tests/expected/bins_static/stdio/scanf.stdout create mode 100644 tests/expected/bins_static/stdio/setvbuf.stderr create mode 100644 tests/expected/bins_static/stdio/setvbuf.stdout create mode 100644 tests/expected/bins_static/stdio/sprintf.stderr create mode 100644 tests/expected/bins_static/stdio/sprintf.stdout create mode 100644 tests/expected/bins_static/stdio/ungetc_ftell.stderr create mode 100644 tests/expected/bins_static/stdio/ungetc_ftell.stdout create mode 100644 tests/expected/bins_static/stdio/ungetc_multiple.stderr create mode 100644 tests/expected/bins_static/stdio/ungetc_multiple.stdout create mode 100644 tests/expected/bins_static/stdlib/a64l.stderr create mode 100644 tests/expected/bins_static/stdlib/a64l.stdout create mode 100644 tests/expected/bins_static/stdlib/alloc.stderr create mode 100644 tests/expected/bins_static/stdlib/alloc.stdout create mode 100644 tests/expected/bins_static/stdlib/atof.stderr create mode 100644 tests/expected/bins_static/stdlib/atof.stdout create mode 100644 tests/expected/bins_static/stdlib/atoi.stderr create mode 100644 tests/expected/bins_static/stdlib/atoi.stdout create mode 100644 tests/expected/bins_static/stdlib/div.stderr create mode 100644 tests/expected/bins_static/stdlib/div.stdout create mode 100644 tests/expected/bins_static/stdlib/env.stderr create mode 100644 tests/expected/bins_static/stdlib/env.stdout create mode 100644 tests/expected/bins_static/stdlib/getsubopt.stderr create mode 100644 tests/expected/bins_static/stdlib/getsubopt.stdout create mode 100644 tests/expected/bins_static/stdlib/mkostemps.stderr create mode 100644 tests/expected/bins_static/stdlib/mkostemps.stdout create mode 100644 tests/expected/bins_static/stdlib/ptsname.stderr create mode 100644 tests/expected/bins_static/stdlib/ptsname.stdout create mode 100644 tests/expected/bins_static/stdlib/qsort.stderr create mode 100644 tests/expected/bins_static/stdlib/qsort.stdout create mode 100644 tests/expected/bins_static/stdlib/rand.stderr create mode 100644 tests/expected/bins_static/stdlib/rand.stdout create mode 100644 tests/expected/bins_static/stdlib/rand48.stderr create mode 100644 tests/expected/bins_static/stdlib/rand48.stdout create mode 100644 tests/expected/bins_static/stdlib/random.stderr create mode 100644 tests/expected/bins_static/stdlib/random.stdout create mode 100644 tests/expected/bins_static/stdlib/strtod.stderr create mode 100644 tests/expected/bins_static/stdlib/strtod.stdout create mode 100644 tests/expected/bins_static/stdlib/strtol.stderr create mode 100644 tests/expected/bins_static/stdlib/strtol.stdout create mode 100644 tests/expected/bins_static/stdlib/strtoul.stderr create mode 100644 tests/expected/bins_static/stdlib/strtoul.stdout create mode 100644 tests/expected/bins_static/stdlib/system.stderr create mode 100644 tests/expected/bins_static/stdlib/system.stdout create mode 100644 tests/expected/bins_static/string/mem.stderr create mode 100644 tests/expected/bins_static/string/mem.stdout create mode 100644 tests/expected/bins_static/string/memcpy.stderr create mode 100644 tests/expected/bins_static/string/memcpy.stdout create mode 100644 tests/expected/bins_static/string/memmem.stderr create mode 100644 tests/expected/bins_static/string/memmem.stdout create mode 100644 tests/expected/bins_static/string/stpcpy.stderr create mode 100644 tests/expected/bins_static/string/stpcpy.stdout create mode 100644 tests/expected/bins_static/string/stpncpy.stderr create mode 100644 tests/expected/bins_static/string/stpncpy.stdout create mode 100644 tests/expected/bins_static/string/strcat.stderr create mode 100644 tests/expected/bins_static/string/strcat.stdout create mode 100644 tests/expected/bins_static/string/strchr.stderr create mode 100644 tests/expected/bins_static/string/strchr.stdout create mode 100644 tests/expected/bins_static/string/strchrnul.stderr create mode 100644 tests/expected/bins_static/string/strchrnul.stdout create mode 100644 tests/expected/bins_static/string/strcpy.stderr create mode 100644 tests/expected/bins_static/string/strcpy.stdout create mode 100644 tests/expected/bins_static/string/strcspn.stderr create mode 100644 tests/expected/bins_static/string/strcspn.stdout create mode 100644 tests/expected/bins_static/string/strlen.stderr create mode 100644 tests/expected/bins_static/string/strlen.stdout create mode 100644 tests/expected/bins_static/string/strncmp.stderr create mode 100644 tests/expected/bins_static/string/strncmp.stdout create mode 100644 tests/expected/bins_static/string/strpbrk.stderr create mode 100644 tests/expected/bins_static/string/strpbrk.stdout create mode 100644 tests/expected/bins_static/string/strrchr.stderr create mode 100644 tests/expected/bins_static/string/strrchr.stdout create mode 100644 tests/expected/bins_static/string/strsep.stderr create mode 100644 tests/expected/bins_static/string/strsep.stdout create mode 100644 tests/expected/bins_static/string/strsignal.stderr create mode 100644 tests/expected/bins_static/string/strsignal.stdout create mode 100644 tests/expected/bins_static/string/strspn.stderr create mode 100644 tests/expected/bins_static/string/strspn.stdout create mode 100644 tests/expected/bins_static/string/strstr.stderr create mode 100644 tests/expected/bins_static/string/strstr.stdout create mode 100644 tests/expected/bins_static/string/strtok.stderr create mode 100644 tests/expected/bins_static/string/strtok.stdout create mode 100644 tests/expected/bins_static/string/strtok_r.stderr create mode 100644 tests/expected/bins_static/string/strtok_r.stdout create mode 100644 tests/expected/bins_static/strings.stderr create mode 100644 tests/expected/bins_static/strings.stdout create mode 100644 tests/expected/bins_static/sys_epoll/epollet.stdout create mode 100644 tests/expected/bins_static/sys_mman.stderr create mode 100644 tests/expected/bins_static/sys_mman.stdout create mode 100644 tests/expected/bins_static/sys_socket/recv.stdout create mode 100644 tests/expected/bins_static/sys_socket/recvfrom.stdout create mode 100644 tests/expected/bins_static/sys_socket/unixpeername.stdout create mode 100644 tests/expected/bins_static/sys_socket/unixrecv.stdout create mode 100644 tests/expected/bins_static/sys_socket/unixrecvfrom.stdout create mode 100644 tests/expected/bins_static/sys_socket/unixsocketpair.stdout create mode 100644 tests/expected/bins_static/sys_stat/chmod.stderr create mode 100644 tests/expected/bins_static/sys_stat/chmod.stdout create mode 100644 tests/expected/bins_static/sys_stat/fstatat.stderr create mode 100644 tests/expected/bins_static/sys_stat/fstatat.stdout create mode 100644 tests/expected/bins_static/sys_stat/lstat.stderr create mode 100644 tests/expected/bins_static/sys_stat/lstat.stdout create mode 100644 tests/expected/bins_static/sys_syslog/syslog.stderr create mode 100644 tests/expected/bins_static/sys_syslog/syslog.stdout create mode 100644 tests/expected/bins_static/termios.stderr create mode 100644 tests/expected/bins_static/termios.stdout create mode 100644 tests/expected/bins_static/time/asctime.stderr create mode 100644 tests/expected/bins_static/time/asctime.stdout create mode 100644 tests/expected/bins_static/time/constants.stderr create mode 100644 tests/expected/bins_static/time/constants.stdout create mode 100644 tests/expected/bins_static/time/gmtime.stderr create mode 100644 tests/expected/bins_static/time/gmtime.stdout create mode 100644 tests/expected/bins_static/time/localtime.stderr create mode 100644 tests/expected/bins_static/time/localtime.stdout create mode 100644 tests/expected/bins_static/time/localtime_r.stderr create mode 100644 tests/expected/bins_static/time/localtime_r.stdout create mode 100644 tests/expected/bins_static/time/macros.stderr create mode 100644 tests/expected/bins_static/time/macros.stdout create mode 100644 tests/expected/bins_static/time/mktime.stderr create mode 100644 tests/expected/bins_static/time/mktime.stdout create mode 100644 tests/expected/bins_static/time/strftime.stderr create mode 100644 tests/expected/bins_static/time/strftime.stdout create mode 100644 tests/expected/bins_static/time/strptime.stderr create mode 100644 tests/expected/bins_static/time/strptime.stdout create mode 100644 tests/expected/bins_static/time/time.stderr create mode 100644 tests/expected/bins_static/time/time.stdout create mode 100644 tests/expected/bins_static/time/timegm.stderr create mode 100644 tests/expected/bins_static/time/timegm.stdout create mode 100644 tests/expected/bins_static/time/tzset.stderr create mode 100644 tests/expected/bins_static/time/tzset.stdout create mode 100644 tests/expected/bins_static/tls.stderr create mode 100644 tests/expected/bins_static/tls.stdout create mode 100644 tests/expected/bins_static/unistd/access.stderr create mode 100644 tests/expected/bins_static/unistd/access.stdout create mode 100644 tests/expected/bins_static/unistd/alarm.stderr create mode 100644 tests/expected/bins_static/unistd/alarm.stdout create mode 100644 tests/expected/bins_static/unistd/brk.stderr create mode 100644 tests/expected/bins_static/unistd/brk.stdout create mode 100644 tests/expected/bins_static/unistd/confstr.stderr create mode 100644 tests/expected/bins_static/unistd/confstr.stdout create mode 100644 tests/expected/bins_static/unistd/constants.stderr create mode 100644 tests/expected/bins_static/unistd/constants.stdout create mode 100644 tests/expected/bins_static/unistd/dup.stderr create mode 100644 tests/expected/bins_static/unistd/dup.stdout create mode 100644 tests/expected/bins_static/unistd/exec.stderr create mode 100644 tests/expected/bins_static/unistd/exec.stdout create mode 100644 tests/expected/bins_static/unistd/fchdir.stderr create mode 100644 tests/expected/bins_static/unistd/fchdir.stdout create mode 100644 tests/expected/bins_static/unistd/fork.stderr create mode 100644 tests/expected/bins_static/unistd/fork.stdout create mode 100644 tests/expected/bins_static/unistd/fsync.stderr create mode 100644 tests/expected/bins_static/unistd/fsync.stdout create mode 100644 tests/expected/bins_static/unistd/ftruncate.stderr create mode 100644 tests/expected/bins_static/unistd/ftruncate.stdout create mode 100644 tests/expected/bins_static/unistd/getopt.stderr create mode 100644 tests/expected/bins_static/unistd/getopt.stdout create mode 100644 tests/expected/bins_static/unistd/getopt_long.stderr create mode 100644 tests/expected/bins_static/unistd/getopt_long.stdout create mode 100644 tests/expected/bins_static/unistd/pipe.stderr create mode 100644 tests/expected/bins_static/unistd/pipe.stdout create mode 100644 tests/expected/bins_static/unistd/readlinkat.stderr create mode 100644 tests/expected/bins_static/unistd/readlinkat.stdout create mode 100644 tests/expected/bins_static/unistd/rmdir.stderr create mode 100644 tests/expected/bins_static/unistd/rmdir.stdout create mode 100644 tests/expected/bins_static/unistd/sleep.stderr create mode 100644 tests/expected/bins_static/unistd/sleep.stdout create mode 100644 tests/expected/bins_static/unistd/swab.stderr create mode 100644 tests/expected/bins_static/unistd/swab.stdout create mode 100644 tests/expected/bins_static/unistd/write.stderr create mode 100644 tests/expected/bins_static/unistd/write.stdout create mode 100644 tests/expected/bins_static/wchar/fgetwc.stderr create mode 100644 tests/expected/bins_static/wchar/fgetwc.stdout create mode 100644 tests/expected/bins_static/wchar/fwide.stderr create mode 100644 tests/expected/bins_static/wchar/fwide.stdout create mode 100644 tests/expected/bins_static/wchar/mbrtowc.stderr create mode 100644 tests/expected/bins_static/wchar/mbrtowc.stdout create mode 100644 tests/expected/bins_static/wchar/mbsrtowcs.stderr create mode 100644 tests/expected/bins_static/wchar/mbsrtowcs.stdout create mode 100644 tests/expected/bins_static/wchar/printf-on-wchars.stderr create mode 100644 tests/expected/bins_static/wchar/printf-on-wchars.stdout create mode 100644 tests/expected/bins_static/wchar/putwchar.stderr create mode 100644 tests/expected/bins_static/wchar/putwchar.stdout create mode 100644 tests/expected/bins_static/wchar/ungetwc.stderr create mode 100644 tests/expected/bins_static/wchar/ungetwc.stdout create mode 100644 tests/expected/bins_static/wchar/wcpcpy.stderr create mode 100644 tests/expected/bins_static/wchar/wcpcpy.stdout create mode 100644 tests/expected/bins_static/wchar/wcpncpy.stderr create mode 100644 tests/expected/bins_static/wchar/wcpncpy.stdout create mode 100644 tests/expected/bins_static/wchar/wcrtomb.stderr create mode 100644 tests/expected/bins_static/wchar/wcrtomb.stdout create mode 100644 tests/expected/bins_static/wchar/wcscasecmp.stderr create mode 100644 tests/expected/bins_static/wchar/wcscasecmp.stdout create mode 100644 tests/expected/bins_static/wchar/wcschr.stderr create mode 100644 tests/expected/bins_static/wchar/wcschr.stdout create mode 100644 tests/expected/bins_static/wchar/wcscspn.stderr create mode 100644 tests/expected/bins_static/wchar/wcscspn.stdout create mode 100644 tests/expected/bins_static/wchar/wcsdup.stderr create mode 100644 tests/expected/bins_static/wchar/wcsdup.stdout create mode 100644 tests/expected/bins_static/wchar/wcsncasecmp.stderr create mode 100644 tests/expected/bins_static/wchar/wcsncasecmp.stdout create mode 100644 tests/expected/bins_static/wchar/wcsnlen.stderr create mode 100644 tests/expected/bins_static/wchar/wcsnlen.stdout create mode 100644 tests/expected/bins_static/wchar/wcsnrtombs.stderr create mode 100644 tests/expected/bins_static/wchar/wcsnrtombs.stdout create mode 100644 tests/expected/bins_static/wchar/wcsrchr.stderr create mode 100644 tests/expected/bins_static/wchar/wcsrchr.stdout create mode 100644 tests/expected/bins_static/wchar/wcsrtombs.stderr create mode 100644 tests/expected/bins_static/wchar/wcsrtombs.stdout create mode 100644 tests/expected/bins_static/wchar/wcsstr.stderr create mode 100644 tests/expected/bins_static/wchar/wcsstr.stdout create mode 100644 tests/expected/bins_static/wchar/wcstod.stderr create mode 100644 tests/expected/bins_static/wchar/wcstod.stdout create mode 100644 tests/expected/bins_static/wchar/wcstoimax.stderr create mode 100644 tests/expected/bins_static/wchar/wcstoimax.stdout create mode 100644 tests/expected/bins_static/wchar/wcstok.stderr create mode 100644 tests/expected/bins_static/wchar/wcstok.stdout create mode 100644 tests/expected/bins_static/wchar/wcstol.stderr create mode 100644 tests/expected/bins_static/wchar/wcstol.stdout create mode 100644 tests/expected/bins_static/wchar/wcstoumax.stderr create mode 100644 tests/expected/bins_static/wchar/wcstoumax.stdout create mode 100644 tests/expected/bins_static/wchar/wcswidth.stderr create mode 100644 tests/expected/bins_static/wchar/wcswidth.stdout create mode 100644 tests/expected/bins_static/wchar/wprintf.stderr create mode 100644 tests/expected/bins_static/wchar/wprintf.stdout create mode 100644 tests/expected/bins_static/wchar/wscanf.stderr create mode 100644 tests/expected/bins_static/wchar/wscanf.stdout create mode 100644 tests/expected/bins_static/wctype/towlower.stderr create mode 100644 tests/expected/bins_static/wctype/towlower.stdout create mode 100644 tests/expected/bins_static/wctype/towupper.stderr create mode 100644 tests/expected/bins_static/wctype/towupper.stdout create mode 100644 tests/expected/endian.stderr create mode 100644 tests/expected/endian.stdout create mode 100644 tests/expected/features.stderr create mode 100644 tests/expected/features.stdout create mode 100644 tests/expected/glob.stderr create mode 100644 tests/expected/glob.stdout create mode 100644 tests/fcntl/create.c create mode 100644 tests/fcntl/fcntl.c create mode 100644 tests/fcntl/open.c create mode 100644 tests/fcntl/posix_fallocate.c create mode 100644 tests/features.c create mode 100644 tests/fnmatch.c create mode 100644 tests/futimens.c create mode 100644 tests/glob.c create mode 100644 tests/grp/getgrgid_r.c create mode 100644 tests/grp/getgrnam_r.c create mode 100644 tests/grp/getgrouplist.c create mode 100644 tests/grp/getgroups.c create mode 100644 tests/grp/gr_iter.c create mode 100644 tests/includes.c create mode 100644 tests/iso646.c create mode 100644 tests/kill-waitpid.c create mode 100644 tests/libfoo.c create mode 100644 tests/libfoobar.c create mode 100644 tests/libgen.c create mode 100644 tests/limits.c create mode 100644 tests/locale/duplocale.c create mode 100644 tests/locale/newlocale.c create mode 100644 tests/locale/setlocale.c create mode 100644 tests/malloc/usable_size.c create mode 100644 tests/math.c create mode 100644 tests/mkfifo.c create mode 100644 tests/mknod.c create mode 100644 tests/mknodat.c create mode 100644 tests/net/if.c create mode 100644 tests/netdb/getaddrinfo.c create mode 100644 tests/netdb/getaddrinfo_null.c create mode 100644 tests/netdb/netdb.c create mode 100644 tests/psignal.c create mode 100644 tests/pthread/barrier.c create mode 100644 tests/pthread/cleanup.c create mode 100644 tests/pthread/common.h create mode 100644 tests/pthread/customstack.c create mode 100644 tests/pthread/exit.c create mode 100644 tests/pthread/extjoin.c create mode 100644 tests/pthread/main.c create mode 100644 tests/pthread/mutex_recursive.c create mode 100644 tests/pthread/once.c create mode 100644 tests/pthread/rwlock_randtest.c create mode 100644 tests/pthread/rwlock_trylock.c create mode 100644 tests/pthread/thread_fork.c create mode 100644 tests/pthread/timedwait.c create mode 100644 tests/pthread/timeout.c create mode 100644 tests/pthread/tls.c create mode 100644 tests/ptrace.c create mode 100644 tests/pty/forkpty.c create mode 100644 tests/pwd.c create mode 100644 tests/regex.c create mode 100644 tests/sa_restart.c create mode 100644 tests/select.c create mode 100644 tests/setjmp.c create mode 100644 tests/sharedlib.c create mode 100644 tests/sigaction.c create mode 100644 tests/sigaltstack.c create mode 100644 tests/sigchld.c create mode 100644 tests/signal.c create mode 100644 tests/signals/kill-child.c create mode 100644 tests/signals/kill-group.c create mode 100644 tests/signals/kill-invalid.c create mode 100644 tests/signals/kill-permission.c create mode 100644 tests/signals/kill-self.c create mode 100644 tests/signals/kill0-self.c create mode 100644 tests/signals/killpg-child.c create mode 100644 tests/signals/killpg-esrch.c create mode 100644 tests/signals/killpg-invalid.c create mode 100644 tests/signals/killpg-self.c create mode 100644 tests/signals/killpg0-self.c create mode 100644 tests/signals/pthread_kill-child.c create mode 100644 tests/signals/pthread_kill-invalid.c create mode 100644 tests/signals/pthread_kill-self.c create mode 100644 tests/signals/pthread_kill0-self.c create mode 100644 tests/signals/raise-compliance.c create mode 100644 tests/signals/sigaddset-add.c create mode 100644 tests/signals/sigdelset-delete.c create mode 100644 tests/signals/sigismember-invalid.c create mode 100644 tests/signals/sigismember-valid.c create mode 100644 tests/signals/signal-h-2.c create mode 100644 tests/signals/signal-h.c create mode 100644 tests/signals/signal-handle_return.c create mode 100644 tests/signals/signal-handler.c create mode 100644 tests/signals/signal-handler2.c create mode 100644 tests/signals/signal-ignore.c create mode 100644 tests/signals/signal-invalid.c create mode 100644 tests/signals/signal-uncatchable.c create mode 100644 tests/signals/signals_list.h create mode 100644 tests/signals/sigpause-error.c create mode 100644 tests/signals/sigpause-invalid.c create mode 100644 tests/signals/sigpause-pause.c create mode 100644 tests/signals/sigpause-revert.c create mode 100644 tests/signals/sigpause-suspend.c create mode 100644 tests/signals/sigprocmask-10.c create mode 100644 tests/signals/sigprocmask-11.c create mode 100644 tests/signals/sigprocmask-3.c create mode 100644 tests/signals/sigprocmask-4.c create mode 100644 tests/signals/sigprocmask-5.c create mode 100644 tests/signals/sigprocmask-6.c create mode 100644 tests/signals/sigprocmask-7.c create mode 100644 tests/signals/sigprocmask-8.c create mode 100644 tests/signals/sigprocmask-9.c create mode 100644 tests/signals/sigprocmask-blocksingle.c create mode 100644 tests/signals/sigrelse-1.c create mode 100644 tests/signals/sigrelse-2.c create mode 100644 tests/signals/sigrelse-3.c create mode 100644 tests/signals/sigset-1.c create mode 100644 tests/signals/sigset-10.c create mode 100644 tests/signals/sigset-2.c create mode 100644 tests/signals/sigset-3.c create mode 100644 tests/signals/sigset-4.c create mode 100644 tests/signals/sigset-5.c create mode 100644 tests/signals/sigset-9.c create mode 100644 tests/sigqueue.c create mode 100644 tests/sigsetjmp.c create mode 100644 tests/src/main.rs create mode 100644 tests/stdio/all.c create mode 100644 tests/stdio/buffer.c create mode 100644 tests/stdio/ctermid.c create mode 100644 tests/stdio/dprintf.c create mode 100644 tests/stdio/fgets.c create mode 100644 tests/stdio/fputs.c create mode 100644 tests/stdio/fputs.out create mode 100644 tests/stdio/fread.c create mode 100644 tests/stdio/fread.in create mode 100644 tests/stdio/freopen.c create mode 100644 tests/stdio/fscanf.c create mode 100644 tests/stdio/fscanf_offby1.c create mode 100644 tests/stdio/fseek.c create mode 100644 tests/stdio/fwrite.c create mode 100644 tests/stdio/fwrite.out create mode 100644 tests/stdio/getc_unget.c create mode 100644 tests/stdio/getline.c create mode 100644 tests/stdio/getline.in create mode 100644 tests/stdio/mutex.c create mode 100644 tests/stdio/popen.c create mode 100644 tests/stdio/printf.c create mode 100644 tests/stdio/printf_neg_pad.c create mode 100644 tests/stdio/printf_space_pad.c create mode 100644 tests/stdio/putc_unlocked.c create mode 100644 tests/stdio/rename.c create mode 100644 tests/stdio/renameat.c create mode 100644 tests/stdio/scanf.c create mode 100644 tests/stdio/setvbuf.c create mode 100644 tests/stdio/sprintf.c create mode 100644 tests/stdio/stdio.in create mode 100644 tests/stdio/tempnam.c create mode 100644 tests/stdio/tmpnam.c create mode 100644 tests/stdio/ungetc_ftell.c create mode 100644 tests/stdio/ungetc_multiple.c create mode 100644 tests/stdlib/a64l.c create mode 100644 tests/stdlib/alloc.c create mode 100644 tests/stdlib/atof.c create mode 100644 tests/stdlib/atoi.c create mode 100644 tests/stdlib/bsearch.c create mode 100644 tests/stdlib/div.c create mode 100644 tests/stdlib/env.c create mode 100644 tests/stdlib/getsubopt.c create mode 100644 tests/stdlib/mkostemps.c create mode 100644 tests/stdlib/mktemp.c create mode 100644 tests/stdlib/ptsname.c create mode 100644 tests/stdlib/qsort.c create mode 100644 tests/stdlib/rand.c create mode 100644 tests/stdlib/rand48.c create mode 100644 tests/stdlib/random.c create mode 100644 tests/stdlib/realpath.c create mode 100644 tests/stdlib/strtod.c create mode 100644 tests/stdlib/strtol.c create mode 100644 tests/stdlib/strtoul.c create mode 100644 tests/stdlib/system.c create mode 100644 tests/string/mem.c create mode 100644 tests/string/memcpy.c create mode 100644 tests/string/memmem.c create mode 100644 tests/string/stpcpy.c create mode 100644 tests/string/stpncpy.c create mode 100644 tests/string/strcat.c create mode 100644 tests/string/strchr.c create mode 100644 tests/string/strchrnul.c create mode 100644 tests/string/strcpy.c create mode 100644 tests/string/strcspn.c create mode 100644 tests/string/strlen.c create mode 100644 tests/string/strncmp.c create mode 100644 tests/string/strpbrk.c create mode 100644 tests/string/strrchr.c create mode 100644 tests/string/strsep.c create mode 100644 tests/string/strsignal.c create mode 100644 tests/string/strspn.c create mode 100644 tests/string/strstr.c create mode 100644 tests/string/strtok.c create mode 100644 tests/string/strtok_r.c create mode 100644 tests/strings.c create mode 100644 tests/sys_epoll/epoll.c create mode 100644 tests/sys_epoll/epollet.c create mode 100644 tests/sys_mman/fmap.c create mode 100644 tests/sys_mman/mmap.c create mode 100644 tests/sys_resource/constants.c create mode 100644 tests/sys_resource/getrusage.c create mode 100644 tests/sys_resource/rlimit_roundtrip.c create mode 100644 tests/sys_socket/getpeername.c create mode 100644 tests/sys_socket/recv.c create mode 100644 tests/sys_socket/recvfrom.c create mode 100644 tests/sys_socket/shutdown.c create mode 100644 tests/sys_socket/unixpeername.c create mode 100644 tests/sys_socket/unixrecv.c create mode 100644 tests/sys_socket/unixrecvearly.c create mode 100644 tests/sys_socket/unixrecvfrom.c create mode 100644 tests/sys_socket/unixsocketpair.c create mode 100644 tests/sys_socket/unixwrite.c create mode 100644 tests/sys_stat/chmod.c create mode 100644 tests/sys_stat/fstatat.c create mode 100644 tests/sys_stat/lstat.c create mode 100644 tests/sys_stat/stat.c create mode 100644 tests/sys_statvfs/statvfs.c create mode 100644 tests/sys_syslog/syslog.c create mode 100644 tests/sys_utsname/uname.c create mode 100644 tests/termios.c create mode 100644 tests/test_helpers.h create mode 100644 tests/time/asctime.c create mode 100644 tests/time/constants.c create mode 100644 tests/time/gettimeofday.c create mode 100644 tests/time/gmtime.c create mode 100644 tests/time/localtime.c create mode 100644 tests/time/localtime_r.c create mode 100644 tests/time/macros.c create mode 100644 tests/time/mktime.c create mode 100644 tests/time/strftime.c create mode 100644 tests/time/strptime.c create mode 100644 tests/time/time.c create mode 100644 tests/time/timegm.c create mode 100644 tests/time/times.c create mode 100644 tests/time/tzset.c create mode 100644 tests/tls.c create mode 100644 tests/unistd/access.c create mode 100644 tests/unistd/alarm.c create mode 100644 tests/unistd/brk.c create mode 100644 tests/unistd/chdir.c create mode 100644 tests/unistd/confstr.c create mode 100644 tests/unistd/constants.c create mode 100644 tests/unistd/dup.c create mode 100644 tests/unistd/exec.c create mode 100644 tests/unistd/fchdir.c create mode 100644 tests/unistd/fork.c create mode 100644 tests/unistd/fsync.c create mode 100644 tests/unistd/ftruncate.c create mode 100644 tests/unistd/getcwd.c create mode 100644 tests/unistd/gethostname.c create mode 100644 tests/unistd/getid.c create mode 100644 tests/unistd/getopt.c create mode 100644 tests/unistd/getopt_long.c create mode 100644 tests/unistd/getpagesize.c create mode 100644 tests/unistd/getpass.c create mode 100644 tests/unistd/getpass.exp create mode 100644 tests/unistd/isatty.c create mode 100644 tests/unistd/link.c create mode 100644 tests/unistd/pathconf.c create mode 100644 tests/unistd/pipe.c create mode 100644 tests/unistd/readlinkat.c create mode 100644 tests/unistd/rmdir.c create mode 100644 tests/unistd/setid.c create mode 100644 tests/unistd/sleep.c create mode 100644 tests/unistd/swab.c create mode 100644 tests/unistd/sysconf.c create mode 100644 tests/unistd/write.c create mode 100644 tests/waitid.c create mode 100644 tests/waitpid.c create mode 100644 tests/waitpid_multiple.c create mode 100644 tests/wchar/fgetwc.c create mode 100644 tests/wchar/fgetwc.in create mode 100644 tests/wchar/fwide.c create mode 100644 tests/wchar/mbrtowc.c create mode 100644 tests/wchar/mbsrtowcs.c create mode 100644 tests/wchar/printf-on-wchars.c create mode 100644 tests/wchar/putwchar.c create mode 100644 tests/wchar/ungetwc.c create mode 100644 tests/wchar/ungetwc.in create mode 100644 tests/wchar/wcpcpy.c create mode 100644 tests/wchar/wcpncpy.c create mode 100644 tests/wchar/wcrtomb.c create mode 100644 tests/wchar/wcscasecmp.c create mode 100644 tests/wchar/wcschr.c create mode 100644 tests/wchar/wcscspn.c create mode 100644 tests/wchar/wcsdup.c create mode 100644 tests/wchar/wcsncasecmp.c create mode 100644 tests/wchar/wcsnlen.c create mode 100644 tests/wchar/wcsnrtombs.c create mode 100644 tests/wchar/wcsrchr.c create mode 100644 tests/wchar/wcsrtombs.c create mode 100644 tests/wchar/wcsstr.c create mode 100644 tests/wchar/wcstod.c create mode 100644 tests/wchar/wcstoimax.c create mode 100644 tests/wchar/wcstok.c create mode 100644 tests/wchar/wcstol.c create mode 100644 tests/wchar/wcstoumax.c create mode 100644 tests/wchar/wcswidth.c create mode 100644 tests/wchar/wprintf.c create mode 100644 tests/wchar/wscanf.c create mode 100644 tests/wctype/towlower.c create mode 100644 tests/wctype/towupper.c diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..91e9b5061d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +[**.c] +indent_size = 4 +indent_style = space + +[**.yml] +indent_size = 4 +indent_style = space diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..e7dcd0d68d --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.idea/ +prefix/ +sysroot/ +**/target/ +.gdb_history +*.patch +*.swp +*.swo +/.vim +.vscode/ + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..61e301ae68 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,73 @@ +image: "redoxos/redoxer:latest" + +variables: + GIT_SUBMODULE_STRATEGY: recursive + +workflow: + rules: + - if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"' + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' + +stages: + - build + - cross-build + - test +before_script: + cargo install cbindgen + +fmt: + stage: build + needs: [] + script: + - rustup component add rustfmt-preview + - ./fmt.sh -- --check + +linux: + stage: build + script: + - ./check.sh --host + +x86_64: + stage: build + script: + - ./check.sh --arch=x86_64 + +i586: + stage: cross-build + script: + - ./check.sh --arch=i586 + +aarch64: + stage: cross-build + image: "redoxos/redoxer:aarch64" + script: + - ./check.sh --arch=aarch64 + +riscv64gc: + stage: cross-build + script: + - ./check.sh --arch=riscv64gc + +test:linux: + stage: test + needs: [linux] + script: + - ./check.sh --host --test + +test:x86_64: + stage: test + needs: [x86_64] + script: + # timeout: https://gitlab.redox-os.org/redox-os/relibc/-/issues/238 + - timeout -s KILL 9m ./check.sh --arch=x86_64 --test + +test:aarch64: + stage: test + needs: [aarch64] + image: "redoxos/redoxer:aarch64" + # many issues that not exist in x86_64, and lack of interest to fix so far + allow_failure: true + script: + - timeout -s KILL 9m ./check.sh --arch=aarch64 --test + +#TODO: Enable more arch once dynamic linker working diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..50a75a071b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "openlibm"] + path = openlibm + url = https://gitlab.redox-os.org/redox-os/openlibm.git + branch = master +[submodule "src/dlmalloc-rs"] + path = dlmalloc-rs + url = https://gitlab.redox-os.org/redox-os/dlmalloc-rs.git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..e1d1a28dca --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,123 @@ +# Contributing + +## Table of contents +1. [What to do](#what-to-do) +2. [Code style](#code-style) +3. [Sending merge requests](#sending-merge-requests) +4. [Writing tests](#writing-tests) +5. [Running tests](#running-tests) + +Maintaining a libc is tough work, and we'd love some help! + +## What to do + +For now, we are still trying to get full libc compatibility before we move on to +any optimisation. + +- We currently have a number of unimplemented functions. Search for + `unimplemented!()` and hop right in! +- If you notice any missing functionality, feel free to add it in + +## Code style + +We have a `rustfmt.toml` in the root directory of relibc. Please run `./fmt.sh` +before sending in any merge requests as it will automatically format your code. + +With regards to general style: + +### Where applicable, prefer using references to raw pointers + +This is most obvious when looking at `stdio` functions. If raw pointers were +used instead of references, then the resulting code would be significantly +uglier. Instead try to check for pointer being valid with `pointer::as_ref()` +and `pointer::as_mut()` and then immediately use those references instead. + +Internal functions should always take references. + +### Use the c types exposed in our platform module instead of Rust's inbuilt integer types + +This is so we can guarantee that everything works across platforms. While it is +generally accepted these days that an `int` has 32 bits (which matches against +an `i32`), some platforms have `int` as having 16 bits, and others have long as +being 32 bits instead of 64. If you use the types in platform, then we can +guarantee that your code will "just work" should we port relibc to a different +architecture. + +### Use our internal functions + +If you need to use a C string, don't reinvent the wheel. We have functions in +the platform module that convert C strings to Rust slices. + +We also have structures that wrap files, wrap writable strings, and wrap various +other commonly used things that you should use instead of rolling your own. + +## Sending merge requests + +If you have sent us a merge request, first of all, thanks for taking your time +to help us! + +The first thing to note is that we do most of our development on our +[GitLab server](https://gitlab.redox-os.org/redox-os/relibc), and as such it is +possible that none of the maintainers will see your merge request if it is +opened on GitHub. + +In your merge request, please put in the description: +- What functions (if any) have been implemented or changed +- The rationale behind your merge request (e.g. why you thought this change was + required. If you are just implementing some functions, you can ignore this) +- Any issues that are related to the merge request + +We have CI attached to our GitLab instance, so all merge requests are checked to +make sure that they are tested before they are merged. Please write tests for +the functions that you add/change and test locally on your own machine +***before*** submitting a merge request. + +## Writing tests + +Every function that gets written needs to have a test in C in order to make sure +it works as intended. Here are a few guidelines for writing good tests. + +### Ensure that any literals you have are mapped to variables instead of being directly passed to a function. + +Sometimes compilers take literals put into libc functions and run them +internally during compilation, which can cause some false positives. All tests +are compiled with `-fno-builtin`, which theoretically solves this issue, but +just in case, it'd be a good idea to map inputs to variables. + +```c +#include "string.h" +#include "stdio.h" + +int main(void) { + // Don't do this + printf("%d\n", strcspn("Hello", "Hi")); + + // Do this + char *first = "Hello"; + char *second = "Hi"; + printf("%d\n", strcspn(first, second)); +} +``` + +### Ensure your tests cover every section of code. + +What happens if a string in `strcmp()` is shorter than the other string? What +happens if the first argument to `strcspn()` is longer than the second string? +In order to make sure that all functions work as expected, we ask that any tests +cover as much of the code that you have written as possible. + +## Running tests + +Running tests is an important part in trying to find bugs. Before opening a +merge request, we ask that you test on your own machine to make sure there are +no regressions. + +You can run tests with `make test` in the root directory of relibc to compile +relibc, compile the tests and run them. This *will* print a lot of output to +stdout, so be warned! + +You can test against verified correct output with `make verify` in the tests +directory. You will need to manually create the correct output and put it in the +tests/expected directory. Running any `make` commands in the tests directory +will ***not*** rebuild relibc, so you'll need to go back to the root directory +if you need to rebuild relibc. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..0ac6eeabe5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,752 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" +dependencies = [ + "blowfish", + "pbkdf2", + "sha2", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "cc" +version = "1.1.22" +source = "git+https://github.com/tea/cc-rs?branch=riscv-abi-arch-fix#588ceacb084af41415690c57688e338a32a1f1b4" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "num-traits", +] + +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crt0" +version = "0.1.0" + +[[package]] +name = "crti" +version = "0.1.0" + +[[package]] +name = "crtn" +version = "0.1.0" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dlmalloc" +version = "0.2.8" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "drm-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" +dependencies = [ + "libc", + "linux-raw-sys", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "generic-rt" +version = "0.1.0" + +[[package]] +name = "goblin" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983a6aafb3b12d4c41ea78d39e189af4298ce747353945ff5105b54a056e5cd9" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ioslice" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e571352c8a3b89074d12e3ee5173ffe162159105352aaaf1fc5764da747e31b" + +[[package]] +name = "ld_so" +version = "0.1.0" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "bitflags", + "libc", + "plain", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "git+https://gitlab.redox-os.org/andypython/object#7270e3f0d06e5ef4c2b80abc6166d31f4ddf4fea" +dependencies = [ + "memchr", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", + "sha2", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "posix-regex" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df580334caab2f744839ab1be85493d7ec731a92d6cf928008ab0b212bf3bc" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "rand_core 0.10.0", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "rand_jitter" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a02dd27aa28665e46e60168c8f355240c73b8a344d2557a92318849441ffda33" +dependencies = [ + "libc", + "rand_core 0.10.0", + "winapi", +] + +[[package]] +name = "rand_xorshift" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60aa6af80be32871323012e02e6e65f8a7cc7890931ae421d217ad8fe0df2ccf" +dependencies = [ + "rand_core 0.10.0", +] + +[[package]] +name = "redox-ioctl" +version = "0.1.0" +dependencies = [ + "drm-sys", + "redox_syscall", +] + +[[package]] +name = "redox-path" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" + +[[package]] +name = "redox-rt" +version = "0.1.0" +dependencies = [ + "bitflags", + "generic-rt", + "goblin", + "ioslice", + "libredox", + "plain", + "redox-path", + "redox_syscall", +] + +[[package]] +name = "redox_event" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea3e412d205440c7b0218af26247226f979ed1201674cda7a33cc70609084b5" +dependencies = [ + "bitflags", + "libredox", + "redox_syscall", +] + +[[package]] +name = "redox_syscall" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "relibc" +version = "0.2.5" +dependencies = [ + "argon2", + "base64ct", + "bcrypt-pbkdf", + "bitflags", + "cbitset", + "cc", + "chrono", + "chrono-tz", + "dlmalloc", + "generic-rt", + "ioslice", + "libc", + "libm", + "libredox", + "log", + "md-5", + "memchr", + "object", + "pbkdf2", + "plain", + "posix-regex", + "rand", + "rand_jitter", + "rand_xorshift", + "redox-ioctl", + "redox-path", + "redox-rt", + "redox_event", + "redox_syscall", + "sc", + "scrypt", + "sha-crypt", + "sha2", + "spin", + "unicode-width", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "sc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed76efe62313ab6610570951494bdaa81568026e0318eaa55f167de70eeea67d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "sha-crypt" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e79009728d8311d42d754f2f319a975f9e38f156fd5e422d2451486c78b286" +dependencies = [ + "base64ct", + "sha2", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..92bd94de32 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,148 @@ +[package] +name = "relibc" +version = "0.2.5" +authors = ["Jeremy Soller "] +edition = "2024" + +[lib] +name = "relibc" +crate-type = ["staticlib"] + +[workspace] +members = [ + "src/crt0", + "src/crti", + "src/crtn", + "redox-rt", + "ld_so", + "generic-rt", +] +exclude = ["tests", "dlmalloc-rs"] + +[workspace.lints.clippy] +borrow_as_ptr = "deny" +cast_lossless = "warn" # TODO review occurrences +cast_possible_truncation = "allow" # TODO review occurrences +cast_possible_wrap = "allow" # TODO review occurrences +cast_precision_loss = "allow" # TODO review occurrences +cast_ptr_alignment = "allow" # TODO review occurrences +cast_sign_loss = "allow" # TODO review occurrences +missing_errors_doc = "allow" # TODO review occurrences +missing_panics_doc = "allow" # TODO review occurrences +missing_safety_doc = "allow" # TODO review occurrences +mut_from_ref = "warn" # TODO review occurrences +precedence = "deny" +ptr_as_ptr = "warn" # TODO review occurrences +ptr_cast_constness = "warn" # TODO review occurrences +ref_as_ptr = "warn" # TODO review occurrences +upper_case_acronyms = "allow" # TODO review occurrences +zero_ptr = "warn" # must allow on public constants due to cbindgen issue + +[workspace.lints.rust] +dangling_pointers_from_temporaries = "deny" +dead_code = "allow" # TODO review occuurences +deprecated = "deny" +improper_ctypes_definitions = "deny" +internal_features = "allow" # core_intrinsics and lang_items +irrefutable_let_patterns = "deny" +mismatched_lifetime_syntaxes = "deny" +non_camel_case_types = "allow" # needed for most POSIX type names +non_snake_case = "allow" # TODO review occuurences +non_upper_case_globals = "allow" # TODO review occuurences +unexpected_cfgs = "deny" +unpredictable_function_pointer_comparisons = "deny" +unreachable_code = "allow" # TODO review occuurences +unsafe_op_in_unsafe_fn = "deny" +unused_imports = "deny" +unused_must_use = "deny" +unused_mut = "deny" +unused_unsafe = "deny" +unused_variables = "allow" # TODO review occurrences (too many for now) + +[lints] +workspace = true + +[workspace.dependencies] +bitflags = "2" +ioslice = { version = "0.6", default-features = false } +plain = "0.2" +redox-path = "0.3" +redox_protocols = { package = "libredox", version = "0.1.16", default-features = false, features = ["protocol"] } +redox_syscall = "0.7.4" + +[build-dependencies] +cc = "1" + +[dependencies] +bitflags.workspace = true +cbitset = "0.2" +posix-regex = { version = "0.1.4", features = ["no_std"] } + +rand = { version = "0.10", default-features = false } +rand_xorshift = "0.5" +rand_jitter = "0.6" + +memchr = { version = "2.2.0", default-features = false } +plain.workspace = true +unicode-width = "0.1" +__libc_only_for_layout_checks = { package = "libc", version = "0.2.149", optional = true } +md5-crypto = { package = "md-5", version = "0.10.6", default-features = false } +sha-crypt = { version = "0.5", default-features = false } +base64ct = { version = "1.6", default-features = false, features = ["alloc"] } +bcrypt-pbkdf = { version = "0.10", default-features = false, features = [ + "alloc", +] } +scrypt = { version = "0.11", default-features = false, features = ["simple"] } +pbkdf2 = { version = "0.12", features = ["sha2"] } +sha2 = { version = "0.10", default-features = false } +generic-rt = { path = "generic-rt" } +chrono-tz = { version = "0.10", default-features = false } +chrono = { version = "0.4", default-features = false, features = ["alloc"] } +libm = "0.2" +log = "0.4" +spin = "0.9.8" +argon2 = "0.5.3" + +[dependencies.dlmalloc] +path = "dlmalloc-rs" +default-features = false +features = ["c_api"] + +[dependencies.object] +version = "0.36.7" +git = "https://gitlab.redox-os.org/andypython/object" +default-features = false +features = ["elf", "read_core"] + +[target.'cfg(target_os = "linux")'.dependencies] +sc = "0.2.7" + +[target.'cfg(target_os = "redox")'.dependencies] +redox_syscall.workspace = true +redox-rt = { path = "redox-rt" } +redox-path.workspace = true +redox_event = { version = "0.4.6", default-features = false, features = [ + "redox_syscall", +] } +ioslice.workspace = true +redox-ioctl = { path = "redox-ioctl" } +redox_protocols.workspace = true + +[features] +# to enable trace level, take out this `no_trace` +default = ["check_against_libc_crate", "ld_so_cache", "no_trace"] +check_against_libc_crate = ["__libc_only_for_layout_checks"] +ld_so_cache = [] +math_libm = [] +no_trace = ["log/release_max_level_debug"] +# for very verbose activity beyond trace level +trace_tls = [] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[patch.crates-io] +cc-11 = { git = "https://github.com/tea/cc-rs", branch = "riscv-abi-arch-fix", package = "cc" } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..897e4bc3ee --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Redox OS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..58ed6cf086 --- /dev/null +++ b/Makefile @@ -0,0 +1,226 @@ +include config.mk + +CARGO?=cargo +CARGO_TEST?=$(CARGO) +CARGO_COMMON_FLAGS=-Z build-std=core,alloc,compiler_builtins +CARGOFLAGS?=$(CARGO_COMMON_FLAGS) +CC_WRAPPER?= +RUSTCFLAGS?= +LINKFLAGS?=-lgcc +USE_RUST_LIBM?= +TESTBIN?= +export OBJCOPY?=objcopy + +export CARGO_TARGET_DIR?=$(shell pwd)/target +BUILD?=$(CARGO_TARGET_DIR)/$(TARGET) +CARGOFLAGS+=--target=$(TARGET) +EXCEPT_MATH=-not -name "math" +FEATURE_MATH= +ifneq ($(USE_RUST_LIBM),) +FEATURE_MATH=--features math_libm +EXCEPT_MATH= +endif + +TARGET_HEADERS?=$(BUILD)/include +export CFLAGS=-I$(TARGET_HEADERS) + +PROFILE?=release + +HEADERS_UNPARSED=$(shell find src/header -mindepth 1 -maxdepth 1 -type d -not -name "_*" $(EXCEPT_MATH) -printf "%f\n") +HEADERS_DEPS=$(shell find src/header -type f \( -name "cbindgen.toml" -o -name "*.rs" \)) +#HEADERS=$(patsubst %,%.h,$(subst _,/,$(HEADERS_UNPARSED))) + +SRC=\ + Cargo.* \ + $(shell find src/ redox-rt/src/ ld_so/src/ redox-ioctl/src/ include/ -type f) + +BUILTINS_VERSION=0.1.70 + +.PHONY: all clean fmt install install-libs install-headers install-tests libs headers submodules test + +all: | headers libs + +headers: $(HEADERS_DEPS) + rm -rf $(TARGET_HEADERS) + mkdir -p $(TARGET_HEADERS) + cp -r include/* $(TARGET_HEADERS) +ifeq ($(USE_RUST_LIBM),) + cp "openlibm/include"/*.h $(TARGET_HEADERS) + cp "openlibm/src"/*.h $(TARGET_HEADERS) +endif + @set -e ; \ + for header in $(HEADERS_UNPARSED); do \ + if test -f "src/header/$$header/cbindgen.toml"; then \ + echo -e "\033[0;36;49mWriting Header $$header\033[0m"; \ + out=`echo "$$header" | sed 's/_/\//g'`; \ + out="$(TARGET_HEADERS)/$$out.h"; \ + cat "src/header/$$header/cbindgen.toml" cbindgen.globdefs.toml \ + | cbindgen "src/header/$$header/mod.rs" --config=/dev/stdin --output "$$out"; \ + fi \ + done; echo -e "\033[0;36;49mAll headers written\033[0m"; + +clean: + $(CARGO) clean + $(MAKE) -C tests clean + rm -rf sysroot + +check: + $(CARGO) check + +fmt: + ./fmt.sh + +install-headers: headers libs + mkdir -pv "$(DESTDIR)/include" + cp -rv "$(TARGET_HEADERS)"/* "$(DESTDIR)/include" + +libs: \ + $(BUILD)/$(PROFILE)/libc.a \ + $(BUILD)/$(PROFILE)/libc.so \ + $(BUILD)/$(PROFILE)/crt0.o \ + $(BUILD)/$(PROFILE)/crti.o \ + $(BUILD)/$(PROFILE)/crtn.o \ + $(BUILD)/$(PROFILE)/ld.so + +install-libs: headers libs + mkdir -pv "$(DESTDIR)/lib" + cp -v "$(BUILD)/$(PROFILE)/libc.a" "$(DESTDIR)/lib" + cp -v "$(BUILD)/$(PROFILE)/libc.so" "$(DESTDIR)/lib" + ln -vnfs libc.so "$(DESTDIR)/lib/libc.so.6" + cp -v "$(BUILD)/$(PROFILE)/crt0.o" "$(DESTDIR)/lib" + ln -vnfs crt0.o "$(DESTDIR)/lib/crt1.o" + cp -v "$(BUILD)/$(PROFILE)/crti.o" "$(DESTDIR)/lib" + cp -v "$(BUILD)/$(PROFILE)/crtn.o" "$(DESTDIR)/lib" + cp -v "$(BUILD)/$(PROFILE)/ld.so" "$(DESTDIR)/$(LD_SO_PATH)" +ifeq ($(USE_RUST_LIBM),) + cp -v "$(BUILD)/openlibm/libopenlibm.a" "$(DESTDIR)/lib/libm.a" +endif + # Empty libraries for dl, pthread, and rt + $(AR) -rcs "$(DESTDIR)/lib/libdl.a" + $(AR) -rcs "$(DESTDIR)/lib/libpthread.a" + $(AR) -rcs "$(DESTDIR)/lib/librt.a" + +install-tests: tests + $(MAKE) -C tests + mkdir -p "$(DESTDIR)/relibc-tests" + cp -vr tests/build_$(TARGET)/* "$(DESTDIR)/relibc-tests/" + +install: install-headers install-libs + +submodules: + git submodule sync + git submodule update --init --recursive + +sysroot: + @mkdir -p $@ + +.PHONY: sysroot/$(TARGET) +sysroot/$(TARGET): | sysroot + rm -rf $@ + rm -rf $@.partial + mkdir -p $@.partial + $(MAKE) install DESTDIR=$(shell pwd)/$@.partial + mv $@.partial $@ + touch $@ + +test: sysroot/$(TARGET) + # TODO: Fix SIGILL when running cargo test + # $(CARGO_TEST) test + $(MAKE) -C tests run + +test-once: sysroot/$(TARGET) + $(MAKE) -C tests run-once TESTBIN=$(TESTBIN) + + +$(BUILD)/$(PROFILE)/libc.so: $(BUILD)/$(PROFILE)/libc.a + $(CC) -nostdlib \ + -shared \ + -Wl,--gc-sections \ + -Wl,-z,pack-relative-relocs \ + -Wl,--sort-common \ + -Wl,--whole-archive $^ -Wl,--no-whole-archive \ + -Wl,-soname,libc.so.6 \ + $(LINKFLAGS) \ + -o $@ + +$(BUILD)/$(PROFILE)/ld.so: $(BUILD)/$(PROFILE)/ld_so.o $(BUILD)/$(PROFILE)/libc.a + # TODO: merge ld.so with libc.so: --dynamic-list=dynamic-list-file + $(LD) --shared -Bsymbolic --no-relax -T ld_so/ld_script/$(TARGET).ld --gc-sections $^ -o $@ + +$(BUILD)/$(PROFILE)/libc.a: $(BUILD)/$(PROFILE)/librelibc.a $(BUILD)/openlibm/libopenlibm.a + echo "create $@" > "$@.mri" + for lib in $^; do\ + echo "addlib $$lib" >> "$@.mri"; \ + done + echo "save" >> "$@.mri" + echo "end" >> "$@.mri" + $(AR) -M < "$@.mri" + +# Debug targets + +$(BUILD)/debug/librelibc.a: $(SRC) + $(CARGO) rustc $(CARGOFLAGS) $(FEATURE_MATH) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS) + ./renamesyms.sh "$@" "$(BUILD)/debug/deps/" + ./stripcore.sh "$@" + touch $@ + +$(BUILD)/debug/crt0.o: $(SRC) + $(CARGO) rustc --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +$(BUILD)/debug/crti.o: $(SRC) + $(CARGO) rustc --manifest-path src/crti/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +$(BUILD)/debug/crtn.o: $(SRC) + $(CARGO) rustc --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +$(BUILD)/debug/ld_so.o: $(SRC) + $(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort -g -C debug-assertions=no $(RUSTCFLAGS) + touch $@ + +# Release targets + +$(BUILD)/release/librelibc.a: $(SRC) + $(CARGO) rustc --release $(CARGOFLAGS) -- --emit link=$@ $(RUSTCFLAGS) + @# TODO: Better to only allow a certain whitelisted set of symbols? Perhaps + @# use some cbindgen hook, specify them manually, or grep for #[unsafe(no_mangle)]. + ./renamesyms.sh "$@" "$(BUILD)/release/deps/" + ./stripcore.sh "$@" + touch $@ + +$(BUILD)/release/crt0.o: $(SRC) + $(CARGO) rustc --release --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +$(BUILD)/release/crti.o: $(SRC) + $(CARGO) rustc --release --manifest-path src/crti/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +$(BUILD)/release/crtn.o: $(SRC) + $(CARGO) rustc --release --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +$(BUILD)/release/ld_so.o: $(SRC) + $(CARGO) rustc --release --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS) + touch $@ + +# Other targets + +$(BUILD)/openlibm: openlibm + rm -rf $@ $@.partial + mkdir -p $(BUILD) + cp -r $< $@.partial + mv $@.partial $@ + touch $@ + +ifeq ($(USE_RUST_LIBM),) +$(BUILD)/openlibm/libopenlibm.a: $(BUILD)/openlibm $(BUILD)/$(PROFILE)/librelibc.a + $(MAKE) -s AR=$(AR) CC="$(CC_WRAPPER) $(CC)" LD=$(LD) CPPFLAGS="$(CPPFLAGS) -fno-stack-protector -I$(shell pwd)/include -I$(TARGET_HEADERS)" -C $< libopenlibm.a + ./renamesyms.sh "$@" "$(BUILD)/release/deps/" +else +$(BUILD)/openlibm/libopenlibm.a: + mkdir -p "$(BUILD)/openlibm" + $(AR) -rcs "$(BUILD)/openlibm/libopenlibm.a" +endif diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0e8c6628dc --- /dev/null +++ b/README.md @@ -0,0 +1,173 @@ +# Redox C Library (relibc) + +relibc is a portable C standard library written in Rust and is under heavy development, this library contain the following items: + +- C, Linux, BSD functions and extensions +- POSIX compatibility layer +- Interfaces for system components + +The motivation for this project is twofold: Reduce issues that the Redox developers were having with [newlib](https://sourceware.org/newlib/), and create a more stable and safe alternative to C standard libraries written in C. It is mainly designed to be used under Redox, as an alternative to newlib, but it also supports Linux via the [sc](https://crates.io/crates/sc) crate. + +Currently Redox and Linux are supported. + +## `redox-rt` + +`redox-rt` is a runtime library that provides much of the code that enables POSIX on Redox, like `fork`, `exec`, signal handling, etc. +Relibc uses it as backend in `src/platform/redox`, and it's intended to eventually be usable independently, without relibc. + +## Repository Layout + +- `include` - Header files (mostly macros and variadic functions `cbindgen` can't generate) +- `src` - Source files +- `src/c` - C code +- `src/crt0` - Runtime code +- `src/crti` - Runtime code +- `src/crtn` - Runtime code +- `src/header` - Header files implementation +- `src/header/*` - Each folder has a `cbindgen.toml` file, it generates a C-to-Rust interface and header files +- `src/ld_so` - Dynamic loader code +- `src/platform` - Platform-specific and common code +- `src/platform/redox` - Redox-specific code +- `src/platform/linux` - Linux-specific code +- `src/pthread` - pthread implementation +- `src/sync` - Synchronization primitives +- `tests` - C tests (each MR needs to give success in all of them) + +## Download the sources + +To download the relibc sources run the following command: + +```sh +git clone --recursive https://gitlab.redox-os.org/redox-os/relibc +``` + +## Build Instructions + +To build relibc out of the Redox build system, do the following steps: + +### Dependencies + +- Install `cbindgen` + +```sh +cargo install cbindgen +``` + +#### Install the `expect` tool + +- Debian, Ubuntu and PopOS: + +```sh +sudo apt install expect +``` + +- Fedora: + +```sh +sudo dnf install expect +``` + +- Arch Linux: + +```sh +sudo pacman -S expect +``` + +### Build Relibc + +To build the relibc library objects, run the following command: + +```sh +make all +``` + +- Clean old library objects and tests + +```sh +make clean +``` + +## Build relibc inside the Redox build system + +Inside of your Redox build system, run: + +```sh +make prefix +``` + +If you need to rebuild `relibc` for testing a Cookbook recipe, run: + +```sh +touch relibc +make prefix r.recipe-name +``` + +Touching (changing the "last modified time" of) the `relibc` folder is needed to trigger recompilation for `make prefix`. Replace `recipe-name` with your desired recipe name. + +Note: Do not edit `relibc` inside `prefix` folder! Do your work on `relibc` folder directly inside your Redox build system instead. + +## Tests + +Relibc has a test suite that also runs every time a new commit get pushed. You can see `.gitlab-ci.yml` to see how it's being executed. That being said, `./check.sh` is the recommended way to run tests. Here's few examples: + ++ `./check.sh` - Run build, without running the test ++ `./check.sh --test` - Run all tests in x86_64 Redox using Redoxer ++ `./check.sh --test --host` - Run all tests in host (Linux) ++ `./check.sh --test --arch=aarch64` - Run all tests in specified arch + - Arch can be `x86_64`, `aarch64`, `i586`, or `riscv64gc` ++ `./check.sh --test=stdio/printf` - Run a single test + - Can be combined with `--host` or `--arch` + - Will run statically linked test in Linux, dynamically linked in Redox + +Couple of notes: + +- Relibc and its tests will rebuild if files changed, however switching between arch or host requires you to run `make clean` +- Redoxer is needed to run tests for Redox without `--host`. You can install it using `cargo install redoxer` +- Tests can hangs, the test runner can anticipate this, assuming the kernel doesn't hang too. + +## Issues + +#### I'm building for my own platform which I run, and am getting `x86_64-linux-gnu-ar: command not found` (or similar) + +The Makefile expects GNU compiler tools prefixed with the platform specifier, as would be present when you installed a cross compiler. Since you are building for your own platform, some Linux distributions (like Manjaro) don't install/symlink the prefixed executables. + +An easy fix would be to replace the corresponding lines in `config.mk`, e.g. + +```diff +ifeq ($(TARGET),x86_64-unknown-linux-gnu) +- export CC=x86_64-linux-gnu-gcc +- export LD=x86_64-linux-gnu-ld +- export AR=x86_64-linux-gnu-ar +- export NM=x86_64-linux-gnu-nm ++ export CC=gcc ++ export LD=ld ++ export AR=ar ++ export NM=nm + export OBJCOPY=objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld64.so.1 +endif +``` + +## Contributing + +Before starting to contribute, read [this](CONTRIBUTING.md) document. + +## Supported OSes + +- Redox OS +- Linux + +## Supported architectures + +- i586 (Intel/AMD) +- x86_64 (Intel/AMD) +- aarch64 (ARM64) +- riscv64gc (RISC-V) + +## Funding - _Unix-style Signals and Process Management_ + +This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals). + +[NLnet foundation logo](https://nlnet.nl) +[NGI Zero Logo](https://nlnet.nl/core) diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..e0fd5be41f --- /dev/null +++ b/build.rs @@ -0,0 +1,30 @@ +extern crate cc; + +use std::{env, fs}; + +fn main() { + let _crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); + let target = env::var("TARGET").unwrap(); + + println!("cargo:rerun-if-changed=src/c"); + + let mut cc_builder = &mut cc::Build::new(); + + cc_builder = cc_builder.flag("-nostdinc").flag("-nostdlib"); + + if target.starts_with("aarch64") { + cc_builder = cc_builder.flag("-mno-outline-atomics") + } + + cc_builder + .flag("-fno-stack-protector") + .flag("-Wno-expansion-to-defined") + .files( + fs::read_dir("src/c") + .expect("src/c directory missing") + .map(|res| res.expect("read_dir error").path()), + ) + .compile("relibc_c"); + + println!("cargo:rustc-link-lib=static=relibc_c"); +} diff --git a/cbindgen.globdefs.toml b/cbindgen.globdefs.toml new file mode 100644 index 0000000000..7e7e19e231 --- /dev/null +++ b/cbindgen.globdefs.toml @@ -0,0 +1,24 @@ + +# needs a leading newline +[defines] +"target_os=redox" = "__redox__" +"target_os=linux" = "__linux__" +"target_pointer_width=64" = "__LP64__" +"target_pointer_width=32" = "__ILP32__" +"target_arch=x86" = "__i386__" +"target_arch=x86_64" = "__x86_64__" +"target_arch=aarch64" = "__aarch64__" +# This is not exact. It should be `defined(__riscv) && defined(__LP64__)`, or `defined(__riscv) && __riscv_xlen==64` +# This will do however, as long as we only support riscv64 and not riscv32 +"target_arch=riscv64" = "__riscv" + +# XXX: silences a warning +"feature = no_std" = "__relibc__" + +# Ensure attributes are passed down from Rust +# must be included where attributes are used in relibc +[fn] +must_use = "__nodiscard" +deprecated = "__deprecated" +deprecated_with_note = "__deprecatedNote({})" +no_return = "__noreturn" diff --git a/check.sh b/check.sh new file mode 100755 index 0000000000..9e4040040b --- /dev/null +++ b/check.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +RED='\033[1;38;5;196m' +GREEN='\033[1;38;5;46m' +NC='\033[0m' + +show_help() { + echo "Usage: $(basename "$0") [OPTIONS]" + echo "" + echo "Description:" + echo " Wrapper for Makefile / Cargo to run checks or tests on Redox OS targets." + echo "" + echo "Options:" + echo " --test Run 'make test' instead of 'make all'" + echo " --test= Run single 'make test'" + echo " --cargo Run 'cargo check' / 'cargo test' instead" + echo " (note: cargo test is currently not maintained for relibc)" + echo " --host Run the command on host (linux) target" + echo " --all-target Run the command on all supported Redox architectures" + echo " --target= Override the target architecture (e.g., i586-unknown-redox)" + echo " --arch= Override the target architecture using arch (e.g., i586)" + echo " --help Show this help message" + echo "" + echo "Supported Targets:" + for t in "${SUPPORTED_TARGETS[@]}"; do + echo " - $t" + done + echo " - $(uname -m)-unknown-linux-gnu" + echo "" + echo "Environment:" + echo " TARGET Sets the default target (overridden by --target)" +} + +if ! command -v cbindgen &> /dev/null; then + echo "Error: 'cbindgen' CLI not found." + echo "Please install it: cargo install cbindgen" + exit 1 +fi + +SUPPORTED_TARGETS=( + "x86_64-unknown-redox" + "i586-unknown-redox" + "aarch64-unknown-redox" + "riscv64gc-unknown-redox" +) + +CURRENT_TARGET="${TARGET:-x86_64-unknown-redox}" +CHECK_ALL=false +CMD_ACTION="make" +CARGO_ACTION="check" +MAKE_ACTION="all" +TEST_BIN="" +IS_HOST=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --all-target) + CHECK_ALL=true + ;; + --test) + MAKE_ACTION="test" + CARGO_ACTION="test" + ;; + --test=*) + TEST_BIN="${1#*=}" + MAKE_ACTION="test-once" + ;; + --cargo) + CMD_ACTION="cargo" + ;; + --host) + CURRENT_TARGET="$(uname -m)-unknown-linux-gnu" + IS_HOST=1 + ;; + --target=*) + CURRENT_TARGET="${1#*=}" + ;; + --arch=*) + CURRENT_TARGET="${1#*=}-unknown-redox" + ;; + --help) + show_help + exit 0 + ;; + *) + echo -e "${RED}Error: Unknown option '$1'${NC}" + show_help + exit 1 + ;; + esac + shift +done + +if [ "$IS_HOST" -eq 0 ]; then +if ! command -v redoxer &> /dev/null; then + echo "Error: 'redoxer' CLI not found." + echo "Please install it: cargo install redoxer" + exit 1 +fi +fi + +run_redoxer() { + export TARGET=$1 + REDOXER_ENV="redoxer env" + if [ "$IS_HOST" -eq 0 ]; then + redoxer toolchain || { echo -e "${RED}Fail: redoxer toolchain for: $target.${NC}" && exit 1; } + export CARGO_TEST="redoxer" + export TEST_RUNNER="redoxer exec --folder ../../sysroot/$TARGET/:/usr --folder . --" +# TODO: Identify hang issue with pthread/barrier and pthread/once tests in multi core to get rid of this limit + export REDOXER_QEMU_ARGS="-smp 1" + + MAKE_ACTION="$MAKE_ACTION IS_REDOX=1" + else + REDOXER_ENV="" + fi + + if [ "$TEST_BIN" != "" ]; then + if [ "$IS_HOST" -eq 0 ]; then + MAKE_ACTION="$MAKE_ACTION TESTBIN=bins_dynamic/$TEST_BIN" + else + MAKE_ACTION="$MAKE_ACTION TESTBIN=bins_static/$TEST_BIN" + fi + fi + + if [ "$CMD_ACTION" == "make" ]; then + CMD_OPT="-j $(nproc) $MAKE_ACTION" + else + CMD_OPT="$CARGO_ACTION" + fi + + echo "----------------------------------------" + echo "Running $REDOXER_ENV $CMD_ACTION $CMD_OPT for: $TARGET" + + if $REDOXER_ENV $CMD_ACTION $CMD_OPT; then + return 0 + else + echo -e "${RED}Fail: $CMD_ACTION $CMD_OPT for $TARGET failed.${NC}" + return 1 + fi +} + +if [ "$CHECK_ALL" = true ]; then + echo "Running $CMD_ACTION for all supported Redox targets..." + + has_error=false + + for target in "${SUPPORTED_TARGETS[@]}"; do + if ! run_redoxer "$target"; then + has_error=true + fi + done + + echo "----------------------------------------" + if [ "$has_error" = true ]; then + echo -e "${RED}Summary: One or more targets failed.${NC}" + exit 1 + else + echo -e "${GREEN}Summary: All targets passed!${NC}" + exit 0 + fi +else + if run_redoxer "$CURRENT_TARGET"; then + echo -e "${GREEN}Success: $CARGO_ACTION for $CURRENT_TARGET passed.${NC}" + exit 0 + else + exit 1 + fi +fi diff --git a/config.mk b/config.mk new file mode 100644 index 0000000000..e9ffa218ae --- /dev/null +++ b/config.mk @@ -0,0 +1,73 @@ +ifndef TARGET + export TARGET:=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-target | cut -d '"' -f4) +endif + +ifeq ($(TARGET),aarch64-unknown-linux-gnu) + export CC=aarch64-linux-gnu-gcc + export LD=aarch64-linux-gnu-ld + export AR=aarch64-linux-gnu-ar + export NM=aarch64-linux-gnu-nm + export OBJCOPY=aarch64-linux-gnu-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld.so.1 +endif + +ifeq ($(TARGET),aarch64-unknown-redox) + export CC=aarch64-unknown-redox-gcc + export LD=aarch64-unknown-redox-ld + export AR=aarch64-unknown-redox-ar + export NM=aarch64-unknown-redox-nm + export OBJCOPY=aarch64-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld.so.1 +endif + +ifeq ($(TARGET),i586-unknown-redox) + export CC=i586-unknown-redox-gcc + export LD=i586-unknown-redox-ld + export AR=i586-unknown-redox-ar + export NM=i586-unknown-redox-nm + export OBJCOPY=i586-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/libc.so.1 +endif + +ifeq ($(TARGET),i686-unknown-redox) + export CC=i686-unknown-redox-gcc + export LD=i686-unknown-redox-ld + export AR=i686-unknown-redox-ar + export NM=i686-unknown-redox-nm + export OBJCOPY=i686-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/libc.so.1 +endif + +ifeq ($(TARGET),x86_64-unknown-linux-gnu) + export CC=x86_64-linux-gnu-gcc + export LD=x86_64-linux-gnu-ld + export AR=x86_64-linux-gnu-ar + export NM=x86_64-linux-gnu-nm + export OBJCOPY=objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld64.so.1 +endif + +ifeq ($(TARGET),x86_64-unknown-redox) + export CC=x86_64-unknown-redox-gcc + export LD=x86_64-unknown-redox-ld + export AR=x86_64-unknown-redox-ar + export NM=x86_64-unknown-redox-nm + export OBJCOPY=x86_64-unknown-redox-objcopy + export CPPFLAGS= + LD_SO_PATH=lib/ld64.so.1 +endif + +ifeq ($(TARGET),riscv64gc-unknown-redox) + export CC=riscv64-unknown-redox-gcc + export LD=riscv64-unknown-redox-ld + export AR=riscv64-unknown-redox-ar + export NM=riscv64-unknown-redox-nm + export OBJCOPY=riscv64-unknown-redox-objcopy + export CPPFLAGS=-march=rv64gc -mabi=lp64d + LD_SO_PATH=lib/ld.so.1 +endif diff --git a/dlmalloc-rs/.github/workflows/main.yml b/dlmalloc-rs/.github/workflows/main.yml new file mode 100644 index 0000000000..2c7a782930 --- /dev/null +++ b/dlmalloc-rs/.github/workflows/main.yml @@ -0,0 +1,117 @@ +name: CI +on: [push, pull_request] + +jobs: + test: + name: Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + rust: stable + - os: ubuntu-latest + rust: beta + - os: ubuntu-latest + rust: nightly + - os: macos-latest + rust: stable + - os: windows-latest + rust: stable + - os: ubuntu-latest + rust: stable + target: wasm32-wasip1 + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + shell: bash + + # Configure cross-builds by adding the rustup target and configuring future + # cargo invocations. + - run: | + rustup target add ${{ matrix.target }} + echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV + if: matrix.target != '' + + # For wasm install wasmtime as a test runner and configure it with Cargo. + - name: Setup `wasmtime` + uses: bytecodealliance/actions/wasmtime/setup@v1 + if: matrix.target == 'wasm32-wasip1' + - run: echo CARGO_TARGET_WASM32_WASIP1_RUNNER=wasmtime >> $GITHUB_ENV + if: matrix.target == 'wasm32-wasip1' + + - run: cargo test + - run: cargo test --features debug + - run: cargo test --features global + - run: cargo test --release + env: + CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS: true + - run: cargo test --release + env: + CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS: false + - run: cargo test --features debug --release + env: + CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS: true + - run: RUSTFLAGS='--cfg test_lots' cargo test --release + shell: bash + env: + CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS: true + - run: RUSTFLAGS='--cfg test_lots' cargo test --release --features debug + shell: bash + env: + CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS: true + + rustfmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable && rustup default stable && rustup component add rustfmt + - run: cargo fmt -- --check + + wasm: + name: WebAssembly + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable && rustup default stable && rustup target add wasm32-unknown-unknown + - run: cargo build --target wasm32-unknown-unknown + - run: cargo build --target wasm32-unknown-unknown --release + + external-platform: + name: external-platform + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable && rustup default stable && rustup target add x86_64-fortanix-unknown-sgx + - run: cargo build --target x86_64-fortanix-unknown-sgx + + fuzz: + name: Build Fuzzers + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - run: cargo install cargo-fuzz + - run: cargo fuzz build --dev + + miri: + name: Miri + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - name: Test with Miri Stack Borrows + run: cargo miri test + - name: Test with Miri Tree Borrows + run: cargo miri test + env: + MIRIFLAGS: -Zmiri-tree-borrows diff --git a/dlmalloc-rs/.gitignore b/dlmalloc-rs/.gitignore new file mode 100644 index 0000000000..6aa106405a --- /dev/null +++ b/dlmalloc-rs/.gitignore @@ -0,0 +1,3 @@ +/target/ +**/*.rs.bk +Cargo.lock diff --git a/dlmalloc-rs/Cargo.toml b/dlmalloc-rs/Cargo.toml new file mode 100644 index 0000000000..f9e20c3e7f --- /dev/null +++ b/dlmalloc-rs/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "dlmalloc" +version = "0.2.8" +authors = ["Alex Crichton "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/alexcrichton/dlmalloc-rs" +homepage = "https://github.com/alexcrichton/dlmalloc-rs" +documentation = "https://docs.rs/dlmalloc" +description = """ +A Rust port of the dlmalloc allocator +""" +edition.workspace = true + +[workspace] +members = ['fuzz'] + +[workspace.package] +edition = '2021' + +[package.metadata.docs.rs] +features = ['global'] + +[lib] +doctest = false + +[target.'cfg(all(unix, not(target_arch = "wasm32")))'.dependencies] +libc = { version = "0.2", default-features = false, optional = true } + +[dependencies] +# For more information on these dependencies see rust-lang/rust's +# `src/tools/rustc-std-workspace` folder +core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } +compiler_builtins = { version = '0.1.0', optional = true } +cfg-if = "1.0" + +[target.'cfg(target_os = "windows")'.dependencies.windows-sys] +version = ">=0.52.0, <=0.59.*" +features = [ + "Win32_Foundation", + "Win32_System_Memory", + "Win32_System_Threading", + "Win32_System_SystemInformation", +] + +[dev-dependencies] +arbitrary = "1.3.2" +rand = { version = "0.8", features = ['small_rng'] } + +[profile.release] +debug-assertions = true + +[features] +# Enable implementations of the `GlobalAlloc` standard library API, exporting a +# new `GlobalDlmalloc` as well which implements this trait. +global = ["system", "rust_api"] + +# Enable very expensive debug checks in this crate +debug = [] + +# Enables OS APIs based on the current target, can be implemented manually +# otherwise. +system = ["libc"] + +rustc-dep-of-std = ['core', 'compiler_builtins/rustc-dep-of-std'] + +c_api = [] +rust_api = [] + +default = ["global", "rust_api"] diff --git a/dlmalloc-rs/LICENSE-APACHE b/dlmalloc-rs/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/dlmalloc-rs/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/dlmalloc-rs/LICENSE-MIT b/dlmalloc-rs/LICENSE-MIT new file mode 100644 index 0000000000..39e0ed6602 --- /dev/null +++ b/dlmalloc-rs/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/dlmalloc-rs/README.md b/dlmalloc-rs/README.md new file mode 100644 index 0000000000..317d8c8bf1 --- /dev/null +++ b/dlmalloc-rs/README.md @@ -0,0 +1,40 @@ +# dlmalloc-rs + +A port of [dlmalloc] to Rust. + +[Documentation](https://docs.rs/dlmalloc) + +[dlmalloc]: https://gee.cs.oswego.edu/dl/html/malloc.html + +## Why dlmalloc? + +This crate is a port of [dlmalloc] to Rust, and doesn't rely on C. The primary +purpose of this crate is to serve as the default allocator for Rust on the +`wasm32-unknown-unknown` target. At the time this was written the wasm target +didn't support C code, so it was required to have a Rust-only solution. + +This allocator is not the most performant by a longshot. It is primarily, I +think, intended for being easy to port and easy to learn. I didn't dive too deep +into the implementation when writing it, it's just a straight port of the C +version. + +It's unlikely that Rust code needs to worry/interact with this allocator in +general. Most of the time you'll be manually switching to a different allocator +:) + +# License + +This project is licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this project by you, as defined in the Apache-2.0 license, +shall be dual licensed as above, without any additional terms or conditions. diff --git a/dlmalloc-rs/fuzz/.gitignore b/dlmalloc-rs/fuzz/.gitignore new file mode 100644 index 0000000000..b400c27826 --- /dev/null +++ b/dlmalloc-rs/fuzz/.gitignore @@ -0,0 +1,2 @@ +corpus +artifacts diff --git a/dlmalloc-rs/fuzz/Cargo.toml b/dlmalloc-rs/fuzz/Cargo.toml new file mode 100644 index 0000000000..fb9970fa97 --- /dev/null +++ b/dlmalloc-rs/fuzz/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "dlmalloc-fuzz" +version = "0.0.1" +publish = false +edition.workspace = true + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = "1.3.2" +dlmalloc = { path = '..' } +libfuzzer-sys = "0.4.7" + +[[bin]] +name = "alloc" +path = "fuzz_targets/alloc.rs" +test = false +bench = false diff --git a/dlmalloc-rs/fuzz/fuzz_targets/alloc.rs b/dlmalloc-rs/fuzz/fuzz_targets/alloc.rs new file mode 100644 index 0000000000..eddfb8b64a --- /dev/null +++ b/dlmalloc-rs/fuzz/fuzz_targets/alloc.rs @@ -0,0 +1,8 @@ +#![no_main] + +use arbitrary::Unstructured; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|bytes: &[u8]| { + let _ = dlmalloc_fuzz::run(&mut Unstructured::new(bytes)); +}); diff --git a/dlmalloc-rs/fuzz/src/lib.rs b/dlmalloc-rs/fuzz/src/lib.rs new file mode 100644 index 0000000000..3b337bb92c --- /dev/null +++ b/dlmalloc-rs/fuzz/src/lib.rs @@ -0,0 +1,108 @@ +use arbitrary::{Result, Unstructured}; +use dlmalloc::Dlmalloc; +use std::cmp; + +const MAX_ALLOCATED: usize = 100 << 20; // 100 MB + +pub fn run(u: &mut Unstructured<'_>) -> Result<()> { + let mut a = Dlmalloc::new(); + let mut ptrs = Vec::new(); + let mut allocated = 0; + unsafe { + while u.arbitrary()? { + // If there are pointers to free then have a chance of deallocating + // a pointer. Try not to deallocate things until there's a "large" + // working set but afterwards give it a 50/50 chance of allocating + // or deallocating. + let free = match ptrs.len() { + 0 => false, + 0..=10_000 => u.ratio(1, 3)?, + _ => u.arbitrary()?, + }; + if free { + let idx = u.choose_index(ptrs.len())?; + let (ptr, size, align) = ptrs.swap_remove(idx); + allocated -= size; + a.free(ptr, size, align); + continue; + } + + // 1/100 chance of reallocating a pointer to a different size. + if ptrs.len() > 0 && u.ratio(1, 100)? { + let idx = u.choose_index(ptrs.len())?; + let (ptr, size, align) = ptrs.swap_remove(idx); + + // Arbitrarily choose whether to make this allocation either + // twice as large or half as small. + let new_size = if u.arbitrary()? { + u.int_in_range(size..=size * 2)? + } else if size > 10 { + u.int_in_range(size / 2..=size)? + } else { + continue; + }; + if allocated + new_size - size > MAX_ALLOCATED { + ptrs.push((ptr, size, align)); + continue; + } + allocated -= size; + allocated += new_size; + + // Perform the `realloc` and assert that all bytes were copied. + let mut tmp = Vec::new(); + for i in 0..cmp::min(size, new_size) { + tmp.push(*ptr.offset(i as isize)); + } + let ptr = a.realloc(ptr, size, align, new_size); + assert!(!ptr.is_null()); + for (i, byte) in tmp.iter().enumerate() { + assert_eq!(*byte, *ptr.offset(i as isize)); + } + ptrs.push((ptr, new_size, align)); + } + + // Aribtrarily choose a size to allocate as well as an alignment. + // Enable small sizes with standard alignment happening a fair bit. + let size = if u.arbitrary()? { + u.int_in_range(1..=128)? + } else { + u.int_in_range(1..=128 * 1024)? + }; + let align = if u.ratio(1, 10)? { + 1 << u.int_in_range(3..=8)? + } else { + 8 + }; + + if size + allocated > MAX_ALLOCATED { + continue; + } + allocated += size; + + // Choose arbitrarily between a zero-allocated chunk and a normal + // allocated chunk. + let zero = u.ratio(1, 50)?; + let ptr = if zero { + a.calloc(size, align) + } else { + a.malloc(size, align) + }; + for i in 0..size { + if zero { + assert_eq!(*ptr.offset(i as isize), 0); + } + *ptr.offset(i as isize) = 0xce; + } + ptrs.push((ptr, size, align)); + } + + // Deallocate everythign when we're done. + for (ptr, size, align) in ptrs { + a.free(ptr, size, align); + } + + a.destroy(); + } + + Ok(()) +} diff --git a/dlmalloc-rs/src/dlmalloc.c b/dlmalloc-rs/src/dlmalloc.c new file mode 100644 index 0000000000..649cfbc705 --- /dev/null +++ b/dlmalloc-rs/src/dlmalloc.c @@ -0,0 +1,6280 @@ +/* + This is a version (aka dlmalloc) of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu + +* Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Quickstart + + This library is all in one file to simplify the most common usage: + ftp it, compile it (-O3), and link it into another program. All of + the compile-time options default to reasonable values for use on + most platforms. You might later want to step through various + compile-time and dynamic tuning options. + + For convenience, an include file for code using this malloc is at: + ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.6.h + You don't really need this .h file unless you call functions not + defined in your system include files. The .h file contains only the + excerpts from this file needed for using this malloc on ANSI C/C++ + systems, so long as you haven't changed compile-time options about + naming and tuning parameters. If you do, then you can create your + own malloc.h that does include all settings by cutting at the point + indicated below. Note that you may already by default be using a C + library containing a malloc that is based on some version of this + malloc (for example in linux). You might still want to use the one + in this file to customize settings or to avoid overheads associated + with library versions. + +* Vital statistics: + + Supported pointer/size_t representation: 4 or 8 bytes + size_t MUST be an unsigned type of the same width as + pointers. (If you are using an ancient system that declares + size_t as a signed type, or need it to be a different width + than pointers, you can use a previous release of this malloc + (e.g. 2.7.2) supporting these.) + + Alignment: 8 bytes (minimum) + This suffices for nearly all current machines and C compilers. + However, you can define MALLOC_ALIGNMENT to be wider than this + if necessary (up to 128bytes), at the expense of using more space. + + Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) + 8 or 16 bytes (if 8byte sizes) + Each malloced chunk has a hidden word of overhead holding size + and status information, and additional cross-check word + if FOOTERS is defined. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) + 8-byte ptrs: 32 bytes (including overhead) + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + The maximum overhead wastage (i.e., number of extra bytes + allocated than were requested in malloc) is less than or equal + to the minimum size, except for requests >= mmap_threshold that + are serviced via mmap(), where the worst case wastage is about + 32 bytes plus the remainder from a system page (the minimal + mmap unit); typically 4096 or 8192 bytes. + + Security: static-safe; optionally more or less + The "security" of malloc refers to the ability of malicious + code to accentuate the effects of errors (for example, freeing + space that is not currently malloc'ed or overwriting past the + ends of chunks) in code that calls malloc. This malloc + guarantees not to modify any memory locations below the base of + heap, i.e., static variables, even in the presence of usage + errors. The routines additionally detect most improper frees + and reallocs. All this holds as long as the static bookkeeping + for malloc itself is not corrupted by some other means. This + is only one aspect of security -- these checks do not, and + cannot, detect all possible programming errors. + + If FOOTERS is defined nonzero, then each allocated chunk + carries an additional check word to verify that it was malloced + from its space. These check words are the same within each + execution of a program using malloc, but differ across + executions, so externally crafted fake chunks cannot be + freed. This improves security by rejecting frees/reallocs that + could corrupt heap memory, in addition to the checks preventing + writes to statics that are always on. This may further improve + security at the expense of time and space overhead. (Note that + FOOTERS may also be worth using with MSPACES.) + + By default detected errors cause the program to abort (calling + "abort()"). You can override this to instead proceed past + errors by defining PROCEED_ON_ERROR. In this case, a bad free + has no effect, and a malloc that encounters a bad address + caused by user overwrites will ignore the bad address by + dropping pointers and indices to all known memory. This may + be appropriate for programs that should continue if at all + possible in the face of programming errors, although they may + run out of memory because dropped memory is never reclaimed. + + If you don't like either of these options, you can define + CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything + else. And if if you are sure that your program using malloc has + no errors or vulnerabilities, you can define INSECURE to 1, + which might (or might not) provide a small performance improvement. + + It is also possible to limit the maximum total allocatable + space, using malloc_set_footprint_limit. This is not + designed as a security feature in itself (calls to set limits + are not screened or privileged), but may be useful as one + aspect of a secure implementation. + + Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero + When USE_LOCKS is defined, each public call to malloc, free, + etc is surrounded with a lock. By default, this uses a plain + pthread mutex, win32 critical section, or a spin-lock if if + available for the platform and not disabled by setting + USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined, + recursive versions are used instead (which are not required for + base functionality but may be needed in layered extensions). + Using a global lock is not especially fast, and can be a major + bottleneck. It is designed only to provide minimal protection + in concurrent environments, and to provide a basis for + extensions. If you are using malloc in a concurrent program, + consider instead using nedmalloc + (http://www.nedprod.com/programs/portable/nedmalloc/) or + ptmalloc (See http://www.malloc.de), which are derived from + versions of this malloc. + + System requirements: Any combination of MORECORE and/or MMAP/MUNMAP + This malloc can use unix sbrk or any emulation (invoked using + the CALL_MORECORE macro) and/or mmap/munmap or any emulation + (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system + memory. On most unix systems, it tends to work best if both + MORECORE and MMAP are enabled. On Win32, it uses emulations + based on VirtualAlloc. It also uses common C library functions + like memset. + + Compliance: I believe it is compliant with the Single Unix Specification + (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably + others as well. + +* Overview of algorithms + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and + tunable. Consistent balance across these factors results in a good + general-purpose allocator for malloc-intensive programs. + + In most ways, this malloc is a best-fit allocator. Generally, it + chooses the best-fitting existing chunk for a request, with ties + broken in approximately least-recently-used order. (This strategy + normally maintains low fragmentation.) However, for requests less + than 256bytes, it deviates from best-fit when there is not an + exactly fitting available chunk by preferring to use space adjacent + to that used for the previous small request, as well as by breaking + ties in approximately most-recently-used order. (These enhance + locality of series of small allocations.) And for very large requests + (>= 256Kb by default), it relies on system memory mapping + facilities, if supported. (This helps avoid carrying around and + possibly fragmenting memory used only for large chunks.) + + All operations (except malloc_stats and mallinfo) have execution + times that are bounded by a constant factor of the number of bits in + a size_t, not counting any clearing in calloc or copying in realloc, + or actions surrounding MORECORE and MMAP that have times + proportional to the number of non-contiguous regions returned by + system allocation routines, which is often just 1. In real-time + applications, you can optionally suppress segment traversals using + NO_SEGMENT_TRAVERSAL, which assures bounded execution even when + system allocators return non-contiguous spaces, at the typical + expense of carrying around more memory and increased fragmentation. + + The implementation is not very modular and seriously overuses + macros. Perhaps someday all C compilers will do as good a job + inlining modular code as can now be done by brute-force expansion, + but now, enough of them seem not to. + + Some compilers issue a lot of warnings about code that is + dead/unreachable only on some platforms, and also about intentional + uses of negation on unsigned types. All known cases of each can be + ignored. + + For a longer but out of date high-level description, see + http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES + If MSPACES is defined, then in addition to malloc, free, etc., + this file also defines mspace_malloc, mspace_free, etc. These + are versions of malloc routines that take an "mspace" argument + obtained using create_mspace, to control all internal bookkeeping. + If ONLY_MSPACES is defined, only these versions are compiled. + So if you would like to use this allocator for only some allocations, + and your system malloc for others, you can compile with + ONLY_MSPACES and then do something like... + static mspace mymspace = create_mspace(0,0); // for example + #define mymalloc(bytes) mspace_malloc(mymspace, bytes) + + (Note: If you only need one instance of an mspace, you can instead + use "USE_DL_PREFIX" to relabel the global malloc.) + + You can similarly create thread-local allocators by storing + mspaces as thread-locals. For example: + static __thread mspace tlms = 0; + void* tlmalloc(size_t bytes) { + if (tlms == 0) tlms = create_mspace(0, 0); + return mspace_malloc(tlms, bytes); + } + void tlfree(void* mem) { mspace_free(tlms, mem); } + + Unless FOOTERS is defined, each mspace is completely independent. + You cannot allocate from one and free to another (although + conformance is only weakly checked, so usage errors are not always + caught). If FOOTERS is defined, then each chunk carries around a tag + indicating its originating mspace, and frees are directed to their + originating spaces. Normally, this requires use of locks. + + ------------------------- Compile-time options --------------------------- + +Be careful in setting #define values for numerical constants of type +size_t. On some systems, literal values are not automatically extended +to size_t precision unless they are explicitly casted. You can also +use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. + +WIN32 default: defined if _WIN32 defined + Defining WIN32 sets up defaults for MS environment and compilers. + Otherwise defaults are for unix. Beware that there seem to be some + cases where this malloc might not be a pure drop-in replacement for + Win32 malloc: Random-looking failures from Win32 GDI API's (eg; + SetDIBits()) may be due to bugs in some video driver implementations + when pixel buffers are malloc()ed, and the region spans more than + one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) + default granularity, pixel buffers may straddle virtual allocation + regions more often than when using the Microsoft allocator. You can + avoid this by using VirtualAlloc() and VirtualFree() for all pixel + buffers rather than using malloc(). If this is not possible, + recompile this malloc with a larger DEFAULT_GRANULARITY. Note: + in cases where MSC and gcc (cygwin) are known to differ on WIN32, + conditions use _MSC_VER to distinguish them. + +DLMALLOC_EXPORT default: extern + Defines how public APIs are declared. If you want to export via a + Windows DLL, you might define this as + #define DLMALLOC_EXPORT extern __declspec(dllexport) + If you want a POSIX ELF shared object, you might use + #define DLMALLOC_EXPORT extern __attribute__((visibility("default"))) + +MALLOC_ALIGNMENT default: (size_t)(2 * sizeof(void *)) + Controls the minimum alignment for malloc'ed chunks. It must be a + power of two and at least 8, even on machines for which smaller + alignments would suffice. It may be defined as larger than this + though. Note however that code and data structures are optimized for + the case of 8-byte alignment. + +MSPACES default: 0 (false) + If true, compile in support for independent allocation spaces. + This is only supported if HAVE_MMAP is true. + +ONLY_MSPACES default: 0 (false) + If true, only compile in mspace versions, not regular versions. + +USE_LOCKS default: 0 (false) + Causes each call to each public routine to be surrounded with + pthread or WIN32 mutex lock/unlock. (If set true, this can be + overridden on a per-mspace basis for mspace versions.) If set to a + non-zero value other than 1, locks are used, but their + implementation is left out, so lock functions must be supplied manually, + as described below. + +USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available + If true, uses custom spin locks for locking. This is currently + supported only gcc >= 4.1, older gccs on x86 platforms, and recent + MS compilers. Otherwise, posix locks or win32 critical sections are + used. + +USE_RECURSIVE_LOCKS default: not defined + If defined nonzero, uses recursive (aka reentrant) locks, otherwise + uses plain mutexes. This is not required for malloc proper, but may + be needed for layered allocators such as nedmalloc. + +LOCK_AT_FORK default: not defined + If defined nonzero, performs pthread_atfork upon initialization + to initialize child lock while holding parent lock. The implementation + assumes that pthread locks (not custom locks) are being used. In other + cases, you may need to customize the implementation. + +FOOTERS default: 0 + If true, provide extra checking and dispatching by placing + information in the footers of allocated chunks. This adds + space and time overhead. + +INSECURE default: 0 + If true, omit checks for usage errors and heap space overwrites. + +USE_DL_PREFIX default: NOT defined + Causes compiler to prefix all public routines with the string 'dl'. + This can be useful when you only want to use this malloc in one part + of a program, using your regular system malloc elsewhere. + +MALLOC_INSPECT_ALL default: NOT defined + If defined, compiles malloc_inspect_all and mspace_inspect_all, that + perform traversal of all heap space. Unless access to these + functions is otherwise restricted, you probably do not want to + include them in secure implementations. + +ABORT default: defined as abort() + Defines how to abort on failed checks. On most systems, a failed + check cannot die with an "assert" or even print an informative + message, because the underlying print routines in turn call malloc, + which will fail again. Generally, the best policy is to simply call + abort(). It's not very useful to do more than this because many + errors due to overwriting will show up as address faults (null, odd + addresses etc) rather than malloc-triggered checks, so will also + abort. Also, most compilers know that abort() does not return, so + can better optimize code conditionally calling it. + +PROCEED_ON_ERROR default: defined as 0 (false) + Controls whether detected bad addresses cause them to bypassed + rather than aborting. If set, detected bad arguments to free and + realloc are ignored. And all bookkeeping information is zeroed out + upon a detected overwrite of freed heap space, thus losing the + ability to ever return it from malloc again, but enabling the + application to proceed. If PROCEED_ON_ERROR is defined, the + static variable malloc_corruption_error_count is compiled in + and can be examined to see if errors have occurred. This option + generates slower code than the default abort policy. + +DEBUG default: NOT defined + The DEBUG setting is mainly intended for people trying to modify + this code or diagnose problems when porting to new platforms. + However, it may also be able to better isolate user errors than just + using runtime checks. The assertions in the check routines spell + out in more detail the assumptions and invariants underlying the + algorithms. The checking is fairly extensive, and will slow down + execution noticeably. Calling malloc_stats or mallinfo with DEBUG + set will attempt to check every non-mmapped allocated and free chunk + in the course of computing the summaries. + +ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) + Debugging assertion failures can be nearly impossible if your + version of the assert macro causes malloc to be called, which will + lead to a cascade of further failures, blowing the runtime stack. + ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), + which will usually make debugging easier. + +MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 + The action to take before "return 0" when malloc fails to be able to + return memory because there is none available. + +HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES + True if this system supports sbrk or an emulation of it. + +MORECORE default: sbrk + The name of the sbrk-style system routine to call to obtain more + memory. See below for guidance on writing custom MORECORE + functions. The type of the argument to sbrk/MORECORE varies across + systems. It cannot be size_t, because it supports negative + arguments, so it is normally the signed type of the same width as + size_t (sometimes declared as "intptr_t"). It doesn't much matter + though. Internally, we only call it with arguments less than half + the max value of a size_t, which should work across all reasonable + possibilities, although sometimes generating compiler warnings. + +MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE + If true, take advantage of fact that consecutive calls to MORECORE + with positive arguments always return contiguous increasing + addresses. This is true of unix sbrk. It does not hurt too much to + set it true anyway, since malloc copes with non-contiguities. + Setting it false when definitely non-contiguous saves time + and possibly wasted space it would take to discover this though. + +MORECORE_CANNOT_TRIM default: NOT defined + True if MORECORE cannot release space back to the system when given + negative arguments. This is generally necessary only if you are + using a hand-crafted MORECORE function that cannot handle negative + arguments. + +NO_SEGMENT_TRAVERSAL default: 0 + If non-zero, suppresses traversals of memory segments + returned by either MORECORE or CALL_MMAP. This disables + merging of segments that are contiguous, and selectively + releasing them to the OS if unused, but bounds execution times. + +HAVE_MMAP default: 1 (true) + True if this system supports mmap or an emulation of it. If so, and + HAVE_MORECORE is not true, MMAP is used for all system + allocation. If set and HAVE_MORECORE is true as well, MMAP is + primarily used to directly allocate very large blocks. It is also + used as a backup strategy in cases where MORECORE fails to provide + space from system. Note: A single call to MUNMAP is assumed to be + able to unmap memory that may have be allocated using multiple calls + to MMAP, so long as they are adjacent. + +HAVE_MREMAP default: 1 on linux, else 0 + If true realloc() uses mremap() to re-allocate large blocks and + extend or shrink allocation spaces. + +MMAP_CLEARS default: 1 except on WINCE. + True if mmap clears memory so calloc doesn't need to. This is true + for standard unix mmap using /dev/zero and on WIN32 except for WINCE. + +USE_BUILTIN_FFS default: 0 (i.e., not used) + Causes malloc to use the builtin ffs() function to compute indices. + Some compilers may recognize and intrinsify ffs to be faster than the + supplied C version. Also, the case of x86 using gcc is special-cased + to an asm instruction, so is already as fast as it can be, and so + this setting has no effect. Similarly for Win32 under recent MS compilers. + (On most x86s, the asm version is only slightly faster than the C version.) + +malloc_getpagesize default: derive from system includes, or 4096. + The system page size. To the extent possible, this malloc manages + memory from the system in page-size units. This may be (and + usually is) a function rather than a constant. This is ignored + if WIN32, where page size is determined using getSystemInfo during + initialization. + +USE_DEV_RANDOM default: 0 (i.e., not used) + Causes malloc to use /dev/random to initialize secure magic seed for + stamping footers. Otherwise, the current time is used. + +NO_MALLINFO default: 0 + If defined, don't compile "mallinfo". This can be a simple way + of dealing with mismatches between system declarations and + those in this file. + +MALLINFO_FIELD_TYPE default: size_t + The type of the fields in the mallinfo struct. This was originally + defined as "int" in SVID etc, but is more usefully defined as + size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set + +NO_MALLOC_STATS default: 0 + If defined, don't compile "malloc_stats". This avoids calls to + fprintf and bringing in stdio dependencies you might not want. + +REALLOC_ZERO_BYTES_FREES default: not defined + This should be set if a call to realloc with zero bytes should + be the same as a call to free. Some people think it should. Otherwise, + since this malloc returns a unique pointer for malloc(0), so does + realloc(p, 0). + +LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H +LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H +LACKS_STDLIB_H LACKS_SCHED_H LACKS_TIME_H default: NOT defined unless on WIN32 + Define these if your system does not have these header files. + You might need to manually insert some of the declarations they provide. + +DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, + system_info.dwAllocationGranularity in WIN32, + otherwise 64K. + Also settable using mallopt(M_GRANULARITY, x) + The unit for allocating and deallocating memory from the system. On + most systems with contiguous MORECORE, there is no reason to + make this more than a page. However, systems with MMAP tend to + either require or encourage larger granularities. You can increase + this value to prevent system allocation functions to be called so + often, especially if they are slow. The value must be at least one + page and must be a power of two. Setting to 0 causes initialization + to either page size or win32 region size. (Note: In previous + versions of malloc, the equivalent of this option was called + "TOP_PAD") + +DEFAULT_TRIM_THRESHOLD default: 2MB + Also settable using mallopt(M_TRIM_THRESHOLD, x) + The maximum amount of unused top-most memory to keep before + releasing via malloc_trim in free(). Automatic trimming is mainly + useful in long-lived programs using contiguous MORECORE. Because + trimming via sbrk can be slow on some systems, and can sometimes be + wasteful (in cases where programs immediately afterward allocate + more large chunks) the value should be high enough so that your + overall system performance would improve by releasing this much + memory. As a rough guide, you might set to a value close to the + average size of a process (program) running on your system. + Releasing this much memory would allow such a process to run in + memory. Generally, it is worth tuning trim thresholds when a + program undergoes phases where several large chunks are allocated + and released in ways that can reuse each other's storage, perhaps + mixed with phases where there are no such chunks at all. The trim + value must be greater than page size to have any useful effect. To + disable trimming completely, you can set to MAX_SIZE_T. Note that the trick + some people use of mallocing a huge space and then freeing it at + program startup, in an attempt to reserve system memory, doesn't + have the intended effect under automatic trimming, since that memory + will immediately be returned to the system. + +DEFAULT_MMAP_THRESHOLD default: 256K + Also settable using mallopt(M_MMAP_THRESHOLD, x) + The request size threshold for using MMAP to directly service a + request. Requests of at least this size that cannot be allocated + using already-existing space will be serviced via mmap. (If enough + normal freed space already exists it is used instead.) Using mmap + segregates relatively large chunks of memory so that they can be + individually obtained and released from the host system. A request + serviced through mmap is never reused by any other request (at least + not directly; the system may just so happen to remap successive + requests to the same locations). Segregating space in this way has + the benefits that: Mmapped space can always be individually released + back to the system, which helps keep the system level memory demands + of a long-lived program low. Also, mapped memory doesn't become + `locked' between other chunks, as can happen with normally allocated + chunks, which means that even trimming via malloc_trim would not + release them. However, it has the disadvantage that the space + cannot be reclaimed, consolidated, and then used to service later + requests, as happens with normal chunks. The advantages of mmap + nearly always outweigh disadvantages for "large" chunks, but the + value of "large" may vary across systems. The default is an + empirically derived value that works well in most systems. You can + disable mmap by setting to MAX_SIZE_T. + +MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP + The number of consolidated frees between checks to release + unused segments when freeing. When using non-contiguous segments, + especially with multiple mspaces, checking only for topmost space + doesn't always suffice to trigger trimming. To compensate for this, + free() will, with a period of MAX_RELEASE_CHECK_RATE (or the + current number of segments, if greater) try to release unused + segments to the OS when freeing chunks that result in + consolidation. The best value for this parameter is a compromise + between slowing down frees with relatively costly checks that + rarely trigger versus holding on to unused memory. To effectively + disable, set to MAX_SIZE_T. This may lead to a very slight speed + improvement at the expense of carrying around more memory. +*/ + +/* Version identifier to allow people to support multiple versions */ +#ifndef DLMALLOC_VERSION +#define DLMALLOC_VERSION 20806 +#endif /* DLMALLOC_VERSION */ + +#ifndef DLMALLOC_EXPORT +#define DLMALLOC_EXPORT extern +#endif + +#ifndef WIN32 +#ifdef _WIN32 +#define WIN32 1 +#endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define WIN32 1 +#endif /* _WIN32_WCE */ +#endif /* WIN32 */ +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#define LACKS_SCHED_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef MMAP_CLEARS +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /*MMAP_CLEARS */ +#endif /* WIN32 */ + +#if defined(DARWIN) || defined(_DARWIN) +/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ +#ifndef HAVE_MORECORE +#define HAVE_MORECORE 0 +#define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* HAVE_MORECORE */ +#endif /* DARWIN */ + +#ifndef LACKS_SYS_TYPES_H +#include /* For size_t */ +#endif /* LACKS_SYS_TYPES_H */ + +/* The maximum possible size_t value has all bits set */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ +#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ + (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) +#endif /* USE_LOCKS */ + +#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ +#if ((defined(__GNUC__) && \ + ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ + defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && _MSC_VER>=1310)) +#ifndef USE_SPIN_LOCKS +#define USE_SPIN_LOCKS 1 +#endif /* USE_SPIN_LOCKS */ +#elif USE_SPIN_LOCKS +#error "USE_SPIN_LOCKS defined without implementation" +#endif /* ... locks available... */ +#elif !defined(USE_SPIN_LOCKS) +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS */ + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 +#endif /* ONLY_MSPACES */ +#ifndef MSPACES +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) +#endif /* MALLOC_ALIGNMENT */ +#ifndef FOOTERS +#define FOOTERS 0 +#endif /* FOOTERS */ +#ifndef ABORT +#define ABORT abort() +#endif /* ABORT */ +#ifndef ABORT_ON_ASSERT_FAILURE +#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ +#ifndef PROCEED_ON_ERROR +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ + +#ifndef INSECURE +#define INSECURE 0 +#endif /* INSECURE */ +#ifndef MALLOC_INSPECT_ALL +#define MALLOC_INSPECT_ALL 0 +#endif /* MALLOC_INSPECT_ALL */ +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif /* HAVE_MMAP */ +#ifndef MMAP_CLEARS +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ +#ifndef HAVE_MREMAP +#ifdef linux +#define HAVE_MREMAP 1 +#define _GNU_SOURCE /* Turns on mremap() definition */ +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef HAVE_MORECORE +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ +#if !HAVE_MORECORE +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#define MORECORE_DEFAULT sbrk +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ +#ifndef DEFAULT_GRANULARITY +#if (MORECORE_CONTIGUOUS || defined(WIN32)) +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ +#ifndef DEFAULT_TRIM_THRESHOLD +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef DEFAULT_MMAP_THRESHOLD +#if HAVE_MMAP +#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ +#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef MAX_RELEASE_CHECK_RATE +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ +#ifndef USE_BUILTIN_FFS +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ +#ifndef USE_DEV_RANDOM +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef NO_MALLOC_STATS +#define NO_MALLOC_STATS 0 +#endif /* NO_MALLOC_STATS */ +#ifndef NO_SEGMENT_TRAVERSAL +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ + +/* + mallopt tuning options. SVID/XPG defines four standard parameter + numbers for mallopt, normally defined in malloc.h. None of these + are used in this malloc, so setting them has no effect. But this + malloc does support the following options. +*/ + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any system that has a + /usr/include/malloc.h defining struct mallinfo. The main + declaration needed is the mallinfo struct that is returned (by-copy) + by mallinfo(). The malloinfo struct contains a bunch of fields that + are not even meaningful in this version of malloc. These fields are + are instead filled by mallinfo() with other numbers that might be of + interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else a compliant version is + declared below. These must be precisely the same for mallinfo() to + work. The original SVID version of this struct, defined on most + systems with mallinfo, declares all fields as ints. But some others + define as unsigned long. If your system defines the fields using a + type of different width than listed here, you MUST #include your + system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ +#ifndef STRUCT_MALLINFO_DECLARED +/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ +#define _STRUCT_MALLINFO +#define STRUCT_MALLINFO_DECLARED 1 +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ + +/* + Try to persuade compilers to inline. The most critical functions for + inlining are defined as macros, so these aren't used for them. +*/ + +#ifndef FORCEINLINE + #if defined(__GNUC__) +#define FORCEINLINE __inline __attribute__ ((always_inline)) + #elif defined(_MSC_VER) + #define FORCEINLINE __forceinline + #endif +#endif +#ifndef NOINLINE + #if defined(__GNUC__) + #define NOINLINE __attribute__ ((noinline)) + #elif defined(_MSC_VER) + #define NOINLINE __declspec(noinline) + #else + #define NOINLINE + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#ifndef FORCEINLINE + #define FORCEINLINE inline +#endif +#endif /* __cplusplus */ +#ifndef FORCEINLINE + #define FORCEINLINE +#endif + +#if !ONLY_MSPACES + +/* ------------------- Declarations of public routines ------------------- */ + +#ifndef USE_DL_PREFIX +#define dlcalloc calloc +#define dlfree free +#define dlmalloc malloc +#define dlmemalign memalign +#define dlposix_memalign posix_memalign +#define dlrealloc realloc +#define dlrealloc_in_place realloc_in_place +#define dlvalloc valloc +#define dlpvalloc pvalloc +#define dlmallinfo mallinfo +#define dlmallopt mallopt +#define dlmalloc_trim malloc_trim +#define dlmalloc_stats malloc_stats +#define dlmalloc_usable_size malloc_usable_size +#define dlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define dlmalloc_footprint_limit malloc_footprint_limit +#define dlmalloc_set_footprint_limit malloc_set_footprint_limit +#define dlmalloc_inspect_all malloc_inspect_all +#define dlindependent_calloc independent_calloc +#define dlindependent_comalloc independent_comalloc +#define dlbulk_free bulk_free +#endif /* USE_DL_PREFIX */ + +/* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. +*/ +DLMALLOC_EXPORT void* dlmalloc(size_t); + +/* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cause the current program to abort. +*/ +DLMALLOC_EXPORT void dlfree(void*); + +/* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. +*/ +DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); + +/* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. +*/ +DLMALLOC_EXPORT void* dlrealloc(void*, size_t); + +/* + realloc_in_place(void* p, size_t n) + Resizes the space allocated for p to size n, only if this can be + done without moving p (i.e., only if there is adjacent space + available if n is greater than p's current allocated size, or n is + less than or equal to p's size). This may be used instead of plain + realloc if an alternative allocation strategy is needed upon failure + to expand space; for example, reallocation of a buffer that must be + memory-aligned or cleared. You can use realloc_in_place to trigger + these alternatives only when needed. + + Returns p if successful; otherwise null. +*/ +DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); + +/* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. +*/ +DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); + +/* + int posix_memalign(void** pp, size_t alignment, size_t n); + Allocates a chunk of n bytes, aligned in accord with the alignment + argument. Differs from memalign only in that it (1) assigns the + allocated memory to *pp rather than returning it, (2) fails and + returns EINVAL if the alignment is not a power of two (3) fails and + returns ENOMEM if memory cannot be allocated. +*/ +DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); + +/* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. +*/ +DLMALLOC_EXPORT void* dlvalloc(size_t); + +/* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. To workaround the fact that mallopt is specified to use int, + not size_t parameters, the value -1 is specially treated as the + maximum unsigned size_t value. + + SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) +*/ +DLMALLOC_EXPORT int dlmallopt(int, int); + +/* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint(void); + +/* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. +*/ +DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); + +/* + malloc_footprint_limit(); + Returns the number of bytes that the heap is allowed to obtain from + the system, returning the last value returned by + malloc_set_footprint_limit, or the maximum size_t value if + never set. The returned value reflects a permission. There is no + guarantee that this number of bytes can actually be obtained from + the system. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); + +/* + malloc_set_footprint_limit(); + Sets the maximum number of bytes to obtain from the system, causing + failure returns from malloc and related functions upon attempts to + exceed this value. The argument value may be subject to page + rounding to an enforceable limit; this actual value is returned. + Using an argument of the maximum possible size_t effectively + disables checks. If the argument is less than or equal to the + current malloc_footprint, then all future allocations that require + additional system memory will fail. However, invocation cannot + retroactively deallocate existing used memory. +*/ +DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); + +#if MALLOC_INSPECT_ALL +/* + malloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg); + Traverses the heap and calls the given handler for each managed + region, skipping all bytes that are (or may be) used for bookkeeping + purposes. Traversal does not include include chunks that have been + directly memory mapped. Each reported region begins at the start + address, and continues up to but not including the end address. The + first used_bytes of the region contain allocated data. If + used_bytes is zero, the region is unallocated. The handler is + invoked with the given callback argument. If locks are defined, they + are held during the entire traversal. It is a bad idea to invoke + other malloc functions from within the handler. + + For example, to count the number of in-use chunks with size greater + than 1000, you could write: + static int count = 0; + void count_chunks(void* start, void* end, size_t used, void* arg) { + if (used >= 1000) ++count; + } + then: + malloc_inspect_all(count_chunks, NULL); + + malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. +*/ +DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg); + +#endif /* MALLOC_INSPECT_ALL */ + +#if !NO_MALLINFO +/* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. +*/ +DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); +#endif /* NO_MALLINFO */ + +/* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } +*/ +DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); + +/* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. +*/ +DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); + +/* + bulk_free(void* array[], size_t n_elements) + Frees and clears (sets to null) each non-null pointer in the given + array. This is likely to be faster than freeing them one-by-one. + If footers are used, pointers that have been allocated in different + mspaces are not freed or cleared, and the count of all such pointers + is returned. For large arrays of pointers with poor locality, it + may be worthwhile to sort this array before calling bulk_free. +*/ +DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); + +/* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ +DLMALLOC_EXPORT void* dlpvalloc(size_t); + +/* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. +*/ +DLMALLOC_EXPORT int dlmalloc_trim(size_t); + +/* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. +*/ +DLMALLOC_EXPORT void dlmalloc_stats(void); + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); +*/ +size_t dlmalloc_usable_size(void*); + +#endif /* ONLY_MSPACES */ + +#if MSPACES + +/* + mspace is an opaque type representing an independent + region of space that supports mspace_malloc, etc. +*/ +typedef void* mspace; + +/* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). +*/ +DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); + +/* + destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. +*/ +DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); + +/* + create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. +*/ +DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); + +/* + mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. +*/ +DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); + + +/* + mspace_malloc behaves as malloc, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); + +/* + mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_free is not actually needed. + free may be called instead of mspace_free because freed chunks from + any space are handled by their originating spaces. +*/ +DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); + +/* + mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_realloc is not actually + needed. realloc may be called instead of mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. +*/ +DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); + +/* + mspace_calloc behaves as calloc, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + +/* + mspace_memalign behaves as memalign, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); + +/* + mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. +*/ +DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + +/* + mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. +*/ +DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + +/* + mspace_footprint() returns the number of bytes obtained from the + system for this space. +*/ +DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); + +/* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. +*/ +DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO +/* + mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. +*/ +DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + +/* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; +*/ +DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); + +/* + mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. +*/ +DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); + +/* + mspace_trim behaves as malloc_trim, but + operates within the given space. +*/ +DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); + +/* + An alias for mallopt. +*/ +DLMALLOC_EXPORT int mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +/* + ======================================================================== + To make a fully customizable malloc.h header file, cut everything + above this line, put into file malloc.h, edit to suit, and #include it + on the next line, as well as in programs that use this malloc. + ======================================================================== +*/ + +/* #include "malloc.h" */ + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef _MSC_VER +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* _MSC_VER */ +#if !NO_MALLOC_STATS +#include /* for printing in malloc_stats */ +#endif /* NO_MALLOC_STATS */ +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#undef assert +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#ifndef assert +#define assert(x) +#endif +#define DEBUG 0 +#endif /* DEBUG */ +#if !defined(WIN32) && !defined(LACKS_TIME_H) +#include /* for magic initialization */ +#endif /* WIN32 */ +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ +#ifndef LACKS_STRING_H +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 +#include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ +#ifndef LACKS_UNISTD_H +#include /* for sbrk, sysconf */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ + +/* Declarations for locking */ +#if USE_LOCKS +#ifndef WIN32 +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#elif !defined(LACKS_SCHED_H) +#include +#endif /* solaris or LACKS_SCHED_H */ +#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS +#include +#endif /* USE_RECURSIVE_LOCKS ... */ +#elif defined(_MSC_VER) +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); +LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif defined(WIN32) && defined(__GNUC__) +#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) +#define interlockedexchange __sync_lock_test_and_set +#endif /* Win32 */ +#else /* USE_LOCKS */ +#endif /* USE_LOCKS */ + +#ifndef LOCK_AT_FORK +#define LOCK_AT_FORK 0 +#endif + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + +#ifndef WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* + If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if HAVE_MMAP + +#ifndef WIN32 +#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif /* MAP_ANON */ +#ifdef MAP_ANONYMOUS +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) +#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) +#else /* MAP_ANONYMOUS */ +/* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. +*/ +#define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ +#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#endif /* MAP_ANONYMOUS */ + +#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) + +#else /* WIN32 */ + +/* Win32 MMAP via VirtualAlloc */ +static FORCEINLINE void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static FORCEINLINE void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* This function supports releasing coalesed segments */ +static FORCEINLINE int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; +} + +#define MMAP_DEFAULT(s) win32mmap(s) +#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MREMAP +#ifndef WIN32 +#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#endif /* WIN32 */ +#endif /* HAVE_MREMAP */ + +/** + * Define CALL_MORECORE + */ +#if HAVE_MORECORE + #ifdef MORECORE + #define CALL_MORECORE(S) MORECORE(S) + #else /* MORECORE */ + #define CALL_MORECORE(S) MORECORE_DEFAULT(S) + #endif /* MORECORE */ +#else /* HAVE_MORECORE */ + #define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/** + * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP + */ +#if HAVE_MMAP + #define USE_MMAP_BIT (SIZE_T_ONE) + + #ifdef MMAP + #define CALL_MMAP(s) MMAP(s) + #else /* MMAP */ + #define CALL_MMAP(s) MMAP_DEFAULT(s) + #endif /* MMAP */ + #ifdef MUNMAP + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #else /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) + #endif /* MUNMAP */ + #ifdef DIRECT_MMAP + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #else /* DIRECT_MMAP */ + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) + #endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ZERO) + + #define MMAP(s) MFAIL + #define MUNMAP(a, s) (-1) + #define DIRECT_MMAP(s) MFAIL + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #define CALL_MMAP(s) MMAP(s) + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ + +/** + * Define CALL_MREMAP + */ +#if HAVE_MMAP && HAVE_MREMAP + #ifdef MREMAP + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else /* MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +/* mstate bit set if continguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +/* --------------------------- Lock preliminaries ------------------------ */ + +/* + When locks are defined, there is one global lock, plus + one per-mspace lock. + + The global lock_ensures that mparams.magic and other unique + mparams values are initialized only once. It also protects + sequences of calls to MORECORE. In many cases sys_alloc requires + two calls, that should not be interleaved with calls by other + threads. This does not protect against direct calls to MORECORE + by other threads not using this lock, so there is still code to + cope the best we can on interference. + + Per-mspace locks surround calls to malloc, free, etc. + By default, locks are simple non-reentrant mutexes. + + Because lock-protected regions generally have bounded times, it is + OK to use the supplied simple spinlocks. Spinlocks are likely to + improve performance for lightly contended applications, but worsen + performance under heavy contention. + + If USE_LOCKS is > 1, the definitions of lock routines here are + bypassed, in which case you will need to define the type MLOCK_T, + and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK + and TRY_LOCK. You must also declare a + static MLOCK_T malloc_global_mutex = { initialization values };. + +*/ + +#if !USE_LOCKS +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) (0) +#define DESTROY_LOCK(l) (0) +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() + +#else +#if USE_LOCKS > 1 +/* ----------------------- User-defined locks ------------------------ */ +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(lk) ... */ +/* #define DESTROY_LOCK(lk) ... */ +/* #define ACQUIRE_LOCK(lk) ... */ +/* #define RELEASE_LOCK(lk) ... */ +/* #define TRY_LOCK(lk) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ + +#elif USE_SPIN_LOCKS + +/* First, define CAS_LOCK and CLEAR_LOCK on ints */ +/* Note CAS_LOCK defined to return 0 on success */ + +#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) +#define CLEAR_LOCK(sl) __sync_lock_release(sl) + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +/* Custom spin locks for older gcc on x86 */ +static FORCEINLINE int x86_cas_lock(int *sl) { + int ret; + int val = 1; + int cmp = 0; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(sl)), "0"(cmp) + : "memory", "cc"); + return ret; +} + +static FORCEINLINE void x86_clear_lock(int* sl) { + assert(*sl != 0); + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(sl)), "0"(prev) + : "memory"); +} + +#define CAS_LOCK(sl) x86_cas_lock(sl) +#define CLEAR_LOCK(sl) x86_clear_lock(sl) + +#else /* Win32 MSC */ +#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) + +#endif /* ... gcc spins locks ... */ + +/* How to yield for a spin lock */ +#define SPINS_PER_YIELD 63 +#if defined(_MSC_VER) +#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ +#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) +#elif defined (__SVR4) && defined (__sun) /* solaris */ +#define SPIN_LOCK_YIELD thr_yield(); +#elif !defined(LACKS_SCHED_H) +#define SPIN_LOCK_YIELD sched_yield(); +#else +#define SPIN_LOCK_YIELD +#endif /* ... yield ... */ + +#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 +/* Plain spin locks use single word (embedded in malloc_states) */ +static int spin_acquire_lock(int *sl) { + int spins = 0; + while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } + return 0; +} + +#define MLOCK_T int +#define TRY_LOCK(sl) !CAS_LOCK(sl) +#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) +#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) +#define INITIAL_LOCK(sl) (*sl = 0) +#define DESTROY_LOCK(sl) (0) +static MLOCK_T malloc_global_mutex = 0; + +#else /* USE_RECURSIVE_LOCKS */ +/* types for lock owners */ +#ifdef WIN32 +#define THREAD_ID_T DWORD +#define CURRENT_THREAD GetCurrentThreadId() +#define EQ_OWNER(X,Y) ((X) == (Y)) +#else +/* + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need to + somehow redefine these or not use spin locks. +*/ +#define THREAD_ID_T pthread_t +#define CURRENT_THREAD pthread_self() +#define EQ_OWNER(X,Y) pthread_equal(X, Y) +#endif + +struct malloc_recursive_lock { + int sl; + unsigned int c; + THREAD_ID_T threadid; +}; + +#define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; + +static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { + assert(lk->sl != 0); + if (--lk->c == 0) { + CLEAR_LOCK(&lk->sl); + } +} + +static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + int spins = 0; + for (;;) { + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 0; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 0; + } + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } +} + +static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 1; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 1; + } + return 0; +} + +#define RELEASE_LOCK(lk) recursive_release_lock(lk) +#define TRY_LOCK(lk) recursive_try_lock(lk) +#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) +#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) +#define DESTROY_LOCK(lk) (0) +#endif /* USE_RECURSIVE_LOCKS */ + +#elif defined(WIN32) /* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) +#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) +#define TRY_LOCK(lk) TryEnterCriticalSection(lk) +#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) +#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile LONG malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status, (LONG)1); + return; + } + SleepEx(0, FALSE); + } +} + +#else /* pthreads-based locks */ +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) +#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) +#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) +#define INITIAL_LOCK(lk) pthread_init_lock(lk) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif /* USE_RECURSIVE_LOCKS ... */ + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int pthread_init_lock (MLOCK_T *lk) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; +#endif + if (pthread_mutex_init(lk, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#endif /* ... lock types ... */ + +/* Common code for all lock types */ +#define USE_LOCK_BIT (2U) + +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif + +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif + +#endif /* USE_LOCKS */ + +/* ----------------------- Chunk representations ------------------------ */ + +/* + (The following includes lightly edited explanations by Colin Plumb.) + + The malloc_chunk declaration below is misleading (but accurate and + necessary). It declares a "view" into memory allowing access to + necessary fields at known offsets from a given base. + + Chunks of memory are maintained using a `boundary tag' method as + originally described by Knuth. (See the paper by Paul Wilson + ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such + techniques.) Sizes of free chunks are stored both in the front of + each chunk and at the end. This makes consolidating fragmented + chunks into bigger chunks fast. The head fields also hold bits + representing whether chunks are free or in use. + + Here are some pictures to make it clearer. They are "exploded" to + show that the state of a chunk can be thought of as extending from + the high 31 bits of the head field of its header through the + prev_foot and PINUSE_BIT bit of the following chunk header. + + A chunk that's in use looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk (if P = 0) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | + chunk-> +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| + | Size of next chunk (may or may not be in use) | +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + And if it's free, it looks like this: + + chunk-> +- -+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| + | Size of next chunk (must be in use, or we would have merged)| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- User payload -+ + : | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0| + +-+ + Note that since we always merge adjacent free chunks, the chunks + adjacent to a free chunk must be in use. + + Given a pointer to a chunk (which can be derived trivially from the + payload pointer) we can, in O(1) time, find out whether the adjacent + chunks are free, and if so, unlink them from the lists that they + are on and merge them with the current chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + The P (PINUSE_BIT) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, preventing + access to non-existent (or non-owned) memory. If pinuse is set for + any given chunk, then you CANNOT determine the size of the + previous chunk, and might even get a memory addressing fault when + trying to do so. + + The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of + the chunk size redundantly records whether the current chunk is + inuse (unless the chunk is mmapped). This redundancy enables usage + checks within free and realloc, and reduces indirection when freeing + and consolidating chunks. + + Each freshly allocated chunk must have both cinuse and pinuse set. + That is, each allocated chunk borders either a previously allocated + and still in-use chunk, or the base of its memory arena. This is + ensured by making all allocations from the `lowest' part of any + found chunk. Further, no free chunk physically borders another one, + so each free chunk is known to be preceded and followed by either + inuse chunks or the ends of memory. + + Note that the `foot' of the current chunk is actually represented + as the prev_foot of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The exceptions to all this are + + 1. The special chunk `top' is the top-most available chunk (i.e., + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. + + 3. Chunks allocated via mmap, have both cinuse and pinuse bits + cleared in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use, unless mmapped, in which case both bits are cleared. + + FLAG4_BIT is not used by this malloc, but might be useful in extensions. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define flag4inuse(p) ((p)->head & FLAG4_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define set_flag4(p) ((p)->head |= FLAG4_BIT) +#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If USE_MMAP_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams.magic. + + Max allowed footprint + The maximum allowed bytes to allocate from system (zero means no limit) + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Trim support + Fields holding the amount of unused topmost memory that should trigger + trimming, and a counter to force periodic scanning to release unused + non-topmost segments. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. + + Extension support + A void* pointer and a size_t field that can be used to help implement + extensions to this malloc. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + size_t footprint_limit; /* zero means no limit */ + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams. Note that the non-zeroness of "magic" + also serves as an initialization flag. +*/ + +struct malloc_params { + size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* Ensure mparams initialized */ +#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + +#if !ONLY_MSPACES + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) + +#endif /* !ONLY_MSPACES */ + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#if USE_LOCKS +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) +#else +#define disable_lock(M) +#endif + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#if HAVE_MMAP +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) +#else +#define disable_mmap(M) +#endif + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + + +/* For mmap, use granularity alignment on windows, else page-align */ +#ifdef WIN32 +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* For sys_alloc, enough padding to ensure can malloc request on success */ +#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* + PREACTION should be defined to return 0 on success, and nonzero on + failure. If you are not using locking, you can redefine these to do + anything you like. +*/ + +#if USE_LOCKS +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* + CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. + USAGE_ERROR_ACTION is triggered on detected bad frees and + reallocs. The argument p is an address that might have triggered the + fault. It is ignored by the two predefined actions, but might be + useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + + +/* -------------------------- Debugging setup ---------------------------- */ + +#if ! DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I. Use x86 asm if possible */ +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + _BitScanReverse((DWORD *) &K, (DWORD) X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + +/* index corresponding to given bit. Use x86 asm if possible */ + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = __builtin_ctz(X); \ + I = (bindex_t)J;\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} + +#elif USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* GNUC */ + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probabalistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_inuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Macros for setting head/foot of non-mmapped chunks */ + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +#if LOCK_AT_FORK +static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } +static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } +static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } +#endif /* LOCK_AT_FORK */ + +/* Initialize mparams */ +static int init_mparams(void) { +#ifdef NEED_GLOBAL_LOCK_INIT + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); +#endif + + ACQUIRE_MALLOC_GLOBAL_LOCK(); + if (mparams.magic == 0) { + size_t magic; + size_t psize; + size_t gsize; + +#ifndef WIN32 + psize = malloc_getpagesize; + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); +#else /* WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + psize = system_info.dwPageSize; + gsize = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + } +#endif /* WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) + ABORT; + mparams.granularity = gsize; + mparams.page_size = psize; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if !ONLY_MSPACES + /* Set up lock for main malloc area */ + gm->mflags = mparams.default_mflags; + (void)INITIAL_LOCK(&gm->mutex); +#endif +#if LOCK_AT_FORK + pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child); +#endif + + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + magic = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ +#ifdef WIN32 + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#elif defined(LACKS_TIME_H) + magic = (size_t)&magic ^ (size_t)0x55555555U; +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + /* Until memory modes commonly available, use volatile-write */ + (*(volatile size_t *)(&(mparams.magic))) = magic; + } + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + return 1; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val; + ensure_initialization(); + val = (value == -1)? MAX_SIZE_T : (size_t)value; + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#if DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!pinuse(chunk_plus_offset(p, sz))); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(is_inuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!is_inuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || is_inuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~INUSE_BITS; + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!is_inuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (is_inuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + /*assert(m->topsize == chunksize(m->top)); redundant */ + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ensure_initialization(); + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!is_inuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +#if !NO_MALLOC_STATS +static void internal_malloc_stats(mstate m) { + ensure_initialization(); + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + POSTACTION(m); /* drop lock */ + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + } +} +#endif /* NO_MALLOC_STATS */ + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(B == smallbin_at(M,I) ||\ + (ok_address(M, B) && B->fd == P))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + assert(is_small(DVS));\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) +#define internal_free(m, mem)\ + if (m == gm) dlfree(mem); else mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) dlmalloc(b) +#define internal_free(m, mem) dlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* + Directly mmapped chunks are set up with an offset to the start of + the mmapped region stored in the prev_foot field of the chunk. This + allows reconstruction of the required argument to MUNMAP when freed, + and also allows adjustment of the returned chunk to meet alignment + requirements (especially in memalign). +*/ + +/* Malloc using mmap */ +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (m->footprint_limit != 0) { + size_t fp = m->footprint + mmsize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset; + p->head = psize; + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { + size_t oldsize = chunksize(oldp); + (void)flags; /* placate people compiling -Wunused */ + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, flags); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = psize; + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallmap = m->treemap = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!is_inuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + size_t asize; /* allocation size */ + + ensure_initialization(); + + /* Directly map large chunks, but only if already initialized */ + if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + asize = granularity_align(nb + SYS_ALLOC_PADDING); + if (asize <= nb) + return 0; /* wraparound */ + if (m->footprint_limit != 0) { + size_t fp = m->footprint + asize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + + In all cases, we need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. + */ + + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + size_t ssize = asize; /* sbrk call size */ + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + ACQUIRE_MALLOC_GLOBAL_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + size_t fp; + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + ssize += (page_align((size_t)base) - (size_t)base); + fp = m->footprint + ssize; /* recheck limits */ + if (ssize > nb && ssize < HALF_MAX_SIZE_T && + (m->footprint_limit == 0 || + (fp > m->footprint && fp <= m->footprint_limit)) && + (br = (char*)(CALL_MORECORE(ssize))) == base) { + tbase = base; + tsize = ssize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); + /* Use mem here only if it did continuously extend old space */ + if (ssize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { + tbase = br; + tsize = ssize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (ssize < HALF_MAX_SIZE_T && + ssize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + ssize += esize; + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-ssize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = ssize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + } + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + char* mp = (char*)(CALL_MMAP(asize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = asize; + mmap_flag = USE_MMAP_BIT; + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MALLOC_GLOBAL_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); +#if !ONLY_MSPACES + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else +#endif + { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + /* Only consider most recent segment if traversal suppressed */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + int nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + ++nsegs; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + break; + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? + (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + ensure_initialization(); + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + (void)newsize; /* placate people compiling -Wunused-variable */ + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* Consolidate and bin a chunk. Differs from exported versions + of free mainly in that the chunk need not be marked as inuse. +*/ +static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + mchunkptr prev; + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + m->footprint -= psize; + return; + } + prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ + if (p != m->dv) { + unlink_chunk(m, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + m->dvsize = psize; + set_free_with_pinuse(p, psize, next); + return; + } + } + else { + CORRUPTION_ERROR_ACTION(m); + return; + } + } + if (RTCHECK(ok_address(m, next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == m->top) { + size_t tsize = m->topsize += psize; + m->top = p; + p->head = tsize | PINUSE_BIT; + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + return; + } + else if (next == m->dv) { + size_t dsize = m->dvsize += psize; + m->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + return; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(m, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == m->dv) { + m->dvsize = psize; + return; + } + } + } + else { + set_free_with_pinuse(p, psize, next); + } + insert_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + } +} + +/* ---------------------------- malloc --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +#if !ONLY_MSPACES + +void* dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +/* ---------------------------- free --------------------------- */ + +void dlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +#endif /* !ONLY_MSPACES */ + +/* ------------ Internal support for realloc, memalign, etc -------------- */ + +/* Try to realloc; only in-place unless can_move true */ +static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, + int can_move) { + mchunkptr newp = 0; + size_t oldsize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, oldsize); + if (RTCHECK(ok_address(m, p) && ok_inuse(p) && + ok_next(p, next) && ok_pinuse(next))) { + if (is_mmapped(p)) { + newp = mmap_resize(m, p, nb, can_move); + } + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + else if (next == m->top) { /* extend into top */ + if (oldsize + m->topsize > nb) { + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = p; + } + } + else if (next == m->dv) { /* extend into dv */ + size_t dvs = m->dvsize; + if (oldsize + dvs >= nb) { + size_t dsize = oldsize + dvs - nb; + if (dsize >= MIN_CHUNK_SIZE) { + mchunkptr r = chunk_plus_offset(p, nb); + mchunkptr n = chunk_plus_offset(r, dsize); + set_inuse(m, p, nb); + set_size_and_pinuse_of_free_chunk(r, dsize); + clear_pinuse(n); + m->dvsize = dsize; + m->dv = r; + } + else { /* exhaust dv */ + size_t newsize = oldsize + dvs; + set_inuse(m, p, newsize); + m->dvsize = 0; + m->dv = 0; + } + newp = p; + } + } + else if (!cinuse(next)) { /* extend into next free chunk */ + size_t nextsize = chunksize(next); + if (oldsize + nextsize >= nb) { + size_t rsize = oldsize + nextsize - nb; + unlink_chunk(m, next, nextsize); + if (rsize < MIN_CHUNK_SIZE) { + size_t newsize = oldsize + nextsize; + set_inuse(m, p, newsize); + } + else { + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + } + } + else { + USAGE_ERROR_ACTION(m, chunk2mem(p)); + } + return newp; +} + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + mem = internal_malloc(m, req); + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (PREACTION(m)) + return 0; + if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = newsize; + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + dispose_chunk(m, p, leadsize); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + dispose_chunk(m, remainder, remainder_size); + } + } + + mem = chunk2mem(p); + assert (chunksize(p) >= nb); + assert(((size_t)mem & (alignment - 1)) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + } + } + return mem; +} + +/* + Common support for independent_X routines, handling + all of the combinations that can result. + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed +*/ +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + ensure_initialization(); + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#if DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + +/* Try to free all pointers in the given array. + Note: this could be made faster, by delaying consolidation, + at the price of disabling some user integrity checks, We + still optimize some consolidations by combining adjacent + chunks before freeing, which will occur often if allocated + with ialloc or the array is sorted. +*/ +static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { + size_t unfreed = 0; + if (!PREACTION(m)) { + void** a; + void** fence = &(array[nelem]); + for (a = array; a != fence; ++a) { + void* mem = *a; + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t psize = chunksize(p); +#if FOOTERS + if (get_mstate_for(p) != m) { + ++unfreed; + continue; + } +#endif + check_inuse_chunk(m, p); + *a = 0; + if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { + void ** b = a + 1; /* try to merge with next chunk */ + mchunkptr next = next_chunk(p); + if (b != fence && *b == chunk2mem(next)) { + size_t newsize = chunksize(next) + psize; + set_inuse(m, p, newsize); + *b = chunk2mem(p); + } + else + dispose_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + break; + } + } + } + if (should_trim(m, m->topsize)) + sys_trim(m, 0); + POSTACTION(m); + } + return unfreed; +} + +/* Traversal */ +#if MALLOC_INSPECT_ALL +static void internal_inspect_all(mstate m, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + if (is_initialized(m)) { + mchunkptr top = m->top; + msegmentptr s; + for (s = &m->seg; s != 0; s = s->next) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { + mchunkptr next = next_chunk(q); + size_t sz = chunksize(q); + size_t used; + void* start; + if (is_inuse(q)) { + used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ + start = chunk2mem(q); + } + else { + used = 0; + if (is_small(sz)) { /* offset by possible bookkeeping */ + start = (void*)((char*)q + sizeof(struct malloc_chunk)); + } + else { + start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); + } + } + if (start < (void*)next) /* skip if all space is bookkeeping */ + handler(start, next, used, arg); + if (q == top) + break; + q = next; + } + } + } +} +#endif /* MALLOC_INSPECT_ALL */ + +/* ------------------ Exported realloc, memalign, etc -------------------- */ + +#if !ONLY_MSPACES + +void* dlrealloc(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem == 0) { + mem = dlmalloc(bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) { + dlfree(oldmem); + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = internal_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + } + } + } + return mem; +} + +void* dlrealloc_in_place(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; +} + +void* dlmemalign(size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) { + return dlmalloc(bytes); + } + return internal_memalign(gm, alignment, bytes); +} + +int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment == MALLOC_ALIGNMENT) + mem = dlmalloc(bytes); + else { + size_t d = alignment / sizeof(void*); + size_t r = alignment % sizeof(void*); + if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) + return EINVAL; + else if (bytes <= MAX_REQUEST - alignment) { + if (alignment < MIN_CHUNK_SIZE) + alignment = MIN_CHUNK_SIZE; + mem = internal_memalign(gm, alignment, bytes); + } + } + if (mem == 0) + return ENOMEM; + else { + *pp = mem; + return 0; + } +} + +void* dlvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, bytes); +} + +void* dlpvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +size_t dlbulk_free(void* array[], size_t nelem) { + return internal_bulk_free(gm, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void dlmalloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + ensure_initialization(); + if (!PREACTION(gm)) { + internal_inspect_all(gm, handler, arg); + POSTACTION(gm); + } +} +#endif /* MALLOC_INSPECT_ALL */ + +int dlmalloc_trim(size_t pad) { + int result = 0; + ensure_initialization(); + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t dlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +size_t dlmalloc_footprint_limit(void) { + size_t maf = gm->footprint_limit; + return maf == 0 ? MAX_SIZE_T : maf; +} + +size_t dlmalloc_set_footprint_limit(size_t bytes) { + size_t result; /* invert sense of 0 */ + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + return gm->footprint_limit = result; +} + +#if !NO_MALLINFO +struct mallinfo dlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +#if !NO_MALLOC_STATS +void dlmalloc_stats() { + internal_malloc_stats(gm); +} +#endif /* NO_MALLOC_STATS */ + +int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +size_t dlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +#endif /* !ONLY_MSPACES */ + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + (void)INITIAL_LOCK(&m->mutex); + msp->head = (msize|INUSE_BITS); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + m->mflags = mparams.default_mflags; + m->extp = 0; + m->exts = 0; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = USE_MMAP_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} + +mspace create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +int mspace_track_large_chunks(mspace msp, int enable) { + int ret = 0; + mstate ms = (mstate)msp; + if (!PREACTION(ms)) { + if (!use_mmap(ms)) { + ret = 1; + } + if (!enable) { + enable_mmap(ms); + } else { + disable_mmap(ms); + } + POSTACTION(ms); + } + return ret; +} + +size_t destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + (void)base; /* placate people compiling -Wunused-variable */ + sp = sp->next; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} + +/* + mspace versions of routines are near-clones of the global + versions. This is not so nice but better than the alternatives. +*/ + +void* mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + + postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + (void)msp; /* placate people compiling -Wunused */ +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +} + +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem == 0) { + mem = mspace_malloc(msp, bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) { + mspace_free(msp, oldmem); + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = mspace_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + mspace_free(m, oldmem); + } + } + } + } + return mem; +} + +void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + (void)msp; /* placate people compiling -Wunused */ + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; +} + +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (alignment <= MALLOC_ALIGNMENT) + return mspace_malloc(msp, bytes); + return internal_memalign(ms, alignment, bytes); +} + +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { + return internal_bulk_free((mstate)msp, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void mspace_inspect_all(mspace msp, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + internal_inspect_all(ms, handler, arg); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* MALLOC_INSPECT_ALL */ + +int mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +#if !NO_MALLOC_STATS +void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* NO_MALLOC_STATS */ + +size_t mspace_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_max_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_footprint_limit(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + size_t maf = ms->footprint_limit; + result = (maf == 0) ? MAX_SIZE_T : maf; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + ms->footprint_limit = result; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +#if !NO_MALLINFO +struct mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +size_t mspace_usable_size(const void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + + +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* + Guidelines for creating a custom version of MORECORE: + + * For best performance, MORECORE should allocate in multiples of pagesize. + * MORECORE may allocate more memory than requested. (Or even less, + but this will usually result in a malloc failure.) + * MORECORE must not allocate memory when given argument zero, but + instead return one past the end address of memory from previous + nonzero call. + * For best performance, consecutive calls to MORECORE with positive + arguments should return increasing addresses, indicating that + space has been contiguously extended. + * Even though consecutive calls to MORECORE need not return contiguous + addresses, it must be OK for malloc'ed chunks to span multiple + regions in those cases where they do happen to be contiguous. + * MORECORE need not handle negative arguments -- it may instead + just return MFAIL when given negative arguments. + Negative arguments are always multiples of pagesize. MORECORE + must not misinterpret negative args as large positive unsigned + args. You can suppress all such calls from even occurring by defining + MORECORE_CANNOT_TRIM, + + As an example alternative MORECORE, here is a custom allocator + kindly contributed for pre-OSX macOS. It uses virtually but not + necessarily physically contiguous non-paged memory (locked in, + present and won't get swapped out). You can use it by uncommenting + this section, adding some #includes, and setting up the appropriate + defines above: + + #define MORECORE osMoreCore + + There is also a shutdown routine that should somehow be called for + cleanup upon program exit. + + #define MAX_POOL_ENTRIES 100 + #define MINIMUM_MORECORE_SIZE (64 * 1024U) + static int next_os_pool; + void *our_os_pools[MAX_POOL_ENTRIES]; + + void *osMoreCore(int size) + { + void *ptr = 0; + static void *sbrk_top = 0; + + if (size > 0) + { + if (size < MINIMUM_MORECORE_SIZE) + size = MINIMUM_MORECORE_SIZE; + if (CurrentExecutionLevel() == kTaskLevel) + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + if (ptr == 0) + { + return (void *) MFAIL; + } + // save ptrs so they can be freed during cleanup + our_os_pools[next_os_pool] = ptr; + next_os_pool++; + ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); + sbrk_top = (char *) ptr + size; + return ptr; + } + else if (size < 0) + { + // we don't currently support shrink behavior + return (void *) MFAIL; + } + else + { + return sbrk_top; + } + } + + // cleanup any allocated memory pools + // called as last thing before shutting down driver + + void osCleanupMem(void) + { + void **ptr; + + for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) + if (*ptr) + { + PoolDeallocate(*ptr); + *ptr = 0; + } + } + +*/ + + +/* ----------------------------------------------------------------------- +History: + v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea + * fix bad comparison in dlposix_memalign + * don't reuse adjusted asize in sys_alloc + * add LOCK_AT_FORK -- thanks to Kirill Artamonov for the suggestion + * reduce compiler warnings -- thanks to all who reported/suggested these + + v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) + * Always perform unlink checks unless INSECURE + * Add posix_memalign. + * Improve realloc to expand in more cases; expose realloc_in_place. + Thanks to Peter Buhr for the suggestion. + * Add footprint_limit, inspect_all, bulk_free. Thanks + to Barry Hayes and others for the suggestions. + * Internal refactorings to avoid calls while holding locks + * Use non-reentrant locks by default. Thanks to Roland McGrath + for the suggestion. + * Small fixes to mspace_destroy, reset_on_error. + * Various configuration extensions/changes. Thanks + to all who contributed these. + + V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu) + * Update Creative Commons URL + + V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) + * Use zeros instead of prev foot for is_mmapped + * Add mspace_track_large_chunks; thanks to Jean Brouwers + * Fix set_inuse in internal_realloc; thanks to Jean Brouwers + * Fix insufficient sys_alloc padding when using 16byte alignment + * Fix bad error check in mspace_footprint + * Adaptations for ptmalloc; thanks to Wolfram Gloger. + * Reentrant spin locks; thanks to Earl Chew and others + * Win32 improvements; thanks to Niall Douglas and Earl Chew + * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options + * Extension hook in malloc_state + * Various small adjustments to reduce warnings on some compilers + * Various configuration extensions/changes for more platforms. Thanks + to all who contributed these. + + V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) + * Add max_footprint functions + * Ensure all appropriate literals are size_t + * Fix conditional compilation problem for some #define settings + * Avoid concatenating segments with the one provided + in create_mspace_with_base + * Rename some variables to avoid compiler shadowing warnings + * Use explicit lock initialization. + * Better handling of sbrk interference. + * Simplify and fix segment insertion, trimming and mspace_destroy + * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x + * Thanks especially to Dennis Flanagan for help on these. + + V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) + * Fix memalign brace error. + + V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) + * Fix improper #endif nesting in C++ + * Add explicit casts needed for C++ + + V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) + * Use trees for large bins + * Support mspaces + * Use segments to unify sbrk-based and mmap-based system allocation, + removing need for emulation on most platforms without sbrk. + * Default safety checks + * Optional footer checks. Thanks to William Robertson for the idea. + * Internal code refactoring + * Incorporate suggestions and platform-specific changes. + Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, + Aaron Bachmann, Emery Berger, and others. + * Speed up non-fastbin processing enough to remove fastbins. + * Remove useless cfree() to avoid conflicts with other apps. + * Remove internal memcpy, memset. Compilers handle builtins better. + * Remove some options that no one ever used and rename others. + + V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) + * Fix malloc_state bitmap array misdeclaration + + V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) + * Allow tuning of FIRST_SORTED_BIN_SIZE + * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. + * Better detection and support for non-contiguousness of MORECORE. + Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger + * Bypass most of malloc if no frees. Thanks To Emery Berger. + * Fix freeing of old top non-contiguous chunk im sysmalloc. + * Raised default trim and map thresholds to 256K. + * Fix mmap-related #defines. Thanks to Lubos Lunak. + * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. + * Branch-free bin calculation + * Default trim and mmap thresholds now 256K. + + V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) + * Introduce independent_comalloc and independent_calloc. + Thanks to Michael Pachos for motivation and help. + * Make optional .h file available + * Allow > 2GB requests on 32bit systems. + * new WIN32 sbrk, mmap, munmap, lock code from . + Thanks also to Andreas Mueller , + and Anonymous. + * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for + helping test this.) + * memalign: check alignment arg + * realloc: don't try to shift chunks backwards, since this + leads to more fragmentation in some programs and doesn't + seem to help in any others. + * Collect all cases in malloc requiring system memory into sysmalloc + * Use mmap as backup to sbrk + * Place all internal state in malloc_state + * Introduce fastbins (although similar to 2.5.1) + * Many minor tunings and cosmetic improvements + * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK + * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS + Thanks to Tony E. Bennett and others. + * Include errno.h to support default failure action. + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/dlmalloc-rs/src/dlmalloc.rs b/dlmalloc-rs/src/dlmalloc.rs new file mode 100644 index 0000000000..f0bf7cada0 --- /dev/null +++ b/dlmalloc-rs/src/dlmalloc.rs @@ -0,0 +1,1900 @@ +// This is a version of dlmalloc.c ported to Rust. You can find the original +// source at ftp://g.oswego.edu/pub/misc/malloc.c +// +// The original source was written by Doug Lea and released to the public domain + +// TODO +#![allow(missing_docs)] + +macro_rules! debug_assert { + ($($arg:tt)*) => { + if cfg!(all(feature = "debug", debug_assertions)) { + assert!($($arg)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($arg:tt)*) => { + if cfg!(all(feature = "debug", debug_assertions)) { + assert_eq!($($arg)*); + } + }; +} + +use core::{cmp, mem, ptr}; + +use crate::Allocator; + +pub struct Dlmalloc { + smallmap: u32, + treemap: u32, + smallbins: [*mut Chunk; (NSMALLBINS + 1) * 2], + treebins: [*mut TreeChunk; NTREEBINS], + dvsize: usize, + topsize: usize, + dv: *mut Chunk, + top: *mut Chunk, + footprint: usize, + max_footprint: usize, + seg: Segment, + trim_check: usize, + least_addr: *mut u8, + release_checks: usize, + system_allocator: A, +} +unsafe impl Send for Dlmalloc {} + +// TODO: document this +const NSMALLBINS: usize = 32; +const NTREEBINS: usize = 32; +const SMALLBIN_SHIFT: usize = 3; +const TREEBIN_SHIFT: usize = 8; + +const NSMALLBINS_U32: u32 = NSMALLBINS as u32; +const NTREEBINS_U32: u32 = NTREEBINS as u32; + +// TODO: runtime configurable? documentation? +const DEFAULT_GRANULARITY: usize = 64 * 1024; +const DEFAULT_TRIM_THRESHOLD: usize = 2 * 1024 * 1024; +const MAX_RELEASE_CHECK_RATE: usize = 4095; + +#[repr(C)] +struct Chunk { + prev_foot: usize, + head: usize, + prev: *mut Chunk, + next: *mut Chunk, +} + +#[repr(C)] +struct TreeChunk { + chunk: Chunk, + child: [*mut TreeChunk; 2], + parent: *mut TreeChunk, + index: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Segment { + base: *mut u8, + size: usize, + next: *mut Segment, + flags: u32, +} + +fn align_up(a: usize, alignment: usize) -> usize { + debug_assert!(alignment.is_power_of_two()); + (a + (alignment - 1)) & !(alignment - 1) +} + +fn left_bits(x: u32) -> u32 { + (x << 1) | (!(x << 1)).wrapping_add(1) +} + +fn least_bit(x: u32) -> u32 { + x & (!x + 1) +} + +fn leftshift_for_tree_index(x: u32) -> u32 { + let x = usize::try_from(x).unwrap(); + if x == NTREEBINS - 1 { + 0 + } else { + (mem::size_of::() * 8 - 1 - ((x >> 1) + TREEBIN_SHIFT - 2)) as u32 + } +} + +impl Dlmalloc { + pub const fn new(system_allocator: A) -> Dlmalloc { + Dlmalloc { + smallmap: 0, + treemap: 0, + smallbins: [ptr::null_mut(); (NSMALLBINS + 1) * 2], + treebins: [ptr::null_mut(); NTREEBINS], + dvsize: 0, + topsize: 0, + dv: ptr::null_mut(), + top: ptr::null_mut(), + footprint: 0, + max_footprint: 0, + seg: Segment { + base: ptr::null_mut(), + size: 0, + next: ptr::null_mut(), + flags: 0, + }, + trim_check: 0, + least_addr: ptr::null_mut(), + release_checks: 0, + system_allocator, + } + } + + pub fn allocator(&self) -> &A { + &self.system_allocator + } +} + +impl Dlmalloc { + // TODO: can we get rid of this? + pub fn malloc_alignment(&self) -> usize { + mem::size_of::() * 2 + } + + // TODO: dox + fn chunk_overhead(&self) -> usize { + mem::size_of::() + } + + fn mmap_chunk_overhead(&self) -> usize { + 2 * mem::size_of::() + } + + // TODO: dox + fn min_large_size(&self) -> usize { + 1 << TREEBIN_SHIFT + } + + // TODO: dox + fn max_small_size(&self) -> usize { + self.min_large_size() - 1 + } + + // TODO: dox + fn max_small_request(&self) -> usize { + self.max_small_size() - (self.malloc_alignment() - 1) - self.chunk_overhead() + } + + // TODO: dox + fn min_chunk_size(&self) -> usize { + align_up(mem::size_of::(), self.malloc_alignment()) + } + + // TODO: dox + fn min_request(&self) -> usize { + self.min_chunk_size() - self.chunk_overhead() - 1 + } + + // TODO: dox + fn max_request(&self) -> usize { + // min_sys_alloc_space: the largest `X` such that + // pad_request(X - 1) -- minus 1, because requests of exactly + // `max_request` will not be honored + // + self.top_foot_size() + // + self.malloc_alignment() + // + DEFAULT_GRANULARITY + // == + // usize::MAX + let min_sys_alloc_space = + ((!0 - (DEFAULT_GRANULARITY + self.top_foot_size() + self.malloc_alignment()) + 1) + & !self.malloc_alignment()) + - self.chunk_overhead() + + 1; + + cmp::min((!self.min_chunk_size() + 1) << 2, min_sys_alloc_space) + } + + fn pad_request(&self, amt: usize) -> usize { + align_up(amt + self.chunk_overhead(), self.malloc_alignment()) + } + + fn small_index(&self, size: usize) -> u32 { + (size >> SMALLBIN_SHIFT) as u32 + } + + fn small_index2size(&self, idx: u32) -> usize { + usize::try_from(idx).unwrap() << SMALLBIN_SHIFT + } + + fn is_small(&self, s: usize) -> bool { + s >> SMALLBIN_SHIFT < NSMALLBINS + } + + fn is_aligned(&self, a: usize) -> bool { + a & (self.malloc_alignment() - 1) == 0 + } + + fn align_offset(&self, addr: *mut u8) -> usize { + addr.align_offset(self.malloc_alignment()) + } + + fn align_offset_usize(&self, addr: usize) -> usize { + align_up(addr, self.malloc_alignment()) - addr + } + + fn top_foot_size(&self) -> usize { + self.align_offset_usize(Chunk::mem_offset()) + + self.pad_request(mem::size_of::()) + + self.min_chunk_size() + } + + fn mmap_foot_pad(&self) -> usize { + 4 * mem::size_of::() + } + + fn align_as_chunk(&self, ptr: *mut u8) -> *mut Chunk { + unsafe { + let chunk = Chunk::to_mem(ptr.cast()); + ptr.add(self.align_offset(chunk)).cast() + } + } + + fn request2size(&self, req: usize) -> usize { + if req < self.min_request() { + self.min_chunk_size() + } else { + self.pad_request(req) + } + } + + unsafe fn overhead_for(&self, p: *mut Chunk) -> usize { + if Chunk::mmapped(p) { + self.mmap_chunk_overhead() + } else { + self.chunk_overhead() + } + } + + pub unsafe fn calloc_must_clear(&self, ptr: *mut u8) -> bool { + !self.system_allocator.allocates_zeros() || !Chunk::mmapped(Chunk::from_mem(ptr)) + } + + pub unsafe fn malloc(&mut self, size: usize) -> *mut u8 { + self.check_malloc_state(); + + let nb; + if size <= self.max_small_request() { + nb = self.request2size(size); + let mut idx = self.small_index(nb); + let smallbits = self.smallmap >> idx; + + // Check the bin for `idx` (the lowest bit) but also check the next + // bin up to use that to satisfy our request, if needed. + if smallbits & 0b11 != 0 { + // If our the lowest bit, our `idx`, is unset then bump up the + // index as we'll be using the next bucket up. + idx += !smallbits & 1; + + let b = self.smallbin_at(idx); + let p = (*b).prev; + self.unlink_first_small_chunk(b, p, idx); + let smallsize = self.small_index2size(idx); + Chunk::set_inuse_and_pinuse(p, smallsize); + let ret = Chunk::to_mem(p); + self.check_malloced_chunk(ret, nb); + return ret; + } + + if nb > self.dvsize { + // If there's some other bin with some memory, then we just use + // the next smallest bin + if smallbits != 0 { + let leftbits = (smallbits << idx) & left_bits(1 << idx); + let leastbit = least_bit(leftbits); + let i = leastbit.trailing_zeros(); + let b = self.smallbin_at(i); + let p = (*b).prev; + debug_assert_eq!(Chunk::size(p), self.small_index2size(i)); + self.unlink_first_small_chunk(b, p, i); + let smallsize = self.small_index2size(i); + let rsize = smallsize - nb; + if mem::size_of::() != 4 && rsize < self.min_chunk_size() { + Chunk::set_inuse_and_pinuse(p, smallsize); + } else { + Chunk::set_size_and_pinuse_of_inuse_chunk(p, nb); + let r = Chunk::plus_offset(p, nb); + Chunk::set_size_and_pinuse_of_free_chunk(r, rsize); + self.replace_dv(r, rsize); + } + let ret = Chunk::to_mem(p); + self.check_malloced_chunk(ret, nb); + return ret; + } else if self.treemap != 0 { + let mem = self.tmalloc_small(nb); + if !mem.is_null() { + self.check_malloced_chunk(mem, nb); + self.check_malloc_state(); + return mem; + } + } + } + } else if size >= self.max_request() { + // TODO: translate this to unsupported + return ptr::null_mut(); + } else { + nb = self.pad_request(size); + if self.treemap != 0 { + let mem = self.tmalloc_large(nb); + if !mem.is_null() { + self.check_malloced_chunk(mem, nb); + self.check_malloc_state(); + return mem; + } + } + } + + // use the `dv` node if we can, splitting it if necessary or otherwise + // exhausting the entire chunk + if nb <= self.dvsize { + let rsize = self.dvsize - nb; + let p = self.dv; + if rsize >= self.min_chunk_size() { + self.dv = Chunk::plus_offset(p, nb); + self.dvsize = rsize; + let r = self.dv; + Chunk::set_size_and_pinuse_of_free_chunk(r, rsize); + Chunk::set_size_and_pinuse_of_inuse_chunk(p, nb); + } else { + let dvs = self.dvsize; + self.dvsize = 0; + self.dv = ptr::null_mut(); + Chunk::set_inuse_and_pinuse(p, dvs); + } + let ret = Chunk::to_mem(p); + self.check_malloced_chunk(ret, nb); + self.check_malloc_state(); + return ret; + } + + // Split the top node if we can + if nb < self.topsize { + self.topsize -= nb; + let rsize = self.topsize; + let p = self.top; + self.top = Chunk::plus_offset(p, nb); + let r = self.top; + (*r).head = rsize | PINUSE; + Chunk::set_size_and_pinuse_of_inuse_chunk(p, nb); + self.check_top_chunk(self.top); + let ret = Chunk::to_mem(p); + self.check_malloced_chunk(ret, nb); + self.check_malloc_state(); + return ret; + } + + self.sys_alloc(nb) + } + + /// allocates system resources + unsafe fn sys_alloc(&mut self, size: usize) -> *mut u8 { + self.check_malloc_state(); + // keep in sync with max_request + let asize = align_up( + size + self.top_foot_size() + self.malloc_alignment(), + DEFAULT_GRANULARITY, + ); + + let (tbase, tsize, flags) = self.system_allocator.alloc(asize); + if tbase.is_null() { + return tbase; + } + + self.footprint += tsize; + self.max_footprint = cmp::max(self.max_footprint, self.footprint); + + if self.top.is_null() { + if self.least_addr.is_null() || tbase < self.least_addr { + self.least_addr = tbase; + } + self.seg.base = tbase; + self.seg.size = tsize; + self.seg.flags = flags; + self.release_checks = MAX_RELEASE_CHECK_RATE; + self.init_bins(); + let tsize = tsize - self.top_foot_size(); + self.init_top(tbase.cast(), tsize); + // let mn = Chunk::next(Chunk::from_mem(self as *mut _ as *mut u8)); + // let top_foot_size = self.top_foot_size(); + // self.init_top(mn, tbase as usize + tsize - mn as usize - top_foot_size); + } else { + let mut sp: *mut Segment = &mut self.seg; + while !sp.is_null() && tbase != Segment::top(sp) { + sp = (*sp).next; + } + if !sp.is_null() + && !Segment::is_extern(sp) + && Segment::sys_flags(sp) == flags + && Segment::holds(sp, self.top.cast()) + { + (*sp).size += tsize; + let ptr = self.top; + let size = self.topsize + tsize; + self.init_top(ptr, size); + } else { + self.least_addr = cmp::min(tbase, self.least_addr); + let mut sp: *mut Segment = &mut self.seg; + while !sp.is_null() && (*sp).base != tbase.add(tsize) { + sp = (*sp).next; + } + if !sp.is_null() && !Segment::is_extern(sp) && Segment::sys_flags(sp) == flags { + let oldbase = (*sp).base; + (*sp).base = tbase; + (*sp).size += tsize; + return self.prepend_alloc(tbase, oldbase, size); + } else { + self.add_segment(tbase, tsize, flags); + } + } + } + + if size < self.topsize { + self.topsize -= size; + let rsize = self.topsize; + let p = self.top; + self.top = Chunk::plus_offset(p, size); + let r = self.top; + (*r).head = rsize | PINUSE; + Chunk::set_size_and_pinuse_of_inuse_chunk(p, size); + let ret = Chunk::to_mem(p); + self.check_top_chunk(self.top); + self.check_malloced_chunk(ret, size); + self.check_malloc_state(); + return ret; + } + + return ptr::null_mut(); + } + + pub unsafe fn realloc(&mut self, oldmem: *mut u8, bytes: usize) -> *mut u8 { + if bytes >= self.max_request() { + return ptr::null_mut(); + } + let nb = self.request2size(bytes); + let oldp = Chunk::from_mem(oldmem); + let newp = self.try_realloc_chunk(oldp, nb, true); + if !newp.is_null() { + self.check_inuse_chunk(newp); + return Chunk::to_mem(newp); + } + let ptr = self.malloc(bytes); + if !ptr.is_null() { + let oc = Chunk::size(oldp) - self.overhead_for(oldp); + ptr::copy_nonoverlapping(oldmem, ptr, cmp::min(oc, bytes)); + self.free(oldmem); + } + return ptr; + } + + unsafe fn try_realloc_chunk(&mut self, p: *mut Chunk, nb: usize, can_move: bool) -> *mut Chunk { + let oldsize = Chunk::size(p); + let next = Chunk::plus_offset(p, oldsize); + + if Chunk::mmapped(p) { + self.mmap_resize(p, nb, can_move) + } else if oldsize >= nb { + let rsize = oldsize - nb; + if rsize >= self.min_chunk_size() { + let r = Chunk::plus_offset(p, nb); + Chunk::set_inuse(p, nb); + Chunk::set_inuse(r, rsize); + self.dispose_chunk(r, rsize); + } + p + } else if next == self.top { + // extend into top + if oldsize + self.topsize <= nb { + return ptr::null_mut(); + } + let newsize = oldsize + self.topsize; + let newtopsize = newsize - nb; + let newtop = Chunk::plus_offset(p, nb); + Chunk::set_inuse(p, nb); + (*newtop).head = newtopsize | PINUSE; + self.top = newtop; + self.topsize = newtopsize; + p + } else if next == self.dv { + // extend into dv + let dvs = self.dvsize; + if oldsize + dvs < nb { + return ptr::null_mut(); + } + let dsize = oldsize + dvs - nb; + if dsize >= self.min_chunk_size() { + let r = Chunk::plus_offset(p, nb); + let n = Chunk::plus_offset(r, dsize); + Chunk::set_inuse(p, nb); + Chunk::set_size_and_pinuse_of_free_chunk(r, dsize); + Chunk::clear_pinuse(n); + self.dvsize = dsize; + self.dv = r; + } else { + // exhaust dv + let newsize = oldsize + dvs; + Chunk::set_inuse(p, newsize); + self.dvsize = 0; + self.dv = ptr::null_mut(); + } + return p; + } else if !Chunk::cinuse(next) { + // extend into the next free chunk + let nextsize = Chunk::size(next); + if oldsize + nextsize < nb { + return ptr::null_mut(); + } + let rsize = oldsize + nextsize - nb; + self.unlink_chunk(next, nextsize); + if rsize < self.min_chunk_size() { + let newsize = oldsize + nextsize; + Chunk::set_inuse(p, newsize); + } else { + let r = Chunk::plus_offset(p, nb); + Chunk::set_inuse(p, nb); + Chunk::set_inuse(r, rsize); + self.dispose_chunk(r, rsize); + } + p + } else { + ptr::null_mut() + } + } + + unsafe fn mmap_resize(&mut self, oldp: *mut Chunk, nb: usize, can_move: bool) -> *mut Chunk { + let oldsize = Chunk::size(oldp); + // Can't shrink mmap regions below a small size + if self.is_small(nb) { + return ptr::null_mut(); + } + + // Keep the old chunk if it's big enough but not too big + if oldsize >= nb + mem::size_of::() && (oldsize - nb) <= (DEFAULT_GRANULARITY << 1) { + return oldp; + } + + let offset = (*oldp).prev_foot; + let oldmmsize = oldsize + offset + self.mmap_foot_pad(); + let newmmsize = + self.mmap_align(nb + 6 * mem::size_of::() + self.malloc_alignment() - 1); + let ptr = self.system_allocator.remap( + oldp.cast::().sub(offset), + oldmmsize, + newmmsize, + can_move, + ); + if ptr.is_null() { + return ptr::null_mut(); + } + let newp = ptr.add(offset).cast::(); + let psize = newmmsize - offset - self.mmap_foot_pad(); + (*newp).head = psize; + (*Chunk::plus_offset(newp, psize)).head = Chunk::fencepost_head(); + (*Chunk::plus_offset(newp, psize + mem::size_of::())).head = 0; + self.least_addr = cmp::min(ptr, self.least_addr); + self.footprint = self.footprint + newmmsize - oldmmsize; + self.max_footprint = cmp::max(self.max_footprint, self.footprint); + self.check_mmapped_chunk(newp); + return newp; + } + + fn mmap_align(&self, a: usize) -> usize { + align_up(a, self.system_allocator.page_size()) + } + + // Only call this with power-of-two alignment and alignment > + // `self.malloc_alignment()` + pub unsafe fn memalign(&mut self, mut alignment: usize, bytes: usize) -> *mut u8 { + if alignment < self.min_chunk_size() { + alignment = self.min_chunk_size(); + } + if bytes >= self.max_request() - alignment { + return ptr::null_mut(); + } + let nb = self.request2size(bytes); + let req = nb + alignment + self.min_chunk_size() - self.chunk_overhead(); + let mem = self.malloc(req); + if mem.is_null() { + return mem; + } + let mut p = Chunk::from_mem(mem); + if mem as usize & (alignment - 1) != 0 { + // Here we find an aligned sopt inside the chunk. Since we need to + // give back leading space in a chunk of at least `min_chunk_size`, + // if the first calculation places us at a spot with less than + // `min_chunk_size` leader we can move to the next aligned spot. + // we've allocated enough total room so that this is always possible + let br = + Chunk::from_mem(((mem as usize + alignment - 1) & (!alignment + 1)) as *mut u8); + let pos = if (br as usize - p as usize) > self.min_chunk_size() { + br.cast::() + } else { + br.cast::().add(alignment) + }; + let newp = pos.cast::(); + let leadsize = pos as usize - p as usize; + let newsize = Chunk::size(p) - leadsize; + + // for mmapped chunks just adjust the offset + if Chunk::mmapped(p) { + (*newp).prev_foot = (*p).prev_foot + leadsize; + (*newp).head = newsize; + } else { + // give back the leader, use the rest + Chunk::set_inuse(newp, newsize); + Chunk::set_inuse(p, leadsize); + self.dispose_chunk(p, leadsize); + } + p = newp; + } + + // give back spare room at the end + if !Chunk::mmapped(p) { + let size = Chunk::size(p); + if size > nb + self.min_chunk_size() { + let remainder_size = size - nb; + let remainder = Chunk::plus_offset(p, nb); + Chunk::set_inuse(p, nb); + Chunk::set_inuse(remainder, remainder_size); + self.dispose_chunk(remainder, remainder_size); + } + } + + let mem = Chunk::to_mem(p); + debug_assert!(Chunk::size(p) >= nb); + debug_assert_eq!(align_up(mem as usize, alignment), mem as usize); + self.check_inuse_chunk(p); + return mem; + } + + // consolidate and bin a chunk, differs from exported versions of free + // mainly in that the chunk need not be marked as inuse + unsafe fn dispose_chunk(&mut self, mut p: *mut Chunk, mut psize: usize) { + let next = Chunk::plus_offset(p, psize); + if !Chunk::pinuse(p) { + let prevsize = (*p).prev_foot; + if Chunk::mmapped(p) { + psize += prevsize + self.mmap_foot_pad(); + if self + .system_allocator + .free(p.cast::().sub(prevsize), psize) + { + self.footprint -= psize; + } + return; + } + let prev = Chunk::minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if p != self.dv { + self.unlink_chunk(p, prevsize); + } else if (*next).head & INUSE == INUSE { + self.dvsize = psize; + Chunk::set_free_with_pinuse(p, psize, next); + return; + } + } + + if !Chunk::cinuse(next) { + // consolidate forward + if next == self.top { + self.topsize += psize; + let tsize = self.topsize; + self.top = p; + (*p).head = tsize | PINUSE; + if p == self.dv { + self.dv = ptr::null_mut(); + self.dvsize = 0; + } + return; + } else if next == self.dv { + self.dvsize += psize; + let dsize = self.dvsize; + self.dv = p; + Chunk::set_size_and_pinuse_of_free_chunk(p, dsize); + return; + } else { + let nsize = Chunk::size(next); + psize += nsize; + self.unlink_chunk(next, nsize); + Chunk::set_size_and_pinuse_of_free_chunk(p, psize); + if p == self.dv { + self.dvsize = psize; + return; + } + } + } else { + Chunk::set_free_with_pinuse(p, psize, next); + } + self.insert_chunk(p, psize); + } + + unsafe fn init_top(&mut self, ptr: *mut Chunk, size: usize) { + let offset = self.align_offset(Chunk::to_mem(ptr)); + let p = Chunk::plus_offset(ptr, offset); + let size = size - offset; + + self.top = p; + self.topsize = size; + (*p).head = size | PINUSE; + (*Chunk::plus_offset(p, size)).head = self.top_foot_size(); + self.trim_check = DEFAULT_TRIM_THRESHOLD; + } + + unsafe fn init_bins(&mut self) { + for i in 0..NSMALLBINS_U32 { + let bin = self.smallbin_at(i); + (*bin).next = bin; + (*bin).prev = bin; + } + } + + unsafe fn prepend_alloc(&mut self, newbase: *mut u8, oldbase: *mut u8, size: usize) -> *mut u8 { + let p = self.align_as_chunk(newbase); + let mut oldfirst = self.align_as_chunk(oldbase); + let psize = oldfirst as usize - p as usize; + let q = Chunk::plus_offset(p, size); + let mut qsize = psize - size; + Chunk::set_size_and_pinuse_of_inuse_chunk(p, size); + + debug_assert!(oldfirst > q); + debug_assert!(Chunk::pinuse(oldfirst)); + debug_assert!(qsize >= self.min_chunk_size()); + + // consolidate the remainder with the first chunk of the old base + if oldfirst == self.top { + self.topsize += qsize; + let tsize = self.topsize; + self.top = q; + (*q).head = tsize | PINUSE; + self.check_top_chunk(q); + } else if oldfirst == self.dv { + self.dvsize += qsize; + let dsize = self.dvsize; + self.dv = q; + Chunk::set_size_and_pinuse_of_free_chunk(q, dsize); + } else { + if !Chunk::inuse(oldfirst) { + let nsize = Chunk::size(oldfirst); + self.unlink_chunk(oldfirst, nsize); + oldfirst = Chunk::plus_offset(oldfirst, nsize); + qsize += nsize; + } + Chunk::set_free_with_pinuse(q, qsize, oldfirst); + self.insert_chunk(q, qsize); + self.check_free_chunk(q); + } + + let ret = Chunk::to_mem(p); + self.check_malloced_chunk(ret, size); + self.check_malloc_state(); + return ret; + } + + // add a segment to hold a new noncontiguous region + unsafe fn add_segment(&mut self, tbase: *mut u8, tsize: usize, flags: u32) { + // TODO: what in the world is this function doing + + // Determine locations and sizes of segment, fenceposts, and the old top + let old_top = self.top.cast::(); + let oldsp = self.segment_holding(old_top); + let old_end = Segment::top(oldsp); + let ssize = self.pad_request(mem::size_of::()); + let offset = ssize + mem::size_of::() * 4 + self.malloc_alignment() - 1; + let rawsp = old_end.sub(offset); + let offset = self.align_offset(Chunk::to_mem(rawsp.cast())); + let asp = rawsp.add(offset); + let csp = if asp < old_top.add(self.min_chunk_size()) { + old_top + } else { + asp + }; + let sp = csp.cast::(); + let ss = Chunk::to_mem(sp).cast::(); + let tnext = Chunk::plus_offset(sp, ssize); + let mut p = tnext; + let mut nfences = 0; + + // reset the top to our new space + let size = tsize - self.top_foot_size(); + self.init_top(tbase.cast(), size); + + // set up our segment record + debug_assert!(self.is_aligned(ss as usize)); + Chunk::set_size_and_pinuse_of_inuse_chunk(sp, ssize); + *ss = self.seg; // push our current record + self.seg.base = tbase; + self.seg.size = tsize; + self.seg.flags = flags; + self.seg.next = ss; + + // insert trailing fences + loop { + let nextp = Chunk::plus_offset(p, mem::size_of::()); + (*p).head = Chunk::fencepost_head(); + nfences += 1; + if ptr::addr_of!((*nextp).head).cast::() < old_end { + p = nextp; + } else { + break; + } + } + debug_assert!(nfences >= 2); + + // insert the rest of the old top into a bin as an ordinary free chunk + if csp != old_top { + let q = old_top.cast::(); + let psize = csp as usize - old_top as usize; + let tn = Chunk::plus_offset(q, psize); + Chunk::set_free_with_pinuse(q, psize, tn); + self.insert_chunk(q, psize); + } + + self.check_top_chunk(self.top); + self.check_malloc_state(); + } + + unsafe fn segment_holding(&self, ptr: *mut u8) -> *mut Segment { + let mut sp = &self.seg as *const Segment as *mut Segment; + while !sp.is_null() { + if (*sp).base <= ptr && ptr < Segment::top(sp) { + return sp; + } + sp = (*sp).next; + } + ptr::null_mut() + } + + unsafe fn tmalloc_small(&mut self, size: usize) -> *mut u8 { + let leastbit = least_bit(self.treemap); + let i = leastbit.trailing_zeros(); + let mut v = *self.treebin_at(i); + let mut t = v; + let mut rsize = Chunk::size(TreeChunk::chunk(t)) - size; + + loop { + t = TreeChunk::leftmost_child(t); + if t.is_null() { + break; + } + let trem = Chunk::size(TreeChunk::chunk(t)) - size; + if trem < rsize { + rsize = trem; + v = t; + } + } + + let vc = TreeChunk::chunk(v); + let r = Chunk::plus_offset(vc, size).cast::(); + debug_assert_eq!(Chunk::size(vc), rsize + size); + self.unlink_large_chunk(v); + if rsize < self.min_chunk_size() { + Chunk::set_inuse_and_pinuse(vc, rsize + size); + } else { + let rc = TreeChunk::chunk(r); + Chunk::set_size_and_pinuse_of_inuse_chunk(vc, size); + Chunk::set_size_and_pinuse_of_free_chunk(rc, rsize); + self.replace_dv(rc, rsize); + } + Chunk::to_mem(vc) + } + + unsafe fn tmalloc_large(&mut self, size: usize) -> *mut u8 { + let mut v = ptr::null_mut(); + let mut rsize = !size + 1; + let idx = self.compute_tree_index(size); + let mut t = *self.treebin_at(idx); + if !t.is_null() { + // Traverse thre tree for this bin looking for a node with size + // equal to the `size` above. + let mut sizebits = size << leftshift_for_tree_index(idx); + // Keep track of the deepest untaken right subtree + let mut rst = ptr::null_mut(); + loop { + let csize = Chunk::size(TreeChunk::chunk(t)); + if csize >= size && csize - size < rsize { + v = t; + rsize = csize - size; + if rsize == 0 { + break; + } + } + let rt = (*t).child[1]; + t = (*t).child[(sizebits >> (mem::size_of::() * 8 - 1)) & 1]; + if !rt.is_null() && rt != t { + rst = rt; + } + if t.is_null() { + // Reset `t` to the least subtree holding sizes greater than + // the `size` above, breaking out + t = rst; + break; + } + sizebits <<= 1; + } + } + + // Set t to the root of the next non-empty treebin + if t.is_null() && v.is_null() { + let leftbits = left_bits(1 << idx) & self.treemap; + if leftbits != 0 { + let leastbit = least_bit(leftbits); + let i = leastbit.trailing_zeros(); + t = *self.treebin_at(i); + } + } + + // Find the smallest of this tree or subtree + while !t.is_null() { + let csize = Chunk::size(TreeChunk::chunk(t)); + if csize >= size && csize - size < rsize { + rsize = csize - size; + v = t; + } + t = TreeChunk::leftmost_child(t); + } + + // If dv is a better fit, then return null so malloc will use it + if v.is_null() || (self.dvsize >= size && !(rsize < self.dvsize - size)) { + return ptr::null_mut(); + } + + let vc = TreeChunk::chunk(v); + let r = Chunk::plus_offset(vc, size); + debug_assert_eq!(Chunk::size(vc), rsize + size); + self.unlink_large_chunk(v); + if rsize < self.min_chunk_size() { + Chunk::set_inuse_and_pinuse(vc, rsize + size); + } else { + Chunk::set_size_and_pinuse_of_inuse_chunk(vc, size); + Chunk::set_size_and_pinuse_of_free_chunk(r, rsize); + self.insert_chunk(r, rsize); + } + Chunk::to_mem(vc) + } + + unsafe fn smallbin_at(&mut self, idx: u32) -> *mut Chunk { + let idx = usize::try_from(idx * 2).unwrap(); + debug_assert!(idx < self.smallbins.len()); + self.smallbins.as_mut_ptr().add(idx).cast() + } + + unsafe fn treebin_at(&mut self, idx: u32) -> *mut *mut TreeChunk { + let idx = usize::try_from(idx).unwrap(); + debug_assert!(idx < self.treebins.len()); + self.treebins.as_mut_ptr().add(idx) + } + + fn compute_tree_index(&self, size: usize) -> u32 { + let x = size >> TREEBIN_SHIFT; + if x == 0 { + 0 + } else if x > 0xffff { + NTREEBINS_U32 - 1 + } else { + let k = mem::size_of_val(&x) * 8 - 1 - (x.leading_zeros() as usize); + ((k << 1) + (size >> (k + TREEBIN_SHIFT - 1) & 1)) as u32 + } + } + + unsafe fn unlink_first_small_chunk(&mut self, head: *mut Chunk, next: *mut Chunk, idx: u32) { + let ptr = (*next).prev; + debug_assert!(next != head); + debug_assert!(next != ptr); + debug_assert_eq!(Chunk::size(next), self.small_index2size(idx)); + if head == ptr { + self.clear_smallmap(idx); + } else { + (*ptr).next = head; + (*head).prev = ptr; + } + } + + unsafe fn replace_dv(&mut self, chunk: *mut Chunk, size: usize) { + let dvs = self.dvsize; + debug_assert!(self.is_small(dvs)); + if dvs != 0 { + let dv = self.dv; + self.insert_small_chunk(dv, dvs); + } + self.dvsize = size; + self.dv = chunk; + } + + unsafe fn insert_chunk(&mut self, chunk: *mut Chunk, size: usize) { + if self.is_small(size) { + self.insert_small_chunk(chunk, size); + } else { + self.insert_large_chunk(chunk.cast(), size); + } + } + + unsafe fn insert_small_chunk(&mut self, chunk: *mut Chunk, size: usize) { + let idx = self.small_index(size); + let head = self.smallbin_at(idx); + let mut f = head; + debug_assert!(size >= self.min_chunk_size()); + if !self.smallmap_is_marked(idx) { + self.mark_smallmap(idx); + } else { + f = (*head).prev; + } + + (*head).prev = chunk; + (*f).next = chunk; + (*chunk).prev = f; + (*chunk).next = head; + } + + unsafe fn insert_large_chunk(&mut self, chunk: *mut TreeChunk, size: usize) { + let idx = self.compute_tree_index(size); + let h = self.treebin_at(idx); + (*chunk).index = idx; + (*chunk).child[0] = ptr::null_mut(); + (*chunk).child[1] = ptr::null_mut(); + let chunkc = TreeChunk::chunk(chunk); + if !self.treemap_is_marked(idx) { + *h = chunk; + (*chunk).parent = h.cast(); // TODO: dubious? + (*chunkc).next = chunkc; + (*chunkc).prev = chunkc; + self.mark_treemap(idx); + } else { + let mut t = *h; + let mut k = size << leftshift_for_tree_index(idx); + loop { + if Chunk::size(TreeChunk::chunk(t)) != size { + let c = &mut (*t).child[(k >> mem::size_of::() * 8 - 1) & 1]; + k <<= 1; + if !c.is_null() { + t = *c; + } else { + *c = chunk; + (*chunk).parent = t; + (*chunkc).next = chunkc; + (*chunkc).prev = chunkc; + break; + } + } else { + let tc = TreeChunk::chunk(t); + let f = (*tc).prev; + (*f).next = chunkc; + (*tc).prev = chunkc; + (*chunkc).prev = f; + (*chunkc).next = tc; + (*chunk).parent = ptr::null_mut(); + break; + } + } + } + } + + unsafe fn smallmap_is_marked(&self, idx: u32) -> bool { + self.smallmap & (1 << idx) != 0 + } + + unsafe fn mark_smallmap(&mut self, idx: u32) { + self.smallmap |= 1 << idx; + } + + unsafe fn clear_smallmap(&mut self, idx: u32) { + self.smallmap &= !(1 << idx); + } + + unsafe fn treemap_is_marked(&self, idx: u32) -> bool { + self.treemap & (1 << idx) != 0 + } + + unsafe fn mark_treemap(&mut self, idx: u32) { + self.treemap |= 1 << idx; + } + + unsafe fn clear_treemap(&mut self, idx: u32) { + self.treemap &= !(1 << idx); + } + + unsafe fn unlink_chunk(&mut self, chunk: *mut Chunk, size: usize) { + if self.is_small(size) { + self.unlink_small_chunk(chunk, size) + } else { + self.unlink_large_chunk(chunk.cast()); + } + } + + unsafe fn unlink_small_chunk(&mut self, chunk: *mut Chunk, size: usize) { + let f = (*chunk).prev; + let b = (*chunk).next; + let idx = self.small_index(size); + debug_assert!(chunk != b); + debug_assert!(chunk != f); + debug_assert_eq!(Chunk::size(chunk), self.small_index2size(idx)); + if b == f { + self.clear_smallmap(idx); + } else { + (*f).next = b; + (*b).prev = f; + } + } + + unsafe fn unlink_large_chunk(&mut self, chunk: *mut TreeChunk) { + let xp = (*chunk).parent; + let mut r; + if TreeChunk::next(chunk) != chunk { + let f = TreeChunk::prev(chunk); + r = TreeChunk::next(chunk); + (*f).chunk.next = TreeChunk::chunk(r); + (*r).chunk.prev = TreeChunk::chunk(f); + } else { + let mut rp = &mut (*chunk).child[1]; + if rp.is_null() { + rp = &mut (*chunk).child[0]; + } + r = *rp; + if !rp.is_null() { + loop { + let mut cp = &mut (**rp).child[1]; + if cp.is_null() { + cp = &mut (**rp).child[0]; + } + if cp.is_null() { + break; + } + rp = cp; + } + r = *rp; + *rp = ptr::null_mut(); + } + } + + if xp.is_null() { + return; + } + + let h = self.treebin_at((*chunk).index); + if chunk == *h { + *h = r; + if r.is_null() { + self.clear_treemap((*chunk).index); + } + } else { + if (*xp).child[0] == chunk { + (*xp).child[0] = r; + } else { + (*xp).child[1] = r; + } + } + + if !r.is_null() { + (*r).parent = xp; + let c0 = (*chunk).child[0]; + if !c0.is_null() { + (*r).child[0] = c0; + (*c0).parent = r; + } + let c1 = (*chunk).child[1]; + if !c1.is_null() { + (*r).child[1] = c1; + (*c1).parent = r; + } + } + } + + pub unsafe fn usable_size(&mut self, ptr: *mut u8) -> usize { + let p = Chunk::from_mem(ptr); + let psize = Chunk::size(p); + + psize + } + + pub unsafe fn validate_size(&mut self, ptr: *mut u8, size: usize) { + let p = Chunk::from_mem(ptr); + let psize = Chunk::size(p); + + let min_overhead = self.overhead_for(p); + assert!(psize >= size + min_overhead); + + if !Chunk::mmapped(p) { + let max_overhead = + min_overhead + self.min_chunk_size() * 2 + mem::align_of::() - 1; + + assert!(psize <= size + max_overhead); + } + } + + pub unsafe fn free(&mut self, mem: *mut u8) { + self.check_malloc_state(); + + let mut p = Chunk::from_mem(mem); + let mut psize = Chunk::size(p); + let next = Chunk::plus_offset(p, psize); + if !Chunk::pinuse(p) { + let prevsize = (*p).prev_foot; + + if Chunk::mmapped(p) { + psize += prevsize + self.mmap_foot_pad(); + if self + .system_allocator + .free(p.cast::().sub(prevsize), psize) + { + self.footprint -= psize; + } + return; + } + + let prev = Chunk::minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if p != self.dv { + self.unlink_chunk(p, prevsize); + } else if (*next).head & INUSE == INUSE { + self.dvsize = psize; + Chunk::set_free_with_pinuse(p, psize, next); + return; + } + } + + // Consolidate forward if we can + if !Chunk::cinuse(next) { + if next == self.top { + self.topsize += psize; + let tsize = self.topsize; + self.top = p; + (*p).head = tsize | PINUSE; + if p == self.dv { + self.dv = ptr::null_mut(); + self.dvsize = 0; + } + if self.should_trim(tsize) { + self.sys_trim(0); + } + return; + } else if next == self.dv { + self.dvsize += psize; + let dsize = self.dvsize; + self.dv = p; + Chunk::set_size_and_pinuse_of_free_chunk(p, dsize); + return; + } else { + let nsize = Chunk::size(next); + psize += nsize; + self.unlink_chunk(next, nsize); + Chunk::set_size_and_pinuse_of_free_chunk(p, psize); + if p == self.dv { + self.dvsize = psize; + return; + } + } + } else { + Chunk::set_free_with_pinuse(p, psize, next); + } + + if self.is_small(psize) { + self.insert_small_chunk(p, psize); + self.check_free_chunk(p); + } else { + self.insert_large_chunk(p.cast(), psize); + self.check_free_chunk(p); + self.release_checks -= 1; + if self.release_checks == 0 { + self.release_unused_segments(); + } + } + } + + fn should_trim(&self, size: usize) -> bool { + size > self.trim_check + } + + unsafe fn sys_trim(&mut self, mut pad: usize) -> bool { + let mut released = 0; + if pad < self.max_request() && !self.top.is_null() { + pad += self.top_foot_size(); + if self.topsize > pad { + let unit = DEFAULT_GRANULARITY; + let extra = ((self.topsize - pad + unit - 1) / unit - 1) * unit; + let sp = self.segment_holding(self.top.cast()); + debug_assert!(!sp.is_null()); + + if !Segment::is_extern(sp) { + if Segment::can_release_part(&self.system_allocator, sp) { + if (*sp).size >= extra && !self.has_segment_link(sp) { + let newsize = (*sp).size - extra; + if self + .system_allocator + .free_part((*sp).base, (*sp).size, newsize) + { + released = extra; + } + } + } + } + + if released != 0 { + (*sp).size -= released; + self.footprint -= released; + let top = self.top; + let topsize = self.topsize - released; + self.init_top(top, topsize); + self.check_top_chunk(self.top); + } + } + + released += self.release_unused_segments(); + + if released == 0 && self.topsize > self.trim_check { + self.trim_check = usize::max_value(); + } + } + + released != 0 + } + + unsafe fn has_segment_link(&self, ptr: *mut Segment) -> bool { + let mut sp = &self.seg as *const Segment as *mut Segment; + while !sp.is_null() { + if Segment::holds(ptr, sp.cast()) { + return true; + } + sp = (*sp).next; + } + false + } + + /// Unmap and unlink any mapped segments that don't contain used chunks + unsafe fn release_unused_segments(&mut self) -> usize { + let mut released = 0; + let mut nsegs = 0; + let mut pred: *mut Segment = &mut self.seg; + let mut sp = (*pred).next; + while !sp.is_null() { + let base = (*sp).base; + let size = (*sp).size; + let next = (*sp).next; + nsegs += 1; + + if Segment::can_release_part(&self.system_allocator, sp) && !Segment::is_extern(sp) { + let p = self.align_as_chunk(base); + let psize = Chunk::size(p); + // We can unmap if the first chunk holds the entire segment and + // isn't pinned. + let chunk_top = p.cast::().add(psize); + let top = base.add(size - self.top_foot_size()); + if !Chunk::inuse(p) && chunk_top >= top { + let tp = p.cast::(); + debug_assert!(Segment::holds(sp, sp.cast())); + if p == self.dv { + self.dv = ptr::null_mut(); + self.dvsize = 0; + } else { + self.unlink_large_chunk(tp); + } + if self.system_allocator.free(base, size) { + released += size; + self.footprint -= size; + // unlink our obsolete record + sp = pred; + (*sp).next = next; + } else { + // back out if we can't unmap + self.insert_large_chunk(tp, psize); + } + } + } + pred = sp; + sp = next; + } + self.release_checks = if nsegs > MAX_RELEASE_CHECK_RATE { + nsegs + } else { + MAX_RELEASE_CHECK_RATE + }; + return released; + } + + // Sanity checks + + unsafe fn check_any_chunk(&self, p: *mut Chunk) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + debug_assert!( + self.is_aligned(Chunk::to_mem(p) as usize) || (*p).head == Chunk::fencepost_head() + ); + debug_assert!(p as *mut u8 >= self.least_addr); + } + + unsafe fn check_top_chunk(&self, p: *mut Chunk) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + let sp = self.segment_holding(p.cast()); + let sz = (*p).head & !INUSE; + debug_assert!(!sp.is_null()); + debug_assert!( + self.is_aligned(Chunk::to_mem(p) as usize) || (*p).head == Chunk::fencepost_head() + ); + debug_assert!(p as *mut u8 >= self.least_addr); + debug_assert_eq!(sz, self.topsize); + debug_assert!(sz > 0); + debug_assert_eq!( + sz, + (*sp).base as usize + (*sp).size - p as usize - self.top_foot_size() + ); + debug_assert!(Chunk::pinuse(p)); + debug_assert!(!Chunk::pinuse(Chunk::plus_offset(p, sz))); + } + + unsafe fn check_malloced_chunk(&self, mem: *mut u8, s: usize) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + if mem.is_null() { + return; + } + let p = Chunk::from_mem(mem); + let sz = (*p).head & !INUSE; + self.check_inuse_chunk(p); + debug_assert_eq!(align_up(sz, self.malloc_alignment()), sz); + debug_assert!(sz >= self.min_chunk_size()); + debug_assert!(sz >= s); + debug_assert!(Chunk::mmapped(p) || sz < (s + self.min_chunk_size())); + } + + unsafe fn check_inuse_chunk(&self, p: *mut Chunk) { + self.check_any_chunk(p); + debug_assert!(Chunk::inuse(p)); + debug_assert!(Chunk::pinuse(Chunk::next(p))); + debug_assert!(Chunk::mmapped(p) || Chunk::pinuse(p) || Chunk::next(Chunk::prev(p)) == p); + if Chunk::mmapped(p) { + self.check_mmapped_chunk(p); + } + } + + unsafe fn check_mmapped_chunk(&self, p: *mut Chunk) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + let sz = Chunk::size(p); + let len = sz + (*p).prev_foot + self.mmap_foot_pad(); + debug_assert!(Chunk::mmapped(p)); + debug_assert!( + self.is_aligned(Chunk::to_mem(p) as usize) || (*p).head == Chunk::fencepost_head() + ); + debug_assert!(p as *mut u8 >= self.least_addr); + debug_assert!(!self.is_small(sz)); + debug_assert_eq!(align_up(len, self.system_allocator.page_size()), len); + debug_assert_eq!((*Chunk::plus_offset(p, sz)).head, Chunk::fencepost_head()); + debug_assert_eq!( + (*Chunk::plus_offset(p, sz + mem::size_of::())).head, + 0 + ); + } + + unsafe fn check_free_chunk(&self, p: *mut Chunk) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + let sz = Chunk::size(p); + let next = Chunk::plus_offset(p, sz); + self.check_any_chunk(p); + debug_assert!(!Chunk::inuse(p)); + debug_assert!(!Chunk::pinuse(Chunk::next(p))); + debug_assert!(!Chunk::mmapped(p)); + if p != self.dv && p != self.top { + if sz >= self.min_chunk_size() { + debug_assert_eq!(align_up(sz, self.malloc_alignment()), sz); + debug_assert!(self.is_aligned(Chunk::to_mem(p) as usize)); + debug_assert_eq!((*next).prev_foot, sz); + debug_assert!(Chunk::pinuse(p)); + debug_assert!(next == self.top || Chunk::inuse(next)); + debug_assert_eq!((*(*p).next).prev, p); + debug_assert_eq!((*(*p).prev).next, p); + } else { + debug_assert_eq!(sz, mem::size_of::()); + } + } + } + + unsafe fn check_malloc_state(&mut self) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + for i in 0..NSMALLBINS_U32 { + self.check_smallbin(i); + } + for i in 0..NTREEBINS_U32 { + self.check_treebin(i); + } + if self.dvsize != 0 { + self.check_any_chunk(self.dv); + debug_assert_eq!(self.dvsize, Chunk::size(self.dv)); + debug_assert!(self.dvsize >= self.min_chunk_size()); + let dv = self.dv; + debug_assert!(!self.bin_find(dv)); + } + if !self.top.is_null() { + self.check_top_chunk(self.top); + debug_assert!(self.topsize > 0); + let top = self.top; + debug_assert!(!self.bin_find(top)); + } + let total = self.traverse_and_check(); + debug_assert!(total <= self.footprint); + debug_assert!(self.footprint <= self.max_footprint); + } + + unsafe fn check_smallbin(&mut self, idx: u32) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + let b = self.smallbin_at(idx); + let mut p = (*b).next; + let empty = self.smallmap & (1 << idx) == 0; + if p == b { + debug_assert!(empty) + } + if !empty { + while p != b { + let size = Chunk::size(p); + self.check_free_chunk(p); + debug_assert_eq!(self.small_index(size), idx); + debug_assert!((*p).next == b || Chunk::size((*p).next) == Chunk::size(p)); + let q = Chunk::next(p); + if (*q).head != Chunk::fencepost_head() { + self.check_inuse_chunk(q); + } + p = (*p).next; + } + } + } + + unsafe fn check_treebin(&mut self, idx: u32) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + let t = *self.treebin_at(idx); + let empty = self.treemap & (1 << idx) == 0; + if t.is_null() { + debug_assert!(empty); + } + if !empty { + self.check_tree(t); + } + } + + unsafe fn check_tree(&mut self, t: *mut TreeChunk) { + if !cfg!(all(feature = "debug", debug_assertions)) { + return; + } + let tc = TreeChunk::chunk(t); + let tindex = (*t).index; + let tsize = Chunk::size(tc); + let idx = self.compute_tree_index(tsize); + debug_assert_eq!(tindex, idx); + debug_assert!(tsize >= self.min_large_size()); + debug_assert!(tsize >= self.min_size_for_tree_index(idx)); + debug_assert!(idx == NTREEBINS_U32 - 1 || tsize < self.min_size_for_tree_index(idx + 1)); + + let mut u = t; + let mut head = ptr::null_mut::(); + loop { + let uc = TreeChunk::chunk(u); + self.check_any_chunk(uc); + debug_assert_eq!((*u).index, tindex); + debug_assert_eq!(Chunk::size(uc), tsize); + debug_assert!(!Chunk::inuse(uc)); + debug_assert!(!Chunk::pinuse(Chunk::next(uc))); + debug_assert_eq!((*(*uc).next).prev, uc); + debug_assert_eq!((*(*uc).prev).next, uc); + let left = (*u).child[0]; + let right = (*u).child[1]; + if (*u).parent.is_null() { + debug_assert!(left.is_null()); + debug_assert!(right.is_null()); + } else { + debug_assert!(head.is_null()); + head = u; + debug_assert!((*u).parent != u); + // TODO: unsure why this triggers UB in stacked borrows in MIRI + // (works in tree borrows though) + #[cfg(not(miri))] + debug_assert!( + (*(*u).parent).child[0] == u + || (*(*u).parent).child[1] == u + || *((*u).parent as *mut *mut TreeChunk) == u + ); + if !left.is_null() { + debug_assert_eq!((*left).parent, u); + debug_assert!(left != u); + self.check_tree(left); + } + if !right.is_null() { + debug_assert_eq!((*right).parent, u); + debug_assert!(right != u); + self.check_tree(right); + } + if !left.is_null() && !right.is_null() { + debug_assert!( + Chunk::size(TreeChunk::chunk(left)) < Chunk::size(TreeChunk::chunk(right)) + ); + } + } + + u = TreeChunk::prev(u); + if u == t { + break; + } + } + debug_assert!(!head.is_null()); + } + + fn min_size_for_tree_index(&self, idx: u32) -> usize { + let idx = usize::try_from(idx).unwrap(); + (1 << ((idx >> 1) + TREEBIN_SHIFT)) | ((idx & 1) << ((idx >> 1) + TREEBIN_SHIFT - 1)) + } + + unsafe fn bin_find(&mut self, chunk: *mut Chunk) -> bool { + let size = Chunk::size(chunk); + if self.is_small(size) { + let sidx = self.small_index(size); + let b = self.smallbin_at(sidx); + if !self.smallmap_is_marked(sidx) { + return false; + } + let mut p = b; + loop { + if p == chunk { + return true; + } + p = (*p).prev; + if p == b { + return false; + } + } + } else { + let tidx = self.compute_tree_index(size); + if !self.treemap_is_marked(tidx) { + return false; + } + let mut t = *self.treebin_at(tidx); + let mut sizebits = size << leftshift_for_tree_index(tidx); + while !t.is_null() && Chunk::size(TreeChunk::chunk(t)) != size { + t = (*t).child[(sizebits >> (mem::size_of::() * 8 - 1)) & 1]; + sizebits <<= 1; + } + if t.is_null() { + return false; + } + let mut u = t; + let chunk = chunk.cast(); + loop { + if u == chunk { + return true; + } + u = TreeChunk::prev(u); + if u == t { + return false; + } + } + } + } + + unsafe fn traverse_and_check(&self) -> usize { + 0 + } + + pub unsafe fn trim(&mut self, pad: usize) -> bool { + self.sys_trim(pad) + } + + pub unsafe fn destroy(mut self) -> usize { + let mut freed = 0; + let mut sp: *mut Segment = &mut self.seg; + while !sp.is_null() { + let base = (*sp).base; + let size = (*sp).size; + let can_free = !base.is_null() && !Segment::is_extern(sp); + sp = (*sp).next; + + if can_free && self.system_allocator.free(base, size) { + freed += size; + } + } + freed + } +} + +const PINUSE: usize = 1 << 0; +const CINUSE: usize = 1 << 1; +const FLAG4: usize = 1 << 2; +const INUSE: usize = PINUSE | CINUSE; +const FLAG_BITS: usize = PINUSE | CINUSE | FLAG4; + +impl Chunk { + unsafe fn fencepost_head() -> usize { + INUSE | mem::size_of::() + } + + unsafe fn size(me: *mut Chunk) -> usize { + (*me).head & !FLAG_BITS + } + + unsafe fn next(me: *mut Chunk) -> *mut Chunk { + me.cast::().add((*me).head & !FLAG_BITS).cast() + } + + unsafe fn prev(me: *mut Chunk) -> *mut Chunk { + me.cast::().sub((*me).prev_foot).cast() + } + + unsafe fn cinuse(me: *mut Chunk) -> bool { + (*me).head & CINUSE != 0 + } + + unsafe fn pinuse(me: *mut Chunk) -> bool { + (*me).head & PINUSE != 0 + } + + unsafe fn clear_pinuse(me: *mut Chunk) { + (*me).head &= !PINUSE; + } + + unsafe fn inuse(me: *mut Chunk) -> bool { + (*me).head & INUSE != PINUSE + } + + unsafe fn mmapped(me: *mut Chunk) -> bool { + (*me).head & INUSE == 0 + } + + unsafe fn set_inuse(me: *mut Chunk, size: usize) { + (*me).head = ((*me).head & PINUSE) | size | CINUSE; + let next = Chunk::plus_offset(me, size); + (*next).head |= PINUSE; + } + + unsafe fn set_inuse_and_pinuse(me: *mut Chunk, size: usize) { + (*me).head = PINUSE | size | CINUSE; + let next = Chunk::plus_offset(me, size); + (*next).head |= PINUSE; + } + + unsafe fn set_size_and_pinuse_of_inuse_chunk(me: *mut Chunk, size: usize) { + (*me).head = size | PINUSE | CINUSE; + } + + unsafe fn set_size_and_pinuse_of_free_chunk(me: *mut Chunk, size: usize) { + (*me).head = size | PINUSE; + Chunk::set_foot(me, size); + } + + unsafe fn set_free_with_pinuse(p: *mut Chunk, size: usize, n: *mut Chunk) { + Chunk::clear_pinuse(n); + Chunk::set_size_and_pinuse_of_free_chunk(p, size); + } + + unsafe fn set_foot(me: *mut Chunk, size: usize) { + let next = Chunk::plus_offset(me, size); + (*next).prev_foot = size; + } + + unsafe fn plus_offset(me: *mut Chunk, offset: usize) -> *mut Chunk { + me.cast::().add(offset).cast() + } + + unsafe fn minus_offset(me: *mut Chunk, offset: usize) -> *mut Chunk { + me.cast::().sub(offset).cast() + } + + unsafe fn to_mem(me: *mut Chunk) -> *mut u8 { + me.cast::().add(Chunk::mem_offset()) + } + + fn mem_offset() -> usize { + 2 * mem::size_of::() + } + + unsafe fn from_mem(mem: *mut u8) -> *mut Chunk { + mem.sub(2 * mem::size_of::()).cast() + } +} + +impl TreeChunk { + unsafe fn leftmost_child(me: *mut TreeChunk) -> *mut TreeChunk { + let left = (*me).child[0]; + if left.is_null() { + (*me).child[1] + } else { + left + } + } + + unsafe fn chunk(me: *mut TreeChunk) -> *mut Chunk { + ptr::addr_of_mut!((*me).chunk) + } + + unsafe fn next(me: *mut TreeChunk) -> *mut TreeChunk { + (*TreeChunk::chunk(me)).next.cast() + } + + unsafe fn prev(me: *mut TreeChunk) -> *mut TreeChunk { + (*TreeChunk::chunk(me)).prev.cast() + } +} + +const EXTERN: u32 = 1 << 0; + +impl Segment { + unsafe fn is_extern(seg: *mut Segment) -> bool { + (*seg).flags & EXTERN != 0 + } + + unsafe fn can_release_part(system_allocator: &A, seg: *mut Segment) -> bool { + system_allocator.can_release_part((*seg).flags >> 1) + } + + unsafe fn sys_flags(seg: *mut Segment) -> u32 { + (*seg).flags >> 1 + } + + unsafe fn holds(seg: *mut Segment, addr: *mut u8) -> bool { + (*seg).base <= addr && addr < Segment::top(seg) + } + + unsafe fn top(seg: *mut Segment) -> *mut u8 { + (*seg).base.add((*seg).size) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::System; + + // Prime the allocator with some allocations such that there will be free + // chunks in the treemap + unsafe fn setup_treemap(a: &mut Dlmalloc) { + let large_request_size = NSMALLBINS * (1 << SMALLBIN_SHIFT); + assert!(!a.is_small(large_request_size)); + let large_request1 = a.malloc(large_request_size); + assert_ne!(large_request1, ptr::null_mut()); + let large_request2 = a.malloc(large_request_size); + assert_ne!(large_request2, ptr::null_mut()); + a.free(large_request1); + assert_ne!(a.treemap, 0); + } + + #[test] + // Test allocating, with a non-empty treemap, a specific size that used to + // trigger an integer overflow bug + fn treemap_alloc_overflow_minimal() { + let mut a = Dlmalloc::new(System::new()); + unsafe { + setup_treemap(&mut a); + let min_idx31_size = (0xc000 << TREEBIN_SHIFT) - a.chunk_overhead() + 1; + assert_ne!(a.malloc(min_idx31_size), ptr::null_mut()); + } + } + + #[test] + #[cfg(not(miri))] + // Test allocating the maximum request size with a non-empty treemap + fn treemap_alloc_max() { + let mut a = Dlmalloc::new(System::new()); + unsafe { + setup_treemap(&mut a); + let max_request_size = a.max_request() - 1; + assert_eq!(a.malloc(max_request_size), ptr::null_mut()); + } + } +} diff --git a/dlmalloc-rs/src/dummy.rs b/dlmalloc-rs/src/dummy.rs new file mode 100644 index 0000000000..8654518638 --- /dev/null +++ b/dlmalloc-rs/src/dummy.rs @@ -0,0 +1,42 @@ +use crate::Allocator; +use core::ptr; + +pub struct System { + _priv: (), +} + +impl System { + pub const fn new() -> System { + System { _priv: () } + } +} + +unsafe impl Allocator for System { + fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) { + (ptr::null_mut(), 0, 0) + } + + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + ptr::null_mut() + } + + fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool { + false + } + + fn free(&self, _ptr: *mut u8, _size: usize) -> bool { + false + } + + fn can_release_part(&self, _flags: u32) -> bool { + false + } + + fn allocates_zeros(&self) -> bool { + false + } + + fn page_size(&self) -> usize { + 1 + } +} diff --git a/dlmalloc-rs/src/global.rs b/dlmalloc-rs/src/global.rs new file mode 100644 index 0000000000..d07fdf953c --- /dev/null +++ b/dlmalloc-rs/src/global.rs @@ -0,0 +1,56 @@ +use crate::Dlmalloc; +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr; + +pub use crate::sys::enable_alloc_after_fork; + +/// An instance of a "global allocator" backed by `Dlmalloc` +/// +/// This API requires the `global` feature is activated, and this type +/// implements the `GlobalAlloc` trait in the standard library. +pub struct GlobalDlmalloc; + +static mut DLMALLOC: Dlmalloc = Dlmalloc::new(); + +unsafe impl GlobalAlloc for GlobalDlmalloc { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let _guard = lock(); + let dlmalloc = ptr::addr_of_mut!(DLMALLOC); + (*dlmalloc).malloc(layout.size(), layout.align()) + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let _guard = lock(); + let dlmalloc = ptr::addr_of_mut!(DLMALLOC); + (*dlmalloc).free(ptr, layout.size(), layout.align()) + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + let _guard = lock(); + let dlmalloc = ptr::addr_of_mut!(DLMALLOC); + (*dlmalloc).calloc(layout.size(), layout.align()) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let _guard = lock(); + let dlmalloc = ptr::addr_of_mut!(DLMALLOC); + (*dlmalloc).realloc(ptr, layout.size(), layout.align(), new_size) + } +} + +unsafe fn lock() -> impl Drop { + crate::sys::acquire_global_lock(); + + struct Guard; + impl Drop for Guard { + fn drop(&mut self) { + crate::sys::release_global_lock() + } + } + + Guard +} diff --git a/dlmalloc-rs/src/lib.rs b/dlmalloc-rs/src/lib.rs new file mode 100644 index 0000000000..ed20c51a91 --- /dev/null +++ b/dlmalloc-rs/src/lib.rs @@ -0,0 +1,230 @@ +//! A Rust port of the `dlmalloc` allocator. +//! +//! The `dlmalloc` allocator is described at +//! and this Rust crate is a straight +//! port of the C code for the allocator into Rust. The implementation is +//! wrapped up in a `Dlmalloc` type and has support for Linux, OSX, and Wasm +//! currently. +//! +//! The primary purpose of this crate is that it serves as the default memory +//! allocator for the `wasm32-unknown-unknown` target in the standard library. +//! Support for other platforms is largely untested and unused, but is used when +//! testing this crate. + +#![allow(dead_code)] +#![no_std] +#![deny(missing_docs)] + +#[cfg(feature = "rust_api")] +use core::{cmp, ptr}; + +#[cfg(feature = "system")] +use sys::System; + +#[cfg(feature = "global")] +pub use self::global::{enable_alloc_after_fork, GlobalDlmalloc}; + +mod dlmalloc; + +#[cfg(feature = "c_api")] +pub use dlmalloc::Dlmalloc as DlmallocCApi; + +#[cfg(feature = "global")] +mod global; + +/// In order for this crate to efficiently manage memory, it needs a way to communicate with the +/// underlying platform. This `Allocator` trait provides an interface for this communication. +pub unsafe trait Allocator: Send { + /// Allocates system memory region of at least `size` bytes + /// Returns a triple of `(base, size, flags)` where `base` is a pointer to the beginning of the + /// allocated memory region. `size` is the actual size of the region while `flags` specifies + /// properties of the allocated region. If `EXTERN_BIT` (bit 0) set in flags, then we did not + /// allocate this segment and so should not try to deallocate or merge with others. + /// This function can return a `std::ptr::null_mut()` when allocation fails (other values of + /// the triple will be ignored). + fn alloc(&self, size: usize) -> (*mut u8, usize, u32); + + /// Remaps system memory region at `ptr` with size `oldsize` to a potential new location with + /// size `newsize`. `can_move` indicates if the location is allowed to move to a completely new + /// location, or that it is only allowed to change in size. Returns a pointer to the new + /// location in memory. + /// This function can return a `std::ptr::null_mut()` to signal an error. + fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8; + + /// Frees a part of a memory chunk. The original memory chunk starts at `ptr` with size `oldsize` + /// and is turned into a memory region starting at the same address but with `newsize` bytes. + /// Returns `true` iff the access memory region could be freed. + fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool; + + /// Frees an entire memory region. Returns `true` iff the operation succeeded. When `false` is + /// returned, the `dlmalloc` may re-use the location on future allocation requests + fn free(&self, ptr: *mut u8, size: usize) -> bool; + + /// Indicates if the system can release a part of memory. For the `flags` argument, see + /// `Allocator::alloc` + fn can_release_part(&self, flags: u32) -> bool; + + /// Indicates whether newly allocated regions contain zeros. + fn allocates_zeros(&self) -> bool; + + /// Returns the page size. Must be a power of two + fn page_size(&self) -> usize; +} + +/// An allocator instance +/// +/// Instances of this type are used to allocate blocks of memory. For best +/// results only use one of these. Currently doesn't implement `Drop` to release +/// lingering memory back to the OS. That may happen eventually though! +#[cfg(feature = "rust_api")] +pub struct Dlmalloc< + #[cfg(feature = "system")] + A = System, + #[cfg(not(feature = "system"))] + A, +>(dlmalloc::Dlmalloc); + +cfg_if::cfg_if! { + if #[cfg(all(feature = "system", target_family = "wasm"))] { + #[path = "wasm.rs"] + mod sys; + } else if #[cfg(all(feature = "system", target_os = "windows"))] { + #[path = "windows.rs"] + mod sys; + } else if #[cfg(all(feature = "system", target_os = "xous"))] { + #[path = "xous.rs"] + mod sys; + } else if #[cfg(all(feature = "system", any(target_os = "linux", target_os = "macos", target_os = "redox")))] { + #[path = "unix.rs"] + mod sys; + } else { + #[path = "dummy.rs"] + mod sys; + } +} + +#[cfg(feature = "system")] +#[cfg(feature = "rust_api")] +impl Dlmalloc { + /// Creates a new instance of an allocator + pub const fn new() -> Dlmalloc { + Dlmalloc(dlmalloc::Dlmalloc::new(System::new())) + } +} + +#[cfg(feature = "rust_api")] +impl Dlmalloc { + /// Creates a new instance of an allocator + pub const fn new_with_allocator(sys_allocator: A) -> Dlmalloc { + Dlmalloc(dlmalloc::Dlmalloc::new(sys_allocator)) + } +} + +#[cfg(feature = "rust_api")] +impl Dlmalloc { + /// Allocates `size` bytes with `align` align. + /// + /// Returns a null pointer if allocation fails. Returns a valid pointer + /// otherwise. + /// + /// Safety and contracts are largely governed by the `GlobalAlloc::alloc` + /// method contracts. + #[inline] + pub unsafe fn malloc(&mut self, size: usize, align: usize) -> *mut u8 { + if align <= self.0.malloc_alignment() { + self.0.malloc(size) + } else { + self.0.memalign(align, size) + } + } + + /// Same as `malloc`, except if the allocation succeeds it's guaranteed to + /// point to `size` bytes of zeros. + #[inline] + pub unsafe fn calloc(&mut self, size: usize, align: usize) -> *mut u8 { + let ptr = self.malloc(size, align); + if !ptr.is_null() && self.0.calloc_must_clear(ptr) { + ptr::write_bytes(ptr, 0, size); + } + ptr + } + + /// Deallocates a `ptr` with `size` and `align` as the previous request used + /// to allocate it. + /// + /// Safety and contracts are largely governed by the `GlobalAlloc::dealloc` + /// method contracts. + #[inline] + pub unsafe fn free(&mut self, ptr: *mut u8, size: usize, align: usize) { + let _ = align; + self.0.validate_size(ptr, size); + self.0.free(ptr) + } + + /// Reallocates `ptr`, a previous allocation with `old_size` and + /// `old_align`, to have `new_size` and the same alignment as before. + /// + /// Returns a null pointer if the memory couldn't be reallocated, but `ptr` + /// is still valid. Returns a valid pointer and frees `ptr` if the request + /// is satisfied. + /// + /// Safety and contracts are largely governed by the `GlobalAlloc::realloc` + /// method contracts. + #[inline] + pub unsafe fn realloc( + &mut self, + ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + ) -> *mut u8 { + self.0.validate_size(ptr, old_size); + + if old_align <= self.0.malloc_alignment() { + self.0.realloc(ptr, new_size) + } else { + let res = self.malloc(new_size, old_align); + if !res.is_null() { + let size = cmp::min(old_size, new_size); + ptr::copy_nonoverlapping(ptr, res, size); + self.free(ptr, old_size, old_align); + } + res + } + } + + /// If possible, gives memory back to the system if there is unused memory + /// at the high end of the malloc pool or in unused segments. + /// + /// You can call this after freeing large blocks of memory to potentially + /// reduce the system-level memory requirements of a program. However, it + /// cannot guarantee to reduce memory. Under some allocation patterns, some + /// large free blocks of memory will be locked between two used chunks, so + /// they cannot be given back to the system. + /// + /// The `pad` argument represents the amount of free trailing space to + /// leave untrimmed. If this argument is zero, only the minimum amount of + /// memory to maintain internal data structures will be left. Non-zero + /// arguments can be supplied to maintain enough trailing space to service + /// future expected allocations without having to re-obtain memory from the + /// system. + /// + /// Returns `true` if it actually released any memory, else `false`. + pub unsafe fn trim(&mut self, pad: usize) -> bool { + self.0.trim(pad) + } + + /// Releases all allocations in this allocator back to the system, + /// consuming self and preventing further use. + /// + /// Returns the number of bytes released to the system. + pub unsafe fn destroy(self) -> usize { + self.0.destroy() + } + + /// Get a reference the underlying [`Allocator`] that this `Dlmalloc` was + /// constructed with. + pub fn allocator(&self) -> &A { + self.0.allocator() + } +} diff --git a/dlmalloc-rs/src/unix.rs b/dlmalloc-rs/src/unix.rs new file mode 100644 index 0000000000..046014f867 --- /dev/null +++ b/dlmalloc-rs/src/unix.rs @@ -0,0 +1,131 @@ +use crate::Allocator; +use core::ptr; + +/// System setting for Linux +pub struct System { + _priv: (), +} + +impl System { + pub const fn new() -> System { + System { _priv: () } + } +} + +#[cfg(feature = "global")] +static mut LOCK: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER; + +unsafe impl Allocator for System { + fn alloc(&self, size: usize) -> (*mut u8, usize, u32) { + let addr = unsafe { + libc::mmap( + ptr::null_mut(), + size, + libc::PROT_WRITE | libc::PROT_READ, + libc::MAP_ANON | libc::MAP_PRIVATE, + -1, + 0, + ) + }; + if addr == libc::MAP_FAILED { + (ptr::null_mut(), 0, 0) + } else { + (addr.cast(), size, 0) + } + } + + #[cfg(target_os = "linux")] + fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8 { + let flags = if can_move { libc::MREMAP_MAYMOVE } else { 0 }; + let ptr = unsafe { libc::mremap(ptr.cast(), oldsize, newsize, flags) }; + if ptr == libc::MAP_FAILED { + ptr::null_mut() + } else { + ptr.cast() + } + } + + #[cfg(any(target_os = "redox", target_os = "macos"))] + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + ptr::null_mut() + } + + #[cfg(target_os = "linux")] + fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool { + unsafe { + let rc = libc::mremap(ptr.cast(), oldsize, newsize, 0); + if rc != libc::MAP_FAILED { + return true; + } + libc::munmap(ptr.add(newsize).cast(), oldsize - newsize) == 0 + } + } + + #[cfg(any(target_os = "redox", target_os = "macos"))] + fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool { + unsafe { libc::munmap(ptr.add(newsize).cast(), oldsize - newsize) == 0 } + } + + fn free(&self, ptr: *mut u8, size: usize) -> bool { + unsafe { libc::munmap(ptr.cast(), size) == 0 } + } + + fn can_release_part(&self, _flags: u32) -> bool { + true + } + + fn allocates_zeros(&self) -> bool { + true + } + + fn page_size(&self) -> usize { + 4096 + } +} + +#[cfg(feature = "global")] +pub fn acquire_global_lock() { + unsafe { assert_eq!(libc::pthread_mutex_lock(ptr::addr_of_mut!(LOCK)), 0) } +} + +#[cfg(feature = "global")] +pub fn release_global_lock() { + unsafe { assert_eq!(libc::pthread_mutex_unlock(ptr::addr_of_mut!(LOCK)), 0) } +} + +#[cfg(feature = "global")] +/// allows the allocator to remain unsable in the child process, +/// after a call to `fork(2)` +/// +/// #Safety +/// +/// if used, this function must be called, +/// before any allocations are made with the global allocator. +pub unsafe fn enable_alloc_after_fork() { + // atfork must only be called once, to avoid a deadlock, + // where the handler attempts to acquire the global lock twice + static mut FORK_PROTECTED: bool = false; + + unsafe extern "C" fn _acquire_global_lock() { + acquire_global_lock() + } + + unsafe extern "C" fn _release_global_lock() { + release_global_lock() + } + + acquire_global_lock(); + // if a process forks, + // it will acquire the lock before any other thread, + // protecting it from deadlock, + // due to the child being created with only the calling thread. + if !FORK_PROTECTED { + libc::pthread_atfork( + Some(_acquire_global_lock), + Some(_release_global_lock), + Some(_release_global_lock), + ); + FORK_PROTECTED = true; + } + release_global_lock(); +} diff --git a/dlmalloc-rs/src/wasm.rs b/dlmalloc-rs/src/wasm.rs new file mode 100644 index 0000000000..a8bf3ef315 --- /dev/null +++ b/dlmalloc-rs/src/wasm.rs @@ -0,0 +1,76 @@ +use crate::Allocator; +#[cfg(target_arch = "wasm32")] +use core::arch::wasm32 as wasm; +#[cfg(target_arch = "wasm64")] +use core::arch::wasm64 as wasm; +use core::ptr; + +/// System setting for Wasm +pub struct System { + _priv: (), +} + +impl System { + pub const fn new() -> System { + System { _priv: () } + } +} + +unsafe impl Allocator for System { + fn alloc(&self, size: usize) -> (*mut u8, usize, u32) { + let pages = size / self.page_size(); + let prev = wasm::memory_grow(0, pages); + if prev == usize::max_value() { + return (ptr::null_mut(), 0, 0); + } + ( + (prev * self.page_size()) as *mut u8, + pages * self.page_size(), + 0, + ) + } + + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + // TODO: I think this can be implemented near the end? + ptr::null_mut() + } + + fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool { + false + } + + fn free(&self, _ptr: *mut u8, _size: usize) -> bool { + false + } + + fn can_release_part(&self, _flags: u32) -> bool { + false + } + + fn allocates_zeros(&self) -> bool { + true + } + + fn page_size(&self) -> usize { + 64 * 1024 + } +} + +#[cfg(feature = "global")] +pub fn acquire_global_lock() { + // single threaded, no need! + assert!(!cfg!(target_feature = "atomics")); +} + +#[cfg(feature = "global")] +pub fn release_global_lock() { + // single threaded, no need! + assert!(!cfg!(target_feature = "atomics")); +} + +#[allow(missing_docs)] +#[cfg(feature = "global")] +pub unsafe fn enable_alloc_after_fork() { + // single threaded, no need! + assert!(!cfg!(target_feature = "atomics")); +} diff --git a/dlmalloc-rs/src/windows.rs b/dlmalloc-rs/src/windows.rs new file mode 100644 index 0000000000..ba88adca68 --- /dev/null +++ b/dlmalloc-rs/src/windows.rs @@ -0,0 +1,88 @@ +use crate::Allocator; +use core::mem::MaybeUninit; +use core::ptr; +use windows_sys::Win32::System::Memory::*; +use windows_sys::Win32::System::SystemInformation::*; +#[cfg(feature = "global")] +use windows_sys::Win32::System::Threading::*; + +pub struct System { + _priv: (), +} + +impl System { + pub const fn new() -> System { + System { _priv: () } + } +} + +unsafe impl Allocator for System { + fn alloc(&self, size: usize) -> (*mut u8, usize, u32) { + let addr = unsafe { + VirtualAlloc( + ptr::null_mut(), + size, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE, + ) + }; + + if addr.is_null() { + (ptr::null_mut(), 0, 0) + } else { + (addr.cast(), size, 0) + } + } + + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + ptr::null_mut() + } + + fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool { + unsafe { VirtualFree(ptr.add(newsize).cast(), oldsize - newsize, MEM_DECOMMIT) != 0 } + } + + fn free(&self, ptr: *mut u8, _size: usize) -> bool { + unsafe { VirtualFree(ptr.cast(), 0, MEM_DECOMMIT) != 0 } + } + + fn can_release_part(&self, _flags: u32) -> bool { + true + } + + fn allocates_zeros(&self) -> bool { + true + } + + fn page_size(&self) -> usize { + unsafe { + let mut info = MaybeUninit::uninit(); + GetSystemInfo(info.as_mut_ptr()); + info.assume_init_ref().dwPageSize as usize + } + } +} + +// NB: `SRWLOCK_INIT` doesn't appear to be in `windows-sys` +#[cfg(feature = "global")] +static mut LOCK: SRWLOCK = SRWLOCK { + Ptr: ptr::null_mut(), +}; + +#[cfg(feature = "global")] +pub fn acquire_global_lock() { + unsafe { + AcquireSRWLockExclusive(ptr::addr_of_mut!(LOCK)); + } +} + +#[cfg(feature = "global")] +pub fn release_global_lock() { + unsafe { + ReleaseSRWLockExclusive(ptr::addr_of_mut!(LOCK)); + } +} + +/// Not needed on Windows +#[cfg(feature = "global")] +pub unsafe fn enable_alloc_after_fork() {} diff --git a/dlmalloc-rs/src/xous.rs b/dlmalloc-rs/src/xous.rs new file mode 100644 index 0000000000..14dbfa1bca --- /dev/null +++ b/dlmalloc-rs/src/xous.rs @@ -0,0 +1,117 @@ +use crate::Allocator; +use core::ptr; + +pub struct System { + _priv: (), +} + +impl System { + pub const fn new() -> System { + System { _priv: () } + } +} + +#[cfg(target_arch = "riscv32")] +mod sys { + use core::arch::asm; + + pub fn increase_heap(length: usize) -> Result<(usize, usize), ()> { + let syscall_no_increase_heap = 10usize; + let memory_flags_read_write = 2usize | 4usize; + + let mut a0 = syscall_no_increase_heap; + let mut a1 = length; + let mut a2 = memory_flags_read_write; + + unsafe { + asm!( + "ecall", + inlateout("a0") a0, + inlateout("a1") a1, + inlateout("a2") a2, + out("a3") _, + out("a4") _, + out("a5") _, + out("a6") _, + out("a7") _, + ) + }; + + let result = a0; + let address = a1; + let length = a2; + + // 3 is the "MemoryRange" type, and the result is only valid + // if we get nonzero address and length. + if result == 3 && address != 0 && length != 0 { + Ok((address, length)) + } else { + Err(()) + } + } +} + +unsafe impl Allocator for System { + /// Allocate an additional `size` bytes on the heap, and return a new + /// chunk of memory, as well as the size of the allocation and some + /// flags. Since flags are unused on this platform, they will always + /// be `0`. + fn alloc(&self, size: usize) -> (*mut u8, usize, u32) { + let size = if size == 0 { + 4096 + } else if size & 4095 == 0 { + size + } else { + size + (4096 - (size & 4095)) + }; + + if let Ok((address, length)) = sys::increase_heap(size) { + let start = address - size + length; + (start as *mut u8, size, 0) + } else { + (ptr::null_mut(), 0, 0) + } + } + + fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 { + // TODO + ptr::null_mut() + } + + fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool { + false + } + + fn free(&self, _ptr: *mut u8, _size: usize) -> bool { + false + } + + fn can_release_part(&self, _flags: u32) -> bool { + false + } + + fn allocates_zeros(&self) -> bool { + true + } + + fn page_size(&self) -> usize { + 4 * 1024 + } +} + +#[cfg(feature = "global")] +pub fn acquire_global_lock() { + // global feature should not be enabled + unimplemented!() +} + +#[cfg(feature = "global")] +pub fn release_global_lock() { + // global feature should not be enabled + unimplemented!() +} + +#[cfg(feature = "global")] +pub unsafe fn enable_alloc_after_fork() { + // platform does not support `fork()` call +} diff --git a/dlmalloc-rs/tests/global.rs b/dlmalloc-rs/tests/global.rs new file mode 100644 index 0000000000..b5cfacd2aa --- /dev/null +++ b/dlmalloc-rs/tests/global.rs @@ -0,0 +1,32 @@ +extern crate dlmalloc; + +use std::collections::HashMap; +use std::thread; + +#[global_allocator] +#[cfg(feature = "global")] +static A: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc; + +#[test] +fn foo() { + println!("hello"); +} + +#[test] +fn map() { + let mut m = HashMap::new(); + m.insert(1, 2); + m.insert(5, 3); + drop(m); +} + +#[test] +fn strings() { + format!("foo, bar, {}", "baz"); +} + +#[test] +#[cfg(not(target_family = "wasm"))] +fn threads() { + assert!(thread::spawn(|| panic!()).join().is_err()); +} diff --git a/dlmalloc-rs/tests/smoke.rs b/dlmalloc-rs/tests/smoke.rs new file mode 100644 index 0000000000..8bcb5d7e46 --- /dev/null +++ b/dlmalloc-rs/tests/smoke.rs @@ -0,0 +1,36 @@ +use arbitrary::Unstructured; +use dlmalloc::Dlmalloc; +use rand::{rngs::SmallRng, RngCore, SeedableRng}; + +#[test] +fn smoke() { + let mut a = Dlmalloc::new(); + unsafe { + let ptr = a.malloc(1, 1); + assert!(!ptr.is_null()); + *ptr = 9; + assert_eq!(*ptr, 9); + a.free(ptr, 1, 1); + + let ptr = a.malloc(1, 1); + assert!(!ptr.is_null()); + *ptr = 10; + assert_eq!(*ptr, 10); + a.free(ptr, 1, 1); + } +} + +#[path = "../fuzz/src/lib.rs"] +mod fuzz; + +#[test] +fn stress() { + let mut rng = SmallRng::seed_from_u64(0); + let mut buf = vec![0; 4096]; + let iters = if cfg!(miri) { 5 } else { 2000 }; + for _ in 0..iters { + rng.fill_bytes(&mut buf); + let mut u = Unstructured::new(&buf); + let _ = fuzz::run(&mut u); + } +} diff --git a/fmt.sh b/fmt.sh new file mode 100755 index 0000000000..8caff707ae --- /dev/null +++ b/fmt.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cargo fmt --package relibc --package crt0 --package redox-rt "$@" diff --git a/generic-rt/Cargo.toml b/generic-rt/Cargo.toml new file mode 100644 index 0000000000..ba01e30d2b --- /dev/null +++ b/generic-rt/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "generic-rt" +version = "0.1.0" +edition = "2024" + +[lints] +workspace = true diff --git a/generic-rt/src/lib.rs b/generic-rt/src/lib.rs new file mode 100644 index 0000000000..dca94521f0 --- /dev/null +++ b/generic-rt/src/lib.rs @@ -0,0 +1,133 @@ +#![no_std] +#![allow(internal_features)] +#![feature(core_intrinsics)] +#![deny(unsafe_op_in_unsafe_fn)] + +use core::{ + arch::asm, + mem::{self, offset_of}, +}; + +#[derive(Debug)] +#[repr(C)] +pub struct GenericTcb { + /// Pointer to the end of static TLS. Must be the first member + pub tls_end: *mut u8, + /// Size of the memory allocated for the static TLS in bytes (multiple of page size) + pub tls_len: usize, + /// Pointer to this structure + pub tcb_ptr: *mut Self, + /// Size of the memory allocated for this structure in bytes (should be same as page size) + pub tcb_len: usize, + pub os_specific: Os, +} +impl GenericTcb { + /// Architecture specific code to read a usize from the TCB - aarch64 + #[allow(unsafe_op_in_unsafe_fn)] + #[inline(always)] + #[cfg(target_arch = "aarch64")] + pub unsafe fn arch_read(offset: usize) -> usize { + let abi_ptr: usize; + asm!( + "mrs {}, tpidr_el0", + out(reg) abi_ptr, + ); + + let tcb_ptr = *(abi_ptr as *const usize); + *((tcb_ptr + offset) as *const usize) + } + + /// Architecture specific code to read a usize from the TCB - x86 + #[allow(unsafe_op_in_unsafe_fn)] + #[inline(always)] + #[cfg(target_arch = "x86")] + pub unsafe fn arch_read(offset: usize) -> usize { + let value; + asm!( + " + mov {}, gs:[{}] + ", + out(reg) value, + in(reg) offset, + ); + value + } + + /// Architecture specific code to read a usize from the TCB - x86_64 + #[allow(unsafe_op_in_unsafe_fn)] + #[inline(always)] + #[cfg(target_arch = "x86_64")] + pub unsafe fn arch_read(offset: usize) -> usize { + let value; + asm!( + " + mov {}, fs:[{}] + ", + out(reg) value, + in(reg) offset, + ); + value + } + + /// Architecture specific code to read a usize from the TCB - riscv64 + #[allow(unsafe_op_in_unsafe_fn)] + #[inline(always)] + #[cfg(target_arch = "riscv64")] + unsafe fn arch_read(offset: usize) -> usize { + let value; + asm!( + "ld {value}, -8(tp)", // TCB + "add {value}, {value}, {offset}", + "ld {value}, 0({value})", + value = out(reg) value, + offset = in(reg) offset, + ); + value + } + + pub unsafe fn current_ptr() -> Option<*mut Self> { + let tcb_ptr = unsafe { Self::arch_read(offset_of!(Self, tcb_ptr)) as *mut Self }; + let tcb_len = unsafe { Self::arch_read(offset_of!(Self, tcb_len)) }; + if tcb_ptr.is_null() || tcb_len < mem::size_of::() { + None + } else { + Some(tcb_ptr) + } + } + pub unsafe fn current() -> Option<&'static mut Self> { + unsafe { Some(&mut *Self::current_ptr()?) } + } +} +pub fn panic_notls(_msg: impl core::fmt::Display) -> ! { + // TODO: actually print _msg, perhaps by having panic_notls take a `T: DebugBackend` that can + // propagate until called by e.g. relibc start + core::intrinsics::abort(); +} + +pub trait ExpectTlsFree { + type Unwrapped; + + fn expect_notls(self, msg: &str) -> Self::Unwrapped; +} +impl ExpectTlsFree for Result { + type Unwrapped = T; + + fn expect_notls(self, msg: &str) -> T { + match self { + Ok(t) => t, + Err(err) => panic_notls(format_args!( + "{msg}: expect failed for Result with err: {err:?}", + )), + } + } +} +impl ExpectTlsFree for Option { + type Unwrapped = T; + + fn expect_notls(self, msg: &str) -> T { + match self { + Some(t) => t, + None => panic_notls(format_args!("{msg}: expect failed for Option")), + } + } +} diff --git a/include/alloca.h b/include/alloca.h new file mode 100644 index 0000000000..047153c476 --- /dev/null +++ b/include/alloca.h @@ -0,0 +1,6 @@ +#ifndef _ALLOCA_H +#define _ALLOCA_H + +#define alloca(size) __builtin_alloca (size) + +#endif /* _ALLOCA_H */ diff --git a/include/complex.h b/include/complex.h new file mode 100644 index 0000000000..5ae2f020d5 --- /dev/null +++ b/include/complex.h @@ -0,0 +1 @@ +#include diff --git a/include/features.h b/include/features.h new file mode 100644 index 0000000000..6d0a0d6225 --- /dev/null +++ b/include/features.h @@ -0,0 +1,95 @@ +/* + * MIT License + * Copyright (c) 2020 Rich Felker musl-libc + */ + +#ifndef _FEATURES_H__RELIBC +#define _FEATURES_H__RELIBC + +// Version metadata for feature gating +// This is useful for divergent implementation specific behavior +// glibc, ulibc, and likely others define a similar macro +// musl does not define an equivalent macro +#define __RELIBC__ 1 +#define __RELIBC__MAJOR 0 +#define __RELIBC__MINOR 2 + +/* + * Sources: + * https://en.cppreference.com/w/c/language/attributes + * https://clang.llvm.org/docs/LanguageExtensions.html + * https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005fc_005fattribute.html + * https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html + */ + +// Clang doesn't define __has_cpp_attribute if compiling C code +#if !defined(__has_cpp_attribute) + #define __has_cpp_attribute(x) 0 +#endif + +// Clang doesn't define __has_c_attribute if compiling C++ code +#if !defined(__has_c_attribute) + #define __has_c_attribute(x) 0 +#endif + +// Check if C23+ attributes are available +#if defined(__cplusplus) +// HACK: GCC backports C++ attributes to C++98 but doesn't accept attributes +// placed before the function like cbindgen emits. +// Let's just disable attributes for C++98 by checking if a random C++11 +// feature is available. +#define __HAS_ATTRIBUTE(x) __cpp_variable_templates &&__has_cpp_attribute(x) +#else +#define __HAS_ATTRIBUTE(x) \ + (__has_c_attribute(x) || __STDC_VERSION__ >= 202311L || \ + __has_cpp_attribute(x)) +#endif + +// TODO: Not emitted with cbindgen +#if __STDC_VERSION__ >= 199901L + #define __restrict restrict +#elif !defined(__GNUC__) + #define __restrict +#endif + +// TODO: Not emitted with cbindgen +#if __STDC_VERSION__ >= 199901L || defined(__cplusplus) + #define __inline inline +#elif !defined(__GNUC__) + #define __inline +#endif + +// Analogous to Rust's Never type +//TODO: clang fails to compile C with [[noreturn]] +#if defined(__cplusplus) && __HAS_ATTRIBUTE(noreturn) && !(__clang__) + #define __noreturn [[noreturn]] +// #elif __STDC_VERSION__ >= 201112L +// FIXME: cbindgen incorrectly places _Noreturn +// #define __noreturn _Noreturn +#elif defined(__GNUC__) + #define __noreturn __attribute__((__noreturn__)) +#else + #define __noreturn +#endif + +// Analogous to Rust's #[must_use] +// C23 only +#if __HAS_ATTRIBUTE(nodiscard) + #define __nodiscard [[nodiscard]] + #define __nodiscardNote(x) [[nodiscard(x)]] +#else + #define __nodiscard + #define __nodiscardNote(x) +#endif + +// Analogous to Rust's #[deprecated] +// C23 only +#if __HAS_ATTRIBUTE(deprecated) + #define __deprecated [[deprecated]] + #define __deprecatedNote(x) [[deprecated(x)]] +#else + #define __deprecated + #define __deprecatedNote(x) +#endif + +#endif diff --git a/include/fenv.h b/include/fenv.h new file mode 100644 index 0000000000..e2437bd8fd --- /dev/null +++ b/include/fenv.h @@ -0,0 +1,3 @@ +#include +#undef complex +#undef I diff --git a/include/iso646.h b/include/iso646.h new file mode 100644 index 0000000000..d94c240909 --- /dev/null +++ b/include/iso646.h @@ -0,0 +1,22 @@ +// Copied from musl + +#ifndef _ISO646_H +#define _ISO646_H + +#ifndef __cplusplus + +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= + +#endif + +#endif diff --git a/include/machine/endian.h b/include/machine/endian.h new file mode 100644 index 0000000000..68da686e7e --- /dev/null +++ b/include/machine/endian.h @@ -0,0 +1,17 @@ +#ifndef __MACHINE_ENDIAN_H__ + +/* TODO: Forcing little endian, if you need a big endian system, fix this { */ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +/* } */ + +#endif /* __MACHINE_ENDIAN_H__ */ diff --git a/include/math.h b/include/math.h new file mode 100644 index 0000000000..eba89dc734 --- /dev/null +++ b/include/math.h @@ -0,0 +1,113 @@ +#include + +// Missing typedefs +typedef float float_t; +typedef double double_t; + +/* double */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif + +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif + +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 /* pi/4 */ +#endif + +#ifndef M_2_PI +#define M_2_PI 0.63661977236758134308 /* 2/pi */ +#endif + +#ifndef M_E +#define M_E 2.7182818284590452354 /* e */ +#endif + +#ifndef M_LOG2E +#define M_LOG2E 1.4426950408889634074 /* log_2 e */ +#endif + +#ifndef M_LOG10E +#define M_LOG10E 0.43429448190325182765 /* log_10 e */ +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#endif + +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#endif + +#ifndef M_1_PI +#define M_1_PI 0.31830988618379067154 /* 1/pi */ +#endif + +#ifndef M_2_SQRTPI +#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#endif + +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#endif + +/* long double */ + +#ifndef M_El +#define M_El 2.718281828459045235360287471352662498L /* e */ +#endif + +#ifndef M_LOG2El +#define M_LOG2El 1.442695040888963407359924681001892137L /* log_2 e */ +#endif + +#ifndef M_LOG10El +#define M_LOG10El 0.434294481903251827651128918916605082L /* log_10 e */ +#endif + +#ifndef M_LN2l +#define M_LN2l 0.693147180559945309417232121458176568L /* log_e 2 */ +#endif + +#ifndef M_LN10l +#define M_LN10l 2.302585092994045684017991454684364208L /* log_e 10 */ +#endif + +#ifndef M_PIl +#define M_PIl 3.141592653589793238462643383279502884L /* pi */ +#endif + +#ifndef M_PI_2l +#define M_PI_2l 1.570796326794896619231321691639751442L /* pi/2 */ +#endif + +#ifndef M_PI_4l +#define M_PI_4l 0.785398163397448309615660845819875721L /* pi/4 */ +#endif + +#ifndef M_1_PIl +#define M_1_PIl 0.318309886183790671537767526745028724L /* 1/pi */ +#endif + +#ifndef M_2_PIl +#define M_2_PIl 0.636619772367581343075535053490057448L /* 2/pi */ +#endif + +#ifndef M_2_SQRTPIl +#define M_2_SQRTPIl 1.128379167095512573896158903121545172L /* 2/sqrt(pi) */ +#endif + +#ifndef M_SQRT2l +#define M_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */ +#endif + +#ifndef M_SQRT1_2l +#define M_SQRT1_2l 0.707106781186547524400844362104849039L /* 1/sqrt(2) */ +#endif \ No newline at end of file diff --git a/include/memory.h b/include/memory.h new file mode 100644 index 0000000000..3b2f590027 --- /dev/null +++ b/include/memory.h @@ -0,0 +1 @@ +#include diff --git a/include/netinet/in_systm.h b/include/netinet/in_systm.h new file mode 100644 index 0000000000..581326eb57 --- /dev/null +++ b/include/netinet/in_systm.h @@ -0,0 +1,10 @@ +#ifndef _NETINET_IN_SYSTM_H +#define _NETINET_IN_SYSTM_H + +#include + +typedef uint16_t n_short; +typedef uint32_t n_long; +typedef uint32_t n_time; + +#endif diff --git a/include/paths.h b/include/paths.h new file mode 100644 index 0000000000..8bbfa920c6 --- /dev/null +++ b/include/paths.h @@ -0,0 +1,6 @@ +#ifndef _RELIBC_PATHS_H +#define _RELIBC_PATHS_H + +#define _PATH_BSHELL "/bin/sh" + +#endif diff --git a/include/setjmp.h b/include/setjmp.h new file mode 100644 index 0000000000..a8bb37622d --- /dev/null +++ b/include/setjmp.h @@ -0,0 +1,81 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +#ifdef __aarch64__ +typedef unsigned long jmp_buf[22]; +#endif + +#ifdef __arm__ +typedef unsigned long long jmp_buf[32]; +#endif + +#ifdef __i386__ +typedef unsigned long jmp_buf[6]; +#endif + +#ifdef __m68k__ +typedef unsigned long jmp_buf[39]; +#endif + +#ifdef __microblaze__ +typedef unsigned long jmp_buf[18]; +#endif + +#ifdef __mips__ +typedef unsigned long long jmp_buf[13]; +#endif + +#ifdef __mips64__ +typedef unsigned long long jmp_buf[23]; +#endif + +#ifdef __mipsn32__ +typedef unsigned long long jmp_buf[23]; +#endif + +#ifdef __or1k__ +typedef unsigned long jmp_buf[13]; +#endif + +#ifdef __powerpc__ +typedef unsigned long long jmp_buf[56]; +#endif + +#ifdef __powerpc64__ +typedef uint128_t jmp_buf[32]; +#endif + +#ifdef __s390x__ +typedef unsigned long jmp_buf[18]; +#endif + +#ifdef __sh__ +typedef unsigned long jmp_buf[15]; +#endif + +#ifdef __x32__ +typedef unsigned long long jmp_buf[8]; +#endif + +#ifdef __x86_64__ +typedef unsigned long jmp_buf[16]; +#endif + +#ifdef __riscv +typedef unsigned long jmp_buf[26]; +#endif + +typedef jmp_buf sigjmp_buf; + +#ifdef __cplusplus +extern "C" { +#endif + +int setjmp(jmp_buf buf); +void longjmp(jmp_buf buf, int value); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _SETJMP_H */ diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100644 index 0000000000..28ff0905ba --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,10 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef __builtin_va_list va_list; +#define va_start(v,l) __builtin_va_start(v,l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v,l) __builtin_va_arg(v,l) +#define va_copy(d,s) __builtin_va_copy(d,s) + +#endif /* _STDARG_H */ diff --git a/include/stdatomic.h b/include/stdatomic.h new file mode 100644 index 0000000000..bfcfdf664c --- /dev/null +++ b/include/stdatomic.h @@ -0,0 +1,243 @@ +/* Copyright (C) 2013-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* ISO C11 Standard: 7.17 Atomics . */ + +#ifndef _STDATOMIC_H +#define _STDATOMIC_H + +typedef enum + { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST + } memory_order; + + +typedef _Atomic _Bool atomic_bool; +typedef _Atomic char atomic_char; +typedef _Atomic signed char atomic_schar; +typedef _Atomic unsigned char atomic_uchar; +typedef _Atomic short atomic_short; +typedef _Atomic unsigned short atomic_ushort; +typedef _Atomic int atomic_int; +typedef _Atomic unsigned int atomic_uint; +typedef _Atomic long atomic_long; +typedef _Atomic unsigned long atomic_ulong; +typedef _Atomic long long atomic_llong; +typedef _Atomic unsigned long long atomic_ullong; +typedef _Atomic __CHAR16_TYPE__ atomic_char16_t; +typedef _Atomic __CHAR32_TYPE__ atomic_char32_t; +typedef _Atomic __WCHAR_TYPE__ atomic_wchar_t; +typedef _Atomic __INT_LEAST8_TYPE__ atomic_int_least8_t; +typedef _Atomic __UINT_LEAST8_TYPE__ atomic_uint_least8_t; +typedef _Atomic __INT_LEAST16_TYPE__ atomic_int_least16_t; +typedef _Atomic __UINT_LEAST16_TYPE__ atomic_uint_least16_t; +typedef _Atomic __INT_LEAST32_TYPE__ atomic_int_least32_t; +typedef _Atomic __UINT_LEAST32_TYPE__ atomic_uint_least32_t; +typedef _Atomic __INT_LEAST64_TYPE__ atomic_int_least64_t; +typedef _Atomic __UINT_LEAST64_TYPE__ atomic_uint_least64_t; +typedef _Atomic __INT_FAST8_TYPE__ atomic_int_fast8_t; +typedef _Atomic __UINT_FAST8_TYPE__ atomic_uint_fast8_t; +typedef _Atomic __INT_FAST16_TYPE__ atomic_int_fast16_t; +typedef _Atomic __UINT_FAST16_TYPE__ atomic_uint_fast16_t; +typedef _Atomic __INT_FAST32_TYPE__ atomic_int_fast32_t; +typedef _Atomic __UINT_FAST32_TYPE__ atomic_uint_fast32_t; +typedef _Atomic __INT_FAST64_TYPE__ atomic_int_fast64_t; +typedef _Atomic __UINT_FAST64_TYPE__ atomic_uint_fast64_t; +typedef _Atomic __INTPTR_TYPE__ atomic_intptr_t; +typedef _Atomic __UINTPTR_TYPE__ atomic_uintptr_t; +typedef _Atomic __SIZE_TYPE__ atomic_size_t; +typedef _Atomic __PTRDIFF_TYPE__ atomic_ptrdiff_t; +typedef _Atomic __INTMAX_TYPE__ atomic_intmax_t; +typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t; + + +#define ATOMIC_VAR_INIT(VALUE) (VALUE) + +/* Initialize an atomic object pointed to by PTR with VAL. */ +#define atomic_init(PTR, VAL) \ + atomic_store_explicit (PTR, VAL, __ATOMIC_RELAXED) + +#define kill_dependency(Y) \ + __extension__ \ + ({ \ + __auto_type __kill_dependency_tmp = (Y); \ + __kill_dependency_tmp; \ + }) + +extern void atomic_thread_fence (memory_order); +#define atomic_thread_fence(MO) __atomic_thread_fence (MO) +extern void atomic_signal_fence (memory_order); +#define atomic_signal_fence(MO) __atomic_signal_fence (MO) +#define atomic_is_lock_free(OBJ) __atomic_is_lock_free (sizeof (*(OBJ)), (OBJ)) + +#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE + + +/* Note that these macros require __auto_type to remove + _Atomic qualifiers (and const qualifiers, if those are valid on + macro operands). + + Also note that the header file uses the generic form of __atomic + builtins, which requires the address to be taken of the value + parameter, and then we pass that value on. This allows the macros + to work for any type, and the compiler is smart enough to convert + these to lock-free _N variants if possible, and throw away the + temps. */ + +#define atomic_store_explicit(PTR, VAL, MO) \ + __extension__ \ + ({ \ + __auto_type __atomic_store_ptr = (PTR); \ + __typeof__ ((void)0, *__atomic_store_ptr) __atomic_store_tmp = (VAL); \ + __atomic_store (__atomic_store_ptr, &__atomic_store_tmp, (MO)); \ + }) + +#define atomic_store(PTR, VAL) \ + atomic_store_explicit (PTR, VAL, __ATOMIC_SEQ_CST) + + +#define atomic_load_explicit(PTR, MO) \ + __extension__ \ + ({ \ + __auto_type __atomic_load_ptr = (PTR); \ + __typeof__ ((void)0, *__atomic_load_ptr) __atomic_load_tmp; \ + __atomic_load (__atomic_load_ptr, &__atomic_load_tmp, (MO)); \ + __atomic_load_tmp; \ + }) + +#define atomic_load(PTR) atomic_load_explicit (PTR, __ATOMIC_SEQ_CST) + + +#define atomic_exchange_explicit(PTR, VAL, MO) \ + __extension__ \ + ({ \ + __auto_type __atomic_exchange_ptr = (PTR); \ + __typeof__ ((void)0, *__atomic_exchange_ptr) __atomic_exchange_val = (VAL); \ + __typeof__ ((void)0, *__atomic_exchange_ptr) __atomic_exchange_tmp; \ + __atomic_exchange (__atomic_exchange_ptr, &__atomic_exchange_val, \ + &__atomic_exchange_tmp, (MO)); \ + __atomic_exchange_tmp; \ + }) + +#define atomic_exchange(PTR, VAL) \ + atomic_exchange_explicit (PTR, VAL, __ATOMIC_SEQ_CST) + + +#define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) \ + __extension__ \ + ({ \ + __auto_type __atomic_compare_exchange_ptr = (PTR); \ + __typeof__ ((void)0, *__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \ + = (DES); \ + __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL), \ + &__atomic_compare_exchange_tmp, 0, \ + (SUC), (FAIL)); \ + }) + +#define atomic_compare_exchange_strong(PTR, VAL, DES) \ + atomic_compare_exchange_strong_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) + +#define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) \ + __extension__ \ + ({ \ + __auto_type __atomic_compare_exchange_ptr = (PTR); \ + __typeof__ ((void)0, *__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \ + = (DES); \ + __atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL), \ + &__atomic_compare_exchange_tmp, 1, \ + (SUC), (FAIL)); \ + }) + +#define atomic_compare_exchange_weak(PTR, VAL, DES) \ + atomic_compare_exchange_weak_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) + + + +#define atomic_fetch_add(PTR, VAL) __atomic_fetch_add ((PTR), (VAL), \ + __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit(PTR, VAL, MO) \ + __atomic_fetch_add ((PTR), (VAL), (MO)) + +#define atomic_fetch_sub(PTR, VAL) __atomic_fetch_sub ((PTR), (VAL), \ + __ATOMIC_SEQ_CST) +#define atomic_fetch_sub_explicit(PTR, VAL, MO) \ + __atomic_fetch_sub ((PTR), (VAL), (MO)) + +#define atomic_fetch_or(PTR, VAL) __atomic_fetch_or ((PTR), (VAL), \ + __ATOMIC_SEQ_CST) +#define atomic_fetch_or_explicit(PTR, VAL, MO) \ + __atomic_fetch_or ((PTR), (VAL), (MO)) + +#define atomic_fetch_xor(PTR, VAL) __atomic_fetch_xor ((PTR), (VAL), \ + __ATOMIC_SEQ_CST) +#define atomic_fetch_xor_explicit(PTR, VAL, MO) \ + __atomic_fetch_xor ((PTR), (VAL), (MO)) + +#define atomic_fetch_and(PTR, VAL) __atomic_fetch_and ((PTR), (VAL), \ + __ATOMIC_SEQ_CST) +#define atomic_fetch_and_explicit(PTR, VAL, MO) \ + __atomic_fetch_and ((PTR), (VAL), (MO)) + + +typedef _Atomic struct +{ +#if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1 + _Bool __val; +#else + unsigned char __val; +#endif +} atomic_flag; + +#define ATOMIC_FLAG_INIT { 0 } + + +extern _Bool atomic_flag_test_and_set (volatile atomic_flag *); +#define atomic_flag_test_and_set(PTR) \ + __atomic_test_and_set ((PTR), __ATOMIC_SEQ_CST) +extern _Bool atomic_flag_test_and_set_explicit (volatile atomic_flag *, + memory_order); +#define atomic_flag_test_and_set_explicit(PTR, MO) \ + __atomic_test_and_set ((PTR), (MO)) + +extern void atomic_flag_clear (volatile atomic_flag *); +#define atomic_flag_clear(PTR) __atomic_clear ((PTR), __ATOMIC_SEQ_CST) +extern void atomic_flag_clear_explicit (volatile atomic_flag *, memory_order); +#define atomic_flag_clear_explicit(PTR, MO) __atomic_clear ((PTR), (MO)) + +#endif /* _STDATOMIC_H */ diff --git a/include/stdbool.h b/include/stdbool.h new file mode 100644 index 0000000000..59cce7a961 --- /dev/null +++ b/include/stdbool.h @@ -0,0 +1,19 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifndef __cplusplus +typedef _Bool bool; +#define true 1 +#define false 0 +#else /* __cplusplus */ +typedef bool _Bool; +#if __cplusplus < 201103L +#define false false +#define true true +#endif /*__cplusplus < 201103L*/ +#endif /* __cplusplus */ + +#define __bool_true_false_are_defined 1 + + +#endif /* _STDBOOL_H */ diff --git a/include/stddef.h b/include/stddef.h new file mode 100644 index 0000000000..334267f457 --- /dev/null +++ b/include/stddef.h @@ -0,0 +1,18 @@ +#ifndef _STDDEF_H +#define _STDDEF_H +#include + +#define NULL 0 + +#ifndef __PTRDIFF_TYPE__ +#define __PTRDIFF_TYPE__ long int +#endif +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +typedef long unsigned int size_t; + +typedef struct { long long __ll; long double __ld; } max_align_t; + +#define offsetof(type, member) __builtin_offsetof(type, member) + +#endif /* _STDDEF_H */ diff --git a/include/stdint.h b/include/stdint.h new file mode 100644 index 0000000000..963b8e9b5d --- /dev/null +++ b/include/stdint.h @@ -0,0 +1,377 @@ +/* Copyright (C) 2008-2018 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* + * ISO C Standard: 7.18 Integer types + */ + +#ifndef _STDINT_H +#define _STDINT_H + +/* 7.8.1.1 Exact-width integer types */ + +#ifdef __INT8_TYPE__ +typedef __INT8_TYPE__ int8_t; +#endif +#ifdef __INT16_TYPE__ +typedef __INT16_TYPE__ int16_t; +#endif +#ifdef __INT32_TYPE__ +typedef __INT32_TYPE__ int32_t; +#endif +#ifdef __INT64_TYPE__ +typedef __INT64_TYPE__ int64_t; +#endif +#ifdef __UINT8_TYPE__ +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT8_TYPE__ u_int8_t; +#endif +#ifdef __UINT16_TYPE__ +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT16_TYPE__ u_int16_t; +#endif +#ifdef __UINT32_TYPE__ +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT32_TYPE__ u_int32_t; +// Required by openlibm +typedef __UINT32_TYPE__ __uint32_t; +#endif +#ifdef __UINT64_TYPE__ +typedef __UINT64_TYPE__ uint64_t; +typedef __UINT64_TYPE__ u_int64_t; +// Required by openlibm +typedef __UINT64_TYPE__ __uint64_t; +#endif + +/* 7.8.1.2 Minimum-width integer types */ + +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; + +/* 7.8.1.3 Fastest minimum-width integer types */ + +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; +typedef __UINT_FAST8_TYPE__ uint_fast8_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; + +/* 7.8.1.4 Integer types capable of holding object pointers */ + +#ifdef __INTPTR_TYPE__ +typedef __INTPTR_TYPE__ intptr_t; +#endif +#ifdef __UINTPTR_TYPE__ +typedef __UINTPTR_TYPE__ uintptr_t; +#endif + +/* 7.8.1.5 Greatest-width integer types */ + +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; + +#if (!defined __cplusplus || __cplusplus >= 201103L \ + || defined __STDC_LIMIT_MACROS) + +/* 7.18.2 Limits of specified-width integer types */ + +#ifdef __INT8_MAX__ +# undef INT8_MAX +# define INT8_MAX __INT8_MAX__ +# undef INT8_MIN +# define INT8_MIN (-INT8_MAX - 1) +#endif +#ifdef __UINT8_MAX__ +# undef UINT8_MAX +# define UINT8_MAX __UINT8_MAX__ +#endif +#ifdef __INT16_MAX__ +# undef INT16_MAX +# define INT16_MAX __INT16_MAX__ +# undef INT16_MIN +# define INT16_MIN (-INT16_MAX - 1) +#endif +#ifdef __UINT16_MAX__ +# undef UINT16_MAX +# define UINT16_MAX __UINT16_MAX__ +#endif +#ifdef __INT32_MAX__ +# undef INT32_MAX +# define INT32_MAX __INT32_MAX__ +# undef INT32_MIN +# define INT32_MIN (-INT32_MAX - 1) +#endif +#ifdef __UINT32_MAX__ +# undef UINT32_MAX +# define UINT32_MAX __UINT32_MAX__ +#endif +#ifdef __INT64_MAX__ +# undef INT64_MAX +# define INT64_MAX __INT64_MAX__ +# undef INT64_MIN +# define INT64_MIN (-INT64_MAX - 1) +#endif +#ifdef __UINT64_MAX__ +# undef UINT64_MAX +# define UINT64_MAX __UINT64_MAX__ +#endif + +#undef INT_LEAST8_MAX +#define INT_LEAST8_MAX __INT_LEAST8_MAX__ +#undef INT_LEAST8_MIN +#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1) +#undef UINT_LEAST8_MAX +#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ +#undef INT_LEAST16_MAX +#define INT_LEAST16_MAX __INT_LEAST16_MAX__ +#undef INT_LEAST16_MIN +#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1) +#undef UINT_LEAST16_MAX +#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ +#undef INT_LEAST32_MAX +#define INT_LEAST32_MAX __INT_LEAST32_MAX__ +#undef INT_LEAST32_MIN +#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1) +#undef UINT_LEAST32_MAX +#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ +#undef INT_LEAST64_MAX +#define INT_LEAST64_MAX __INT_LEAST64_MAX__ +#undef INT_LEAST64_MIN +#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1) +#undef UINT_LEAST64_MAX +#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ + +#undef INT_FAST8_MAX +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#undef INT_FAST8_MIN +#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) +#undef UINT_FAST8_MAX +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#undef INT_FAST16_MAX +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#undef INT_FAST16_MIN +#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) +#undef UINT_FAST16_MAX +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ +#undef INT_FAST32_MAX +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#undef INT_FAST32_MIN +#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) +#undef UINT_FAST32_MAX +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#undef INT_FAST64_MAX +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#undef INT_FAST64_MIN +#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) +#undef UINT_FAST64_MAX +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ + +#ifdef __INTPTR_MAX__ +# undef INTPTR_MAX +# define INTPTR_MAX __INTPTR_MAX__ +# undef INTPTR_MIN +# define INTPTR_MIN (-INTPTR_MAX - 1) +#endif +#ifdef __UINTPTR_MAX__ +# undef UINTPTR_MAX +# define UINTPTR_MAX __UINTPTR_MAX__ +#endif + +#undef INTMAX_MAX +#define INTMAX_MAX __INTMAX_MAX__ +#undef INTMAX_MIN +#define INTMAX_MIN (-INTMAX_MAX - 1) +#undef UINTMAX_MAX +#define UINTMAX_MAX __UINTMAX_MAX__ + +/* 7.18.3 Limits of other integer types */ + +#undef PTRDIFF_MAX +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#undef PTRDIFF_MIN +#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) + +#undef SIG_ATOMIC_MAX +#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ +#undef SIG_ATOMIC_MIN +#define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__ + +#undef SIZE_MAX +#define SIZE_MAX __SIZE_MAX__ + +#undef WCHAR_MAX +#define WCHAR_MAX __WCHAR_MAX__ +#undef WCHAR_MIN +#define WCHAR_MIN __WCHAR_MIN__ + +#undef WINT_MAX +#define WINT_MAX __WINT_MAX__ +#undef WINT_MIN +#define WINT_MIN __WINT_MIN__ + +#endif /* (!defined __cplusplus || __cplusplus >= 201103L + || defined __STDC_LIMIT_MACROS) */ + +#if (!defined __cplusplus || __cplusplus >= 201103L \ + || defined __STDC_CONSTANT_MACROS) + +#undef INT8_C +#define INT8_C(c) __INT8_C(c) +#undef INT16_C +#define INT16_C(c) __INT16_C(c) +#undef INT32_C +#define INT32_C(c) __INT32_C(c) +#undef INT64_C +#define INT64_C(c) __INT64_C(c) +#undef UINT8_C +#define UINT8_C(c) __UINT8_C(c) +#undef UINT16_C +#define UINT16_C(c) __UINT16_C(c) +#undef UINT32_C +#define UINT32_C(c) __UINT32_C(c) +#undef UINT64_C +#define UINT64_C(c) __UINT64_C(c) +#undef INTMAX_C +#define INTMAX_C(c) __INTMAX_C(c) +#undef UINTMAX_C +#define UINTMAX_C(c) __UINTMAX_C(c) + +#endif /* (!defined __cplusplus || __cplusplus >= 201103L + || defined __STDC_CONSTANT_MACROS) */ + +#ifdef __STDC_WANT_IEC_60559_BFP_EXT__ +/* TS 18661-1 widths of integer types. */ + +#ifdef __INT8_TYPE__ +# undef INT8_WIDTH +# define INT8_WIDTH 8 +#endif +#ifdef __UINT8_TYPE__ +# undef UINT8_WIDTH +# define UINT8_WIDTH 8 +#endif +#ifdef __INT16_TYPE__ +# undef INT16_WIDTH +# define INT16_WIDTH 16 +#endif +#ifdef __UINT16_TYPE__ +# undef UINT16_WIDTH +# define UINT16_WIDTH 16 +#endif +#ifdef __INT32_TYPE__ +# undef INT32_WIDTH +# define INT32_WIDTH 32 +#endif +#ifdef __UINT32_TYPE__ +# undef UINT32_WIDTH +# define UINT32_WIDTH 32 +#endif +#ifdef __INT64_TYPE__ +# undef INT64_WIDTH +# define INT64_WIDTH 64 +#endif +#ifdef __UINT64_TYPE__ +# undef UINT64_WIDTH +# define UINT64_WIDTH 64 +#endif + +#undef INT_LEAST8_WIDTH +#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__ +#undef UINT_LEAST8_WIDTH +#define UINT_LEAST8_WIDTH __INT_LEAST8_WIDTH__ +#undef INT_LEAST16_WIDTH +#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__ +#undef UINT_LEAST16_WIDTH +#define UINT_LEAST16_WIDTH __INT_LEAST16_WIDTH__ +#undef INT_LEAST32_WIDTH +#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__ +#undef UINT_LEAST32_WIDTH +#define UINT_LEAST32_WIDTH __INT_LEAST32_WIDTH__ +#undef INT_LEAST64_WIDTH +#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__ +#undef UINT_LEAST64_WIDTH +#define UINT_LEAST64_WIDTH __INT_LEAST64_WIDTH__ + +#undef INT_FAST8_WIDTH +#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__ +#undef UINT_FAST8_WIDTH +#define UINT_FAST8_WIDTH __INT_FAST8_WIDTH__ +#undef INT_FAST16_WIDTH +#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__ +#undef UINT_FAST16_WIDTH +#define UINT_FAST16_WIDTH __INT_FAST16_WIDTH__ +#undef INT_FAST32_WIDTH +#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__ +#undef UINT_FAST32_WIDTH +#define UINT_FAST32_WIDTH __INT_FAST32_WIDTH__ +#undef INT_FAST64_WIDTH +#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__ +#undef UINT_FAST64_WIDTH +#define UINT_FAST64_WIDTH __INT_FAST64_WIDTH__ + +#ifdef __INTPTR_TYPE__ +# undef INTPTR_WIDTH +# define INTPTR_WIDTH __INTPTR_WIDTH__ +#endif +#ifdef __UINTPTR_TYPE__ +# undef UINTPTR_WIDTH +# define UINTPTR_WIDTH __INTPTR_WIDTH__ +#endif + +#undef INTMAX_WIDTH +#define INTMAX_WIDTH __INTMAX_WIDTH__ +#undef UINTMAX_WIDTH +#define UINTMAX_WIDTH __INTMAX_WIDTH__ + +#undef PTRDIFF_WIDTH +#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__ + +#undef SIG_ATOMIC_WIDTH +#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__ + +#undef SIZE_WIDTH +#define SIZE_WIDTH __SIZE_WIDTH__ + +#undef WCHAR_WIDTH +#define WCHAR_WIDTH __WCHAR_WIDTH__ + +#undef WINT_WIDTH +#define WINT_WIDTH __WINT_WIDTH__ + +#ifdef __ILP32__ +#define SIZE_MAX UINT32_MAX +#else +#define SIZE_MAX UINT64_MAX +#endif +#endif + +#endif /* _GCC_STDINT_H */ diff --git a/include/stdio_ext.h b/include/stdio_ext.h new file mode 100644 index 0000000000..aa95d16fcf --- /dev/null +++ b/include/stdio_ext.h @@ -0,0 +1,6 @@ +#ifndef _STDIO_EXT_H +#define _STDIO_EXT_H + +#include + +#endif /* _STDIO_EXT_H */ diff --git a/include/stdnoreturn.h b/include/stdnoreturn.h new file mode 100644 index 0000000000..7df19e742f --- /dev/null +++ b/include/stdnoreturn.h @@ -0,0 +1,19 @@ +/* Spec: + * The header shall define the macro noreturn which shall + * expand to _Noreturn */ + +#ifndef _STDNORETURN_H +#define _STDNORETURN_H + +#ifndef __cplusplus +/* Borrowed from musl */ +#if __STDC_VERSION__ >= 201112L +#elif defined(__GNUC__) + #define _Noreturn __attribute__((__noreturn__)) +#else + #define _Noreturn +#endif +#define noreturn _Noreturn +#endif + +#endif diff --git a/include/sys/param.h b/include/sys/param.h new file mode 100644 index 0000000000..e52f9bca51 --- /dev/null +++ b/include/sys/param.h @@ -0,0 +1,36 @@ +#ifndef _SYS_PARAM_H +#define _SYS_PARAM_H + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define __bitop(array, index, op) ((array)[(index) / 8] op (1 << (index) % 8)) +#define setbit(array, index) __bitop(array, index, |=) +#define clrbit(array, index) __bitop(array, index, &= ~) +#define isset(array, index) __bitop(array, index, &) +#define isclr(array, index) !isset(array, index) + +#define howmany(bits, size) (((bits) + (size) - 1) / (size)) +#define roundup(bits, size) (howmany(bits, size) * (size)) +#define powerof2(n) !(((n) - 1) & (n)) + +// Shamelessly copied from musl. +// Tweak as needed. +#define MAXSYMLINKS 20 +#define MAXHOSTNAMELEN 64 +#define MAXNAMLEN 255 +#define MAXPATHLEN 4096 +#define NBBY 8 +#define NGROUPS 32 +#define CANBSIZ 255 +#define NOFILE 256 +#define NCARGS 131072 +#define DEV_BSIZE 512 +#define NOGROUP (-1) + +#include +#include + +#include + +#endif diff --git a/include/sys/poll.h b/include/sys/poll.h new file mode 100644 index 0000000000..779ec774fe --- /dev/null +++ b/include/sys/poll.h @@ -0,0 +1 @@ +#include diff --git a/include/sys/queue.h b/include/sys/queue.h new file mode 100644 index 0000000000..a38499a267 --- /dev/null +++ b/include/sys/queue.h @@ -0,0 +1,846 @@ +/* $NetBSD: queue.h,v 1.70 2015/11/02 15:21:23 christos Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Include the definition of NULL only on NetBSD because sys/null.h + * is not available elsewhere. This conditional makes the header + * portable and it can simply be dropped verbatim into any system. + * The caveat is that on other systems some other header + * must provide NULL before the macros can be used. + */ +#ifdef __NetBSD__ +#include +#endif + +#if defined(QUEUEDEBUG) +# if defined(_KERNEL) +# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__) +# else +# include +# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__) +# endif +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; \ + (var) != SLIST_END(head); \ + (var) = (var)->field.sle_next) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) != SLIST_END(head) && \ + ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = SLIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_AFTER(slistelm, field) do { \ + (slistelm)->field.sle_next = \ + SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var) != LIST_END(head); \ + (var) = ((var)->field.le_next)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) != LIST_END(head) && \ + ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_MOVE(head1, head2) do { \ + LIST_INIT((head2)); \ + if (!LIST_EMPTY((head1))) { \ + (head2)->lh_first = (head1)->lh_first; \ + LIST_INIT((head1)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * List functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.le_prev != (elm)) \ + QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = LIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != \ + LIST_END(head)) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head); \ + (var) = ((var)->field.sqe_next)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head) && \ + ((next = ((var)->field.sqe_next)), 1); \ + (var) = (next)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_LAST(head, type, field) \ + (SIMPLEQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->sqh_last) - offsetof(struct type, field)))) + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { TAILQ_END(head), &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) (NULL) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)(void *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)(void *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head)) + + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head) && \ + ((next) = TAILQ_NEXT(var, field), 1); (var) = (next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) != TAILQ_END(head) && \ + ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev)) + +/* + * Tail queue functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.tqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = TAILQ_END(head); \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = TAILQ_END(head); \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + + +#ifndef _KERNEL +/* + * Circular queue definitions. Do not use. We still keep the macros + * for compatibility but because of pointer aliasing issues their use + * is discouraged! + */ + +/* + * __launder_type(): We use this ugly hack to work around the the compiler + * noticing that two types may not alias each other and elide tests in code. + * We hit this in the CIRCLEQ macros when comparing 'struct name *' and + * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC + * 4.8) declare these comparisons as always false, causing the code to + * not run as designed. + * + * This hack is only to be used for comparisons and thus can be fully const. + * Do not use for assignment. + * + * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix + * this by changing the head/tail sentinal values, but see the note above + * this one. + */ +static __inline const void * __launder_type(const void *); +static __inline const void * +__launder_type(const void *__x) +{ + __asm __volatile("" : "+r" (__x)); + return __x; +} + +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ + if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \ + (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \ + QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \ + __FILE__, __LINE__); \ + if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \ + (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \ + QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ + if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \ + if ((head)->cqh_last != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } \ + if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \ + if ((head)->cqh_first != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ + (elm)->field.cqe_next = (void *)1L; \ + (elm)->field.cqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) +#endif + +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ + if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != CIRCLEQ_ENDC(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != CIRCLEQ_ENDC(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +/* For comparisons */ +#define CIRCLEQ_ENDC(head) (__launder_type(head)) +/* For assignments */ +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head)) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) +#endif /* !_KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/include/sys/redox.h b/include/sys/redox.h new file mode 100644 index 0000000000..14b906c05d --- /dev/null +++ b/include/sys/redox.h @@ -0,0 +1,22 @@ +#ifndef _SYS_REDOX_H +#define _SYS_REDOX_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __redox__ + +ssize_t redox_fpath(int fd, void * buf, size_t count); +void * redox_physalloc(size_t size); +int redox_physfree(void * physical_address, size_t size); + +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/include/sys/sysmacros.h b/include/sys/sysmacros.h new file mode 100644 index 0000000000..efe296e7f0 --- /dev/null +++ b/include/sys/sysmacros.h @@ -0,0 +1,16 @@ +// From musl, license MIT +#ifndef _SYS_SYSMACROS_H +#define _SYS_SYSMACROS_H + +#define major(x) \ + ((unsigned)( (((x)>>31>>1) & 0xfffff000) | (((x)>>8) & 0x00000fff) )) +#define minor(x) \ + ((unsigned)( (((x)>>12) & 0xffffff00) | ((x) & 0x000000ff) )) + +#define makedev(x,y) ( \ + (((x)&0xfffff000ULL) << 32) | \ + (((x)&0x00000fffULL) << 8) | \ + (((y)&0xffffff00ULL) << 12) | \ + (((y)&0x000000ffULL)) ) + +#endif \ No newline at end of file diff --git a/include/sys/user.h b/include/sys/user.h new file mode 100644 index 0000000000..aadbc7a659 --- /dev/null +++ b/include/sys/user.h @@ -0,0 +1,13 @@ +#ifndef _SYS_USER_H +#define _SYS_USER_H +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) +#include +#elif defined(__aarch64__) +#include +#elif defined(__riscv) && __riscv_xlen==64 +#include +#else +#error "Unknown architecture" +#endif + +#endif diff --git a/include/sysexits.h b/include/sysexits.h new file mode 100644 index 0000000000..4d4b28e778 --- /dev/null +++ b/include/sysexits.h @@ -0,0 +1,21 @@ +#ifndef _SYSEXITS_H +#define _SYSEXITS_H + +#define EX_OK 0 +#define EX_USAGE 64 +#define EX_DATAERR 65 +#define EX_NOINPUT 66 +#define EX_NOUSER 67 +#define EX_NOHOST 68 +#define EX_UNAVAILABLE 69 +#define EX_SOFTWARE 70 +#define EX_OSERR 71 +#define EX_OSFILE 72 +#define EX_CANTCREAT 73 +#define EX_IOERR 74 +#define EX_TEMPFAIL 75 +#define EX_PROTOCOL 76 +#define EX_NOPERM 77 +#define EX_CONFIG 78 + +#endif /* _SYSEXITS_H */ diff --git a/include/syslog.h b/include/syslog.h new file mode 100644 index 0000000000..830b4928ad --- /dev/null +++ b/include/syslog.h @@ -0,0 +1 @@ +#include diff --git a/ld_so/Cargo.toml b/ld_so/Cargo.toml new file mode 100644 index 0000000000..93b1fb1130 --- /dev/null +++ b/ld_so/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ld_so" +version = "0.1.0" +authors = ["Jeremy Soller "] +edition = "2024" + +[lib] +name = "ld_so" +crate-type = ["staticlib"] + +[lints] +workspace = true diff --git a/ld_so/ld_script/aarch64-unknown-linux-gnu.ld b/ld_so/ld_script/aarch64-unknown-linux-gnu.ld new file mode 100644 index 0000000000..a344feef5d --- /dev/null +++ b/ld_so/ld_script/aarch64-unknown-linux-gnu.ld @@ -0,0 +1,263 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2025 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64") +OUTPUT_ARCH(aarch64) +ENTRY(_start) +SEARCH_DIR("=/usr/aarch64-linux-gnu/lib64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/aarch64-linux-gnu/lib"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); + . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; + /* Place the build-id as close to the ELF headers as possible. This + maximises the chance the build-id will be present in core files, + which GDB can then use to locate the associated debuginfo file. */ + .note.gnu.build-id : { *(.note.gnu.build-id) } + .interp : { *(.interp) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .relr.dyn : { *(.relr.dyn) } + /* Start of the executable code region. */ + .init : + { + KEEP (*(SORT_NONE(.init))) + } =0x1f2003d5 + .plt : ALIGN(16) { *(.plt) *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(SORT(.text.sorted.*)) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } =0x1f2003d5 + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } =0x1f2003d5 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + /* Start of the Read Only Data region. */ + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .sframe : ONLY_IF_RO { *(.sframe) *(.sframe.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Various note sections. Placed here so that they are always included + in the read-only segment and not treated as orphan sections. The + current orphan handling algorithm does place note sections after R/O + data, but this is not guaranteed to always be the case. */ + .note.build-id : { *(.note.build-id) } + .note.GNU-stack : { *(.note.GNU-stack) } + .note.gnu-property : { *(.note.gnu-property) } + .note.ABI-tag : { *(.note.ABI-tag) } + .note.package : { *(.note.package) } + .note.dlopen : { *(.note.dlopen) } + .note.netbsd.ident : { *(.note.netbsd.ident) } + .note.openbsd.ident : { *(.note.openbsd.ident) } + /* Start of the Read Write Data region. */ + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling. */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .sframe : ONLY_IF_RW { *(.sframe) *(.sframe.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections. */ + /* .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + /* .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } */ + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (24, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + PROVIDE (__data_start = .); + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(ALIGNOF(NEXT_SECTION)); + __bss_start = .; + __bss_start__ = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that in the common case of there only being one + type of .bss section, the section occupies space up to _end. + Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + _bss_end__ = .; __bss_end__ = .; + . = ALIGN(64 / 8); + /* Start of the Large Data region. */ + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(64 / 8); + __end__ = .; + _end = .; + PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Start of the Tiny Data region. */ + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 (INFO) : { *(.comment); LINKER_VERSION; } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1. */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions. */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2. */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2. */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions. */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3. */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF 5. */ + .debug_addr 0 : { *(.debug_addr) } + .debug_line_str 0 : { *(.debug_line_str) } + .debug_loclists 0 : { *(.debug_loclists) } + .debug_macro 0 : { *(.debug_macro) } + .debug_names 0 : { *(.debug_names) } + .debug_rnglists 0 : { *(.debug_rnglists) } + .debug_str_offsets 0 : { *(.debug_str_offsets) } + .debug_sup 0 : { *(.debug_sup) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array also depends on TLS and is discarded as we don't need it. + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + *(.init_array) + } +} diff --git a/ld_so/ld_script/aarch64-unknown-redox.ld b/ld_so/ld_script/aarch64-unknown-redox.ld new file mode 100644 index 0000000000..64ead42b5e --- /dev/null +++ b/ld_so/ld_script/aarch64-unknown-redox.ld @@ -0,0 +1,255 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", + "elf64-littleaarch64") +OUTPUT_ARCH(aarch64) +ENTRY(_start) +SEARCH_DIR("/aarch64-unknown-redox/lib"); +SEARCH_DIR("/usr/local/lib64"); +SEARCH_DIR("/lib64"); +SEARCH_DIR("/usr/lib64"); +SEARCH_DIR("/usr/local/lib"); +SEARCH_DIR("/lib"); +SEARCH_DIR("/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + /* .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array need to be not discarded unlike x86_64 linker variant + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + } +} diff --git a/ld_so/ld_script/i586-unknown-redox.ld b/ld_so/ld_script/i586-unknown-redox.ld new file mode 100644 index 0000000000..86b9e46c4c --- /dev/null +++ b/ld_so/ld_script/i586-unknown-redox.ld @@ -0,0 +1,256 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SEARCH_DIR("/i686-unknown-redox/lib"); +SEARCH_DIR("/usr/local/lib32"); +SEARCH_DIR("/lib32"); +SEARCH_DIR("/usr/lib32"); +SEARCH_DIR("/usr/local/lib"); +SEARCH_DIR("/lib"); +SEARCH_DIR("/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + /* .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + /* .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } */ + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array also depends on TLS and is discarded as we don't need it. + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + *(.init_array) + } +} diff --git a/ld_so/ld_script/i686-unknown-redox.ld b/ld_so/ld_script/i686-unknown-redox.ld new file mode 100644 index 0000000000..7e68c6c8bb --- /dev/null +++ b/ld_so/ld_script/i686-unknown-redox.ld @@ -0,0 +1,256 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SEARCH_DIR("/i686-unknown-redox/lib"); +SEARCH_DIR("/usr/local/lib32"); +SEARCH_DIR("/lib32"); +SEARCH_DIR("/usr/lib32"); +SEARCH_DIR("/usr/local/lib"); +SEARCH_DIR("/lib"); +SEARCH_DIR("/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + /* .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + /* .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } */ + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array also depends on TLS and is discarded as we don't need it. + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + *(.init_array) + } +} diff --git a/ld_so/ld_script/riscv64gc-unknown-redox.ld b/ld_so/ld_script/riscv64gc-unknown-redox.ld new file mode 100644 index 0000000000..fc657797ea --- /dev/null +++ b/ld_so/ld_script/riscv64gc-unknown-redox.ld @@ -0,0 +1,247 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv" ) +OUTPUT_ARCH(riscv) +ENTRY(_start) +SEARCH_DIR("/riscv64-unknown-redox/lib"); +SEARCH_DIR("/usr/local/lib64"); +SEARCH_DIR("/lib64"); +SEARCH_DIR("/usr/lib64"); +SEARCH_DIR("/usr/local/lib"); +SEARCH_DIR("/lib"); +SEARCH_DIR("/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + /* .tdata : + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + /* .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } */ + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array also depends on TLS and is discarded as we don't need it. + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + *(.init_array) + } +} diff --git a/ld_so/ld_script/x86_64-unknown-linux-gnu.ld b/ld_so/ld_script/x86_64-unknown-linux-gnu.ld new file mode 100644 index 0000000000..be84e120e1 --- /dev/null +++ b/ld_so/ld_script/x86_64-unknown-linux-gnu.ld @@ -0,0 +1,259 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", + "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64"); +SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.33.164"); +SEARCH_DIR("/usr/local/lib64"); +SEARCH_DIR("/lib64"); +SEARCH_DIR("/usr/lib64"); +SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib"); +SEARCH_DIR("/usr/lib64/binutils/x86_64-pc-linux-gnu/2.33.1"); +SEARCH_DIR("/usr/local/lib"); +SEARCH_DIR("/lib"); +SEARCH_DIR("/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + /* .tdata : ALIGN(4K) + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + /* .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } */ + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array also depends on TLS and is discarded as we don't need it. + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + *(.init_array) + } +} diff --git a/ld_so/ld_script/x86_64-unknown-redox.ld b/ld_so/ld_script/x86_64-unknown-redox.ld new file mode 100644 index 0000000000..3e54c4a6d7 --- /dev/null +++ b/ld_so/ld_script/x86_64-unknown-redox.ld @@ -0,0 +1,256 @@ +/* Script for -z combreloc */ +/* Copyright (C) 2014-2020 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", + "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SEARCH_DIR("/x86_64-unknown-redox/lib"); +SEARCH_DIR("/usr/local/lib64"); +SEARCH_DIR("/lib64"); +SEARCH_DIR("/usr/lib64"); +SEARCH_DIR("/usr/local/lib"); +SEARCH_DIR("/lib"); +SEARCH_DIR("/usr/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) + *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) + *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) + *(.rela.ifunc) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + . = ALIGN(CONSTANT (MAXPAGESIZE)); + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) *(.iplt) } +.plt.got : { *(.plt.got) } +.plt.sec : { *(.plt.sec) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + . = ALIGN(CONSTANT (MAXPAGESIZE)); + /* Adjust the address for the rodata segment. We want to adjust up to + the same address within the page on the next page up. */ + . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1))); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges*) } + /* Thread Local Storage sections */ + /* .tdata : ALIGN(4K) + { + PROVIDE_HIDDEN (__tdata_start = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } */ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + /* .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } */ + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got) *(.igot) } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we do not + pad the .data section. */ + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + .lbss : + { + *(.dynlbss) + *(.lbss .lbss.* .gnu.linkonce.lb.*) + *(LARGE_COMMON) + } + . = ALIGN(64 / 8); + . = SEGMENT_START("ldata-segment", .); + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { + *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) + /* + * XXX: As of now, ld.so links with relibc which has the main functionality. In the next refactor, + * ld.so will be moved out of relibc. So, till that time, we have to discard any sections + * that may reference use thread local storage. + * + * .init_array also depends on TLS and is discarded as we don't need it. + */ + *(.gnu.linkonce.tb.*) *(.tcommon) + *(.init_array) + } +} diff --git a/ld_so/src/lib.rs b/ld_so/src/lib.rs new file mode 100644 index 0000000000..e68f260ef6 --- /dev/null +++ b/ld_so/src/lib.rs @@ -0,0 +1,136 @@ +#![no_std] +#![feature(linkage)] +#![deny(unsafe_op_in_unsafe_fn)] + +use core::arch::global_asm; + +#[cfg(target_arch = "aarch64")] +global_asm!( + " +.weak _DYNAMIC +.hidden _DYNAMIC + +.global _start +_start: + mov x28, sp + // align stack to 16 bytes + and sp, x28, #0xfffffffffffffff0 + adr x1, _start + mov x0, x28 + adrp x2, _DYNAMIC + add x2, x2, #:lo12:_DYNAMIC + // ld_so_start(stack=x0, ld_entry=x1, dynamic=x2) + bl relibc_ld_so_start + // restore original stack, clear registers, and jump to the new start function + mov sp, x28 + mov x1, xzr + mov x2, xzr + mov x3, xzr + mov x4, xzr + mov x5, xzr + mov x6, xzr + mov x7, xzr + mov x8, xzr + mov x9, xzr + mov x10, xzr + mov x11, xzr + mov x12, xzr + mov x13, xzr + mov x14, xzr + mov x15, xzr + mov x16, xzr + mov x17, xzr + mov x18, xzr + mov x19, xzr + mov x20, xzr + mov x21, xzr + mov x22, xzr + mov x23, xzr + mov x24, xzr + mov x25, xzr + mov x26, xzr + mov x27, xzr + mov x28, xzr + mov x29, xzr + mov x30, xzr + br x0 + udf #0 +" +); + +#[cfg(target_arch = "x86")] +global_asm!( + " +.globl _start +_start: + push esp + call relibc_ld_so_start + pop esp + # TODO: x86 + ud2 +" +); + +#[cfg(target_arch = "x86_64")] +global_asm!( + " +.weak _DYNAMIC +.hidden _DYNAMIC + +.globl _start +_start: + lea rsi, [rip + _start] + + # Save original stack and align stack to 16 bytes + mov rbp, rsp + and rsp, 0xfffffffffffffff0 + + # Call ld_so_start(stack=rdi, ld_entry=rsi, dynamic=rdx) + mov rdi, rbp + lea rdx, [rip + _DYNAMIC] + call relibc_ld_so_start + + # Restore original stack, clear registers, and jump to new start function + mov rsp, rbp + xor rcx, rcx + xor rdx, rdx + xor rdi, rdi + xor rsi, rsi + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + fninit + jmp rax + ud2 +" +); + +#[cfg(target_arch = "riscv64")] +global_asm!( + " +.globl _start +_start: + mv a0, sp + jal relibc_ld_so_start + unimp +" +); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn main(_argc: isize, _argv: *const *const i8) -> usize { + // LD + 0x1D +} + +#[linkage = "weak"] +#[unsafe(no_mangle)] +extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! { + loop {} +} + +#[panic_handler] +#[linkage = "weak"] +pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! { + relibc_panic(pi) +} diff --git a/openlibm/.github/dependabot.yml b/openlibm/.github/dependabot.yml new file mode 100644 index 0000000000..d60f0707fc --- /dev/null +++ b/openlibm/.github/dependabot.yml @@ -0,0 +1,7 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "monthly" diff --git a/openlibm/.github/workflows/ci.yml b/openlibm/.github/workflows/ci.yml new file mode 100644 index 0000000000..fcc6303373 --- /dev/null +++ b/openlibm/.github/workflows/ci.yml @@ -0,0 +1,93 @@ +name: CI +on: + pull_request: + branches: + - master + push: + branches: + - master + tags: '*' +jobs: + test-unix: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + steps: + - uses: actions/checkout@v5 + - run: make + - run: make test + windows: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - { sys: mingw64, env: x86_64 } + - { sys: mingw32, env: i686 } + - { sys: ucrt64, env: ucrt-x86_64 } # Experimental! + - { sys: clang64, env: clang-x86_64 } # Experimental! + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v5 + - name: Set up the desired MSYS2 environment + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + install: base-devel mingw-w64-${{matrix.env}}-toolchain + - run: make + - run: make test + code-coverage-old: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Setup LCOV + uses: hrishikesh-kadam/setup-lcov@v1 + - name: Build and Run tests + run: make coverage -j + # - name: Upload coverage to Codecov + # uses: codecov/codecov-action@v5 + # with: + # files: ./cov-html/libopenlibm.info + # token: ${{ secrets.CODECOV_TOKEN }} + - uses: actions/upload-artifact@v4 + with: + name: code-coverage-report-old + path: ./cov-html/ + code-coverage: + runs-on: ubuntu-latest + steps: + - name: Checkout Openlibm + uses: actions/checkout@v5 + - name: Checkout Openlibm-test + uses: actions/checkout@v5 + with: + repository: 'JuliaMath/openlibm-test' + path: 'openlibm-test' + - name: Setup LCOV + uses: hrishikesh-kadam/setup-lcov@v1 + - name: Build Openlibm + run: make -j`nproc` CODE_COVERAGE=1 + - name: Run Test + run: | + make -j`nproc` -C openlibm-test \ + USE_OPENLIBM=1 OPENLIBM_HOME="$(pwd)" \ + SKIP_FP_EXCEPT_TEST=1 \ + - name: Show Test Result + run: cat openlibm-test/src/REPORT + - name: Gen Coverage Report + run: make gen-cov-report + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: ./cov-html/libopenlibm.info + token: ${{ secrets.CODECOV_TOKEN }} + - uses: actions/upload-artifact@v4 + with: + name: code-coverage-report + path: ./cov-html/ diff --git a/openlibm/.github/workflows/cross-loongarch64.yml b/openlibm/.github/workflows/cross-loongarch64.yml new file mode 100644 index 0000000000..b8c4b9444e --- /dev/null +++ b/openlibm/.github/workflows/cross-loongarch64.yml @@ -0,0 +1,54 @@ +# merge this file into cross.yml +# when we can `sudo apt install gcc-loongarch64-linux-gnu` on ubuntu +name: Cross + +on: + pull_request: + branches: + - master + push: + branches: + - master + tags: '*' + +jobs: + build-cross-qemu: + # TODO: We need Ubuntu 24.04 to use newer version of qemu, + # switch to ubuntu-latest when `ubuntu-latest >= 24.04` + runs-on: ubuntu-24.04 + name: build-cross-qemu-${{ matrix.config.arch }} + strategy: + fail-fast: false + matrix: + config: + - { arch: loongarch64, triple: loongarch64-linux-gnu } + env: + ARCH: ${{ matrix.config.arch }} + TRIPLE: ${{ matrix.config.triple }} + steps: + - uses: actions/checkout@v5 + - name: Install qemu + run: | + sudo apt update + sudo apt install -y qemu-user qemu-user-binfmt + - name: Install gcc-${{ matrix.config.triple }} + # package gcc-loongarch64-linux-gnu seems not exist + # https://packages.debian.org/sid/amd64/gcc-loongarch64-linux-gnu + run: sudo apt install -y gcc-14-loongarch64-linux-gnu + - name: Build with ${{ matrix.config.triple }}-gcc + run: | + make ARCH=$ARCH TOOLPREFIX=$TRIPLE- \ + CC='loongarch64-linux-gnu-gcc-14' \ + AR='loongarch64-linux-gnu-gcc-ar-14' \ + - name: Build tests + run: | + make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE- \ + CC='loongarch64-linux-gnu-gcc-14' \ + AR='loongarch64-linux-gnu-gcc-ar-14' \ + - name: Run Tests + env: + QEMU_EXEC: qemu-${{ matrix.config.arch }} + CROSS_LIB: /usr/${{ matrix.config.triple }} + run: | + $QEMU_EXEC -L . -L $CROSS_LIB/ test/test-float + $QEMU_EXEC -L . -L $CROSS_LIB/ test/test-double diff --git a/openlibm/.github/workflows/cross.yml b/openlibm/.github/workflows/cross.yml new file mode 100644 index 0000000000..fd01c6bcbb --- /dev/null +++ b/openlibm/.github/workflows/cross.yml @@ -0,0 +1,53 @@ +name: Cross + +on: + pull_request: + branches: + - master + push: + branches: + - master + tags: '*' + +jobs: + build-cross-qemu: + # TODO: We need Ubuntu 24.04 to use newer version of qemu, + # switch to ubuntu-latest when `ubuntu-latest >= 24.04` + runs-on: ubuntu-24.04 + name: build-cross-qemu-${{ matrix.config.arch }} + strategy: + fail-fast: false + matrix: + config: + - { arch: arm, triple: arm-linux-gnueabihf } + - { arch: aarch64, triple: aarch64-linux-gnu } + - { arch: ppc, triple: powerpc-linux-gnu } + - { arch: ppc64, triple: powerpc64-linux-gnu } + - { arch: ppc64le, triple: powerpc64le-linux-gnu } + - { arch: mips, triple: mips-linux-gnu } + - { arch: mipsel, triple: mipsel-linux-gnu } + # Builds successfully, but tests fail. + # - { arch: mips64, triple: mips64-linux-gnuabi64 } + # - { arch: mips64el, triple: mips64el-linux-gnuabi64 } + - { arch: riscv64, triple: riscv64-linux-gnu } + - { arch: s390x, triple: s390x-linux-gnu } + env: + ARCH: ${{ matrix.config.arch }} + TRIPLE: ${{ matrix.config.triple }} + steps: + - uses: actions/checkout@v5 + - name: Install qemu and toolchain gcc-${{ matrix.config.triple }} + run: | + sudo apt update + sudo apt install qemu-user qemu-user-binfmt gcc-$TRIPLE -y + - name: Build with ${{ matrix.config.triple }}-gcc + run: make ARCH=$ARCH TOOLPREFIX=$TRIPLE- + - name: Build tests + run: make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE- + - name: Run Tests + env: + QEMU_EXEC: qemu-${{ matrix.config.arch }} + CROSS_LIB: /usr/${{ matrix.config.triple }} + run: | + $QEMU_EXEC -L . -L $CROSS_LIB/ test/test-float + $QEMU_EXEC -L . -L $CROSS_LIB/ test/test-double diff --git a/openlibm/.gitignore b/openlibm/.gitignore new file mode 100644 index 0000000000..9f5c0be62a --- /dev/null +++ b/openlibm/.gitignore @@ -0,0 +1,13 @@ +*.o +*~ +*.a +*.dll* +*.so* +*.dylib* +*.pc + +# code coverage +openlibm-test +cov-html/ +*.gcda +*.gcno diff --git a/openlibm/.mailmap b/openlibm/.mailmap new file mode 100644 index 0000000000..637106fe7a --- /dev/null +++ b/openlibm/.mailmap @@ -0,0 +1,61 @@ +JuliaLang +JuliaLang + +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson + +Stefan Karpinski +Stefan Karpinski +Stefan Karpinski + +Viral B. Shah +Viral B. Shah +Viral B. Shah +Viral B. Shah + +George Xing +George Xing + +Stephan Boyer +Stephan Boyer +Stephan Boyer +Stephan Boyer + +Giuseppe Zingales +Giuseppe Zingales + +Jameson Nash +Jameson Nash +Jameson Nash + +Alan Edelman + +PlayMyCode +PlayMyCode + +Corey M. Hoffstein +Corey M. Hoffstein + +Stefan Kroboth + +Tim Holy +Tim Holy + +Patrick O'Leary + +Ivan Mantova + +Keno Fischer +Keno Fischer +Keno Fischer diff --git a/openlibm/CMakeLists.txt b/openlibm/CMakeLists.txt new file mode 100755 index 0000000000..f46f037119 --- /dev/null +++ b/openlibm/CMakeLists.txt @@ -0,0 +1,576 @@ +cmake_minimum_required(VERSION 3.25) + +# Get version string from Make.inc +file(READ "${CMAKE_CURRENT_LIST_DIR}/Make.inc" MAKE_FILE) +string(REGEX MATCH "VERSION = ([0-9\.]+)" _ ${MAKE_FILE}) + +project(openlibm + VERSION ${CMAKE_MATCH_1} + LANGUAGES C ASM) + +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +add_library("${PROJECT_NAME}") + +# Find the relevant folder depending on the architecture +set(OPENLIBM_ARCH_FOLDER ${CMAKE_SYSTEM_PROCESSOR}) +string(TOLOWER "${OPENLIBM_ARCH_FOLDER}" OPENLIBM_ARCH_FOLDER) + +if(${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "x86_64") + set(OPENLIBM_ARCH_FOLDER "amd64") +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "arm64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64") + set(OPENLIBM_ARCH_FOLDER "aarch64") +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "armv7-a") + set(OPENLIBM_ARCH_FOLDER "arm") +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "x86" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "i686") + set(OPENLIBM_ARCH_FOLDER "i387") +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc") + set(OPENLIBM_ARCH_FOLDER "powerpc") +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64") + set(OPENLIBM_ARCH_FOLDER "riscv64") +else() + message(FATAL_ERROR "${PROJECT_NAME} not set up for detected architecture: ${OPENLIBM_ARCH_FOLDER}") +endif() + + +# Compile flags +list(APPEND C_ASM_COMPILE_FLAGS "-ffp-contract=off" "-fno-fast-math" "-fno-rounding-math" "-fno-math-errno") +list(APPEND C_ASM_COMPILE_FLAGS "-fPIC" "-std=c99" "-fno-builtin") +list(APPEND C_ASM_COMPILE_FLAGS "-Wall" "-Wno-implicit-function-declaration") +list(APPEND C_ASM_COMPILE_FLAGS "-DASSEMBLER" "-D__BSD_VISIBLE" "-O3") + +# Compiler-specific compile flags +if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") + list(APPEND C_ASM_COMPILE_FLAGS "-fno-strict-aliasing" "-ffp-exception-behavior=strict") +elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + list(APPEND C_ASM_COMPILE_FLAGS "-fno-gnu89-inline") +else() + message(FATAL_ERROR "${PROJECT_NAME} not set up to be compiled with ${CMAKE_C_COMPILER_ID}") +endif() + +# Architecture-specific compile flags - take advantage of sse on x86 +if(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387") + list(APPEND C_ASM_COMPILE_FLAGS "-march=i686" "-m32" "-msse2" "-mfpmath=sse") +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64") + list(APPEND C_ASM_COMPILE_FLAGS "-m64" "-msse2" "-mfpmath=sse") +endif() + +# Suppress warnings if requested +if(OPENLIBM_SUPPRESS_WARNINGS) + list(APPEND C_ASM_COMPILE_FLAGS "-w") +endif() + +# Add compile flags +target_compile_options("${PROJECT_NAME}" PUBLIC ${C_ASM_COMPILE_FLAGS}) + +# Project Source +set(PROJECT_SRC "${CMAKE_CURRENT_SOURCE_DIR}") + +# Common +list(APPEND OPENLIBM_C_SOURCE + # src + "${PROJECT_SRC}/src/common.c" + "${PROJECT_SRC}/src/e_acos.c" + "${PROJECT_SRC}/src/e_acosf.c" + "${PROJECT_SRC}/src/e_acosh.c" + "${PROJECT_SRC}/src/e_acoshf.c" + "${PROJECT_SRC}/src/e_asin.c" + "${PROJECT_SRC}/src/e_asinf.c" + "${PROJECT_SRC}/src/e_atan2.c" + "${PROJECT_SRC}/src/e_atan2f.c" + "${PROJECT_SRC}/src/e_atanh.c" + "${PROJECT_SRC}/src/e_atanhf.c" + "${PROJECT_SRC}/src/e_cosh.c" + "${PROJECT_SRC}/src/e_coshf.c" + "${PROJECT_SRC}/src/e_exp.c" + "${PROJECT_SRC}/src/e_expf.c" + "${PROJECT_SRC}/src/e_fmod.c" + "${PROJECT_SRC}/src/e_fmodf.c" + "${PROJECT_SRC}/src/e_hypot.c" + "${PROJECT_SRC}/src/e_hypotf.c" + "${PROJECT_SRC}/src/e_j0.c" + "${PROJECT_SRC}/src/e_j0f.c" + "${PROJECT_SRC}/src/e_j1.c" + "${PROJECT_SRC}/src/e_j1f.c" + "${PROJECT_SRC}/src/e_jn.c" + "${PROJECT_SRC}/src/e_jnf.c" + "${PROJECT_SRC}/src/e_lgamma.c" + "${PROJECT_SRC}/src/e_lgamma_r.c" + "${PROJECT_SRC}/src/e_lgammaf.c" + "${PROJECT_SRC}/src/e_lgammaf_r.c" + "${PROJECT_SRC}/src/e_log.c" + "${PROJECT_SRC}/src/e_log10.c" + "${PROJECT_SRC}/src/e_log10f.c" + "${PROJECT_SRC}/src/e_log2.c" + "${PROJECT_SRC}/src/e_log2f.c" + "${PROJECT_SRC}/src/e_logf.c" + "${PROJECT_SRC}/src/e_pow.c" + "${PROJECT_SRC}/src/e_powf.c" + "${PROJECT_SRC}/src/e_remainder.c" + "${PROJECT_SRC}/src/e_remainderf.c" + "${PROJECT_SRC}/src/e_rem_pio2.c" + "${PROJECT_SRC}/src/e_rem_pio2f.c" + "${PROJECT_SRC}/src/e_sinh.c" + "${PROJECT_SRC}/src/e_sinhf.c" + "${PROJECT_SRC}/src/e_sqrt.c" + "${PROJECT_SRC}/src/e_sqrtf.c" + "${PROJECT_SRC}/src/k_cos.c" + "${PROJECT_SRC}/src/k_exp.c" + "${PROJECT_SRC}/src/k_expf.c" + "${PROJECT_SRC}/src/k_rem_pio2.c" + "${PROJECT_SRC}/src/k_sin.c" + "${PROJECT_SRC}/src/k_tan.c" + "${PROJECT_SRC}/src/k_cosf.c" + "${PROJECT_SRC}/src/k_sinf.c" + "${PROJECT_SRC}/src/k_tanf.c" + "${PROJECT_SRC}/src/s_asinh.c" + "${PROJECT_SRC}/src/s_asinhf.c" + "${PROJECT_SRC}/src/s_atan.c" + "${PROJECT_SRC}/src/s_atanf.c" + "${PROJECT_SRC}/src/s_carg.c" + "${PROJECT_SRC}/src/s_cargf.c" + "${PROJECT_SRC}/src/s_cbrt.c" + "${PROJECT_SRC}/src/s_cbrtf.c" + "${PROJECT_SRC}/src/s_ceil.c" + "${PROJECT_SRC}/src/s_ceilf.c" + "${PROJECT_SRC}/src/s_copysign.c" + "${PROJECT_SRC}/src/s_copysignf.c" + "${PROJECT_SRC}/src/s_cos.c" + "${PROJECT_SRC}/src/s_cosf.c" + "${PROJECT_SRC}/src/s_csqrt.c" + "${PROJECT_SRC}/src/s_csqrtf.c" + "${PROJECT_SRC}/src/s_erf.c" + "${PROJECT_SRC}/src/s_erff.c" + "${PROJECT_SRC}/src/s_exp2.c" + "${PROJECT_SRC}/src/s_exp2f.c" + "${PROJECT_SRC}/src/s_expm1.c" + "${PROJECT_SRC}/src/s_expm1f.c" + "${PROJECT_SRC}/src/s_fabs.c" + "${PROJECT_SRC}/src/s_fabsf.c" + "${PROJECT_SRC}/src/s_fdim.c" + "${PROJECT_SRC}/src/s_floor.c" + "${PROJECT_SRC}/src/s_floorf.c" + "${PROJECT_SRC}/src/s_fmax.c" + "${PROJECT_SRC}/src/s_fmaxf.c" + "${PROJECT_SRC}/src/s_fmin.c" + "${PROJECT_SRC}/src/s_fminf.c" + "${PROJECT_SRC}/src/s_fpclassify.c" + "${PROJECT_SRC}/src/s_frexp.c" + "${PROJECT_SRC}/src/s_frexpf.c" + "${PROJECT_SRC}/src/s_ilogb.c" + "${PROJECT_SRC}/src/s_ilogbf.c" + "${PROJECT_SRC}/src/s_isinf.c" + "${PROJECT_SRC}/src/s_isfinite.c" + "${PROJECT_SRC}/src/s_isnormal.c" + "${PROJECT_SRC}/src/s_isnan.c" + "${PROJECT_SRC}/src/s_log1p.c" + "${PROJECT_SRC}/src/s_log1pf.c" + "${PROJECT_SRC}/src/s_logb.c" + "${PROJECT_SRC}/src/s_logbf.c" + "${PROJECT_SRC}/src/s_modf.c" + "${PROJECT_SRC}/src/s_modff.c" + "${PROJECT_SRC}/src/s_nextafter.c" + "${PROJECT_SRC}/src/s_nextafterf.c" + "${PROJECT_SRC}/src/s_nexttowardf.c" + "${PROJECT_SRC}/src/s_remquo.c" + "${PROJECT_SRC}/src/s_remquof.c" + "${PROJECT_SRC}/src/s_rint.c" + "${PROJECT_SRC}/src/s_rintf.c" + "${PROJECT_SRC}/src/s_round.c" + "${PROJECT_SRC}/src/s_roundf.c" + "${PROJECT_SRC}/src/s_scalbln.c" + "${PROJECT_SRC}/src/s_scalbn.c" + "${PROJECT_SRC}/src/s_scalbnf.c" + "${PROJECT_SRC}/src/s_signbit.c" + "${PROJECT_SRC}/src/s_signgam.c" + "${PROJECT_SRC}/src/s_sin.c" + "${PROJECT_SRC}/src/s_sincos.c" + "${PROJECT_SRC}/src/s_sinf.c" + "${PROJECT_SRC}/src/s_sincosf.c" + "${PROJECT_SRC}/src/s_tan.c" + "${PROJECT_SRC}/src/s_tanf.c" + "${PROJECT_SRC}/src/s_tanh.c" + "${PROJECT_SRC}/src/s_tanhf.c" + "${PROJECT_SRC}/src/s_tgammaf.c" + "${PROJECT_SRC}/src/s_trunc.c" + "${PROJECT_SRC}/src/s_truncf.c" + "${PROJECT_SRC}/src/s_cpow.c" + "${PROJECT_SRC}/src/s_cpowf.c" + "${PROJECT_SRC}/src/w_cabs.c" + "${PROJECT_SRC}/src/w_cabsf.c" + + "${PROJECT_SRC}/src/s_fma.c" + "${PROJECT_SRC}/src/s_fmaf.c" + "${PROJECT_SRC}/src/s_lrint.c" + "${PROJECT_SRC}/src/s_lrintf.c" + "${PROJECT_SRC}/src/s_lround.c" + "${PROJECT_SRC}/src/s_lroundf.c" + "${PROJECT_SRC}/src/s_llrint.c" + "${PROJECT_SRC}/src/s_llrintf.c" + "${PROJECT_SRC}/src/s_llround.c" + "${PROJECT_SRC}/src/s_llroundf.c" + "${PROJECT_SRC}/src/s_nearbyint.c" + + # C99 complex functions + "${PROJECT_SRC}/src/s_ccosh.c" + "${PROJECT_SRC}/src/s_ccoshf.c" + "${PROJECT_SRC}/src/s_cexp.c" + "${PROJECT_SRC}/src/s_cexpf.c" + "${PROJECT_SRC}/src/s_cimag.c" + "${PROJECT_SRC}/src/s_cimagf.c" + "${PROJECT_SRC}/src/s_conj.c" + "${PROJECT_SRC}/src/s_conjf.c" + "${PROJECT_SRC}/src/s_cproj.c" + "${PROJECT_SRC}/src/s_cprojf.c" + "${PROJECT_SRC}/src/s_creal.c" + "${PROJECT_SRC}/src/s_crealf.c" + "${PROJECT_SRC}/src/s_csinh.c" + "${PROJECT_SRC}/src/s_csinhf.c" + "${PROJECT_SRC}/src/s_ctanh.c" + "${PROJECT_SRC}/src/s_ctanhf.c" + "${PROJECT_SRC}/src/s_cacos.c" + "${PROJECT_SRC}/src/s_cacosf.c" + "${PROJECT_SRC}/src/s_cacosh.c" + "${PROJECT_SRC}/src/s_cacoshf.c" + "${PROJECT_SRC}/src/s_casin.c" + "${PROJECT_SRC}/src/s_casinf.c" + "${PROJECT_SRC}/src/s_casinh.c" + "${PROJECT_SRC}/src/s_casinhf.c" + "${PROJECT_SRC}/src/s_catan.c" + "${PROJECT_SRC}/src/s_catanf.c" + "${PROJECT_SRC}/src/s_catanh.c" + "${PROJECT_SRC}/src/s_catanhf.c" + "${PROJECT_SRC}/src/s_clog.c" + "${PROJECT_SRC}/src/s_clogf.c" + + # bsdsrc + "${PROJECT_SRC}/bsdsrc/b_exp.c" + "${PROJECT_SRC}/bsdsrc/b_log.c" + "${PROJECT_SRC}/bsdsrc/b_tgamma.c" +) + +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/src/s_nan.c" + ) +endif() + +# Determine if long double and double are the same size +include(CheckCSourceCompiles) +check_c_source_compiles(" +#include +#if (LDBL_MANT_DIG == DBL_MANT_DIG) +#error \"long double and double are the same size\" +#endif +int main(void ) { return 0; } +" LONG_DOUBLE_NOT_DOUBLE) + +# Add in long double functions for x86, x64 and aarch64 +if(LONG_DOUBLE_NOT_DOUBLE) + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/src/s_copysignl.c" + "${PROJECT_SRC}/src/s_fabsl.c" + "${PROJECT_SRC}/src/s_llrintl.c" + "${PROJECT_SRC}/src/s_lrintl.c" + "${PROJECT_SRC}/src/s_modfl.c" + + "${PROJECT_SRC}/src/e_acosl.c" + "${PROJECT_SRC}/src/e_asinl.c" + "${PROJECT_SRC}/src/e_atan2l.c" + "${PROJECT_SRC}/src/e_fmodl.c" + "${PROJECT_SRC}/src/s_fmaxl.c" + "${PROJECT_SRC}/src/s_fminl.c" + "${PROJECT_SRC}/src/s_ilogbl.c" + "${PROJECT_SRC}/src/e_hypotl.c" + "${PROJECT_SRC}/src/e_lgammal.c" + "${PROJECT_SRC}/src/e_remainderl.c" + "${PROJECT_SRC}/src/e_sqrtl.c" + "${PROJECT_SRC}/src/s_atanl.c" + "${PROJECT_SRC}/src/s_ceill.c" + "${PROJECT_SRC}/src/s_cosl.c" + "${PROJECT_SRC}/src/s_cprojl.c" + "${PROJECT_SRC}/src/s_csqrtl.c" + "${PROJECT_SRC}/src/s_floorl.c" + "${PROJECT_SRC}/src/s_fmal.c" + "${PROJECT_SRC}/src/s_frexpl.c" + "${PROJECT_SRC}/src/s_logbl.c" + "${PROJECT_SRC}/src/s_nexttoward.c" + "${PROJECT_SRC}/src/s_remquol.c" + "${PROJECT_SRC}/src/s_roundl.c" + "${PROJECT_SRC}/src/s_lroundl.c" + "${PROJECT_SRC}/src/s_llroundl.c" + "${PROJECT_SRC}/src/s_cpowl.c" + "${PROJECT_SRC}/src/s_cargl.c" + "${PROJECT_SRC}/src/s_sinl.c" + "${PROJECT_SRC}/src/s_sincosl.c" + "${PROJECT_SRC}/src/s_tanl.c" + "${PROJECT_SRC}/src/s_truncl.c" + "${PROJECT_SRC}/src/w_cabsl.c" + "${PROJECT_SRC}/src/s_nextafterl.c" + "${PROJECT_SRC}/src/s_rintl.c" + "${PROJECT_SRC}/src/s_scalbnl.c" + "${PROJECT_SRC}/src/polevll.c" + "${PROJECT_SRC}/src/s_casinl.c" + "${PROJECT_SRC}/src/s_ctanl.c" + "${PROJECT_SRC}/src/s_cimagl.c" + "${PROJECT_SRC}/src/s_conjl.c" + "${PROJECT_SRC}/src/s_creall.c" + "${PROJECT_SRC}/src/s_cacoshl.c" + "${PROJECT_SRC}/src/s_catanhl.c" + "${PROJECT_SRC}/src/s_casinhl.c" + "${PROJECT_SRC}/src/s_catanl.c" + "${PROJECT_SRC}/src/s_csinl.c" + "${PROJECT_SRC}/src/s_cacosl.c" + "${PROJECT_SRC}/src/s_cexpl.c" + "${PROJECT_SRC}/src/s_csinhl.c" + "${PROJECT_SRC}/src/s_ccoshl.c" + "${PROJECT_SRC}/src/s_clogl.c" + "${PROJECT_SRC}/src/s_ctanhl.c" + "${PROJECT_SRC}/src/s_ccosl.c" + "${PROJECT_SRC}/src/s_cbrtl.c" + ) +endif() + +if (LONG_DOUBLE_NOT_DOUBLE) + if (${OPENLIBM_ARCH_FOLDER} STREQUAL "i387" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64") + list(APPEND OPENLIBM_C_SOURCE + # ld80 + "${PROJECT_SRC}/ld80/invtrig.c" + "${PROJECT_SRC}/ld80/e_acoshl.c" + "${PROJECT_SRC}/ld80/e_powl.c" + "${PROJECT_SRC}/ld80/k_tanl.c" + "${PROJECT_SRC}/ld80/s_exp2l.c" + "${PROJECT_SRC}/ld80/e_atanhl.c" + "${PROJECT_SRC}/ld80/e_lgammal_r.c" + "${PROJECT_SRC}/ld80/e_sinhl.c" + "${PROJECT_SRC}/ld80/s_asinhl.c" + "${PROJECT_SRC}/ld80/s_expm1l.c" + "${PROJECT_SRC}/ld80/e_coshl.c" + "${PROJECT_SRC}/ld80/e_log10l.c" + "${PROJECT_SRC}/ld80/e_tgammal.c" + "${PROJECT_SRC}/ld80/e_expl.c" + "${PROJECT_SRC}/ld80/e_log2l.c" + "${PROJECT_SRC}/ld80/k_cosl.c" + "${PROJECT_SRC}/ld80/s_log1pl.c" + "${PROJECT_SRC}/ld80/s_tanhl.c" + "${PROJECT_SRC}/ld80/e_logl.c" + "${PROJECT_SRC}/ld80/k_sinl.c" + "${PROJECT_SRC}/ld80/s_erfl.c" + ) + + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/ld80/s_nanl.c" + ) + endif() + else() + if(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64") + list(APPEND OPENLIBM_C_SOURCE + # ld128 + "${PROJECT_SRC}/ld128/invtrig.c" + "${PROJECT_SRC}/ld128/e_acoshl.c" + "${PROJECT_SRC}/ld128/e_powl.c" + "${PROJECT_SRC}/ld128/k_tanl.c" + "${PROJECT_SRC}/ld128/s_exp2l.c" + "${PROJECT_SRC}/ld128/e_atanhl.c" + "${PROJECT_SRC}/ld128/e_lgammal_r.c" + "${PROJECT_SRC}/ld128/e_sinhl.c" + "${PROJECT_SRC}/ld128/s_asinhl.c" + "${PROJECT_SRC}/ld128/s_expm1l.c" + "${PROJECT_SRC}/ld128/e_coshl.c" + "${PROJECT_SRC}/ld128/e_log10l.c" + "${PROJECT_SRC}/ld128/e_tgammal.c" + "${PROJECT_SRC}/ld128/e_expl.c" + "${PROJECT_SRC}/ld128/e_log2l.c" + "${PROJECT_SRC}/ld128/k_cosl.c" + "${PROJECT_SRC}/ld128/s_log1pl.c" + "${PROJECT_SRC}/ld128/s_tanhl.c" + "${PROJECT_SRC}/ld128/e_logl.c" + "${PROJECT_SRC}/ld128/k_sinl.c" + "${PROJECT_SRC}/ld128/s_erfl.c" + ) + + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/ld128/s_nanl.c" + ) + endif() + endif() + endif() +endif() + +# Architecture-specific sources +if (${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/amd64/fenv.c" + ) + + list(APPEND OPENLIBM_ASM_SOURCE + "${PROJECT_SRC}/amd64/e_remainder.S" + "${PROJECT_SRC}/amd64/e_remainderf.S" + "${PROJECT_SRC}/amd64/e_remainderl.S" + "${PROJECT_SRC}/amd64/e_sqrt.S" + "${PROJECT_SRC}/amd64/e_sqrtf.S" + "${PROJECT_SRC}/amd64/e_sqrtl.S" + "${PROJECT_SRC}/amd64/s_llrint.S" + "${PROJECT_SRC}/amd64/s_llrintf.S" + "${PROJECT_SRC}/amd64/s_llrintl.S" + "${PROJECT_SRC}/amd64/s_logbl.S" + "${PROJECT_SRC}/amd64/s_lrint.S" + "${PROJECT_SRC}/amd64/s_lrintf.S" + "${PROJECT_SRC}/amd64/s_lrintl.S" + "${PROJECT_SRC}/amd64/s_remquo.S" + "${PROJECT_SRC}/amd64/s_remquof.S" + "${PROJECT_SRC}/amd64/s_remquol.S" + "${PROJECT_SRC}/amd64/s_rintl.S" + "${PROJECT_SRC}/amd64/s_scalbn.S" + "${PROJECT_SRC}/amd64/s_scalbnf.S" + "${PROJECT_SRC}/amd64/s_scalbnl.S" + "${PROJECT_SRC}/amd64/e_fmod.S" + "${PROJECT_SRC}/amd64/e_fmodf.S" + "${PROJECT_SRC}/amd64/e_fmodl.S" + ) + +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/aarch64/fenv.c" + ) + +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "arm") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/${OPENLIBM_ARCH_FOLDER}/fenv.c" + ) + +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/i387/fenv.c" + ) + + list(APPEND OPENLIBM_ASM_SOURCE + "${PROJECT_SRC}/i387/e_exp.S" + "${PROJECT_SRC}/i387/e_fmod.S" + "${PROJECT_SRC}/i387/e_log.S" + "${PROJECT_SRC}/i387/e_log10.S" + "${PROJECT_SRC}/i387/e_remainder.S" + "${PROJECT_SRC}/i387/e_sqrt.S" + "${PROJECT_SRC}/i387/s_ceil.S" + "${PROJECT_SRC}/i387/s_copysign.S" + "${PROJECT_SRC}/i387/s_floor.S" + "${PROJECT_SRC}/i387/s_llrint.S" + "${PROJECT_SRC}/i387/s_logb.S" + "${PROJECT_SRC}/i387/s_lrint.S" + "${PROJECT_SRC}/i387/s_remquo.S" + "${PROJECT_SRC}/i387/s_rint.S" + "${PROJECT_SRC}/i387/s_tan.S" + "${PROJECT_SRC}/i387/s_trunc.S" + + # float counterparts + "${PROJECT_SRC}/i387/e_log10f.S" + "${PROJECT_SRC}/i387/e_logf.S" + "${PROJECT_SRC}/i387/e_remainderf.S" + "${PROJECT_SRC}/i387/e_sqrtf.S" + "${PROJECT_SRC}/i387/s_ceilf.S" + "${PROJECT_SRC}/i387/s_copysignf.S" + "${PROJECT_SRC}/i387/s_floorf.S" + "${PROJECT_SRC}/i387/s_llrintf.S" + "${PROJECT_SRC}/i387/s_logbf.S" + "${PROJECT_SRC}/i387/s_lrintf.S" + "${PROJECT_SRC}/i387/s_remquof.S" + "${PROJECT_SRC}/i387/s_rintf.S" + "${PROJECT_SRC}/i387/s_truncf.S" + + # long double counterparts + "${PROJECT_SRC}/i387/e_remainderl.S" + "${PROJECT_SRC}/i387/e_sqrtl.S" + "${PROJECT_SRC}/i387/s_ceill.S" + "${PROJECT_SRC}/i387/s_copysignl.S" + "${PROJECT_SRC}/i387/s_floorl.S" + "${PROJECT_SRC}/i387/s_llrintl.S" + "${PROJECT_SRC}/i387/s_logbl.S" + "${PROJECT_SRC}/i387/s_lrintl.S" + "${PROJECT_SRC}/i387/s_remquol.S" + "${PROJECT_SRC}/i387/s_rintl.S" + "${PROJECT_SRC}/i387/s_truncl.S" + ) + + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + list(APPEND OPENLIBM_ASM_SOURCE + "${PROJECT_SRC}/i387/s_scalbn.S" + "${PROJECT_SRC}/i387/s_scalbnf.S" + "${PROJECT_SRC}/i387/s_scalbnl.S" + ) + endif() + +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/powerpc/fenv.c" + ) +elseif(${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64") + list(APPEND OPENLIBM_C_SOURCE + "${PROJECT_SRC}/riscv64/fenv.c") +else() + message(FATAL_ERROR "${PROJECT_NAME} CMake build is not set up for ${OPENLIBM_ARCH_FOLDER}") +endif() + + +# Filter out C implementation from compilation list if a native implementation exists +foreach(FILE_TO_REMOVE ${OPENLIBM_ASM_SOURCE}) + # Get filename and strip out extension + cmake_path(GET FILE_TO_REMOVE FILENAME FILENAME_TO_REMOVE) + cmake_path(REMOVE_EXTENSION FILENAME_TO_REMOVE OUTPUT_VARIABLE FILENAME_TO_REMOVE) + message(DEBUG "Filename to remove: ${FILENAME_TO_REMOVE}") + + # Go through files and remove one with the same name + foreach(CUR_FILE ${OPENLIBM_C_SOURCE}) + cmake_path(GET CUR_FILE FILENAME CUR_FILENAME) + cmake_path(REMOVE_EXTENSION CUR_FILENAME OUTPUT_VARIABLE CUR_FILENAME) + + if(${CUR_FILENAME} STREQUAL ${FILENAME_TO_REMOVE}) + list(REMOVE_ITEM OPENLIBM_C_SOURCE ${CUR_FILE}) + message(DEBUG "Removed source file from compilation list: ${CUR_FILE}") + break() + endif() + endforeach() +endforeach() + + +# Add sources +target_sources("${PROJECT_NAME}" PRIVATE ${OPENLIBM_C_SOURCE} + ${OPENLIBM_ASM_SOURCE} +) + + +# Include directories +list(APPEND OPENLIBM_INCLUDE_DIRS + "${PROJECT_SRC}" + "${PROJECT_SRC}/include" + "${PROJECT_SRC}/${OPENLIBM_ARCH_FOLDER}" + "${PROJECT_SRC}/src" +) + +if(${OPENLIBM_ARCH_FOLDER} STREQUAL "i387" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "amd64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "powerpc") + list(APPEND OPENLIBM_INCLUDE_DIRS "${PROJECT_SRC}/ld80") +else() + if(${OPENLIBM_ARCH_FOLDER} STREQUAL "aarch64" OR ${OPENLIBM_ARCH_FOLDER} STREQUAL "riscv64") + list(APPEND OPENLIBM_INCLUDE_DIRS "${PROJECT_SRC}/ld128") + endif() +endif() + +target_include_directories("${PROJECT_NAME}" PUBLIC ${OPENLIBM_INCLUDE_DIRS}) + +file(GLOB PUBLIC_HEADERS "*.h" "include/*.h" "${OPENLIBM_ARCH_FOLDER}/*.h" "src/*.h") +set_target_properties("${PROJECT_NAME}" PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}") +install (TARGETS "${PROJECT_NAME}") + +# Can't use configure_file because openlibm.pc.in uses $var instead of CMake configure @var's +# Would rather string replace variables here instead of editing .pc.in, because editing .pc.in +# might build break autotools build. +file(READ "${PROJECT_SRC}/openlibm.pc.in" PC_FILE) +string(REPLACE "\${version}" "${CMAKE_PROJECT_VERSION}" PC_FILE ${PC_FILE}) +string(PREPEND PC_FILE "prefix=${CMAKE_INSTALL_PREFIX} +includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR} +libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n +") +file(WRITE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc" ${PC_FILE}) +install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/openlibm/LICENSE.md b/openlibm/LICENSE.md new file mode 100644 index 0000000000..d76019ef1d --- /dev/null +++ b/openlibm/LICENSE.md @@ -0,0 +1,115 @@ +## OpenLibm + +OpenLibm contains code that is covered by various licenses. + +The OpenLibm code derives from the FreeBSD msun and OpenBSD libm +implementations, which in turn derives from FDLIBM 5.3. As a result, it +has a number of fixes and updates that have accumulated over the years +in msun, and also optimized assembly versions of many functions. These +improvements are provided under the BSD and ISC licenses. The msun +library also includes work placed under the public domain, which is +noted in the individual files. Further work on making a standalone +OpenLibm library from msun, as part of the Julia project is covered +under the MIT license. The test files, test-double.c and test-float.c +are under the LGPL. + +## Parts copyrighted by the Julia project (MIT License) + +> Copyright (c) 2011-14 The Julia Project. +> https://github.com/JuliaMath/openlibm/graphs/contributors +> +> Permission is hereby granted, free of charge, to any person obtaining +> a copy of this software and associated documentation files (the +> "Software"), to deal in the Software without restriction, including +> without limitation the rights to use, copy, modify, merge, publish, +> distribute, sublicense, and/or sell copies of the Software, and to +> permit persons to whom the Software is furnished to do so, subject to +> the following conditions: +> +> The above copyright notice and this permission notice shall be +> included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## Parts copyrighted by Stephen L. Moshier (ISC License) + +> Copyright (c) 2008 Stephen L. Moshier +> +> Permission to use, copy, modify, and distribute this software for any +> purpose with or without fee is hereby granted, provided that the above +> copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +> WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +> MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +> ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +> WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +> ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +> OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +## FREEBSD MSUN (FreeBSD/2-clause BSD/Simplified BSD License) + +> Copyright 1992-2011 The FreeBSD Project. All rights reserved. +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted provided that the following conditions are +> met: +> +> 1. Redistributions of source code must retain the above copyright +> notice, this list of conditions and the following disclaimer. +> +> 2. Redistributions in binary form must reproduce the above copyright +> notice, this list of conditions and the following disclaimer in the +> documentation and/or other materials provided with the distribution. +> THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY +> EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +> PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR +> CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +> EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +> PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +> PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +> LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +> NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +> SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +> +> The views and conclusions contained in the software and documentation +> are those of the authors and should not be interpreted as representing +> official policies, either expressed or implied, of the FreeBSD +> Project. + +## FDLIBM + +> Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +> +> Developed at SunPro, a Sun Microsystems, Inc. business. +> Permission to use, copy, modify, and distribute this +> software is freely granted, provided that this notice +> is preserved. + +## Tests + +> Copyright (C) 1997, 1999 Free Software Foundation, Inc. +> This file is part of the GNU C Library. +> Contributed by Andreas Jaeger , 1997. +> +> The GNU C Library is free software; you can redistribute it and/or +> modify it under the terms of the GNU Lesser General Public +> License as published by the Free Software Foundation; either +> version 2.1 of the License, or (at your option) any later version. +> +> The GNU C Library is distributed in the hope that it will be useful, +> but WITHOUT ANY WARRANTY; without even the implied warranty of +> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +> Lesser General Public License for more details. +> +> You should have received a copy of the GNU Lesser General Public +> License along with the GNU C Library; if not, write to the Free +> Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +> 02111-1307 USA. diff --git a/openlibm/Make.inc b/openlibm/Make.inc new file mode 100644 index 0000000000..7914e8215f --- /dev/null +++ b/openlibm/Make.inc @@ -0,0 +1,197 @@ +# -*- mode: makefile-gmake -*- +# vi:ft=make + +# Default build rule for any Makefile in this project: all +default: all + +OS := $(shell uname) +# Do not forget to bump SOMINOR when changing VERSION, +# and SOMAJOR when breaking ABI in a backward-incompatible way +VERSION = 0.8.0 +SOMAJOR = 4 +SOMINOR = 0 +DESTDIR = +prefix ?= /usr/local +bindir ?= $(prefix)/bin +libdir ?= $(prefix)/lib +includedir ?= $(prefix)/include + +ifeq ($(OS), FreeBSD) +pkgconfigdir ?= $(prefix)/libdata/pkgconfig +else +pkgconfigdir ?= $(libdir)/pkgconfig +endif + +# Build with Code Coverage +# Only test with Ubuntu + gcc + lcov, may not work for other platform. +# deps: https://github.com/linux-test-project/lcov +# You don't need to set this flag manually, `make coverage` will do it for you. +# Just Run: make clean && make coverage -j +CODE_COVERAGE ?= 0 + +ifneq (,$(findstring $(OS),Darwin FreeBSD OpenBSD)) +USEGCC ?= 0 +USECLANG ?= 1 +endif + +ifneq (,$(findstring CLANG,$(MSYSTEM))) +# In MSYS2 +USEGCC = 0 +USECLANG = 1 +endif + +ifeq ($(ARCH),wasm32) +USECLANG = 1 +USEGCC = 0 +TOOLPREFIX = llvm- +endif + +USEGCC ?= 1 +USECLANG ?= 0 +TOOLPREFIX ?= + +AR := $(TOOLPREFIX)ar + +ifeq ($(USECLANG),1) +USEGCC = 0 +CC = clang +CFLAGS_add += -fno-builtin -fno-strict-aliasing +endif + +ifeq ($(USEGCC),1) +CC := $(TOOLPREFIX)gcc +CFLAGS_add += -fno-gnu89-inline -fno-builtin +endif + +ARCH ?= $(shell $(CC) -dumpmachine | sed "s/\([^-]*\).*$$/\1/") + +ifeq ($(ARCH),mingw32) +$(error "the mingw32 compiler you are using fails the openblas testsuite. please see the Julia README.windows.md document for a replacement") +endif + +# OS-specific stuff +ifeq ($(ARCH),arm64) +override ARCH := aarch64 +endif +ifeq ($(findstring arm,$(ARCH)),arm) +override ARCH := arm +MARCH ?= armv7-a+fp +CFLAGS_add += -mhard-float +endif +ifeq ($(findstring powerpc,$(ARCH)),powerpc) +override ARCH := powerpc +endif +ifeq ($(findstring ppc,$(ARCH)),ppc) +override ARCH := powerpc +endif +ifeq ($(findstring s390,$(ARCH)),s390) +override ARCH := s390 +endif +ifneq ($(filter $(ARCH),i386 i486 i586 i686 i387 i487 i587 i687),) +override ARCH := i387 +MARCH ?= i686 +endif +ifeq ($(ARCH),x86_64) +override ARCH := amd64 +endif +ifeq ($(findstring mips,$(ARCH)),mips) +override ARCH := mips +endif +ifeq ($(findstring riscv64,$(ARCH)),riscv64) +override ARCH := riscv64 +endif +ifeq ($(findstring loongarch64,$(ARCH)),loongarch64) +override ARCH := loongarch64 +endif + +# If CFLAGS does not contain a -O optimization flag, default to -O3 +ifeq ($(findstring -O,$(CFLAGS)),) +CFLAGS_add += -O3 +endif + +ifneq (,$(findstring MINGW,$(OS))) +override OS=WINNT +endif + +#keep these if statements separate +ifeq ($(OS), WINNT) +SHLIB_EXT = dll +SONAME_FLAG = +shlibdir = $(bindir) +else +ifeq ($(OS), Darwin) +SHLIB_EXT = dylib +SONAME_FLAG = -install_name +else +SHLIB_EXT = so +SONAME_FLAG = -soname +endif +CFLAGS_add += -fPIC +shlibdir = $(libdir) +endif + +# Add `-march` to our CFLAGS if it's defined +ifneq ($(MARCH),) +CFLAGS_arch += -march=$(MARCH) +endif + +ifeq ($(ARCH),i387) +CFLAGS_arch += -m32 +SFLAGS_arch += -m32 +LDFLAGS_arch += -m32 +endif + +ifeq ($(ARCH),amd64) +CFLAGS_arch += -m64 +SFLAGS_arch += -m64 +LDFLAGS_arch += -m64 +endif + +ifeq ($(ARCH),wasm32) +CFLAGS_arch += -ffreestanding -nostdlib -nostdinc --target=wasm32-unknown-unknown +endif + +# Add our "arch"-related FLAGS in. We separate arch-related flags out so that +# we can conveniently get at them for targets that don't want the rest of +# *FLAGS_add, such as the testing Makefile targets +CFLAGS_add += $(CFLAGS_arch) +SFLAGS_add += $(SFLAGS_arch) +LDFLAGS_add += $(LDFLAGS_arch) + +CFLAGS_add += -std=c99 -Wall -I$(OPENLIBM_HOME) -I$(OPENLIBM_HOME)/include -I$(OPENLIBM_HOME)/$(ARCH) -I$(OPENLIBM_HOME)/src -DASSEMBLER -D__BSD_VISIBLE -Wno-implicit-function-declaration +ifneq ($(filter $(ARCH),i387 amd64 powerpc),) +CFLAGS_add += -I$(OPENLIBM_HOME)/ld80 +else +ifneq ($(filter $(ARCH),aarch64 riscv64),) +CFLAGS_add += -I$(OPENLIBM_HOME)/ld128 +endif +endif + +ifneq ($(filter $(ARCH),i387 amd64),) +# Determines whether `long double` is the same as `double` on this arch. +# linux x86_64, for instance, `long double` is 80 bits wide, whereas on macOS aarch64, +# `long double` is the same as `double`. +LONG_DOUBLE_NOT_DOUBLE := 1 +else ifeq ($(ARCH), aarch64) +ifeq ($(filter $(OS),Darwin WINNT),) +LONG_DOUBLE_NOT_DOUBLE := 1 +endif +endif + +ifeq ($(CODE_COVERAGE),1) +CFLAGS_add += -g -O0 --coverage +LDFLAGS_add += --coverage +endif # CODE_COVERAGE==1 + + +%.c.o: %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) -c $< -o $@ + +%.S.o: %.S + $(CC) $(CPPFLAGS) $(SFLAGS) $(SFLAGS_add) $(filter -m% -B% -I% -D%,$(CFLAGS_add)) -c $< -o $@ + + +# Makefile debugging trick: +# call print-VARIABLE to see the runtime value of any variable +print-%: + @echo '$*=$($*)' diff --git a/openlibm/Makefile b/openlibm/Makefile new file mode 100644 index 0000000000..0d7d9185e8 --- /dev/null +++ b/openlibm/Makefile @@ -0,0 +1,140 @@ +OPENLIBM_HOME=$(abspath .) +include ./Make.inc + +SUBDIRS = src $(ARCH) bsdsrc +ifeq ($(LONG_DOUBLE_NOT_DOUBLE),1) +# Add ld80 directory on x86 and x64 +ifneq ($(filter $(ARCH),i387 amd64),) +SUBDIRS += ld80 +else +ifneq ($(filter $(ARCH),aarch64),) +SUBDIRS += ld128 +else +endif +endif +endif + +define INC_template +TEST=test +override CUR_SRCS = $(1)_SRCS +include $(1)/Make.files +SRCS += $$(addprefix $(1)/,$$($(1)_SRCS)) +endef + +DIR=test + +$(foreach dir,$(SUBDIRS),$(eval $(call INC_template,$(dir)))) + +DUPLICATE_NAMES = $(filter $(patsubst %.S,%,$($(ARCH)_SRCS)),$(patsubst %.c,%,$(src_SRCS))) +DUPLICATE_SRCS = $(addsuffix .c,$(DUPLICATE_NAMES)) + +OBJS = $(patsubst %.f,%.f.o,\ + $(patsubst %.S,%.S.o,\ + $(patsubst %.c,%.c.o,$(filter-out $(addprefix src/,$(DUPLICATE_SRCS)),$(SRCS))))) + +# If we're on windows, don't do versioned shared libraries. Also, generate an import library +# for the DLL. If we're on OSX, put the version number before the .dylib. Otherwise, +# put it after. +ifeq ($(OS), WINNT) +OLM_MAJOR_MINOR_SHLIB_EXT := $(SHLIB_EXT) +LDFLAGS_add += -Wl,--out-implib,libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT).a +else +ifeq ($(OS), Darwin) +OLM_MAJOR_MINOR_SHLIB_EXT := $(SOMAJOR).$(SOMINOR).$(SHLIB_EXT) +OLM_MAJOR_SHLIB_EXT := $(SOMAJOR).$(SHLIB_EXT) +else +OLM_MAJOR_MINOR_SHLIB_EXT := $(SHLIB_EXT).$(SOMAJOR).$(SOMINOR) +OLM_MAJOR_SHLIB_EXT := $(SHLIB_EXT).$(SOMAJOR) +endif +LDFLAGS_add += -Wl,$(SONAME_FLAG),libopenlibm.$(OLM_MAJOR_SHLIB_EXT) +endif + +.PHONY: all check test clean distclean \ + install install-static install-shared install-pkgconfig install-headers + + +OLM_LIBS := libopenlibm.a +ifneq ($(ARCH), wasm32) +OLM_LIBS += libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT) +endif + +all : $(OLM_LIBS) + +check test: test/test-double test/test-float + test/test-double + test/test-float + +libopenlibm.a: $(OBJS) + $(AR) -rcs libopenlibm.a $(OBJS) + +libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT): $(OBJS) + $(CC) -shared $(OBJS) $(LDFLAGS) $(LDFLAGS_add) -o $@ +ifneq ($(OS),WINNT) + ln -sf $@ libopenlibm.$(OLM_MAJOR_SHLIB_EXT) + ln -sf $@ libopenlibm.$(SHLIB_EXT) +endif + +test/test-double: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT) + $(MAKE) -C test test-double + +test/test-float: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT) + $(MAKE) -C test test-float + +COVERAGE_DIR:=cov-html +COVERAGE_FILE:=$(COVERAGE_DIR)/libopenlibm.info +# Gen cov report with: make clean && make coverage -j +coverage: clean-coverage + $(MAKE) test CODE_COVERAGE=1 + $(MAKE) gen-cov-report + +gen-cov-report: + -mkdir $(COVERAGE_DIR) + lcov -d amd64 -d bsdsrc -d ld80 -d src \ + --rc lcov_branch_coverage=1 --capture --output-file $(COVERAGE_FILE) + genhtml --legend --branch-coverage \ + --title "Openlibm commit `git rev-parse HEAD`" \ + --output-directory $(COVERAGE_DIR)/ \ + $(COVERAGE_FILE) + +# Zero coverage statistics and Delete report +clean-coverage: + -lcov -d amd64 -d bsdsrc -d ld80 -d src --zerocounters + rm -f ./*/*.gcda + rm -rf $(COVERAGE_DIR)/ + +clean: clean-coverage + rm -f aarch64/*.o amd64/*.o arm/*.o bsdsrc/*.o i387/*.o loongarch64/*.o ld80/*.o ld128/*.o src/*.o powerpc/*.o mips/*.o s390/*.o riscv64/*.o + rm -f libopenlibm.a libopenlibm.*$(SHLIB_EXT)* + rm -f ./*/*.gcno + $(MAKE) -C test clean + +openlibm.pc: openlibm.pc.in Make.inc Makefile + echo "version=${VERSION}" > openlibm.pc + echo "libdir=$(DESTDIR)$(libdir)" >> openlibm.pc + echo "includedir=$(DESTDIR)$(includedir)/openlibm" >> openlibm.pc + cat openlibm.pc.in >> openlibm.pc + +install-static: libopenlibm.a + mkdir -p $(DESTDIR)$(libdir) + cp -RpP -f libopenlibm.a $(DESTDIR)$(libdir)/ + +install-shared: libopenlibm.$(OLM_MAJOR_MINOR_SHLIB_EXT) + mkdir -p $(DESTDIR)$(shlibdir) +ifeq ($(OS), WINNT) + mkdir -p $(DESTDIR)$(libdir) + cp -RpP -f libopenlibm.*$(SHLIB_EXT) $(DESTDIR)$(shlibdir)/ + cp -RpP -f libopenlibm.*$(SHLIB_EXT).a $(DESTDIR)$(libdir)/ +else + cp -RpP -f libopenlibm.*$(SHLIB_EXT)* $(DESTDIR)$(shlibdir)/ +endif + +install-pkgconfig: openlibm.pc + mkdir -p $(DESTDIR)$(pkgconfigdir) + cp -RpP -f openlibm.pc $(DESTDIR)$(pkgconfigdir)/ + +install-headers: + mkdir -p $(DESTDIR)$(includedir)/openlibm + cp -RpP -f include/*.h $(DESTDIR)$(includedir)/openlibm + cp -RpP -f src/*.h $(DESTDIR)$(includedir)/openlibm + +install: install-static install-shared install-pkgconfig install-headers diff --git a/openlibm/README.md b/openlibm/README.md new file mode 100644 index 0000000000..d543a3bc97 --- /dev/null +++ b/openlibm/README.md @@ -0,0 +1,71 @@ +# OpenLibm + +[![codecov](https://codecov.io/gh/JuliaMath/openlibm/graph/badge.svg?token=eTAdN7d9cg)](https://codecov.io/gh/JuliaMath/openlibm) + +[OpenLibm](https://openlibm.org/) is an effort to have a high quality, portable, standalone +C mathematical library ([`libm`](http://en.wikipedia.org/wiki/libm)). +It can be used standalone in applications and programming language +implementations. + +The project was born out of a need to have a good `libm` for the +[Julia programming language](http://www.julialang.org) that worked +consistently across compilers and operating systems, and in 32-bit and +64-bit environments. + +## Platform support + +OpenLibm builds on Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD, and +DragonFly BSD. It builds with both GCC and clang. Although largely +tested and widely used on the x86 and x86-64 architectures, OpenLibm +also supports arm, aarch64, ppc64le, mips, wasm32, riscv, s390(x) and +loongarch64. + +## Build instructions + +### GNU Make + +1. Use GNU Make to build OpenLibm. This is `make` on most systems, but `gmake` on BSDs. +2. Use `make USEGCC=1` to build with GCC. This is the default on + Linux and Windows. +3. Use `make USECLANG=1` to build with clang. This is the default on OS X, FreeBSD, + and OpenBSD. +4. Use `make ARCH=wasm32` to build the wasm32 library with clang. +5. Architectures are auto-detected. Use `make ARCH=i386` to force a + build for i386. Other supported architectures are i486, i586, and + i686. GCC 4.8 is the minimum requirement for correct codegen on + older 32-bit architectures. + + +**Cross Build** +Take `riscv64` as example: +1. install `qemu-riscv64-static`, `gcc-riscv64-linux-gnu` +2. Cross build: +```sh +ARCH=riscv64 +TRIPLE=$ARCH-linux-gnu +make ARCH=$ARCH TOOLPREFIX=$TRIPLE- -j +make -C test ARCH=$ARCH TOOLPREFIX=$TRIPLE- -j +``` + +3. Run test with qemu: +```sh +qemu-$ARCH-static -L . -L /usr/$TRIPLE/ test/test-float +qemu-$ARCH-static -L . -L /usr/$TRIPLE/ test/test-double +``` + + +### CMake + +1. Create build directory with `mkdir build` and navigate into it with `cd build`. +2. Run CMake to configure project and generate native build system with `cmake /path/to/openlibm/` +or generate project with build system of choice e.g. `cmake /path/to/openlib/ -G "MinGW Makefiles"`. +3. Build with the build system with `cmake --build .`. + +Default CMake configuration builds a shared library, this can easily be configured using +[BUILD_SHARED_LIBS](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) +configuration option. + + +## Acknowledgements + +PowerPC support for openlibm was graciously sponsored by IBM. diff --git a/openlibm/aarch64/Make.files b/openlibm/aarch64/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/aarch64/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/aarch64/fenv.c b/openlibm/aarch64/fenv.c new file mode 100644 index 0000000000..8ed8557650 --- /dev/null +++ b/openlibm/aarch64/fenv.c @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/arm/fenv.c,v 1.3 2011/10/16 05:37:56 das Exp $ + */ + +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +/* + * Hopefully the system ID byte is immutable, so it's valid to use + * this as a default environment. + */ +const fenv_t __fe_dfl_env = {0}; + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); diff --git a/openlibm/amd64/Make.files b/openlibm/amd64/Make.files new file mode 100644 index 0000000000..662071c792 --- /dev/null +++ b/openlibm/amd64/Make.files @@ -0,0 +1,7 @@ +$(CUR_SRCS) = fenv.c e_remainder.S e_remainderf.S e_remainderl.S \ + e_sqrt.S e_sqrtf.S e_sqrtl.S \ + s_llrint.S s_llrintf.S s_llrintl.S \ + s_logbl.S s_lrint.S s_lrintf.S s_lrintl.S \ + s_remquo.S s_remquof.S s_remquol.S \ + s_rintl.S s_scalbn.S s_scalbnf.S s_scalbnl.S \ + e_fmod.S e_fmodf.S e_fmodl.S diff --git a/openlibm/amd64/bsd_asm.h b/openlibm/amd64/bsd_asm.h new file mode 100644 index 0000000000..b9c815b14f --- /dev/null +++ b/openlibm/amd64/bsd_asm.h @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)DEFS.h 5.1 (Berkeley) 4/23/90 + * $FreeBSD: src/sys/amd64/include/asm.h,v 1.18 2007/08/22 04:26:07 jkoshy Exp $ + */ + +#ifndef _BSD_ASM_H_ +#define _BSD_ASM_H_ + +#ifdef __APPLE__ +#include "../i387/osx_asm.h" +#define CNAME(x) EXT(x) +#else +#include "bsd_cdefs.h" + +#ifdef PIC +#define PIC_PLT(x) x@PLT +#define PIC_GOT(x) x@GOTPCREL(%rip) +#else +#define PIC_PLT(x) x +#define PIC_GOT(x) x +#endif + +/* + * CNAME and HIDENAME manage the relationship between symbol names in C + * and the equivalent assembly language names. CNAME is given a name as + * it would be used in a C program. It expands to the equivalent assembly + * language name. HIDENAME is given an assembly-language name, and expands + * to a possibly-modified form that will be invisible to C programs. + */ +#define CNAME(csym) csym +#define HIDENAME(asmsym) .asmsym + +#define _START_ENTRY .p2align 4,0x90 + +#if defined(__ELF__) +#define _ENTRY(x) .text; _START_ENTRY; \ + .globl CNAME(x); .type CNAME(x),@function; CNAME(x): +#define END(x) .size x, . - x + +#elif defined(_WIN32) +#ifndef _MSC_VER +#define END(x) .end +#define _START_ENTRY_WIN .text; _START_ENTRY +#else +#define END(x) end +#define _START_ENTRY_WIN .code; _START_ENTRY +#endif +#define _ENTRY(x) _START_ENTRY_WIN; \ + .globl CNAME(x); .section .drectve; .ascii " -export:", #x; \ + .section .text; .def CNAME(x); .scl 2; .type 32; .endef; CNAME(x): +#endif + +#ifdef PROF +#define ALTENTRY(x) _ENTRY(x); \ + pushq %rbp; movq %rsp,%rbp; \ + call PIC_PLT(HIDENAME(mcount)); \ + popq %rbp; \ + jmp 9f +#define ENTRY(x) _ENTRY(x); \ + pushq %rbp; movq %rsp,%rbp; \ + call PIC_PLT(HIDENAME(mcount)); \ + popq %rbp; \ + 9: +#else +#define ALTENTRY(x) _ENTRY(x) +#define ENTRY(x) _ENTRY(x) +#endif + + +#define RCSID(x) .text; .asciz x + +#undef __FBSDID +#if !defined(lint) && !defined(STRIP_FBSDID) +#define __FBSDID(s) .ident s +#else +#define __FBSDID(s) /* nothing */ +#endif /* not lint and not STRIP_FBSDID */ + +#endif +#endif /* !_BSD_ASM_H_ */ diff --git a/openlibm/amd64/bsd_fpu.h b/openlibm/amd64/bsd_fpu.h new file mode 100644 index 0000000000..513b3456f9 --- /dev/null +++ b/openlibm/amd64/bsd_fpu.h @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)npx.h 5.3 (Berkeley) 1/18/91 + * $FreeBSD: src/sys/x86/include/fpu.h,v 1.1 2012/03/16 20:24:30 tijl Exp $ + */ + +/* + * Floating Point Data Structures and Constants + * W. Jolitz 1/90 + */ + +#ifndef _BSD_FPU_H_ +#define _BSD_FPU_H_ + +#include "types-compat.h" + +/* Environment information of floating point unit. */ +struct env87 { + int32_t en_cw; /* control word (16bits) */ + int32_t en_sw; /* status word (16bits) */ + int32_t en_tw; /* tag word (16bits) */ + int32_t en_fip; /* fp instruction pointer */ + uint16_t en_fcs; /* fp code segment selector */ + uint16_t en_opcode; /* opcode last executed (11 bits) */ + int32_t en_foo; /* fp operand offset */ + int32_t en_fos; /* fp operand segment selector */ +}; + +/* Contents of each x87 floating point accumulator. */ +struct fpacc87 { + uint8_t fp_bytes[10]; +}; + +/* Floating point context. (i386 fnsave/frstor) */ +struct save87 { + struct env87 sv_env; /* floating point control/status */ + struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */ + uint8_t sv_pad0[4]; /* saved status word (now unused) */ + /* + * Bogus padding for emulators. Emulators should use their own + * struct and arrange to store into this struct (ending here) + * before it is inspected for ptracing or for core dumps. Some + * emulators overwrite the whole struct. We have no good way of + * knowing how much padding to leave. Leave just enough for the + * GPL emulator's i387_union (176 bytes total). + */ + uint8_t sv_pad[64]; /* padding; used by emulators */ +}; + +/* Contents of each SSE extended accumulator. */ +struct xmmacc { + uint8_t xmm_bytes[16]; +}; + +/* Contents of the upper 16 bytes of each AVX extended accumulator. */ +struct ymmacc { + uint8_t ymm_bytes[16]; +}; + +/* Rename structs below depending on machine architecture. */ +#ifdef __i386__ +#define __envxmm32 envxmm +#else +#define __envxmm32 envxmm32 +#define __envxmm64 envxmm +#endif + +struct __envxmm32 { + uint16_t en_cw; /* control word (16bits) */ + uint16_t en_sw; /* status word (16bits) */ + uint16_t en_tw; /* tag word (16bits) */ + uint16_t en_opcode; /* opcode last executed (11 bits) */ + uint32_t en_fip; /* fp instruction pointer */ + uint16_t en_fcs; /* fp code segment selector */ + uint16_t en_pad0; /* padding */ + uint32_t en_foo; /* fp operand offset */ + uint16_t en_fos; /* fp operand segment selector */ + uint16_t en_pad1; /* padding */ + uint32_t en_mxcsr; /* SSE control/status register */ + uint32_t en_mxcsr_mask; /* valid bits in mxcsr */ +}; + +struct __envxmm64 { + uint16_t en_cw; /* control word (16bits) */ + uint16_t en_sw; /* status word (16bits) */ + uint8_t en_tw; /* tag word (8bits) */ + uint8_t en_zero; + uint16_t en_opcode; /* opcode last executed (11 bits ) */ + uint64_t en_rip; /* fp instruction pointer */ + uint64_t en_rdp; /* fp operand pointer */ + uint32_t en_mxcsr; /* SSE control/status register */ + uint32_t en_mxcsr_mask; /* valid bits in mxcsr */ +}; + +/* Floating point context. (i386 fxsave/fxrstor) */ +struct savexmm { + struct __envxmm32 sv_env; + struct { + struct fpacc87 fp_acc; + uint8_t fp_pad[6]; /* padding */ + } sv_fp[8]; + struct xmmacc sv_xmm[8]; + uint8_t sv_pad[224]; +} __attribute__ ((aligned(16))); + +#ifdef __i386__ +union savefpu { + struct save87 sv_87; + struct savexmm sv_xmm; +}; +#else +/* Floating point context. (amd64 fxsave/fxrstor) */ +struct savefpu { + struct __envxmm64 sv_env; + struct { + struct fpacc87 fp_acc; + uint8_t fp_pad[6]; /* padding */ + } sv_fp[8]; + struct xmmacc sv_xmm[16]; + uint8_t sv_pad[96]; +} __attribute__ ((aligned(16))); +#endif + +struct xstate_hdr { + uint64_t xstate_bv; + uint8_t xstate_rsrv0[16]; + uint8_t xstate_rsrv[40]; +}; + +struct savexmm_xstate { + struct xstate_hdr sx_hd; + struct ymmacc sx_ymm[16]; +}; + +struct savexmm_ymm { + struct __envxmm32 sv_env; + struct { + struct fpacc87 fp_acc; + int8_t fp_pad[6]; /* padding */ + } sv_fp[8]; + struct xmmacc sv_xmm[16]; + uint8_t sv_pad[96]; + struct savexmm_xstate sv_xstate; +} __attribute__ ((aligned(16))); + +struct savefpu_xstate { + struct xstate_hdr sx_hd; + struct ymmacc sx_ymm[16]; +}; + +struct savefpu_ymm { + struct __envxmm64 sv_env; + struct { + struct fpacc87 fp_acc; + int8_t fp_pad[6]; /* padding */ + } sv_fp[8]; + struct xmmacc sv_xmm[16]; + uint8_t sv_pad[96]; + struct savefpu_xstate sv_xstate; +} __attribute__ ((aligned(64))); + +#undef __envxmm32 +#undef __envxmm64 + +/* + * The hardware default control word for i387's and later coprocessors is + * 0x37F, giving: + * + * round to nearest + * 64-bit precision + * all exceptions masked. + * + * FreeBSD/i386 uses 53 bit precision for things like fadd/fsub/fsqrt etc + * because of the difference between memory and fpu register stack arguments. + * If its using an intermediate fpu register, it has 80/64 bits to work + * with. If it uses memory, it has 64/53 bits to work with. However, + * gcc is aware of this and goes to a fair bit of trouble to make the + * best use of it. + * + * This is mostly academic for AMD64, because the ABI prefers the use + * SSE2 based math. For FreeBSD/amd64, we go with the default settings. + */ +#define __INITIAL_FPUCW__ 0x037F +#define __INITIAL_FPUCW_I386__ 0x127F +#define __INITIAL_NPXCW__ __INITIAL_FPUCW_I386__ +#define __INITIAL_MXCSR__ 0x1F80 +#define __INITIAL_MXCSR_MASK__ 0xFFBF + +#endif /* !_BSD_FPU_H_ */ diff --git a/openlibm/amd64/bsd_ieeefp.h b/openlibm/amd64/bsd_ieeefp.h new file mode 100644 index 0000000000..6103d50645 --- /dev/null +++ b/openlibm/amd64/bsd_ieeefp.h @@ -0,0 +1,272 @@ +/*- + * Copyright (c) 2003 Peter Wemm. + * Copyright (c) 1990 Andrew Moore, Talke Studio + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#) ieeefp.h 1.0 (Berkeley) 9/23/93 + * $FreeBSD: src/sys/amd64/include/ieeefp.h,v 1.14 2005/04/12 23:12:00 jhb Exp $ + */ + +/* + * IEEE floating point type and constant definitions. + */ + +#ifndef _BSD_IEEEFP_H_ +#define _BSD_IEEEFP_H_ + +/* + * FP rounding modes + */ +typedef enum { + FP_RN=0, /* round to nearest */ + FP_RM, /* round down to minus infinity */ + FP_RP, /* round up to plus infinity */ + FP_RZ /* truncate */ +} fp_rnd_t; + +/* + * FP precision modes + */ +typedef enum { + FP_PS=0, /* 24 bit (single-precision) */ + FP_PRS, /* reserved */ + FP_PD, /* 53 bit (double-precision) */ + FP_PE /* 64 bit (extended-precision) */ +} fp_prec_t; + +#define fp_except_t int + +/* + * FP exception masks + */ +#define FP_X_INV 0x01 /* invalid operation */ +#define FP_X_DNML 0x02 /* denormal */ +#define FP_X_DZ 0x04 /* zero divide */ +#define FP_X_OFL 0x08 /* overflow */ +#define FP_X_UFL 0x10 /* underflow */ +#define FP_X_IMP 0x20 /* (im)precision */ +#define FP_X_STK 0x40 /* stack fault */ + +/* + * FP registers + */ +#define FP_MSKS_REG 0 /* exception masks */ +#define FP_PRC_REG 0 /* precision */ +#define FP_RND_REG 0 /* direction */ +#define FP_STKY_REG 1 /* sticky flags */ + +/* + * FP register bit field masks + */ +#define FP_MSKS_FLD 0x3f /* exception masks field */ +#define FP_PRC_FLD 0x300 /* precision control field */ +#define FP_RND_FLD 0xc00 /* round control field */ +#define FP_STKY_FLD 0x3f /* sticky flags field */ + +/* + * SSE mxcsr register bit field masks + */ +#define SSE_STKY_FLD 0x3f /* exception flags */ +#define SSE_DAZ_FLD 0x40 /* Denormals are zero */ +#define SSE_MSKS_FLD 0x1f80 /* exception masks field */ +#define SSE_RND_FLD 0x6000 /* rounding control */ +#define SSE_FZ_FLD 0x8000 /* flush to zero on underflow */ + +/* + * FP register bit field offsets + */ +#define FP_MSKS_OFF 0 /* exception masks offset */ +#define FP_PRC_OFF 8 /* precision control offset */ +#define FP_RND_OFF 10 /* round control offset */ +#define FP_STKY_OFF 0 /* sticky flags offset */ + +/* + * SSE mxcsr register bit field offsets + */ +#define SSE_STKY_OFF 0 /* exception flags offset */ +#define SSE_DAZ_OFF 6 /* DAZ exception mask offset */ +#define SSE_MSKS_OFF 7 /* other exception masks offset */ +#define SSE_RND_OFF 13 /* rounding control offset */ +#define SSE_FZ_OFF 15 /* flush to zero offset */ + +#if (defined(__GNUCLIKE_ASM) && defined(__CC_SUPPORTS___INLINE__)) || defined(_WIN32) \ + && !defined(__cplusplus) + +#define __fldenv(addr) __asm __volatile("fldenv %0" : : "m" (*(addr))) +#define __fnstenv(addr) __asm __volatile("fnstenv %0" : "=m" (*(addr))) +#define __fldcw(addr) __asm __volatile("fldcw %0" : : "m" (*(addr))) +#define __fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) +#define __fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) +#define __ldmxcsr(addr) __asm __volatile("ldmxcsr %0" : : "m" (*(addr))) +#define __stmxcsr(addr) __asm __volatile("stmxcsr %0" : "=m" (*(addr))) + +/* + * General notes about conflicting SSE vs FP status bits. + * This code assumes that software will not fiddle with the control + * bits of the SSE and x87 in such a way to get them out of sync and + * still expect this to work. Break this at your peril. + * Because I based this on the i386 port, the x87 state is used for + * the fpget*() functions, and is shadowed into the SSE state for + * the fpset*() functions. For dual source fpget*() functions, I + * merge the two together. I think. + */ + +/* Set rounding control */ +static __inline__ fp_rnd_t +__fpgetround(void) +{ + unsigned short _cw; + + __fnstcw(&_cw); + return ((_cw & FP_RND_FLD) >> FP_RND_OFF); +} + +static __inline__ fp_rnd_t +__fpsetround(fp_rnd_t _m) +{ + unsigned short _cw; + unsigned int _mxcsr; + fp_rnd_t _p; + + __fnstcw(&_cw); + _p = (_cw & FP_RND_FLD) >> FP_RND_OFF; + _cw &= ~FP_RND_FLD; + _cw |= (_m << FP_RND_OFF) & FP_RND_FLD; + __fldcw(&_cw); + __stmxcsr(&_mxcsr); + _mxcsr &= ~SSE_RND_FLD; + _mxcsr |= (_m << SSE_RND_OFF) & SSE_RND_FLD; + __ldmxcsr(&_mxcsr); + return (_p); +} + +/* + * Set precision for fadd/fsub/fsqrt etc x87 instructions + * There is no equivalent SSE mode or control. + */ +static __inline__ fp_prec_t +__fpgetprec(void) +{ + unsigned short _cw; + + __fnstcw(&_cw); + return ((_cw & FP_PRC_FLD) >> FP_PRC_OFF); +} + +static __inline__ fp_prec_t +__fpsetprec(fp_rnd_t _m) +{ + unsigned short _cw; + fp_prec_t _p; + + __fnstcw(&_cw); + _p = (_cw & FP_PRC_FLD) >> FP_PRC_OFF; + _cw &= ~FP_PRC_FLD; + _cw |= (_m << FP_PRC_OFF) & FP_PRC_FLD; + __fldcw(&_cw); + return (_p); +} + +/* + * Look at the exception masks + * Note that x87 masks are inverse of the fp*() functions + * API. ie: mask = 1 means disable for x87 and SSE, but + * for the fp*() api, mask = 1 means enabled. + */ +static __inline__ fp_except_t +__fpgetmask(void) +{ + unsigned short _cw; + + __fnstcw(&_cw); + return ((~_cw) & FP_MSKS_FLD); +} + +static __inline__ fp_except_t +__fpsetmask(fp_except_t _m) +{ + unsigned short _cw; + unsigned int _mxcsr; + fp_except_t _p; + + __fnstcw(&_cw); + _p = (~_cw) & FP_MSKS_FLD; + _cw &= ~FP_MSKS_FLD; + _cw |= (~_m) & FP_MSKS_FLD; + __fldcw(&_cw); + __stmxcsr(&_mxcsr); + /* XXX should we clear non-ieee SSE_DAZ_FLD and SSE_FZ_FLD ? */ + _mxcsr &= ~SSE_MSKS_FLD; + _mxcsr |= ((~_m) << SSE_MSKS_OFF) & SSE_MSKS_FLD; + __ldmxcsr(&_mxcsr); + return (_p); +} + +/* See which sticky exceptions are pending, and reset them */ +static __inline__ fp_except_t +__fpgetsticky(void) +{ + unsigned short _sw; + unsigned int _mxcsr; + fp_except_t _ex; + + __fnstsw(&_sw); + _ex = _sw & FP_STKY_FLD; + __stmxcsr(&_mxcsr); + _ex |= _mxcsr & SSE_STKY_FLD; + return (_ex); +} + +#endif /* __GNUCLIKE_ASM && __CC_SUPPORTS___INLINE__ && !__cplusplus */ + +#if !defined(__IEEEFP_NOINLINES__) && !defined(__cplusplus) \ + && defined(__GNUCLIKE_ASM) && defined(__CC_SUPPORTS___INLINE__) + +#define fpgetround() __fpgetround() +#define fpsetround(_m) __fpsetround(_m) +#define fpgetprec() __fpgetprec() +#define fpsetprec(_m) __fpsetprec(_m) +#define fpgetmask() __fpgetmask() +#define fpsetmask(_m) __fpsetmask(_m) +#define fpgetsticky() __fpgetsticky() + +/* Suppress prototypes in the MI header. */ +#define _IEEEFP_INLINED_ 1 + +#else /* !__IEEEFP_NOINLINES__ && !__cplusplus && __GNUCLIKE_ASM + && __CC_SUPPORTS___INLINE__ */ + +/* Augment the userland declarations */ +__BEGIN_DECLS +extern fp_prec_t fpgetprec(void); +extern fp_prec_t fpsetprec(fp_prec_t); +__END_DECLS + +#endif /* !__IEEEFP_NOINLINES__ && !__cplusplus && __GNUCLIKE_ASM + && __CC_SUPPORTS___INLINE__ */ + +#endif /* !_BSD_IEEEFP_H_ */ diff --git a/openlibm/amd64/e_fmod.S b/openlibm/amd64/e_fmod.S new file mode 100644 index 0000000000..d2c8ecd910 --- /dev/null +++ b/openlibm/amd64/e_fmod.S @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1993,94 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Based on the i387 version written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +#include + +ENTRY(fmod) + movsd %xmm0,-8(%rsp) + movsd %xmm1,-16(%rsp) + fldl -16(%rsp) + fldl -8(%rsp) +1: fprem + fstsw %ax + testw $0x400,%ax + jne 1b + fstpl -8(%rsp) + movsd -8(%rsp),%xmm0 + fstp %st + ret +END(fmod) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_fmodf.S b/openlibm/amd64/e_fmodf.S new file mode 100644 index 0000000000..b045e73554 --- /dev/null +++ b/openlibm/amd64/e_fmodf.S @@ -0,0 +1,26 @@ +/* + * Based on the i387 version written by J.T. Conklin . + * Public domain. + */ + +#include + +ENTRY(fmodf) + movss %xmm0,-4(%rsp) + movss %xmm1,-8(%rsp) + flds -8(%rsp) + flds -4(%rsp) +1: fprem + fstsw %ax + testw $0x400,%ax + jne 1b + fstps -4(%rsp) + movss -4(%rsp),%xmm0 + fstp %st + ret +END(fmodf) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_fmodl.S b/openlibm/amd64/e_fmodl.S new file mode 100644 index 0000000000..cab539d5ea --- /dev/null +++ b/openlibm/amd64/e_fmodl.S @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1993,94 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Based on the i387 version written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + */ + +#include + +ENTRY(fmodl) + fldt 24(%rsp) + fldt 8(%rsp) +1: fprem + fstsw %ax + testw $0x400,%ax + jne 1b + fstp %st(1) + ret +END(fmodl) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_remainder.S b/openlibm/amd64/e_remainder.S new file mode 100644 index 0000000000..a0b4900fb5 --- /dev/null +++ b/openlibm/amd64/e_remainder.S @@ -0,0 +1,30 @@ +/* + * Based on the i387 version written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include + +//RCSID("from: FreeBSD: src/lib/msun/i387/e_remainder.S,v 1.8 2005/02/04 14:08:32 das Exp") +//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_remainder.S,v 1.2 2011/01/07 16:13:12 kib Exp $") + +ENTRY(remainder) + movsd %xmm0,-8(%rsp) + movsd %xmm1,-16(%rsp) + fldl -16(%rsp) + fldl -8(%rsp) +1: fprem1 + fstsw %ax + testw $0x400,%ax + jne 1b + fstpl -8(%rsp) + movsd -8(%rsp),%xmm0 + fstp %st + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_remainderf.S b/openlibm/amd64/e_remainderf.S new file mode 100644 index 0000000000..c63e97ea51 --- /dev/null +++ b/openlibm/amd64/e_remainderf.S @@ -0,0 +1,29 @@ +/* + * Based on the i387 version written by J.T. Conklin . + * Public domain. + */ + +#include + +//RCSID("from: $NetBSD: e_remainderf.S,v 1.2 1995/05/08 23:49:47 jtc Exp $") +//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_remainderf.S,v 1.2 2011/01/07 16:13:12 kib Exp $") + +ENTRY(remainderf) + movss %xmm0,-4(%rsp) + movss %xmm1,-8(%rsp) + flds -8(%rsp) + flds -4(%rsp) +1: fprem1 + fstsw %ax + testw $0x400,%ax + jne 1b + fstps -4(%rsp) + movss -4(%rsp),%xmm0 + fstp %st + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_remainderl.S b/openlibm/amd64/e_remainderl.S new file mode 100644 index 0000000000..5f21b933fb --- /dev/null +++ b/openlibm/amd64/e_remainderl.S @@ -0,0 +1,35 @@ +/* + * Based on the i387 version written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_remainderl.S,v 1.2 2011/01/07 16:13:12 kib Exp $") + +ENTRY(remainderl) +#ifndef _WIN64 + fldt 24(%rsp) + fldt 8(%rsp) +#else + fldt (%r8) + fldt (%rdx) +#endif +1: fprem1 + fstsw %ax + testw $0x400,%ax + jne 1b + fstp %st(1) +#ifdef _WIN64 + mov %rcx,%rax + movq $0x0,0x8(%rcx) + fstpt (%rcx) +#endif + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_sqrt.S b/openlibm/amd64/e_sqrt.S new file mode 100644 index 0000000000..a44e41f7a0 --- /dev/null +++ b/openlibm/amd64/e_sqrt.S @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_sqrt.S,v 1.4 2011/01/07 16:13:12 kib Exp $") + +ENTRY(sqrt) + sqrtsd %xmm0, %xmm0 + ret +END(sqrt) + + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_sqrtf.S b/openlibm/amd64/e_sqrtf.S new file mode 100644 index 0000000000..f74175b72b --- /dev/null +++ b/openlibm/amd64/e_sqrtf.S @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_sqrtf.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(sqrtf) + sqrtss %xmm0, %xmm0 + ret +END(sqrtf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/e_sqrtl.S b/openlibm/amd64/e_sqrtl.S new file mode 100644 index 0000000000..6e7eac054b --- /dev/null +++ b/openlibm/amd64/e_sqrtl.S @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/e_sqrtl.S,v 1.2 2011/01/07 16:13:12 kib Exp $") + +ENTRY(sqrtl) +#ifndef _WIN64 + fldt 8(%rsp) + fsqrt +#else + fldt (%rdx) + fsqrt + mov %rcx,%rax + movq $0x0,0x8(%rcx) + fstpt (%rcx) +#endif + ret + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/fenv.c b/openlibm/amd64/fenv.c new file mode 100644 index 0000000000..ddf7dba6d0 --- /dev/null +++ b/openlibm/amd64/fenv.c @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/amd64/fenv.c,v 1.8 2011/10/21 06:25:31 das Exp $ + */ + +#include "bsd_fpu.h" +#include "math_private.h" + +#ifdef _WIN32 +#define __fenv_static OLM_DLLEXPORT +#endif +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +const fenv_t __fe_dfl_env = { + { 0xffff0000 | __INITIAL_FPUCW__, + 0xffff0000, + 0xffffffff, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff } + }, + __INITIAL_MXCSR__ +}; + +extern inline OLM_DLLEXPORT int feclearexcept(int __excepts); +extern inline OLM_DLLEXPORT int fegetexceptflag(fexcept_t *__flagp, int __excepts); + +OLM_DLLEXPORT int +fesetexceptflag(const fexcept_t *flagp, int excepts) +{ + fenv_t env; + + __fnstenv(&env.__x87); + env.__x87.__status &= ~excepts; + env.__x87.__status |= *flagp & excepts; + __fldenv(env.__x87); + + __stmxcsr(&env.__mxcsr); + env.__mxcsr &= ~excepts; + env.__mxcsr |= *flagp & excepts; + __ldmxcsr(env.__mxcsr); + + return (0); +} + +OLM_DLLEXPORT int +feraiseexcept(int excepts) +{ + fexcept_t ex = excepts; + + fesetexceptflag(&ex, excepts); + __fwait(); + return (0); +} + +extern inline OLM_DLLEXPORT int fetestexcept(int __excepts); +extern inline OLM_DLLEXPORT int fegetround(void); +extern inline OLM_DLLEXPORT int fesetround(int __round); + +OLM_DLLEXPORT int +fegetenv(fenv_t *envp) +{ + + __fnstenv(&envp->__x87); + __stmxcsr(&envp->__mxcsr); + /* + * fnstenv masks all exceptions, so we need to restore the + * control word to avoid this side effect. + */ + __fldcw(envp->__x87.__control); + return (0); +} + +OLM_DLLEXPORT int +feholdexcept(fenv_t *envp) +{ + uint32_t mxcsr; + + __stmxcsr(&mxcsr); + __fnstenv(&envp->__x87); + __fnclex(); + envp->__mxcsr = mxcsr; + mxcsr &= ~FE_ALL_EXCEPT; + mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT; + __ldmxcsr(mxcsr); + return (0); +} + +extern inline OLM_DLLEXPORT int fesetenv(const fenv_t *__envp); + +OLM_DLLEXPORT int +feupdateenv(const fenv_t *envp) +{ + uint32_t mxcsr; + uint16_t status; + + __fnstsw(&status); + __stmxcsr(&mxcsr); + fesetenv(envp); + feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT); + return (0); +} + +int +feenableexcept(int mask) +{ + uint32_t mxcsr, omask; + uint16_t control; + + mask &= FE_ALL_EXCEPT; + __fnstcw(&control); + __stmxcsr(&mxcsr); + omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; + control &= ~mask; + __fldcw(control); + mxcsr &= ~(mask << _SSE_EMASK_SHIFT); + __ldmxcsr(mxcsr); + return (omask); +} + +int +fedisableexcept(int mask) +{ + uint32_t mxcsr, omask; + uint16_t control; + + mask &= FE_ALL_EXCEPT; + __fnstcw(&control); + __stmxcsr(&mxcsr); + omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; + control |= mask; + __fldcw(control); + mxcsr |= mask << _SSE_EMASK_SHIFT; + __ldmxcsr(mxcsr); + return (omask); +} diff --git a/openlibm/amd64/s_llrint.S b/openlibm/amd64/s_llrint.S new file mode 100644 index 0000000000..d7a0f74fdc --- /dev/null +++ b/openlibm/amd64/s_llrint.S @@ -0,0 +1,12 @@ +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_llrint.S,v 1.3 2011/02/04 21:54:06 kib Exp $") + +ENTRY(llrint) + cvtsd2si %xmm0, %rax + ret +END(llrint) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_llrintf.S b/openlibm/amd64/s_llrintf.S new file mode 100644 index 0000000000..f5d39b35d0 --- /dev/null +++ b/openlibm/amd64/s_llrintf.S @@ -0,0 +1,12 @@ +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_llrintf.S,v 1.3 2011/02/04 21:54:06 kib Exp $") + +ENTRY(llrintf) + cvtss2si %xmm0, %rax + ret +END(llrintf) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_llrintl.S b/openlibm/amd64/s_llrintl.S new file mode 100644 index 0000000000..12f2d33e3d --- /dev/null +++ b/openlibm/amd64/s_llrintl.S @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_llrintl.S,v 1.2 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(llrintl) +#ifndef _WIN64 + fldt 8(%rsp) +#else + fldt (%rcx) +#endif + subq $8,%rsp + fistpll (%rsp) + popq %rax + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_logbl.S b/openlibm/amd64/s_logbl.S new file mode 100644 index 0000000000..d22afeb1c5 --- /dev/null +++ b/openlibm/amd64/s_logbl.S @@ -0,0 +1,29 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_logbl.S,v 1.4 2011/01/07 16:13:12 kib Exp $") + +ENTRY(logbl) +#ifndef _WIN64 + fldt 8(%rsp) +#else + fldt (%rdx) +#endif + fxtract + fstp %st +#ifdef _WIN64 + mov %rcx,%rax + movq $0x0,0x8(%rcx) + fstpt (%rcx) +#endif + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_lrint.S b/openlibm/amd64/s_lrint.S new file mode 100644 index 0000000000..9e1b917e9f --- /dev/null +++ b/openlibm/amd64/s_lrint.S @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_lrint.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(lrint) +#ifndef _WIN64 + cvtsd2si %xmm0, %rax +#else + cvtsd2si %xmm0, %eax +#endif + ret +END(lrint) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_lrintf.S b/openlibm/amd64/s_lrintf.S new file mode 100644 index 0000000000..8e0b0c2ecc --- /dev/null +++ b/openlibm/amd64/s_lrintf.S @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_lrintf.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(lrintf) +#ifndef _WIN64 + cvtss2si %xmm0, %rax +#else + cvtss2si %xmm0, %eax +#endif + ret +END(lrintf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_lrintl.S b/openlibm/amd64/s_lrintl.S new file mode 100644 index 0000000000..288c0bbee2 --- /dev/null +++ b/openlibm/amd64/s_lrintl.S @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_lrintl.S,v 1.2 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(lrintl) +#ifndef _WIN64 + fldt 8(%rsp) +#else + fldt (%rcx) +#endif + subq $8,%rsp + fistpll (%rsp) + popq %rax + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_remquo.S b/openlibm/amd64/s_remquo.S new file mode 100644 index 0000000000..8ef8425c6b --- /dev/null +++ b/openlibm/amd64/s_remquo.S @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on public-domain remainder routine by J.T. Conklin . + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_remquo.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(remquo) + movsd %xmm0,-8(%rsp) + movsd %xmm1,-16(%rsp) + fldl -16(%rsp) + fldl -8(%rsp) +1: fprem1 + fstsw %ax + btw $10,%ax + jc 1b + fstp %st(1) +/* Extract the three low-order bits of the quotient from C0,C3,C1. */ + shrl $6,%eax + movl %eax,%ecx + andl $0x108,%eax + rorl $7,%eax + orl %eax,%ecx + roll $4,%eax + orl %ecx,%eax + andl $7,%eax +/* Negate the quotient bits if x*y<0. Avoid using an unpredictable branch. */ + movl -12(%rsp),%ecx + xorl -4(%rsp),%ecx + sarl $16,%ecx + sarl $16,%ecx + xorl %ecx,%eax + andl $1,%ecx + addl %ecx,%eax +/* Store the quotient and return. */ +#ifndef _WIN64 + movl %eax,(%rdi) +#else + movl %eax,(%r8) +#endif + fstpl -8(%rsp) + movsd -8(%rsp),%xmm0 + ret +END(remquo) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_remquof.S b/openlibm/amd64/s_remquof.S new file mode 100644 index 0000000000..129a807d53 --- /dev/null +++ b/openlibm/amd64/s_remquof.S @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on public-domain remainder routine by J.T. Conklin . + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_remquof.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(remquof) + movss %xmm0,-4(%rsp) + movss %xmm1,-8(%rsp) + flds -8(%rsp) + flds -4(%rsp) +1: fprem1 + fstsw %ax + btw $10,%ax + jc 1b + fstp %st(1) +/* Extract the three low-order bits of the quotient from C0,C3,C1. */ + shrl $6,%eax + movl %eax,%ecx + andl $0x108,%eax + rorl $7,%eax + orl %eax,%ecx + roll $4,%eax + orl %ecx,%eax + andl $7,%eax +/* Negate the quotient bits if x*y<0. Avoid using an unpredictable branch. */ + movl -8(%rsp),%ecx + xorl -4(%rsp),%ecx + sarl $16,%ecx + sarl $16,%ecx + xorl %ecx,%eax + andl $1,%ecx + addl %ecx,%eax +/* Store the quotient and return. */ +#ifndef _WIN64 + movl %eax,(%rdi) +#else + movl %eax,(%r8) +#endif + fstps -4(%rsp) + movss -4(%rsp),%xmm0 + ret +END(remquof) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_remquol.S b/openlibm/amd64/s_remquol.S new file mode 100644 index 0000000000..344e1fb930 --- /dev/null +++ b/openlibm/amd64/s_remquol.S @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2005-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on public-domain remainder routine by J.T. Conklin . + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_remquol.S,v 1.2 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(remquol) +#ifndef _WIN64 + fldt 24(%rsp) + fldt 8(%rsp) +#else + fldt (%r8) + fldt (%rdx) + mov %rcx,%r8 +#endif +1: fprem1 + fstsw %ax + btw $10,%ax + jc 1b + fstp %st(1) +/* Extract the three low-order bits of the quotient from C0,C3,C1. */ + shrl $6,%eax + movl %eax,%ecx + andl $0x108,%eax + rorl $7,%eax + orl %eax,%ecx + roll $4,%eax + orl %ecx,%eax + andl $7,%eax +/* Negate the quotient bits if x*y<0. Avoid using an unpredictable branch. */ + movl 32(%rsp),%ecx + xorl 16(%rsp),%ecx + movsx %cx,%ecx + sarl $16,%ecx + sarl $16,%ecx + xorl %ecx,%eax + andl $1,%ecx + addl %ecx,%eax +/* Store the quotient and return. */ +#ifndef _WIN64 + movl %eax,(%rdi) +#else + movl %eax,(%r9) + mov %r8,%rax + movq $0x0,0x8(%r8) + fstpt (%r8) +#endif + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_rintl.S b/openlibm/amd64/s_rintl.S new file mode 100644 index 0000000000..479e836501 --- /dev/null +++ b/openlibm/amd64/s_rintl.S @@ -0,0 +1,26 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include + +ENTRY(rintl) +#ifndef _WIN64 + fldt 8(%rsp) + frndint +#else + fldt (%rdx) + frndint + mov %rcx,%rax + movq $0x0,0x8(%rcx) + fstpt (%rcx) +#endif + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_scalbn.S b/openlibm/amd64/s_scalbn.S new file mode 100644 index 0000000000..9d4152be84 --- /dev/null +++ b/openlibm/amd64/s_scalbn.S @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_scalbn.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(scalbn) + movsd %xmm0,-8(%rsp) +#ifndef _WIN64 + movl %edi,-12(%rsp) +#else + movl %edx,-12(%rsp) +#endif + fildl -12(%rsp) + fldl -8(%rsp) + fscale + fstp %st(1) + fstpl -8(%rsp) + movsd -8(%rsp),%xmm0 + ret +#ifndef _WIN64 +END(scalbn) +.globl CNAME(ldexp) +#else +.globl CNAME(ldexp); .section .drectve; .ascii " -export:ldexp" +#endif +.set CNAME(ldexp),CNAME(scalbn) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_scalbnf.S b/openlibm/amd64/s_scalbnf.S new file mode 100644 index 0000000000..75b4bb2042 --- /dev/null +++ b/openlibm/amd64/s_scalbnf.S @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_scalbnf.S,v 1.4 2011/01/07 16:13:12 kib Exp $") + +ENTRY(scalbnf) + movss %xmm0,-8(%rsp) +#ifndef _WIN64 + movl %edi,-4(%rsp) +#else + movl %edx,-4(%rsp) +#endif + fildl -4(%rsp) + flds -8(%rsp) + fscale + fstp %st(1) + fstps -8(%rsp) + movss -8(%rsp),%xmm0 + ret +#ifndef _WIN64 +END(scalbnf) +.globl CNAME(ldexpf) +#else +.globl CNAME(ldexpf); .section .drectve; .ascii " -export:ldexpf" +#endif +.set CNAME(ldexpf),CNAME(scalbnf) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/amd64/s_scalbnl.S b/openlibm/amd64/s_scalbnl.S new file mode 100644 index 0000000000..fa0d2bff3d --- /dev/null +++ b/openlibm/amd64/s_scalbnl.S @@ -0,0 +1,40 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/amd64/s_scalbnl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") +/* //RCSID("$NetBSD: s_scalbnf.S,v 1.4 1999/01/02 05:15:40 kristerw Exp $") */ + +ENTRY(scalbnl) +#ifndef _WIN64 + movl %edi,-4(%rsp) + fildl -4(%rsp) + fldt 8(%rsp) +#else + mov %r8,%rax + movl %eax,-4(%rsp) + fildl -4(%rsp) + fldt (%rdx) +#endif + fscale + fstp %st(1) +#ifdef _WIN64 + mov %rcx,%rax + movq $0x0,0x8(%rcx) + fstpt (%rcx) +#endif + ret +#ifndef _WIN64 +END(scalbnl) +.globl CNAME(ldexpl) +#else +.globl CNAME(ldexpl); .section .drectve; .ascii " -export:ldexpl" +#endif +.set CNAME(ldexpl),CNAME(scalbnl) + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/arm/Make.files b/openlibm/arm/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/arm/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/arm/fenv.c b/openlibm/arm/fenv.c new file mode 100644 index 0000000000..c9abf145b4 --- /dev/null +++ b/openlibm/arm/fenv.c @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/arm/fenv.c,v 1.3 2011/10/16 05:37:56 das Exp $ + */ + +#define __fenv_static +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +/* + * Hopefully the system ID byte is immutable, so it's valid to use + * this as a default environment. + */ +const fenv_t __fe_dfl_env = 0; + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); diff --git a/openlibm/bsdsrc/Make.files b/openlibm/bsdsrc/Make.files new file mode 100644 index 0000000000..6777fe2377 --- /dev/null +++ b/openlibm/bsdsrc/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) += b_exp.c b_log.c b_tgamma.c diff --git a/openlibm/bsdsrc/b_exp.c b/openlibm/bsdsrc/b_exp.c new file mode 100644 index 0000000000..6af8dd7108 --- /dev/null +++ b/openlibm/bsdsrc/b_exp.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* @(#)exp.c 8.1 (Berkeley) 6/4/93 */ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/bsdsrc/b_exp.c,v 1.9 2011/10/16 05:37:20 das Exp $"); + +#include + +/* EXP(X) + * RETURN THE EXPONENTIAL OF X + * DOUBLE PRECISION (IEEE 53 bits, VAX D FORMAT 56 BITS) + * CODED IN C BY K.C. NG, 1/19/85; + * REVISED BY K.C. NG on 2/6/85, 2/15/85, 3/7/85, 3/24/85, 4/16/85, 6/14/86. + * + * Required system supported functions: + * scalb(x,n) + * copysign(x,y) + * finite(x) + * + * Method: + * 1. Argument Reduction: given the input x, find r and integer k such + * that + * x = k*ln2 + r, |r| <= 0.5*ln2 . + * r will be represented as r := z+c for better accuracy. + * + * 2. Compute exp(r) by + * + * exp(r) = 1 + r + r*R1/(2-R1), + * where + * R1 = x - x^2*(p1+x^2*(p2+x^2*(p3+x^2*(p4+p5*x^2)))). + * + * 3. exp(x) = 2^k * exp(r) . + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF)= 0; + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * exp(x) returns the exponential of x nearly rounded. In a test run + * with 1,156,000 random arguments on a VAX, the maximum observed + * error was 0.869 ulps (units in the last place). + */ + +#include "mathimpl.h" + +static const double p1 = 0x1.555555555553ep-3; +static const double p2 = -0x1.6c16c16bebd93p-9; +static const double p3 = 0x1.1566aaf25de2cp-14; +static const double p4 = -0x1.bbd41c5d26bf1p-20; +static const double p5 = 0x1.6376972bea4d0p-25; +static const double ln2hi = 0x1.62e42fee00000p-1; +static const double ln2lo = 0x1.a39ef35793c76p-33; +static const double lnhuge = 0x1.6602b15b7ecf2p9; +static const double lntiny = -0x1.77af8ebeae354p9; +static const double invln2 = 0x1.71547652b82fep0; + +#if 0 +OLM_DLLEXPORT double exp(x) +double x; +{ + double z,hi,lo,c; + int k; + +#if !defined(vax)&&!defined(tahoe) + if(x!=x) return(x); /* x is NaN */ +#endif /* !defined(vax)&&!defined(tahoe) */ + if( x <= lnhuge ) { + if( x >= lntiny ) { + + /* argument reduction : x --> x - k*ln2 */ + + k=invln2*x+copysign(0.5,x); /* k=NINT(x/ln2) */ + + /* express x-k*ln2 as hi-lo and let x=hi-lo rounded */ + + hi=x-k*ln2hi; + x=hi-(lo=k*ln2lo); + + /* return 2^k*[1+x+x*c/(2+c)] */ + z=x*x; + c= x - z*(p1+z*(p2+z*(p3+z*(p4+z*p5)))); + return scalb(1.0+(hi-(lo-(x*c)/(2.0-c))),k); + + } + /* end of x > lntiny */ + + else + /* exp(-big#) underflows to zero */ + if(finite(x)) return(scalb(1.0,-5000)); + + /* exp(-INF) is zero */ + else return(0.0); + } + /* end of x < lnhuge */ + + else + /* exp(INF) is INF, exp(+big#) overflows to INF */ + return( finite(x) ? scalb(1.0,5000) : x); +} +#endif + +/* returns exp(r = x + c) for |c| < |x| with no overlap. */ + +double __exp__D(x, c) +double x, c; +{ + double z,hi,lo; + int k; + + if (x != x) /* x is NaN */ + return(x); + if ( x <= lnhuge ) { + if ( x >= lntiny ) { + + /* argument reduction : x --> x - k*ln2 */ + z = invln2*x; + k = z + copysign(.5, x); + + /* express (x+c)-k*ln2 as hi-lo and let x=hi-lo rounded */ + + hi=(x-k*ln2hi); /* Exact. */ + x= hi - (lo = k*ln2lo-c); + /* return 2^k*[1+x+x*c/(2+c)] */ + z=x*x; + c= x - z*(p1+z*(p2+z*(p3+z*(p4+z*p5)))); + c = (x*c)/(2.0-c); + + return scalbn(1.+(hi-(lo - c)), k); + } + /* end of x > lntiny */ + + else + /* exp(-big#) underflows to zero */ + if(isfinite(x)) return(scalbn(1.0,-5000)); + + /* exp(-INF) is zero */ + else return(0.0); + } + /* end of x < lnhuge */ + + else + /* exp(INF) is INF, exp(+big#) overflows to INF */ + return( isfinite(x) ? scalbn(1.0,5000) : x); +} diff --git a/openlibm/bsdsrc/b_log.c b/openlibm/bsdsrc/b_log.c new file mode 100644 index 0000000000..c20d290384 --- /dev/null +++ b/openlibm/bsdsrc/b_log.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* @(#)log.c 8.2 (Berkeley) 11/30/93 */ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/bsdsrc/b_log.c,v 1.9 2008/02/22 02:26:51 das Exp $"); + +#include + +#include "mathimpl.h" + +/* Table-driven natural logarithm. + * + * This code was derived, with minor modifications, from: + * Peter Tang, "Table-Driven Implementation of the + * Logarithm in IEEE Floating-Point arithmetic." ACM Trans. + * Math Software, vol 16. no 4, pp 378-400, Dec 1990). + * + * Calculates log(2^m*F*(1+f/F)), |f/j| <= 1/256, + * where F = j/128 for j an integer in [0, 128]. + * + * log(2^m) = log2_hi*m + log2_tail*m + * since m is an integer, the dominant term is exact. + * m has at most 10 digits (for subnormal numbers), + * and log2_hi has 11 trailing zero bits. + * + * log(F) = logF_hi[j] + logF_lo[j] is in tabular form in log_table.h + * logF_hi[] + 512 is exact. + * + * log(1+f/F) = 2*f/(2*F + f) + 1/12 * (2*f/(2*F + f))**3 + ... + * the leading term is calculated to extra precision in two + * parts, the larger of which adds exactly to the dominant + * m and F terms. + * There are two cases: + * 1. when m, j are non-zero (m | j), use absolute + * precision for the leading term. + * 2. when m = j = 0, |1-x| < 1/256, and log(x) ~= (x-1). + * In this case, use a relative precision of 24 bits. + * (This is done differently in the original paper) + * + * Special cases: + * 0 return signalling -Inf + * neg return signalling NaN + * +Inf return +Inf +*/ + +#define N 128 + +/* Table of log(Fj) = logF_head[j] + logF_tail[j], for Fj = 1+j/128. + * Used for generation of extend precision logarithms. + * The constant 35184372088832 is 2^45, so the divide is exact. + * It ensures correct reading of logF_head, even for inaccurate + * decimal-to-binary conversion routines. (Everybody gets the + * right answer for integers less than 2^53.) + * Values for log(F) were generated using error < 10^-57 absolute + * with the bc -l package. +*/ +static double A1 = .08333333333333178827; +static double A2 = .01250000000377174923; +static double A3 = .002232139987919447809; +static double A4 = .0004348877777076145742; + +static double logF_head[N+1] = { + 0., + .007782140442060381246, + .015504186535963526694, + .023167059281547608406, + .030771658666765233647, + .038318864302141264488, + .045809536031242714670, + .053244514518837604555, + .060624621816486978786, + .067950661908525944454, + .075223421237524235039, + .082443669210988446138, + .089612158689760690322, + .096729626458454731618, + .103796793681567578460, + .110814366340264314203, + .117783035656430001836, + .124703478501032805070, + .131576357788617315236, + .138402322859292326029, + .145182009844575077295, + .151916042025732167530, + .158605030176659056451, + .165249572895390883786, + .171850256926518341060, + .178407657472689606947, + .184922338493834104156, + .191394852999565046047, + .197825743329758552135, + .204215541428766300668, + .210564769107350002741, + .216873938300523150246, + .223143551314024080056, + .229374101064877322642, + .235566071312860003672, + .241719936886966024758, + .247836163904594286577, + .253915209980732470285, + .259957524436686071567, + .265963548496984003577, + .271933715484010463114, + .277868451003087102435, + .283768173130738432519, + .289633292582948342896, + .295464212893421063199, + .301261330578199704177, + .307025035294827830512, + .312755710004239517729, + .318453731118097493890, + .324119468654316733591, + .329753286372579168528, + .335355541920762334484, + .340926586970454081892, + .346466767346100823488, + .351976423156884266063, + .357455888922231679316, + .362905493689140712376, + .368325561158599157352, + .373716409793814818840, + .379078352934811846353, + .384411698910298582632, + .389716751140440464951, + .394993808240542421117, + .400243164127459749579, + .405465108107819105498, + .410659924985338875558, + .415827895143593195825, + .420969294644237379543, + .426084395310681429691, + .431173464818130014464, + .436236766774527495726, + .441274560805140936281, + .446287102628048160113, + .451274644139630254358, + .456237433481874177232, + .461175715122408291790, + .466089729924533457960, + .470979715219073113985, + .475845904869856894947, + .480688529345570714212, + .485507815781602403149, + .490303988045525329653, + .495077266798034543171, + .499827869556611403822, + .504556010751912253908, + .509261901790523552335, + .513945751101346104405, + .518607764208354637958, + .523248143765158602036, + .527867089620485785417, + .532464798869114019908, + .537041465897345915436, + .541597282432121573947, + .546132437597407260909, + .550647117952394182793, + .555141507540611200965, + .559615787935399566777, + .564070138285387656651, + .568504735352689749561, + .572919753562018740922, + .577315365035246941260, + .581691739635061821900, + .586049045003164792433, + .590387446602107957005, + .594707107746216934174, + .599008189645246602594, + .603290851438941899687, + .607555250224322662688, + .611801541106615331955, + .616029877215623855590, + .620240409751204424537, + .624433288012369303032, + .628608659422752680256, + .632766669570628437213, + .636907462236194987781, + .641031179420679109171, + .645137961373620782978, + .649227946625615004450, + .653301272011958644725, + .657358072709030238911, + .661398482245203922502, + .665422632544505177065, + .669430653942981734871, + .673422675212350441142, + .677398823590920073911, + .681359224807238206267, + .685304003098281100392, + .689233281238557538017, + .693147180560117703862 +}; + +static double logF_tail[N+1] = { + 0., + -.00000000000000543229938420049, + .00000000000000172745674997061, + -.00000000000001323017818229233, + -.00000000000001154527628289872, + -.00000000000000466529469958300, + .00000000000005148849572685810, + -.00000000000002532168943117445, + -.00000000000005213620639136504, + -.00000000000001819506003016881, + .00000000000006329065958724544, + .00000000000008614512936087814, + -.00000000000007355770219435028, + .00000000000009638067658552277, + .00000000000007598636597194141, + .00000000000002579999128306990, + -.00000000000004654729747598444, + -.00000000000007556920687451336, + .00000000000010195735223708472, + -.00000000000017319034406422306, + -.00000000000007718001336828098, + .00000000000010980754099855238, + -.00000000000002047235780046195, + -.00000000000008372091099235912, + .00000000000014088127937111135, + .00000000000012869017157588257, + .00000000000017788850778198106, + .00000000000006440856150696891, + .00000000000016132822667240822, + -.00000000000007540916511956188, + -.00000000000000036507188831790, + .00000000000009120937249914984, + .00000000000018567570959796010, + -.00000000000003149265065191483, + -.00000000000009309459495196889, + .00000000000017914338601329117, + -.00000000000001302979717330866, + .00000000000023097385217586939, + .00000000000023999540484211737, + .00000000000015393776174455408, + -.00000000000036870428315837678, + .00000000000036920375082080089, + -.00000000000009383417223663699, + .00000000000009433398189512690, + .00000000000041481318704258568, + -.00000000000003792316480209314, + .00000000000008403156304792424, + -.00000000000034262934348285429, + .00000000000043712191957429145, + -.00000000000010475750058776541, + -.00000000000011118671389559323, + .00000000000037549577257259853, + .00000000000013912841212197565, + .00000000000010775743037572640, + .00000000000029391859187648000, + -.00000000000042790509060060774, + .00000000000022774076114039555, + .00000000000010849569622967912, + -.00000000000023073801945705758, + .00000000000015761203773969435, + .00000000000003345710269544082, + -.00000000000041525158063436123, + .00000000000032655698896907146, + -.00000000000044704265010452446, + .00000000000034527647952039772, + -.00000000000007048962392109746, + .00000000000011776978751369214, + -.00000000000010774341461609578, + .00000000000021863343293215910, + .00000000000024132639491333131, + .00000000000039057462209830700, + -.00000000000026570679203560751, + .00000000000037135141919592021, + -.00000000000017166921336082431, + -.00000000000028658285157914353, + -.00000000000023812542263446809, + .00000000000006576659768580062, + -.00000000000028210143846181267, + .00000000000010701931762114254, + .00000000000018119346366441110, + .00000000000009840465278232627, + -.00000000000033149150282752542, + -.00000000000018302857356041668, + -.00000000000016207400156744949, + .00000000000048303314949553201, + -.00000000000071560553172382115, + .00000000000088821239518571855, + -.00000000000030900580513238244, + -.00000000000061076551972851496, + .00000000000035659969663347830, + .00000000000035782396591276383, + -.00000000000046226087001544578, + .00000000000062279762917225156, + .00000000000072838947272065741, + .00000000000026809646615211673, + -.00000000000010960825046059278, + .00000000000002311949383800537, + -.00000000000058469058005299247, + -.00000000000002103748251144494, + -.00000000000023323182945587408, + -.00000000000042333694288141916, + -.00000000000043933937969737844, + .00000000000041341647073835565, + .00000000000006841763641591466, + .00000000000047585534004430641, + .00000000000083679678674757695, + -.00000000000085763734646658640, + .00000000000021913281229340092, + -.00000000000062242842536431148, + -.00000000000010983594325438430, + .00000000000065310431377633651, + -.00000000000047580199021710769, + -.00000000000037854251265457040, + .00000000000040939233218678664, + .00000000000087424383914858291, + .00000000000025218188456842882, + -.00000000000003608131360422557, + -.00000000000050518555924280902, + .00000000000078699403323355317, + -.00000000000067020876961949060, + .00000000000016108575753932458, + .00000000000058527188436251509, + -.00000000000035246757297904791, + -.00000000000018372084495629058, + .00000000000088606689813494916, + .00000000000066486268071468700, + .00000000000063831615170646519, + .00000000000025144230728376072, + -.00000000000017239444525614834 +}; + +#if 0 +OLM_DLLEXPORT double +#ifdef _ANSI_SOURCE +log(double x) +#else +log(x) double x; +#endif +{ + int m, j; + double F, f, g, q, u, u2, v, zero = 0.0, one = 1.0; + volatile double u1; + + /* Catch special cases */ + if (x <= 0) + if (x == zero) /* log(0) = -Inf */ + return (-one/zero); + else /* log(neg) = NaN */ + return (zero/zero); + else if (!finite(x)) + return (x+x); /* x = NaN, Inf */ + + /* Argument reduction: 1 <= g < 2; x/2^m = g; */ + /* y = F*(1 + f/F) for |f| <= 2^-8 */ + + m = logb(x); + g = ldexp(x, -m); + if (m == -1022) { + j = logb(g), m += j; + g = ldexp(g, -j); + } + j = N*(g-1) + .5; + F = (1.0/N) * j + 1; /* F*128 is an integer in [128, 512] */ + f = g - F; + + /* Approximate expansion for log(1+f/F) ~= u + q */ + g = 1/(2*F+f); + u = 2*f*g; + v = u*u; + q = u*v*(A1 + v*(A2 + v*(A3 + v*A4))); + + /* case 1: u1 = u rounded to 2^-43 absolute. Since u < 2^-8, + * u1 has at most 35 bits, and F*u1 is exact, as F has < 8 bits. + * It also adds exactly to |m*log2_hi + log_F_head[j] | < 750 + */ + if (m | j) + u1 = u + 513, u1 -= 513; + + /* case 2: |1-x| < 1/256. The m- and j- dependent terms are zero; + * u1 = u to 24 bits. + */ + else + u1 = u, TRUNC(u1); + u2 = (2.0*(f - F*u1) - u1*f) * g; + /* u1 + u2 = 2f/(2F+f) to extra precision. */ + + /* log(x) = log(2^m*F*(1+f/F)) = */ + /* (m*log2_hi+logF_head[j]+u1) + (m*log2_lo+logF_tail[j]+q); */ + /* (exact) + (tiny) */ + + u1 += m*logF_head[N] + logF_head[j]; /* exact */ + u2 = (u2 + logF_tail[j]) + q; /* tiny */ + u2 += logF_tail[N]*m; + return (u1 + u2); +} +#endif + +/* + * Extra precision variant, returning struct {double a, b;}; + * log(x) = a+b to 63 bits, with a rounded to 26 bits. + */ +struct Double +#ifdef _ANSI_SOURCE +__log__D(double x) +#else +__log__D(x) double x; +#endif +{ + int m, j; + double F, f, g, q, u, v, u2; + volatile double u1; + struct Double r; + + /* Argument reduction: 1 <= g < 2; x/2^m = g; */ + /* y = F*(1 + f/F) for |f| <= 2^-8 */ + + m = logb(x); + g = ldexp(x, -m); + if (m == -1022) { + j = logb(g), m += j; + g = ldexp(g, -j); + } + j = N*(g-1) + .5; + F = (1.0/N) * j + 1; + f = g - F; + + g = 1/(2*F+f); + u = 2*f*g; + v = u*u; + q = u*v*(A1 + v*(A2 + v*(A3 + v*A4))); + if (m | j) + u1 = u + 513, u1 -= 513; + else + u1 = u, TRUNC(u1); + u2 = (2.0*(f - F*u1) - u1*f) * g; + + u1 += m*logF_head[N] + logF_head[j]; + + u2 += logF_tail[j]; u2 += q; + u2 += logF_tail[N]*m; + r.a = u1 + u2; /* Only difference is here */ + TRUNC(r.a); + r.b = (u1 - r.a) + u2; + return (r); +} diff --git a/openlibm/bsdsrc/b_tgamma.c b/openlibm/bsdsrc/b_tgamma.c new file mode 100644 index 0000000000..f3871ded88 --- /dev/null +++ b/openlibm/bsdsrc/b_tgamma.c @@ -0,0 +1,319 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* @(#)gamma.c 8.1 (Berkeley) 6/4/93 */ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/bsdsrc/b_tgamma.c,v 1.10 2008/02/22 02:26:51 das Exp $"); + +/* + * This code by P. McIlroy, Oct 1992; + * + * The financial support of UUNET Communications Services is greatfully + * acknowledged. + */ + +#include +#include + +#include "mathimpl.h" + +/* METHOD: + * x < 0: Use reflection formula, G(x) = pi/(sin(pi*x)*x*G(x)) + * At negative integers, return NaN and raise invalid. + * + * x < 6.5: + * Use argument reduction G(x+1) = xG(x) to reach the + * range [1.066124,2.066124]. Use a rational + * approximation centered at the minimum (x0+1) to + * ensure monotonicity. + * + * x >= 6.5: Use the asymptotic approximation (Stirling's formula) + * adjusted for equal-ripples: + * + * log(G(x)) ~= (x-.5)*(log(x)-1) + .5(log(2*pi)-1) + 1/x*P(1/(x*x)) + * + * Keep extra precision in multiplying (x-.5)(log(x)-1), to + * avoid premature round-off. + * + * Special values: + * -Inf: return NaN and raise invalid; + * negative integer: return NaN and raise invalid; + * other x ~< 177.79: return +-0 and raise underflow; + * +-0: return +-Inf and raise divide-by-zero; + * finite x ~> 171.63: return +Inf and raise overflow; + * +Inf: return +Inf; + * NaN: return NaN. + * + * Accuracy: tgamma(x) is accurate to within + * x > 0: error provably < 0.9ulp. + * Maximum observed in 1,000,000 trials was .87ulp. + * x < 0: + * Maximum observed error < 4ulp in 1,000,000 trials. + */ + +static double neg_gam(double); +static double small_gam(double); +static double smaller_gam(double); +static struct Double large_gam(double); +static struct Double ratfun_gam(double, double); + +/* + * Rational approximation, A0 + x*x*P(x)/Q(x), on the interval + * [1.066.., 2.066..] accurate to 4.25e-19. + */ +#define LEFT -.3955078125 /* left boundary for rat. approx */ +#define x0 .461632144968362356785 /* xmin - 1 */ + +#define a0_hi 0.88560319441088874992 +#define a0_lo -.00000000000000004996427036469019695 +#define P0 6.21389571821820863029017800727e-01 +#define P1 2.65757198651533466104979197553e-01 +#define P2 5.53859446429917461063308081748e-03 +#define P3 1.38456698304096573887145282811e-03 +#define P4 2.40659950032711365819348969808e-03 +#define Q0 1.45019531250000000000000000000e+00 +#define Q1 1.06258521948016171343454061571e+00 +#define Q2 -2.07474561943859936441469926649e-01 +#define Q3 -1.46734131782005422506287573015e-01 +#define Q4 3.07878176156175520361557573779e-02 +#define Q5 5.12449347980666221336054633184e-03 +#define Q6 -1.76012741431666995019222898833e-03 +#define Q7 9.35021023573788935372153030556e-05 +#define Q8 6.13275507472443958924745652239e-06 +/* + * Constants for large x approximation (x in [6, Inf]) + * (Accurate to 2.8*10^-19 absolute) + */ +#define lns2pi_hi 0.418945312500000 +#define lns2pi_lo -.000006779295327258219670263595 +#define Pa0 8.33333333333333148296162562474e-02 +#define Pa1 -2.77777777774548123579378966497e-03 +#define Pa2 7.93650778754435631476282786423e-04 +#define Pa3 -5.95235082566672847950717262222e-04 +#define Pa4 8.41428560346653702135821806252e-04 +#define Pa5 -1.89773526463879200348872089421e-03 +#define Pa6 5.69394463439411649408050664078e-03 +#define Pa7 -1.44705562421428915453880392761e-02 + +static const double zero = 0., one = 1.0, tiny = 1e-300; + +OLM_DLLEXPORT double +tgamma(x) + double x; +{ + struct Double u; + + if (isgreaterequal(x, 6)) { + if(x > 171.63) + return (x / zero); + u = large_gam(x); + return(__exp__D(u.a, u.b)); + } else if (isgreaterequal(x, 1.0 + LEFT + x0)) + return (small_gam(x)); + else if (isgreater(x, 1.e-17)) + return (smaller_gam(x)); + else if (isgreater(x, -1.e-17)) { + if (x != 0.0) + u.a = one - tiny; /* raise inexact */ + return (one/x); + } else if (!isfinite(x)) + return (x - x); /* x is NaN or -Inf */ + else + return (neg_gam(x)); +} +/* + * Accurate to max(ulp(1/128) absolute, 2^-66 relative) error. + */ +static struct Double +large_gam(x) + double x; +{ + double z, p; + struct Double t, u, v; + + z = one/(x*x); + p = Pa0+z*(Pa1+z*(Pa2+z*(Pa3+z*(Pa4+z*(Pa5+z*(Pa6+z*Pa7)))))); + p = p/x; + + u = __log__D(x); + u.a -= one; + v.a = (x -= .5); + TRUNC(v.a); + v.b = x - v.a; + t.a = v.a*u.a; /* t = (x-.5)*(log(x)-1) */ + t.b = v.b*u.a + x*u.b; + /* return t.a + t.b + lns2pi_hi + lns2pi_lo + p */ + t.b += lns2pi_lo; t.b += p; + u.a = lns2pi_hi + t.b; u.a += t.a; + u.b = t.a - u.a; + u.b += lns2pi_hi; u.b += t.b; + return (u); +} +/* + * Good to < 1 ulp. (provably .90 ulp; .87 ulp on 1,000,000 runs.) + * It also has correct monotonicity. + */ +static double +small_gam(x) + double x; +{ + double y, ym1, t; + struct Double yy, r; + y = x - one; + ym1 = y - one; + if (y <= 1.0 + (LEFT + x0)) { + yy = ratfun_gam(y - x0, 0); + return (yy.a + yy.b); + } + r.a = y; + TRUNC(r.a); + yy.a = r.a - one; + y = ym1; + yy.b = r.b = y - yy.a; + /* Argument reduction: G(x+1) = x*G(x) */ + for (ym1 = y-one; ym1 > LEFT + x0; y = ym1--, yy.a--) { + t = r.a*yy.a; + r.b = r.a*yy.b + y*r.b; + r.a = t; + TRUNC(r.a); + r.b += (t - r.a); + } + /* Return r*tgamma(y). */ + yy = ratfun_gam(y - x0, 0); + y = r.b*(yy.a + yy.b) + r.a*yy.b; + y += yy.a*r.a; + return (y); +} +/* + * Good on (0, 1+x0+LEFT]. Accurate to 1ulp. + */ +static double +smaller_gam(x) + double x; +{ + double t, d; + struct Double r, xx; + if (x < x0 + LEFT) { + t = x, TRUNC(t); + d = (t+x)*(x-t); + t *= t; + xx.a = (t + x), TRUNC(xx.a); + xx.b = x - xx.a; xx.b += t; xx.b += d; + t = (one-x0); t += x; + d = (one-x0); d -= t; d += x; + x = xx.a + xx.b; + } else { + xx.a = x, TRUNC(xx.a); + xx.b = x - xx.a; + t = x - x0; + d = (-x0 -t); d += x; + } + r = ratfun_gam(t, d); + d = r.a/x, TRUNC(d); + r.a -= d*xx.a; r.a -= d*xx.b; r.a += r.b; + return (d + r.a/x); +} +/* + * returns (z+c)^2 * P(z)/Q(z) + a0 + */ +static struct Double +ratfun_gam(z, c) + double z, c; +{ + double p, q; + struct Double r, t; + + q = Q0 +z*(Q1+z*(Q2+z*(Q3+z*(Q4+z*(Q5+z*(Q6+z*(Q7+z*Q8))))))); + p = P0 + z*(P1 + z*(P2 + z*(P3 + z*P4))); + + /* return r.a + r.b = a0 + (z+c)^2*p/q, with r.a truncated to 26 bits. */ + p = p/q; + t.a = z, TRUNC(t.a); /* t ~= z + c */ + t.b = (z - t.a) + c; + t.b *= (t.a + z); + q = (t.a *= t.a); /* t = (z+c)^2 */ + TRUNC(t.a); + t.b += (q - t.a); + r.a = p, TRUNC(r.a); /* r = P/Q */ + r.b = p - r.a; + t.b = t.b*p + t.a*r.b + a0_lo; + t.a *= r.a; /* t = (z+c)^2*(P/Q) */ + r.a = t.a + a0_hi, TRUNC(r.a); + r.b = ((a0_hi-r.a) + t.a) + t.b; + return (r); /* r = a0 + t */ +} + +static double +neg_gam(x) + double x; +{ + int sgn = 1; + struct Double lg, lsine; + double y, z; + + y = ceil(x); + if (y == x) /* Negative integer. */ + return ((x - x) / zero); + z = y - x; + if (z > 0.5) + z = one - z; + y = 0.5 * y; + if (y == ceil(y)) + sgn = -1; + if (z < .25) + z = sin(M_PI*z); + else + z = cos(M_PI*(0.5-z)); + /* Special case: G(1-x) = Inf; G(x) may be nonzero. */ + if (x < -170) { + if (x < -190) + return ((double)sgn*tiny*tiny); + y = one - x; /* exact: 128 < |x| < 255 */ + lg = large_gam(y); + lsine = __log__D(M_PI/z); /* = TRUNC(log(u)) + small */ + lg.a -= lsine.a; /* exact (opposite signs) */ + lg.b -= lsine.b; + y = -(lg.a + lg.b); + z = (y + lg.a) + lg.b; + y = __exp__D(y, z); + if (sgn < 0) y = -y; + return (y); + } + y = one-x; + if (one-y == x) + y = tgamma(y); + else /* 1-x is inexact */ + y = -x*tgamma(-x); + if (sgn < 0) y = -y; + return (M_PI / (y*z)); +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(tgamma, tgammal); +#endif diff --git a/openlibm/bsdsrc/mathimpl.h b/openlibm/bsdsrc/mathimpl.h new file mode 100644 index 0000000000..983c4eb366 --- /dev/null +++ b/openlibm/bsdsrc/mathimpl.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mathimpl.h 8.1 (Berkeley) 6/4/93 + * $FreeBSD: src/lib/msun/bsdsrc/mathimpl.h,v 1.7 2005/11/18 05:03:12 bde Exp $ + */ + +#ifndef _MATHIMPL_H_ +#define _MATHIMPL_H_ + +#include "cdefs-compat.h" +#include "math_private.h" + +/* + * TRUNC() is a macro that sets the trailing 27 bits in the mantissa of an + * IEEE double variable to zero. It must be expression-like for syntactic + * reasons, and we implement this expression using an inline function + * instead of a pure macro to avoid depending on the gcc feature of + * statement-expressions. + */ +#define TRUNC(d) (_b_trunc(&(d))) + +static __inline void +_b_trunc(volatile double *_dp) +{ + //VBS + //u_int32_t _lw; + u_int32_t _lw; + + GET_LOW_WORD(_lw, *_dp); + SET_LOW_WORD(*_dp, _lw & 0xf8000000); +} + +struct Double { + double a; + double b; +}; + +/* + * Functions internal to the math package, yet not static. + */ +double __exp__D(double, double); +struct Double __log__D(double); + +#endif /* !_MATHIMPL_H_ */ diff --git a/openlibm/docs/CNAME b/openlibm/docs/CNAME new file mode 100644 index 0000000000..60f23f5bb7 --- /dev/null +++ b/openlibm/docs/CNAME @@ -0,0 +1 @@ +openlibm.org \ No newline at end of file diff --git a/openlibm/docs/images/arrow-down.png b/openlibm/docs/images/arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..5c55c6a8c9edfaa47379862d770304c36c3ac7ff GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eW!3HFke0{SLNX_?jaSYLzn4I9i%II*Xp^aIh zpz+6!8``B?P5)0c^x#?E7OdXC_y6D5zw1+Ygru)Ji1RWzykR(WyifLD&pJjGw#n?P zLqkH=Y}vm3H~*pZ^K<_6Lqu1mGH{74&_2}6eISp?=$Y`s3cD`%ef7U|_uU`DGt*!YlC@Am|DD>an&JK*_ z<>mi__UCKux;y>4Pqor7j|)IU*M9Gw z-IGbu2m_ZdUGO1GBT$h55cdP|c_4ld#Or}LA6*>)0_L))J+G2e00000NkvXXu0mjf DP-vTT literal 0 HcmV?d00001 diff --git a/openlibm/docs/index.html b/openlibm/docs/index.html new file mode 100644 index 0000000000..5c84d7e6d3 --- /dev/null +++ b/openlibm/docs/index.html @@ -0,0 +1,94 @@ + + + + + + OpenLibm + + + + + + + +
+
+

OpenLibm

+

A high quality system independent, portable, open source libm implementation

+ +
+ +

This project is maintained by the Julia Project

+ + +
+
+

+OpenLibm

+ +

OpenLibm is an effort to have a high quality, portable, standalone +C mathematical library (libm). +It can be used standalone in applications and programming language +implementations.

+ +

The project was born out of a need to have a good libm for the +Julia programming langage that worked +consistently across compilers and operating systems, and in 32-bit and +64-bit environments.

+ +

+History

+ +

The OpenLibm code derives from the FreeBSD +msun and OpenBSD +libm +implementations, which in turn derive from FDLIBM +5.3. Over and above that, OpenLibm itself has received a number of patches to make it platform independent and portable.

+ +

+Platform support

+ +

OpenLibm builds on Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD, and +DragonFly BSD. It builds with both GCC and clang. OpenLibm supports x86, amd64, +arm, aarch64, riscv64, ppc64le, mips, wasm32, s390(x), and loongarch64. + +

+Other relevant projects

+ +
    +
  1. MUSL The libm library in the musl-libc project
  2. +
  3. FDLIBM: Freely Distributable Math Library
  4. +
  5. FreeBSD msun: FreeBSD's math library
  6. +
  7. CRlibm: Correctly Rounded mathematical library
  8. +
  9. MUSL
  10. +
  11. CORE-MATH Project
  12. +
+ +

+Acknowledgements

+ +

PowerPC support for OpenLibm was graciously sponsored by IBM. + + +

+ +
+ + + + + + diff --git a/openlibm/docs/javascripts/scale.fix.js b/openlibm/docs/javascripts/scale.fix.js new file mode 100644 index 0000000000..08716c0060 --- /dev/null +++ b/openlibm/docs/javascripts/scale.fix.js @@ -0,0 +1,20 @@ +fixScale = function(doc) { + + var addEvent = 'addEventListener', + type = 'gesturestart', + qsa = 'querySelectorAll', + scales = [1, 1], + meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : []; + + function fix() { + meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1]; + doc.removeEventListener(type, fix, true); + } + + if ((meta = meta[meta.length - 1]) && addEvent in doc) { + fix(); + scales = [.25, 1.6]; + doc[addEvent](type, fix, true); + } + +}; \ No newline at end of file diff --git a/openlibm/docs/params.json b/openlibm/docs/params.json new file mode 100644 index 0000000000..cd0a5ed40e --- /dev/null +++ b/openlibm/docs/params.json @@ -0,0 +1 @@ +{"name":"OpenLibm","tagline":"A high quality system independent, portable, open source libm implementation","body":"## OpenLibm\r\n\r\n[OpenLibm](http://www.openlibm.org) is an effort to have a high quality, portable, standalone\r\nC mathematical library ([`libm`](http://en.wikipedia.org/wiki/libm)).\r\nIt can be used standalone in applications and programming language\r\nimplementations.\r\n\r\nThe project was born out of a need to have a good `libm` for the\r\n[Julia programming langage](http://www.julialang.org) that worked\r\nconsistently across compilers and operating systems, and in 32-bit and\r\n64-bit environments.\r\n\r\n### History\r\n\r\nThe OpenLibm code derives from the [FreeBSD\r\nmsun](http://svnweb.freebsd.org/base/head/lib/msun/) and [OpenBSD\r\nlibm](http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/)\r\nimplementations, which in turn derives from [FDLIBM\r\n5.3](http://www.netlib.org/fdlibm/). As a result, it includes a number\r\nof fixes and updates to FDLIBM that have accumulated over the years in\r\n`msun`, and optimized versions of many functions.\r\n\r\n### Platform support\r\n\r\nOpenLibm builds on Linux, Mac OS X, and Windows, and with little\r\neffort, should build on FreeBSD as well. It builds with both GCC and\r\nclang. Although largely tested on x86, it also includes experimental\r\nsupport for ARM. The original `msun` also includes support for mips,\r\nsparc64, powerpc, ia64, and alpha. These are present in the OpenLibm\r\nsource tree, but no attempt has been made to build any of these.\r\n\r\n### Build instructions\r\n\r\n1. `make` or `make USEGCC=1` to build with GCC. This is the default on\r\n Linux and Windows.\r\n2. `make USECLANG=1` to build with clang. This is the default on OS X.\r\n","google":"UA-28835595-4","note":"Don't delete this file! It's used internally to help with page regeneration."} \ No newline at end of file diff --git a/openlibm/docs/stylesheets/pygment_trac.css b/openlibm/docs/stylesheets/pygment_trac.css new file mode 100644 index 0000000000..c6a6452d24 --- /dev/null +++ b/openlibm/docs/stylesheets/pygment_trac.css @@ -0,0 +1,69 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #999988; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #009999 } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #000080 } /* Name.Tag */ +.highlight .nv { color: #008080 } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #009999 } /* Literal.Number.Float */ +.highlight .mh { color: #009999 } /* Literal.Number.Hex */ +.highlight .mi { color: #009999 } /* Literal.Number.Integer */ +.highlight .mo { color: #009999 } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #008080 } /* Name.Variable.Class */ +.highlight .vg { color: #008080 } /* Name.Variable.Global */ +.highlight .vi { color: #008080 } /* Name.Variable.Instance */ +.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ + +.type-csharp .highlight .k { color: #0000FF } +.type-csharp .highlight .kt { color: #0000FF } +.type-csharp .highlight .nf { color: #000000; font-weight: normal } +.type-csharp .highlight .nc { color: #2B91AF } +.type-csharp .highlight .nn { color: #000000 } +.type-csharp .highlight .s { color: #A31515 } +.type-csharp .highlight .sc { color: #A31515 } diff --git a/openlibm/docs/stylesheets/styles.css b/openlibm/docs/stylesheets/styles.css new file mode 100644 index 0000000000..647f08dbe1 --- /dev/null +++ b/openlibm/docs/stylesheets/styles.css @@ -0,0 +1,423 @@ +@import url(https://fonts.googleapis.com/css?family=Arvo:400,700,400italic); + +/* MeyerWeb Reset */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; +} + + +/* Base text styles */ + +body { + padding:10px 50px 0 0; + font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + color: #232323; + background-color: #FBFAF7; + margin: 0; + line-height: 1.8em; + -webkit-font-smoothing: antialiased; + +} + +h1, h2, h3, h4, h5, h6 { + color:#232323; + margin:36px 0 10px; +} + +p, ul, ol, table, dl { + margin:0 0 22px; +} + +h1, h2, h3 { + font-family: Arvo, Monaco, serif; + line-height:1.3; + font-weight: normal; +} + +h1,h2, h3 { + display: block; + border-bottom: 1px solid #ccc; + padding-bottom: 5px; +} + +h1 { + font-size: 30px; +} + +h2 { + font-size: 24px; +} + +h3 { + font-size: 18px; +} + +h4, h5, h6 { + font-family: Arvo, Monaco, serif; + font-weight: 700; +} + +a { + color:#C30000; + font-weight:200; + text-decoration:none; +} + +a:hover { + text-decoration: underline; +} + +a small { + font-size: 12px; +} + +em { + font-style: italic; +} + +strong { + font-weight:700; +} + +ul { + list-style-position: inside; + list-style: disc; + padding-left: 25px; +} + +ol { + list-style-position: inside; + list-style: decimal; + padding-left: 25px; +} + +blockquote { + margin: 0; + padding: 0 0 0 20px; + font-style: italic; +} + +dl, dt, dd, dl p { + font-color: #444; +} + +dl dt { + font-weight: bold; +} + +dl dd { + padding-left: 20px; + font-style: italic; +} + +dl p { + padding-left: 20px; + font-style: italic; +} + +hr { + border:0; + background:#ccc; + height:1px; + margin:0 0 24px; +} + +/* Images */ + +img { + position: relative; + margin: 0 auto; + max-width: 650px; + padding: 5px; + margin: 10px 0 32px 0; + border: 1px solid #ccc; +} + +p img { + display: inline; + margin: 0; + padding: 0; + vertical-align: middle; + text-align: center; + border: none; +} + +/* Code blocks */ + +code, pre { + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + color:#000; + font-size:14px; +} + +pre { + padding: 4px 12px; + background: #FDFEFB; + border-radius:4px; + border:1px solid #D7D8C8; + overflow: auto; + overflow-y: hidden; + margin-bottom: 32px; +} + + +/* Tables */ + +table { + width:100%; +} + +table { + border: 1px solid #ccc; + margin-bottom: 32px; + text-align: left; + } + +th { + font-family: 'Arvo', Helvetica, Arial, sans-serif; + font-size: 18px; + font-weight: normal; + padding: 10px; + background: #232323; + color: #FDFEFB; + } + +td { + padding: 10px; + background: #ccc; + } + + +/* Wrapper */ +.wrapper { + width:960px; +} + + +/* Header */ + +header { + background-color: #171717; + color: #FDFDFB; + width:170px; + float:left; + position:fixed; + border: 1px solid #000; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + padding: 34px 25px 22px 50px; + margin: 30px 25px 0 0; + -webkit-font-smoothing: antialiased; +} + +p.header { + font-size: 16px; +} + +h1.header { + font-family: Arvo, sans-serif; + font-size: 30px; + font-weight: 300; + line-height: 1.3em; + border-bottom: none; + margin-top: 0; +} + + +h1.header, a.header, a.name, header a{ + color: #fff; +} + +a.header { + text-decoration: underline; +} + +a.name { + white-space: nowrap; +} + +header ul { + list-style:none; + padding:0; +} + +header li { + list-style-type: none; + width:132px; + height:15px; + margin-bottom: 12px; + line-height: 1em; + padding: 6px 6px 6px 7px; + + background: #AF0011; + background: -moz-linear-gradient(top, #AF0011 0%, #820011 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #AF0011 0%,#820011 100%); + background: -o-linear-gradient(top, #AF0011 0%,#820011 100%); + background: -ms-linear-gradient(top, #AF0011 0%,#820011 100%); + background: linear-gradient(top, #AF0011 0%,#820011 100%); + + border-radius:4px; + border:1px solid #0D0D0D; + + -webkit-box-shadow: inset 0px 1px 1px 0 rgba(233,2,38, 1); + box-shadow: inset 0px 1px 1px 0 rgba(233,2,38, 1); + +} + +header li:hover { + background: #C3001D; + background: -moz-linear-gradient(top, #C3001D 0%, #950119 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); + background: -webkit-linear-gradient(top, #C3001D 0%,#950119 100%); + background: -o-linear-gradient(top, #C3001D 0%,#950119 100%); + background: -ms-linear-gradient(top, #C3001D 0%,#950119 100%); + background: linear-gradient(top, #C3001D 0%,#950119 100%); +} + +a.buttons { + -webkit-font-smoothing: antialiased; + background: url(../images/arrow-down.png) no-repeat; + font-weight: normal; + text-shadow: rgba(0, 0, 0, 0.4) 0 -1px 0; + padding: 2px 2px 2px 22px; + height: 30px; +} + +a.github { + background: url(../images/octocat-small.png) no-repeat 1px; +} + +a.buttons:hover { + color: #fff; + text-decoration: none; +} + + +/* Section - for main page content */ + +section { + width:650px; + float:right; + padding-bottom:50px; +} + + +/* Footer */ + +footer { + width:170px; + float:left; + position:fixed; + bottom:10px; + padding-left: 50px; +} + +@media print, screen and (max-width: 960px) { + + div.wrapper { + width:auto; + margin:0; + } + + header, section, footer { + float:none; + position:static; + width:auto; + } + + footer { + border-top: 1px solid #ccc; + margin:0 84px 0 50px; + padding:0; + } + + header { + padding-right:320px; + } + + section { + padding:20px 84px 20px 50px; + margin:0 0 20px; + } + + header a small { + display:inline; + } + + header ul { + position:absolute; + right:130px; + top:84px; + } +} + +@media print, screen and (max-width: 720px) { + body { + word-wrap:break-word; + } + + header { + padding:10px 20px 0; + margin-right: 0; + } + + section { + padding:10px 0 10px 20px; + margin:0 0 30px; + } + + footer { + margin: 0 0 0 30px; + } + + header ul, header p.view { + position:static; + } +} + +@media print, screen and (max-width: 480px) { + + header ul li.download { + display:none; + } + + footer { + margin: 0 0 0 20px; + } + + footer a{ + display:block; + } + +} + +@media print { + body { + padding:0.4in; + font-size:12pt; + color:#444; + } +} \ No newline at end of file diff --git a/openlibm/i387/Make.files b/openlibm/i387/Make.files new file mode 100644 index 0000000000..369c541264 --- /dev/null +++ b/openlibm/i387/Make.files @@ -0,0 +1,21 @@ +$(CUR_SRCS) = e_exp.S e_fmod.S e_log.S e_log10.S \ + e_remainder.S e_sqrt.S s_ceil.S s_copysign.S \ + s_floor.S s_llrint.S s_logb.S s_lrint.S \ + s_remquo.S s_rint.S s_tan.S s_trunc.S + +ifneq ($(OS), WINNT) +$(CUR_SRCS) += s_scalbn.S s_scalbnf.S s_scalbnl.S +endif + +# float counterparts +$(CUR_SRCS)+= e_log10f.S e_logf.S e_remainderf.S \ + e_sqrtf.S s_ceilf.S s_copysignf.S s_floorf.S \ + s_llrintf.S s_logbf.S s_lrintf.S \ + s_remquof.S s_rintf.S s_truncf.S + +# long double counterparts +$(CUR_SRCS)+= e_remainderl.S e_sqrtl.S s_ceill.S s_copysignl.S \ + s_floorl.S s_llrintl.S \ + s_logbl.S s_lrintl.S s_remquol.S s_rintl.S s_truncl.S + +$(CUR_SRCS)+= fenv.c diff --git a/openlibm/i387/bsd_asm.h b/openlibm/i387/bsd_asm.h new file mode 100644 index 0000000000..93ac61729d --- /dev/null +++ b/openlibm/i387/bsd_asm.h @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)DEFS.h 5.1 (Berkeley) 4/23/90 + * $FreeBSD: src/sys/i386/include/asm.h,v 1.14 2007/08/22 04:26:07 jkoshy Exp $ + */ + +#ifndef _MACHINE_ASM_H_ +#define _MACHINE_ASM_H_ + +#if defined(__APPLE__) +#include "osx_asm.h" +#define CNAME(x) EXT(x) +#else +#include "cdefs-compat.h" + +#ifdef PIC +#define PIC_PROLOGUE \ + pushl %ebx; \ + call 1f; \ +1: \ + popl %ebx; \ + addl $_GLOBAL_OFFSET_TABLE_+[.-1b],%ebx +#define PIC_EPILOGUE \ + popl %ebx +#define PIC_PLT(x) x@PLT +#define PIC_GOT(x) x@GOT(%ebx) +#else +#define PIC_PROLOGUE +#define PIC_EPILOGUE +#define PIC_PLT(x) x +#define PIC_GOT(x) x +#endif + +/* + * CNAME and HIDENAME manage the relationship between symbol names in C + * and the equivalent assembly language names. CNAME is given a name as + * it would be used in a C program. It expands to the equivalent assembly + * language name. HIDENAME is given an assembly-language name, and expands + * to a possibly-modified form that will be invisible to C programs. + */ + + +/* XXX should use .p2align 4,0x90 for -m486. */ +#define _START_ENTRY .p2align 2,0x90 + +#if defined(__ELF__) +#define CNAME(csym) csym +#define HIDENAME(asmsym) .asmsym +#define _ENTRY(x) .text; _START_ENTRY; \ + .globl CNAME(x); .type CNAME(x),@function; CNAME(x): +#define END(x) .size x, . - x +#elif defined(_WIN32) +#ifndef _MSC_VER +#define END(x) .end +#define _START_ENTRY_WIN .text; _START_ENTRY +#else +#define END(x) end +#define _START_ENTRY_WIN .code; _START_ENTRY +#endif +#define CNAME(csym) _##csym +#define HIDENAME(asmsym) .asmsym +#define _ENTRY(x) _START_ENTRY_WIN; \ + .globl CNAME(x); .section .drectve; .ascii " -export:", #x; \ + .section .text; .def CNAME(x); .scl 2; .type 32; .endef; CNAME(x): +#endif + +#ifdef PROF +#define ALTENTRY(x) _ENTRY(x); \ + pushl %ebp; movl %esp,%ebp; \ + call PIC_PLT(HIDENAME(mcount)); \ + popl %ebp; \ + jmp 9f +#define ENTRY(x) _ENTRY(x); \ + pushl %ebp; movl %esp,%ebp; \ + call PIC_PLT(HIDENAME(mcount)); \ + popl %ebp; \ + 9: +#else +#define ALTENTRY(x) _ENTRY(x) +#define ENTRY(x) _ENTRY(x) +#endif + +#define RCSID(x) .text; .asciz x + +#undef __FBSDID +#define __FBSDID(s) /* nothing */ + +#endif +#endif /* !_MACHINE_ASM_H_ */ diff --git a/openlibm/i387/bsd_ieeefp.h b/openlibm/i387/bsd_ieeefp.h new file mode 100644 index 0000000000..79c7bf32be --- /dev/null +++ b/openlibm/i387/bsd_ieeefp.h @@ -0,0 +1,265 @@ +/*- + * Copyright (c) 2003 Peter Wemm. + * Copyright (c) 1990 Andrew Moore, Talke Studio + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#) ieeefp.h 1.0 (Berkeley) 9/23/93 + * $FreeBSD$ + */ + +#ifndef _MACHINE_IEEEFP_H_ +#define _MACHINE_IEEEFP_H_ + +/* + * Deprecated historical FPU control interface + * + * IEEE floating point type, constant and function definitions. + * XXX: FP*FLD and FP*OFF are undocumented pollution. + */ + +/* VBS + +#ifndef _SYS_CDEFS_H_ +#error this file needs sys/cdefs.h as a prerequisite +#endif + +*/ + +/* + * Rounding modes. + */ +typedef enum { + FP_RN=0, /* round to nearest */ + FP_RM, /* round down towards minus infinity */ + FP_RP, /* round up towards plus infinity */ + FP_RZ /* truncate */ +} fp_rnd_t; + +/* + * Precision (i.e., rounding precision) modes. + */ +typedef enum { + FP_PS=0, /* 24 bit (single-precision) */ + FP_PRS, /* reserved */ + FP_PD, /* 53 bit (double-precision) */ + FP_PE /* 64 bit (extended-precision) */ +} fp_prec_t; + +#define fp_except_t int + +/* + * Exception bit masks. + */ +#define FP_X_INV 0x01 /* invalid operation */ +#define FP_X_DNML 0x02 /* denormal */ +#define FP_X_DZ 0x04 /* zero divide */ +#define FP_X_OFL 0x08 /* overflow */ +#define FP_X_UFL 0x10 /* underflow */ +#define FP_X_IMP 0x20 /* (im)precision */ +#define FP_X_STK 0x40 /* stack fault */ + +/* + * FPU control word bit-field masks. + */ +#define FP_MSKS_FLD 0x3f /* exception masks field */ +#define FP_PRC_FLD 0x300 /* precision control field */ +#define FP_RND_FLD 0xc00 /* rounding control field */ + +/* + * FPU status word bit-field masks. + */ +#define FP_STKY_FLD 0x3f /* sticky flags field */ + +/* + * FPU control word bit-field offsets (shift counts). + */ +#define FP_MSKS_OFF 0 /* exception masks offset */ +#define FP_PRC_OFF 8 /* precision control offset */ +#define FP_RND_OFF 10 /* rounding control offset */ + +/* + * FPU status word bit-field offsets (shift counts). + */ +#define FP_STKY_OFF 0 /* sticky flags offset */ + +//VBS +//#ifdef __GNUCLIKE_ASM + +#define __fldcw(addr) __asm __volatile("fldcw %0" : : "m" (*(addr))) +#define __fldenv(addr) __asm __volatile("fldenv %0" : : "m" (*(addr))) +#define __fnclex() __asm __volatile("fnclex") +#define __fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) +#define __fnstenv(addr) __asm __volatile("fnstenv %0" : "=m" (*(addr))) +#define __fnstsw(addr) __asm __volatile("fnstsw %0" : "=m" (*(addr))) + +/* + * Load the control word. Be careful not to trap if there is a currently + * unmasked exception (ones that will become freshly unmasked are not a + * problem). This case must be handled by a save/restore of the + * environment or even of the full x87 state. Accessing the environment + * is very inefficient, so only do it when necessary. + */ +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + struct { + unsigned _cw; + unsigned _other[6]; + } _env; + unsigned short _sw; + + if ((_cw & FP_MSKS_FLD) != FP_MSKS_FLD) { + __fnstsw(&_sw); + if (((_sw & ~_cw) & FP_STKY_FLD) != 0) { + __fnstenv(&_env); + _env._cw = _newcw; + __fldenv(&_env); + return; + } + } + __fldcw(&_newcw); +} + +static __inline fp_rnd_t +fpgetround(void) +{ + unsigned short _cw; + + __fnstcw(&_cw); + return ((fp_rnd_t)((_cw & FP_RND_FLD) >> FP_RND_OFF)); +} + +static __inline fp_rnd_t +fpsetround(fp_rnd_t _m) +{ + fp_rnd_t _p; + unsigned short _cw, _newcw; + + __fnstcw(&_cw); + _p = (fp_rnd_t)((_cw & FP_RND_FLD) >> FP_RND_OFF); + _newcw = _cw & ~FP_RND_FLD; + _newcw |= (_m << FP_RND_OFF) & FP_RND_FLD; + __fnldcw(_cw, _newcw); + return (_p); +} + +//static __inline fp_prec_t +OLM_DLLEXPORT fp_prec_t +fpgetprec(void) +{ + unsigned short _cw; + + __fnstcw(&_cw); + return ((fp_prec_t)((_cw & FP_PRC_FLD) >> FP_PRC_OFF)); +} + +//static __inline fp_prec_t +OLM_DLLEXPORT fp_prec_t +fpsetprec(fp_prec_t _m) +{ + fp_prec_t _p; + unsigned short _cw, _newcw; + + __fnstcw(&_cw); + _p = (fp_prec_t)((_cw & FP_PRC_FLD) >> FP_PRC_OFF); + _newcw = _cw & ~FP_PRC_FLD; + _newcw |= (_m << FP_PRC_OFF) & FP_PRC_FLD; + __fnldcw(_cw, _newcw); + return (_p); +} + +/* + * Get or set the exception mask. + * Note that the x87 mask bits are inverted by the API -- a mask bit of 1 + * means disable for x87 and SSE, but for fp*mask() it means enable. + */ + +static __inline fp_except_t +fpgetmask(void) +{ + unsigned short _cw; + + __fnstcw(&_cw); + return ((~_cw & FP_MSKS_FLD) >> FP_MSKS_OFF); +} + +static __inline fp_except_t +fpsetmask(fp_except_t _m) +{ + fp_except_t _p; + unsigned short _cw, _newcw; + + __fnstcw(&_cw); + _p = (~_cw & FP_MSKS_FLD) >> FP_MSKS_OFF; + _newcw = _cw & ~FP_MSKS_FLD; + _newcw |= (~_m << FP_MSKS_OFF) & FP_MSKS_FLD; + __fnldcw(_cw, _newcw); + return (_p); +} + +static __inline fp_except_t +fpgetsticky(void) +{ + unsigned _ex; + unsigned short _sw; + + __fnstsw(&_sw); + _ex = (_sw & FP_STKY_FLD) >> FP_STKY_OFF; + return ((fp_except_t)_ex); +} + +static __inline fp_except_t +fpresetsticky(fp_except_t _m) +{ + struct { + unsigned _cw; + unsigned _sw; + unsigned _other[5]; + } _env; + fp_except_t _p; + + _m &= FP_STKY_FLD >> FP_STKY_OFF; + _p = fpgetsticky(); + if ((_p & ~_m) == _p) + return (_p); + if ((_p & ~_m) == 0) { + __fnclex(); + return (_p); + } + __fnstenv(&_env); + _env._sw &= ~_m; + __fldenv(&_env); + return (_p); +} + +//#endif /* __GNUCLIKE_ASM */ + +#endif /* !_MACHINE_IEEEFP_H_ */ diff --git a/openlibm/i387/bsd_npx.h b/openlibm/i387/bsd_npx.h new file mode 100644 index 0000000000..78c5cbe6f5 --- /dev/null +++ b/openlibm/i387/bsd_npx.h @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)npx.h 5.3 (Berkeley) 1/18/91 + * $FreeBSD: src/sys/i386/include/npx.h,v 1.29.2.1 2006/07/01 00:57:55 davidxu Exp $ + */ + +/* + * 287/387 NPX Coprocessor Data Structures and Constants + * W. Jolitz 1/90 + */ + +#ifndef _MACHINE_NPX_H_ +#define _MACHINE_NPX_H_ + +/* Environment information of floating point unit */ +struct env87 { + long en_cw; /* control word (16bits) */ + long en_sw; /* status word (16bits) */ + long en_tw; /* tag word (16bits) */ + long en_fip; /* floating point instruction pointer */ + unsigned short en_fcs; /* floating code segment selector */ + unsigned short en_opcode; /* opcode last executed (11 bits ) */ + long en_foo; /* floating operand offset */ + long en_fos; /* floating operand segment selector */ +}; + +/* Contents of each floating point accumulator */ +struct fpacc87 { +#ifdef dontdef /* too unportable */ + unsigned long fp_mantlo; /* mantissa low (31:0) */ + unsigned long fp_manthi; /* mantissa high (63:32) */ + int fp_exp:15; /* exponent */ + int fp_sgn:1; /* mantissa sign */ +#else + unsigned char fp_bytes[10]; +#endif +}; + +/* Floating point context */ +struct save87 { + struct env87 sv_env; /* floating point control/status */ + struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */ + unsigned char sv_pad0[4]; /* padding for (now unused) saved status word */ + /* + * Bogus padding for emulators. Emulators should use their own + * struct and arrange to store into this struct (ending here) + * before it is inspected for ptracing or for core dumps. Some + * emulators overwrite the whole struct. We have no good way of + * knowing how much padding to leave. Leave just enough for the + * GPL emulator's i387_union (176 bytes total). + */ + unsigned char sv_pad[64]; /* padding; used by emulators */ +}; + +struct envxmm { + uint16_t en_cw; /* control word (16bits) */ + uint16_t en_sw; /* status word (16bits) */ + uint16_t en_tw; /* tag word (16bits) */ + uint16_t en_opcode; /* opcode last executed (11 bits ) */ + uint32_t en_fip; /* floating point instruction pointer */ + uint16_t en_fcs; /* floating code segment selector */ + uint16_t en_pad0; /* padding */ + uint32_t en_foo; /* floating operand offset */ + uint16_t en_fos; /* floating operand segment selector */ + uint16_t en_pad1; /* padding */ + uint32_t en_mxcsr; /* SSE sontorol/status register */ + uint32_t en_mxcsr_mask; /* valid bits in mxcsr */ +}; + +/* Contents of each SSE extended accumulator */ +struct xmmacc { + unsigned char xmm_bytes[16]; +}; + +struct savexmm { + struct envxmm sv_env; + struct { + struct fpacc87 fp_acc; + unsigned char fp_pad[6]; /* padding */ + } sv_fp[8]; + struct xmmacc sv_xmm[8]; + unsigned char sv_pad[224]; +} __attribute__((__aligned__(16))); + +union savefpu { + struct save87 sv_87; + struct savexmm sv_xmm; +}; + +/* + * The hardware default control word for i387's and later coprocessors is + * 0x37F, giving: + * + * round to nearest + * 64-bit precision + * all exceptions masked. + * + * We modify the affine mode bit and precision bits in this to give: + * + * affine mode for 287's (if they work at all) (1 in bitfield 1<<12) + * 53-bit precision (2 in bitfield 3<<8) + * + * 64-bit precision often gives bad results with high level languages + * because it makes the results of calculations depend on whether + * intermediate values are stored in memory or in FPU registers. + */ +#define __INITIAL_NPXCW__ 0x127F +#define __INITIAL_MXCSR__ 0x1F80 + +#ifdef _KERNEL + +#define IO_NPX 0x0F0 /* Numeric Coprocessor */ +#define IO_NPXSIZE 16 /* 80387/80487 NPX registers */ + +#define IRQ_NPX 13 + +/* full reset on some systems, NOP on others */ +#define npx_full_reset() outb(IO_NPX + 1, 0) + +int npxdna(void); +void npxdrop(void); +void npxexit(struct thread *td); +int npxformat(void); +int npxgetregs(struct thread *td, union savefpu *addr); +void npxinit(unsigned short control); +void npxsave(union savefpu *addr); +void npxsetregs(struct thread *td, union savefpu *addr); +int npxtrap(void); +#endif + +#endif /* !_MACHINE_NPX_H_ */ \ No newline at end of file diff --git a/openlibm/i387/e_exp.S b/openlibm/i387/e_exp.S new file mode 100644 index 0000000000..0f03f9e34b --- /dev/null +++ b/openlibm/i387/e_exp.S @@ -0,0 +1,76 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include + +/* e^x = 2^(x * log2(e)) */ + +ENTRY(exp) + /* + * If x is +-Inf, then the subtraction would give Inf-Inf = NaN. + * Avoid this. Also avoid it if x is NaN for convenience. + */ + movl 8(%esp),%eax + andl $0x7fffffff,%eax + cmpl $0x7ff00000,%eax + jae x_Inf_or_NaN + + fldl 4(%esp) + + /* + * Extended precision is needed to reduce the maximum error from + * hundreds of ulps to less than 1 ulp. Switch to it if necessary. + * We may as well set the rounding mode to to-nearest and mask traps + * if we switch. + */ + fstcw 4(%esp) + movl 4(%esp),%eax + andl $0x0300,%eax + cmpl $0x0300,%eax /* RC == 0 && PC == 3? */ + je 1f /* jump if mode is good */ + movl $0x137f,8(%esp) + fldcw 8(%esp) +1: + fldl2e + fmulp /* x * log2(e) */ + fst %st(1) + frndint /* int(x * log2(e)) */ + fst %st(2) + fsubrp /* fract(x * log2(e)) */ + f2xm1 /* 2^(fract(x * log2(e))) - 1 */ + fld1 + faddp /* 2^(fract(x * log2(e))) */ + fscale /* e^x */ + fstp %st(1) + je 1f + fldcw 4(%esp) +1: + ret + +x_Inf_or_NaN: + /* + * Return 0 if x is -Inf. Otherwise just return x; when x is Inf + * this gives Inf, and when x is a NaN this gives the same result + * as (x + x) (x quieted). + */ + cmpl $0xfff00000,8(%esp) + jne x_not_minus_Inf + cmpl $0,4(%esp) + jne x_not_minus_Inf + fldz + ret + +x_not_minus_Inf: + fldl 4(%esp) + ret +END(exp) + + // + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_fmod.S b/openlibm/i387/e_fmod.S new file mode 100644 index 0000000000..44e52e5d84 --- /dev/null +++ b/openlibm/i387/e_fmod.S @@ -0,0 +1,25 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_fmod.S,v 1.11 2011/01/07 16:13:12 kib Exp $") + +ENTRY(fmod) + fldl 12(%esp) + fldl 4(%esp) +1: fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + ret +END(fmod) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_log.S b/openlibm/i387/e_log.S new file mode 100644 index 0000000000..ebd6ae787e --- /dev/null +++ b/openlibm/i387/e_log.S @@ -0,0 +1,21 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_log.S,v 1.10 2011/01/07 16:13:12 kib Exp $") + +ENTRY(log) + fldln2 + fldl 4(%esp) + fyl2x + ret +END(log) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_log10.S b/openlibm/i387/e_log10.S new file mode 100644 index 0000000000..b559a74d59 --- /dev/null +++ b/openlibm/i387/e_log10.S @@ -0,0 +1,21 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_log10.S,v 1.10 2011/01/07 16:13:12 kib Exp $") + +ENTRY(log10) + fldlg2 + fldl 4(%esp) + fyl2x + ret +END(log10) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_log10f.S b/openlibm/i387/e_log10f.S new file mode 100644 index 0000000000..afdf1b0318 --- /dev/null +++ b/openlibm/i387/e_log10f.S @@ -0,0 +1,22 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_log10f.S,v 1.4 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: e_log10f.S,v 1.1 1996/07/03 16:50:22 jtc Exp $") */ + +ENTRY(log10f) + fldlg2 + flds 4(%esp) + fyl2x + ret +END(log10f) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_logf.S b/openlibm/i387/e_logf.S new file mode 100644 index 0000000000..af06fd8a4d --- /dev/null +++ b/openlibm/i387/e_logf.S @@ -0,0 +1,21 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_logf.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: e_logf.S,v 1.2 1996/07/06 00:15:45 jtc Exp $") */ + +ENTRY(logf) + fldln2 + flds 4(%esp) + fyl2x + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_remainder.S b/openlibm/i387/e_remainder.S new file mode 100644 index 0000000000..2aa3113f56 --- /dev/null +++ b/openlibm/i387/e_remainder.S @@ -0,0 +1,25 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_remainder.S,v 1.11 2011/01/07 16:13:12 kib Exp $") + +ENTRY(remainder) + fldl 12(%esp) + fldl 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) + ret +END(remainder) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_remainderf.S b/openlibm/i387/e_remainderf.S new file mode 100644 index 0000000000..2b7b59726a --- /dev/null +++ b/openlibm/i387/e_remainderf.S @@ -0,0 +1,26 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_remainderf.S,v 1.4 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: e_remainderf.S,v 1.2 1995/05/08 23:49:47 jtc Exp $") */ + +ENTRY(remainderf) + flds 8(%esp) + flds 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) + ret +END(remainderf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_remainderl.S b/openlibm/i387/e_remainderl.S new file mode 100644 index 0000000000..04d460b690 --- /dev/null +++ b/openlibm/i387/e_remainderl.S @@ -0,0 +1,25 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_remainderl.S,v 1.2 2011/01/07 16:13:12 kib Exp $") + +ENTRY(remainderl) + fldt 16(%esp) + fldt 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_sqrt.S b/openlibm/i387/e_sqrt.S new file mode 100644 index 0000000000..5cd45b09f7 --- /dev/null +++ b/openlibm/i387/e_sqrt.S @@ -0,0 +1,37 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_sqrt.S,v 1.10 2011/01/07 16:13:12 kib Exp $") + +ENTRY(sqrt) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + andw $0xfeff,%dx /* Set precision field to 64 bits (53 bit mantissa). + We assume it's set to 0b11 (extended precision), + so zeroing out the low bit of the precision field, + will correctly set the precision */ + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldl 8(%ebp) + fsqrt + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(sqrt) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_sqrtf.S b/openlibm/i387/e_sqrtf.S new file mode 100644 index 0000000000..05fb77fbb7 --- /dev/null +++ b/openlibm/i387/e_sqrtf.S @@ -0,0 +1,21 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_sqrtf.S,v 1.4 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: e_sqrtf.S,v 1.2 1995/05/08 23:50:14 jtc Exp $") */ + +ENTRY(sqrtf) + flds 4(%esp) + fsqrt + ret +END(sqrtf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/e_sqrtl.S b/openlibm/i387/e_sqrtl.S new file mode 100644 index 0000000000..5317f95476 --- /dev/null +++ b/openlibm/i387/e_sqrtl.S @@ -0,0 +1,19 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/e_sqrtl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(sqrtl) + fldt 4(%esp) + fsqrt + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/fenv.c b/openlibm/i387/fenv.c new file mode 100644 index 0000000000..6afe7662b9 --- /dev/null +++ b/openlibm/i387/fenv.c @@ -0,0 +1,226 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/i387/fenv.c,v 1.8 2011/10/21 06:25:31 das Exp $ + */ + +#include "cdefs-compat.h" +#include "types-compat.h" +#include "math_private.h" +#include "i387/bsd_npx.h" + +#define __fenv_static +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +const fenv_t __fe_dfl_env = { + __INITIAL_NPXCW__, + 0x0000, + 0x0000, + 0x1f80, + 0xffffffff, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff } +}; + +enum __sse_support __has_sse = +#ifdef __SSE__ + __SSE_YES; +#else + __SSE_UNK; +#endif + +#define getfl(x) __asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x))) +#define setfl(x) __asm __volatile("pushl %0\n\tpopfl" : : "g" (x)) +#define cpuid_dx(x) __asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t" \ + "cpuid\n\tpopl %%ebx" \ + : "=d" (*(x)) : : "eax", "ecx") + +/* + * Test for SSE support on this processor. We need to do this because + * we need to use ldmxcsr/stmxcsr to get correct results if any part + * of the program was compiled to use SSE floating-point, but we can't + * use SSE on older processors. + */ +int +__test_sse(void) +{ + int flag, nflag; + int dx_features; + + /* Am I a 486? */ + getfl(&flag); + nflag = flag ^ 0x200000; + setfl(nflag); + getfl(&nflag); + if (flag != nflag) { + /* Not a 486, so CPUID should work. */ + cpuid_dx(&dx_features); + if (dx_features & 0x2000000) { + __has_sse = __SSE_YES; + return (1); + } + } + __has_sse = __SSE_NO; + return (0); +} + +extern inline OLM_DLLEXPORT int feclearexcept(int __excepts); +extern inline OLM_DLLEXPORT int fegetexceptflag(fexcept_t *__flagp, int __excepts); + +OLM_DLLEXPORT int +fesetexceptflag(const fexcept_t *flagp, int excepts) +{ + fenv_t env; + uint32_t mxcsr; + + __fnstenv(&env); + env.__status &= ~excepts; + env.__status |= *flagp & excepts; + __fldenv(env); + + if (__HAS_SSE()) { + __stmxcsr(&mxcsr); + mxcsr &= ~excepts; + mxcsr |= *flagp & excepts; + __ldmxcsr(mxcsr); + } + + return (0); +} + +OLM_DLLEXPORT int +feraiseexcept(int excepts) +{ + fexcept_t ex = excepts; + + fesetexceptflag(&ex, excepts); + __fwait(); + return (0); +} + +extern inline OLM_DLLEXPORT int fetestexcept(int __excepts); +extern inline OLM_DLLEXPORT int fegetround(void); +extern inline OLM_DLLEXPORT int fesetround(int __round); + +int +fegetenv(fenv_t *envp) +{ + uint32_t mxcsr; + + __fnstenv(envp); + /* + * fnstenv masks all exceptions, so we need to restore + * the old control word to avoid this side effect. + */ + __fldcw(envp->__control); + if (__HAS_SSE()) { + __stmxcsr(&mxcsr); + __set_mxcsr(*envp, mxcsr); + } + return (0); +} + +int +feholdexcept(fenv_t *envp) +{ + uint32_t mxcsr; + + __fnstenv(envp); + __fnclex(); + if (__HAS_SSE()) { + __stmxcsr(&mxcsr); + __set_mxcsr(*envp, mxcsr); + mxcsr &= ~FE_ALL_EXCEPT; + mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT; + __ldmxcsr(mxcsr); + } + return (0); +} + +extern inline OLM_DLLEXPORT int fesetenv(const fenv_t *__envp); + +OLM_DLLEXPORT int +feupdateenv(const fenv_t *envp) +{ + uint32_t mxcsr; + uint16_t status; + + __fnstsw(&status); + if (__HAS_SSE()) + __stmxcsr(&mxcsr); + else + mxcsr = 0; + fesetenv(envp); + feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT); + return (0); +} + +int +feenableexcept(int mask) +{ + uint32_t mxcsr, omask; + uint16_t control; + + mask &= FE_ALL_EXCEPT; + __fnstcw(&control); + if (__HAS_SSE()) + __stmxcsr(&mxcsr); + else + mxcsr = 0; + omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; + control &= ~mask; + __fldcw(control); + if (__HAS_SSE()) { + mxcsr &= ~(mask << _SSE_EMASK_SHIFT); + __ldmxcsr(mxcsr); + } + return (omask); +} + +int +fedisableexcept(int mask) +{ + uint32_t mxcsr, omask; + uint16_t control; + + mask &= FE_ALL_EXCEPT; + __fnstcw(&control); + if (__HAS_SSE()) + __stmxcsr(&mxcsr); + else + mxcsr = 0; + omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; + control |= mask; + __fldcw(control); + if (__HAS_SSE()) { + mxcsr |= mask << _SSE_EMASK_SHIFT; + __ldmxcsr(mxcsr); + } + return (omask); +} diff --git a/openlibm/i387/invtrig.c b/openlibm/i387/invtrig.c new file mode 100644 index 0000000000..4afd832178 --- /dev/null +++ b/openlibm/i387/invtrig.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/invtrig.c,v 1.1 2008/08/02 03:56:22 das Exp $"); + +#include + +#define STRUCT_DECLS +#include "invtrig.h" + +/* + * asinl() and acosl() + */ +const LONGDOUBLE +pS0 = { 0xaaaaaaaaaaaaaaa8ULL, 0x3ffcU }, /* 1.66666666666666666631e-01L */ +pS1 = { 0xd5271b6699b48bfaULL, 0xbffdU }, /* -4.16313987993683104320e-01L */ +pS2 = { 0xbcf67ca9e9f669cfULL, 0x3ffdU }, /* 3.69068046323246813704e-01L */ +pS3 = { 0x8b7baa3d15f9830dULL, 0xbffcU }, /* -1.36213932016738603108e-01L */ +pS4 = { 0x92154b093a3bff1cULL, 0x3ff9U }, /* 1.78324189708471965733e-02L */ +pS5 = { 0xe5dd76401964508cULL, 0xbff2U }, /* -2.19216428382605211588e-04L */ +pS6 = { 0xee69c5b0fdb76951ULL, 0xbfedU }, /* -7.10526623669075243183e-06L */ +qS1 = { 0xbcaa2159c01436a0ULL, 0xc000U }, /* -2.94788392796209867269e+00L */ +qS2 = { 0xd17a73d1e1564c29ULL, 0x4000U }, /* 3.27309890266528636716e+00L */ +qS3 = { 0xd767e411c9cf4c2cULL, 0xbfffU }, /* -1.68285799854822427013e+00L */ +qS4 = { 0xc809c0dfb9b0d0b7ULL, 0x3ffdU }, /* 3.90699412641738801874e-01L */ +qS5 = { 0x80c3a2197c8ced57ULL, 0xbffaU }; /* -3.14365703596053263322e-02L */ + +/* + * atanl() + */ +const LONGDOUBLE atanhi[] = { + { 0xed63382b0dda7b45ULL, 0x3ffdU }, /* 4.63647609000806116202e-01L */ + { 0xc90fdaa22168c235ULL, 0x3ffeU }, /* 7.85398163397448309628e-01L */ + { 0xfb985e940fb4d900ULL, 0x3ffeU }, /* 9.82793723247329067960e-01L */ + { 0xc90fdaa22168c235ULL, 0x3fffU }, /* 1.57079632679489661926e+00L */ +}; + +const LONGDOUBLE atanlo[] = { + { 0xdfc88bd978751a07ULL, 0x3fbcU }, /* 1.18469937025062860669e-20L */ + { 0xece675d1fc8f8cbbULL, 0xbfbcU }, /* -1.25413940316708300586e-20L */ + { 0xf10f5e197793c283ULL, 0x3fbdU }, /* 2.55232234165405176172e-20L */ + { 0xece675d1fc8f8cbbULL, 0xbfbdU }, /* -2.50827880633416601173e-20L */ +}; + +const LONGDOUBLE aT[] = { + { 0xaaaaaaaaaaaaaa9fULL, 0x3ffdU }, /* 3.33333333333333333017e-01L */ + { 0xcccccccccccc62bcULL, 0xbffcU }, /* -1.99999999999999632011e-01L */ + { 0x9249249248b81e3fULL, 0x3ffcU }, /* 1.42857142857046531280e-01L */ + { 0xe38e38e3316f3de5ULL, 0xbffbU }, /* -1.11111111100562372733e-01L */ + { 0xba2e8b8dc280726aULL, 0x3ffbU }, /* 9.09090902935647302252e-02L */ + { 0x9d89d5b4c6847ec4ULL, 0xbffbU }, /* -7.69230552476207730353e-02L */ + { 0x8888461d3099c677ULL, 0x3ffbU }, /* 6.66661718042406260546e-02L */ + { 0xf0e8ee0f5328dc29ULL, 0xbffaU }, /* -5.88158892835030888692e-02L */ + { 0xd73ea84d24bae54aULL, 0x3ffaU }, /* 5.25499891539726639379e-02L */ + { 0xc08fa381dcd9213aULL, 0xbffaU }, /* -4.70119845393155721494e-02L */ + { 0xa54a26f4095f2a3aULL, 0x3ffaU }, /* 4.03539201366454414072e-02L */ + { 0xeea2d8d059ef3ad6ULL, 0xbff9U }, /* -2.91303858419364158725e-02L */ + { 0xcc82292ab894b051ULL, 0x3ff8U }, /* 1.24822046299269234080e-02L */ +}; + +const LONGDOUBLE +pi_lo = { 0xece675d1fc8f8cbbULL, 0xbfbeU }; /* -5.01655761266833202345e-20L */ diff --git a/openlibm/i387/osx_asm.h b/openlibm/i387/osx_asm.h new file mode 100644 index 0000000000..284224c84d --- /dev/null +++ b/openlibm/i387/osx_asm.h @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#ifndef _I386_ASM_H_ +#define _I386_ASM_H_ + +#ifdef _KERNEL +#include +#endif /* _KERNEL */ + +#ifdef MACH_KERNEL +#include +#else /* !MACH_KERNEL */ +#define MACH_KDB 0 +#endif /* !MACH_KERNEL */ + + +#if defined(MACH_KERNEL) || defined(_KERNEL) +#include +#endif /* MACH_KERNEL || _KERNEL */ + +#if defined(__i386__) + +#define S_PC (%esp) +#define S_ARG0 4(%esp) +#define S_ARG1 8(%esp) +#define S_ARG2 12(%esp) +#define S_ARG3 16(%esp) +#define S_ARG4 20(%esp) + +#define FRAME pushl %ebp; movl %esp, %ebp +#define EMARF leave + +#define B_LINK (%ebp) +#define B_PC 4(%ebp) +#define B_ARG0 8(%ebp) +#define B_ARG1 12(%ebp) +#define B_ARG2 16(%ebp) +#define B_ARG3 20(%ebp) + +#elif defined(__x86_64__) + +#define S_PC (%rsp) + +#define FRAME pushq %rbp; movq %rsp, %rbp +#define EMARF leave + +#define B_LINK (%rbp) +#define B_PC 8(%rbp) + +#else +#error unsupported architecture +#endif + +/* There is another definition of ALIGN for .c sources */ +#ifdef ASSEMBLER +#define ALIGN 4,0x90 +#endif /* ASSEMBLER */ + +#ifndef FALIGN +#define FALIGN ALIGN +#endif + +#define LB(x,n) n +#if __STDC__ +#ifndef __NO_UNDERSCORES__ +#define LCL(x) L ## x +#define EXT(x) _ ## x +#define LEXT(x) _ ## x ## : +#else +#define LCL(x) .L ## x +#define EXT(x) x +#define LEXT(x) x ## : +#endif +#define LBc(x,n) n ## : +#define LBb(x,n) n ## b +#define LBf(x,n) n ## f +#else /* __STDC__ */ +#ifndef __NO_UNDERSCORES__ +#define LCL(x) L/**/x +#define EXT(x) _/**/x +#define LEXT(x) _/**/x/**/: +#else /* __NO_UNDERSCORES__ */ +#define LCL(x) .L/**/x +#define EXT(x) x +#define LEXT(x) x/**/: +#endif /* __NO_UNDERSCORES__ */ +#define LBc(x,n) n/**/: +#define LBb(x,n) n/**/b +#define LBf(x,n) n/**/f +#endif /* __STDC__ */ + +#define SVC .byte 0x9a; .long 0; .word 0x7 + +#define RPC_SVC .byte 0x9a; .long 0; .word 0xf + +#define String .asciz +#define Value .word +#define Times(a,b) (a*b) +#define Divide(a,b) (a/b) + +#define INB inb %dx, %al +#define OUTB outb %al, %dx +#define INL inl %dx, %eax +#define OUTL outl %eax, %dx + +#define data16 .byte 0x66 +#define addr16 .byte 0x67 + +#if !GPROF +#define MCOUNT + +#elif defined(__SHARED__) +#define MCOUNT ; .data;\ + .align ALIGN;\ + LBc(x, 8) .long 0;\ + .text;\ + Gpush;\ + Gload;\ + leal Gotoff(LBb(x,8)),%edx;\ + Egaddr(%eax,_mcount_ptr);\ + Gpop;\ + call *(%eax); + +#else /* !GPROF, !__SHARED__ */ +#define MCOUNT ; call mcount; +#endif /* GPROF */ + +#ifdef __ELF__ +#define ELF_FUNC(x) .type x,@function +#define ELF_DATA(x) .type x,@object +#define ELF_SIZE(x,s) .size x,s +#else +#define ELF_FUNC(x) +#define ELF_DATA(x) +#define ELF_SIZE(x,s) +#endif + +#define Entry(x) .globl EXT(x); ELF_FUNC(EXT(x)); .align FALIGN; LEXT(x) +#define ENTRY(x) Entry(x) MCOUNT +#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \ + ELF_FUNC(EXT(x)); ELF_FUNC(EXT(y)); \ + .align FALIGN; LEXT(x); LEXT(y) \ + MCOUNT +#if __STDC__ +#define ASENTRY(x) .globl x; .align FALIGN; x ## : ELF_FUNC(x) MCOUNT +#else +#define ASENTRY(x) .globl x; .align FALIGN; x: ELF_FUNC(x) MCOUNT +#endif /* __STDC__ */ + +#define DATA(x) .globl EXT(x); ELF_DATA(EXT(x)); .align ALIGN; LEXT(x) + +#define End(x) ELF_SIZE(x,.-x) +#define END(x) End(EXT(x)) +#define ENDDATA(x) END(x) +#define Enddata(x) End(x) + +/* + * ELF shared library accessor macros. + * Gpush saves the %ebx register used for the GOT address + * Gpop pops %ebx if we need a GOT + * Gload loads %ebx with the GOT address if shared libraries are used + * Gcall calls an external function. + * Gotoff allows you to reference local labels. + * Gotoff2 allows you to reference local labels with an index reg. + * Gotoff3 allows you to reference local labels with an index reg & size. + * Gaddr loads up a register with an address of an external item. + * Gstack is the number of bytes that Gpush pushes on the stack. + * + * Varients of the above with E or L prefixes do EXT(name) or LCL(name) + * respectively. + */ + +#ifndef __SHARED__ +#define Gpush +#define Gpop +#define Gload +#define Gcall(func) call func +#define Gotoff(lab) lab +#define Gotoff2(l,r) l(r) +#define Gotoff3(l,r,s) l(,r,s) +#define Gaddr(to,lab) movl $lab,to +#define Gcmp(lab,reg) cmpl $lab,reg +#define Gmemload(lab,reg) movl lab,reg +#define Gmemstore(reg,lab,tmp) movl reg,lab +#define Gstack 0 + +#else +#ifdef __ELF__ /* ELF shared libraries */ +#define Gpush pushl %ebx +#define Gpop popl %ebx +#define Gload call 9f; 9: popl %ebx; addl $_GLOBAL_OFFSET_TABLE_+[.-9b],%ebx +#define Gcall(func) call EXT(func)@PLT +#define Gotoff(lab) lab@GOTOFF(%ebx) +#define Gotoff2(l,r) l@GOTOFF(%ebx,r) +#define Gotoff3(l,r,s) l@GOTOFF(%ebx,r,s) +#define Gaddr(to,lab) movl lab@GOT(%ebx),to +#define Gcmp(lab,reg) cmpl reg,lab@GOT(%ebx) +#define Gmemload(lab,reg) movl lab@GOT(%ebx),reg; movl (reg),reg +#define Gmemstore(reg,lab,tmp) movl lab@GOT(%ebx),tmp; movl reg,(tmp) +#define Gstack 4 + +#else /* ROSE shared libraries */ +#define Gpush +#define Gpop +#define Gload +#define Gcall(func) call *9f; .data; .align ALIGN; 9: .long func; .text +#define Gotoff(lab) lab +#define Gotoff2(l,r) l(r) +#define Gotoff3(l,r,s) l(,r,s) +#define Gaddr(to,lab) movl 9f,to; .data; .align ALIGN; 9: .long lab; .text +#define Gcmp(lab,reg) cmpl reg,9f; .data; .align ALIGN; 9: .long lab; .text +#define Gmemload(lab,reg) movl 9f,reg; movl (reg),reg; .data; .align ALIGN; 9: .long lab; .text +#define Gmemstore(reg,lab,tmp) movl 9f,tmp; movl reg,(tmp); .data; .align ALIGN; 9: .long lab; .text +#define Gstack 0 +#endif /* __ELF__ */ +#endif /* __SHARED__ */ + +/* Egotoff is not provided, since external symbols should not use @GOTOFF + relocations. */ +#define Egcall(func) Gcall(EXT(func)) +#define Egaddr(to,lab) Gaddr(to,EXT(lab)) +#define Egcmp(lab,reg) Gcmp(EXT(lab),reg) +#define Egmemload(lab,reg) Gmemload(EXT(lab),reg) +#define Egmemstore(reg,lab,tmp) Gmemstore(reg,EXT(lab),tmp) + +#define Lgotoff(lab) Gotoff(LCL(lab)) +#define Lgotoff2(l,r) Gotoff2(LCL(l),r) +#define Lgotoff3(l,r,s) Gotoff3(LCL(l),r,s) +#define Lgcmp(lab,reg) Gcmp(LCL(lab),reg) +#define Lgmemload(lab,reg) movl Lgotoff(lab),reg +#define Lgmemstore(reg,lab,tmp) movl reg,Lgotoff(lab) + +#ifdef ASSEMBLER +#if MACH_KDB +#include +/* + * This pseudo-assembler line is added so that there will be at least + * one N_SO entry in the symbol stable to define the current file name. + */ +#endif /* MACH_KDB */ + +#else /* NOT ASSEMBLER */ + +/* These defines are here for .c files that wish to reference global symbols + * within __asm__ statements. + */ +#ifndef __NO_UNDERSCORES__ +#define CC_SYM_PREFIX "_" +#else +#define CC_SYM_PREFIX "" +#endif /* __NO_UNDERSCORES__ */ +#endif /* ASSEMBLER */ + +/* + * The following macros make calls into C code. + * They dynamically align the stack to 16 bytes. + */ +#if defined(__i386__) +/* + * Arguments are moved (not pushed) onto the correctly aligned stack. + * NOTE: ESI is destroyed in the process, and hence cannot + * be directly used as a parameter. Users of this macro must + * independently preserve ESI (a non-volatile) if the routine is + * intended to be called from C, for instance. + */ + +#define CCALL(fn) \ + movl %esp, %esi ;\ + andl $0xFFFFFFF0, %esp ;\ + call EXT(fn) ;\ + movl %esi, %esp + +#define CCALL1(fn, arg1) \ + movl %esp, %esi ;\ + subl $4, %esp ;\ + andl $0xFFFFFFF0, %esp ;\ + movl arg1, (%esp) ;\ + call EXT(fn) ;\ + movl %esi, %esp + +#define CCALL2(fn, arg1, arg2) \ + movl %esp, %esi ;\ + subl $8, %esp ;\ + andl $0xFFFFFFF0, %esp ;\ + movl arg2, 4(%esp) ;\ + movl arg1, (%esp) ;\ + call EXT(fn) ;\ + movl %esi, %esp + +/* This variant exists to permit adjustment of the stack by "dtrace" */ +#define CCALL1WITHSP(fn, arg1) \ + movl %esp, %esi ;\ + subl $12, %esp ;\ + andl $0xFFFFFFF0, %esp ;\ + movl %esi, 8(%esp) ;\ + leal 8(%esp), %esi ;\ + movl %esi, 4(%esp) ;\ + movl arg1, (%esp) ;\ + call EXT(fn) ;\ + movl 8(%esp), %esp + +/* + * CCALL5 is used for callee functions with 3 arguments but + * where arg2 (a3:a2) and arg3 (a5:a4) are 64-bit values. + */ +#define CCALL5(fn, a1, a2, a3, a4, a5) \ + movl %esp, %esi ;\ + subl $20, %esp ;\ + andl $0xFFFFFFF0, %esp ;\ + movl a5, 16(%esp) ;\ + movl a4, 12(%esp) ;\ + movl a3, 8(%esp) ;\ + movl a2, 4(%esp) ;\ + movl a1, (%esp) ;\ + call EXT(fn) ;\ + movl %esi, %esp + +#elif defined(__x86_64__) + +/* This variant exists to permit adjustment of the stack by "dtrace" */ +#define CCALLWITHSP(fn) \ + mov %rsp, %r12 ;\ + sub $8, %rsp ;\ + and $0xFFFFFFFFFFFFFFF0, %rsp ;\ + mov %r12, (%rsp) ;\ + leaq (%rsp), %rsi ;\ + call EXT(fn) ;\ + mov (%rsp), %rsp + +#define CCALL(fn) \ + mov %rsp, %r12 ;\ + and $0xFFFFFFFFFFFFFFF0, %rsp ;\ + call EXT(fn) ;\ + mov %r12, %rsp + +#define CCALL1(fn, arg1) \ + mov arg1, %rdi ;\ + CCALL(fn) + +#define CCALL2(fn, arg1, arg2) \ + mov arg1, %rdi ;\ + CCALL(fn) + +#define CCALL3(fn, arg1, arg2, arg3) \ + mov arg1, %rdi ;\ + mov arg2, %rsi ;\ + mov arg3, %rdx ;\ + CCALL(fn) + +#else +#error unsupported architecture +#endif + +#endif /* _I386_ASM_H_ */ diff --git a/openlibm/i387/s_ceil.S b/openlibm/i387/s_ceil.S new file mode 100644 index 0000000000..80de70ad53 --- /dev/null +++ b/openlibm/i387/s_ceil.S @@ -0,0 +1,34 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include + +ENTRY(ceil) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0800,%dx /* round towards +oo */ + andw $0xfbff,%dx + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldl 8(%ebp); /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(ceil) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_ceilf.S b/openlibm/i387/s_ceilf.S new file mode 100644 index 0000000000..7ea898a467 --- /dev/null +++ b/openlibm/i387/s_ceilf.S @@ -0,0 +1,36 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_ceilf.S,v 1.4 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_ceilf.S,v 1.3 1995/05/08 23:52:44 jtc Exp $") */ + +ENTRY(ceilf) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0800,%dx /* round towards +oo */ + andw $0xfbff,%dx + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + flds 8(%ebp); /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(ceilf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_ceill.S b/openlibm/i387/s_ceill.S new file mode 100644 index 0000000000..838edd1bc4 --- /dev/null +++ b/openlibm/i387/s_ceill.S @@ -0,0 +1,34 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_ceill.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(ceill) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0800,%dx /* round towards +oo */ + andw $0xfbff,%dx + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldt 8(%ebp) /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(ceill) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_copysign.S b/openlibm/i387/s_copysign.S new file mode 100644 index 0000000000..4386747bea --- /dev/null +++ b/openlibm/i387/s_copysign.S @@ -0,0 +1,25 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_copysign.S,v 1.9 2011/01/07 16:13:12 kib Exp $") + +ENTRY(copysign) + movl 16(%esp),%edx + andl $0x80000000,%edx + movl 8(%esp),%eax + andl $0x7fffffff,%eax + orl %edx,%eax + movl %eax,8(%esp) + fldl 4(%esp) + ret +END(copysign) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_copysignf.S b/openlibm/i387/s_copysignf.S new file mode 100644 index 0000000000..5d0169b52c --- /dev/null +++ b/openlibm/i387/s_copysignf.S @@ -0,0 +1,26 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_copysignf.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_copysignf.S,v 1.3 1995/05/08 23:53:25 jtc Exp $") */ + +ENTRY(copysignf) + movl 8(%esp),%edx + andl $0x80000000,%edx + movl 4(%esp),%eax + andl $0x7fffffff,%eax + orl %edx,%eax + movl %eax,4(%esp) + flds 4(%esp) + ret +END(copysignf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_copysignl.S b/openlibm/i387/s_copysignl.S new file mode 100644 index 0000000000..1a9a06ada5 --- /dev/null +++ b/openlibm/i387/s_copysignl.S @@ -0,0 +1,24 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_copysignl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(copysignl) + movl 24(%esp),%edx + andl $0x8000,%edx + movl 12(%esp),%eax + andl $0x7fff,%eax + orl %edx,%eax + movl %eax,12(%esp) + fldt 4(%esp) + ret +END(copysignl) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_cos.S b/openlibm/i387/s_cos.S new file mode 100644 index 0000000000..4e29ec36de --- /dev/null +++ b/openlibm/i387/s_cos.S @@ -0,0 +1,33 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_cos.S,v 1.9 2011/01/07 16:13:12 kib Exp $") + +ENTRY(cos) + fldl 4(%esp) + fcos + fnstsw %ax + andw $0x400,%ax + jnz 1f + ret +1: fldpi + fadd %st(0) + fxch %st(1) +2: fprem1 + fnstsw %ax + andw $0x400,%ax + jnz 2b + fstp %st(1) + fcos + ret +END(cos) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_floor.S b/openlibm/i387/s_floor.S new file mode 100644 index 0000000000..13097eb7ff --- /dev/null +++ b/openlibm/i387/s_floor.S @@ -0,0 +1,35 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_floor.S,v 1.10 2011/01/07 16:13:12 kib Exp $") + +ENTRY(floor) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0400,%dx /* round towards -oo */ + andw $0xf7ff,%dx + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldl 8(%ebp); /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(floor) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_floorf.S b/openlibm/i387/s_floorf.S new file mode 100644 index 0000000000..c3a4e09530 --- /dev/null +++ b/openlibm/i387/s_floorf.S @@ -0,0 +1,36 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_floorf.S,v 1.4 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_floorf.S,v 1.3 1995/05/09 00:04:32 jtc Exp $") */ + +ENTRY(floorf) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0400,%dx /* round towards -oo */ + andw $0xf7ff,%dx + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + flds 8(%ebp); /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(floorf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_floorl.S b/openlibm/i387/s_floorl.S new file mode 100644 index 0000000000..949116fe87 --- /dev/null +++ b/openlibm/i387/s_floorl.S @@ -0,0 +1,34 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_floorl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(floorl) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0400,%dx /* round towards -oo */ + andw $0xf7ff,%dx + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldt 8(%ebp) /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(floorl) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_llrint.S b/openlibm/i387/s_llrint.S new file mode 100644 index 0000000000..97c292e41e --- /dev/null +++ b/openlibm/i387/s_llrint.S @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_llrint.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(llrint) + fldl 4(%esp) + subl $8,%esp + fistpll (%esp) + popl %eax + popl %edx + ret +END(llrint) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_llrintf.S b/openlibm/i387/s_llrintf.S new file mode 100644 index 0000000000..447e77aa3e --- /dev/null +++ b/openlibm/i387/s_llrintf.S @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_llrintf.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(llrintf) + flds 4(%esp) + subl $8,%esp + fistpll (%esp) + popl %eax + popl %edx + ret +END(llrintf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_llrintl.S b/openlibm/i387/s_llrintl.S new file mode 100644 index 0000000000..ad6a7c978b --- /dev/null +++ b/openlibm/i387/s_llrintl.S @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_llrintl.S,v 1.2 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(llrintl) + fldt 4(%esp) + subl $8,%esp + fistpll (%esp) + popl %eax + popl %edx + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_logb.S b/openlibm/i387/s_logb.S new file mode 100644 index 0000000000..d0fc5d60e4 --- /dev/null +++ b/openlibm/i387/s_logb.S @@ -0,0 +1,21 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_logb.S,v 1.10 2011/01/07 16:13:12 kib Exp $") + +ENTRY(logb) + fldl 4(%esp) + fxtract + fstp %st + ret +END(logb) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_logbf.S b/openlibm/i387/s_logbf.S new file mode 100644 index 0000000000..3e3568cad0 --- /dev/null +++ b/openlibm/i387/s_logbf.S @@ -0,0 +1,22 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_logbf.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_logbf.S,v 1.3 1995/05/09 00:15:12 jtc Exp $") */ + +ENTRY(logbf) + flds 4(%esp) + fxtract + fstp %st + ret +END(logbf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_logbl.S b/openlibm/i387/s_logbl.S new file mode 100644 index 0000000000..6d2de64b42 --- /dev/null +++ b/openlibm/i387/s_logbl.S @@ -0,0 +1,20 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_logbl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(logbl) + fldt 4(%esp) + fxtract + fstp %st + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_lrint.S b/openlibm/i387/s_lrint.S new file mode 100644 index 0000000000..1e125390b7 --- /dev/null +++ b/openlibm/i387/s_lrint.S @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_lrint.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(lrint) + fldl 4(%esp) + subl $4,%esp + fistpl (%esp) + popl %eax + ret +END(lrint) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_lrintf.S b/openlibm/i387/s_lrintf.S new file mode 100644 index 0000000000..b588c1a7c8 --- /dev/null +++ b/openlibm/i387/s_lrintf.S @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_lrintf.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(lrintf) + flds 4(%esp) + subl $4,%esp + fistpl (%esp) + popl %eax + ret +END(lrintf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_lrintl.S b/openlibm/i387/s_lrintl.S new file mode 100644 index 0000000000..94cc6cbef9 --- /dev/null +++ b/openlibm/i387/s_lrintl.S @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_lrintl.S,v 1.2 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(lrintl) + fldt 4(%esp) + subl $4,%esp + fistpl (%esp) + popl %eax + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_remquo.S b/openlibm/i387/s_remquo.S new file mode 100644 index 0000000000..78a588d89b --- /dev/null +++ b/openlibm/i387/s_remquo.S @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on public-domain remainder routine by J.T. Conklin . + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_remquo.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(remquo) + fldl 12(%esp) + fldl 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) +/* Extract the three low-order bits of the quotient from C0,C3,C1. */ + shrl $6,%eax + movl %eax,%ecx + andl $0x108,%eax + rorl $7,%eax + orl %eax,%ecx + roll $4,%eax + orl %ecx,%eax + andl $7,%eax +/* Negate the quotient bits if x*y<0. Avoid using an unpredictable branch. */ + movl 16(%esp),%ecx + xorl 8(%esp),%ecx + sarl $16,%ecx + sarl $16,%ecx + xorl %ecx,%eax + andl $1,%ecx + addl %ecx,%eax +/* Store the quotient and return. */ + movl 20(%esp),%ecx + movl %eax,(%ecx) + ret +END(remquo) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_remquof.S b/openlibm/i387/s_remquof.S new file mode 100644 index 0000000000..8f532c3b6a --- /dev/null +++ b/openlibm/i387/s_remquof.S @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on public-domain remainder routine by J.T. Conklin . + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_remquof.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(remquof) + flds 8(%esp) + flds 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) +/* Extract the three low-order bits of the quotient from C0,C3,C1. */ + shrl $6,%eax + movl %eax,%ecx + andl $0x108,%eax + rorl $7,%eax + orl %eax,%ecx + roll $4,%eax + orl %ecx,%eax + andl $7,%eax +/* Negate the quotient bits if x*y<0. Avoid using an unpredictable branch. */ + movl 8(%esp),%ecx + xorl 4(%esp),%ecx + sarl $16,%ecx + sarl $16,%ecx + xorl %ecx,%eax + andl $1,%ecx + addl %ecx,%eax +/* Store the quotient and return. */ + movl 12(%esp),%ecx + movl %eax,(%ecx) + ret +END(remquof) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_remquol.S b/openlibm/i387/s_remquol.S new file mode 100644 index 0000000000..b89563c892 --- /dev/null +++ b/openlibm/i387/s_remquol.S @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2005-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Based on public-domain remainder routine by J.T. Conklin . + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_remquol.S,v 1.2 2011/01/07 16:13:12 kib Exp $"); + +ENTRY(remquol) + fldt 16(%esp) + fldt 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) +/* Extract the three low-order bits of the quotient from C0,C3,C1. */ + shrl $6,%eax + movl %eax,%ecx + andl $0x108,%eax + rorl $7,%eax + orl %eax,%ecx + roll $4,%eax + orl %ecx,%eax + andl $7,%eax +/* Negate the quotient bits if x*y<0. Avoid using an unpredictable branch. */ + movl 24(%esp),%ecx + xorl 12(%esp),%ecx + movsx %cx,%ecx + sarl $16,%ecx + sarl $16,%ecx + xorl %ecx,%eax + andl $1,%ecx + addl %ecx,%eax +/* Store the quotient and return. */ + movl 28(%esp),%ecx + movl %eax,(%ecx) + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_rint.S b/openlibm/i387/s_rint.S new file mode 100644 index 0000000000..a8109cd405 --- /dev/null +++ b/openlibm/i387/s_rint.S @@ -0,0 +1,20 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_rint.S,v 1.9 2011/01/07 16:13:12 kib Exp $") + +ENTRY(rint) + fldl 4(%esp) + frndint + ret +END(rint) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_rintf.S b/openlibm/i387/s_rintf.S new file mode 100644 index 0000000000..184a16f80b --- /dev/null +++ b/openlibm/i387/s_rintf.S @@ -0,0 +1,21 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_rintf.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_rintf.S,v 1.3 1995/05/09 00:17:22 jtc Exp $") */ + +ENTRY(rintf) + flds 4(%esp) + frndint + ret +END(rintf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_rintl.S b/openlibm/i387/s_rintl.S new file mode 100644 index 0000000000..8727322a9b --- /dev/null +++ b/openlibm/i387/s_rintl.S @@ -0,0 +1,19 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_rintl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(rintl) + fldt 4(%esp) + frndint + ret + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_scalbn.S b/openlibm/i387/s_scalbn.S new file mode 100644 index 0000000000..d690ff1314 --- /dev/null +++ b/openlibm/i387/s_scalbn.S @@ -0,0 +1,23 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_scalbn.S,v 1.10 2011/01/07 16:13:12 kib Exp $") + +ENTRY(scalbn) + fildl 12(%esp) + fldl 4(%esp) + fscale + fstp %st(1) + ret +END(scalbn) + +.globl CNAME(ldexp) +.set CNAME(ldexp),CNAME(scalbn) +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_scalbnf.S b/openlibm/i387/s_scalbnf.S new file mode 100644 index 0000000000..7f4a2bc61a --- /dev/null +++ b/openlibm/i387/s_scalbnf.S @@ -0,0 +1,26 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_scalbnf.S,v 1.4 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_scalbnf.S,v 1.4 1999/01/02 05:15:40 kristerw Exp $") */ + +ENTRY(scalbnf) + fildl 8(%esp) + flds 4(%esp) + fscale + fstp %st(1) /* bug fix for fp stack overflow */ + ret +END(scalbnf) + +.globl CNAME(ldexpf) +.set CNAME(ldexpf),CNAME(scalbnf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_scalbnl.S b/openlibm/i387/s_scalbnl.S new file mode 100644 index 0000000000..5d359eba52 --- /dev/null +++ b/openlibm/i387/s_scalbnl.S @@ -0,0 +1,26 @@ +/* + * Written by J.T. Conklin . + * Public domain. + */ + +#include + +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_scalbnl.S,v 1.3 2011/01/07 16:13:12 kib Exp $"); +/* RCSID("$NetBSD: s_scalbnf.S,v 1.4 1999/01/02 05:15:40 kristerw Exp $") */ + +ENTRY(scalbnl) + fildl 16(%esp) + fldt 4(%esp) + fscale + fstp %st(1) + ret +END(scalbnl) + +.globl CNAME(ldexpl) +.set CNAME(ldexpl),CNAME(scalbnl) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_sin.S b/openlibm/i387/s_sin.S new file mode 100644 index 0000000000..6b714550e4 --- /dev/null +++ b/openlibm/i387/s_sin.S @@ -0,0 +1,33 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_sin.S,v 1.9 2011/01/07 16:13:12 kib Exp $") + +ENTRY(sin) + fldl 4(%esp) + fsin + fnstsw %ax + andw $0x400,%ax + jnz 1f + ret +1: fldpi + fadd %st(0) + fxch %st(1) +2: fprem1 + fnstsw %ax + andw $0x400,%ax + jnz 2b + fstp %st(1) + fsin + ret +END(sin) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_tan.S b/openlibm/i387/s_tan.S new file mode 100644 index 0000000000..7b25c3a7d4 --- /dev/null +++ b/openlibm/i387/s_tan.S @@ -0,0 +1,35 @@ +/* + * Written by: + * J.T. Conklin (jtc@netbsd.org) + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_tan.S,v 1.9 2011/01/07 16:13:12 kib Exp $") + +ENTRY(tan) + fldl 4(%esp) + fptan + fnstsw %ax + andw $0x400,%ax + jnz 1f + fstp %st(0) + ret +1: fldpi + fadd %st(0) + fxch %st(1) +2: fprem1 + fstsw %ax + andw $0x400,%ax + jnz 2b + fstp %st(1) + fptan + fstp %st(0) + ret +END(tan) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_trunc.S b/openlibm/i387/s_trunc.S new file mode 100644 index 0000000000..5422770d22 --- /dev/null +++ b/openlibm/i387/s_trunc.S @@ -0,0 +1,33 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_trunc.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(trunc) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0c00,%dx /* round towards -oo */ + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldl 8(%ebp) /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(trunc) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_truncf.S b/openlibm/i387/s_truncf.S new file mode 100644 index 0000000000..c9d32bb875 --- /dev/null +++ b/openlibm/i387/s_truncf.S @@ -0,0 +1,33 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_truncf.S,v 1.4 2011/01/07 16:13:12 kib Exp $") + +ENTRY(truncf) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0c00,%dx /* round towards -oo */ + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + flds 8(%ebp) /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(truncf) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/i387/s_truncl.S b/openlibm/i387/s_truncl.S new file mode 100644 index 0000000000..71526386c0 --- /dev/null +++ b/openlibm/i387/s_truncl.S @@ -0,0 +1,33 @@ +/* + * Based on code written by J.T. Conklin . + * Public domain. + */ + +#include +//__FBSDID("$FreeBSD: src/lib/msun/i387/s_truncl.S,v 1.3 2011/01/07 16:13:12 kib Exp $") + +ENTRY(truncl) + pushl %ebp + movl %esp,%ebp + subl $8,%esp + + fstcw -4(%ebp) /* store fpu control word */ + movw -4(%ebp),%dx + orw $0x0c00,%dx /* round towards -oo */ + movw %dx,-8(%ebp) + fldcw -8(%ebp) /* load modfied control word */ + + fldt 8(%ebp) /* round */ + frndint + + fldcw -4(%ebp) /* restore original control word */ + + leave + ret +END(truncl) + + +/* Enable stack protection */ +#if defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/openlibm/include/openlibm.h b/openlibm/include/openlibm.h new file mode 100644 index 0000000000..d85194538b --- /dev/null +++ b/openlibm/include/openlibm.h @@ -0,0 +1,8 @@ +#ifndef OPENLIBM_H +#define OPENLIBM_H + +#include +#include +#include + +#endif /* !OPENLIBM_H */ diff --git a/openlibm/include/openlibm_complex.h b/openlibm/include/openlibm_complex.h new file mode 100644 index 0000000000..7afc992e1f --- /dev/null +++ b/openlibm/include/openlibm_complex.h @@ -0,0 +1,179 @@ +/* $OpenBSD: complex.h,v 1.5 2014/03/16 18:38:30 guenther Exp $ */ +/* + * Copyright (c) 2008 Martynas Venckus + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef OPENLIBM_USE_HOST_COMPLEX_H +#include +#else /* !OPENLIBM_USE_HOST_COMPLEX_H */ + +#ifndef OPENLIBM_COMPLEX_H +#define OPENLIBM_COMPLEX_H + +#define complex _Complex + +#define _Complex_I 1.0fi +#define I _Complex_I + +/* + * Macros that can be used to construct complex values. + * + * The C99 standard intends x+I*y to be used for this, but x+I*y is + * currently unusable in general since gcc introduces many overflow, + * underflow, sign and efficiency bugs by rewriting I*y as + * (0.0+I)*(y+0.0*I) and laboriously computing the full complex product. + * In particular, I*Inf is corrupted to NaN+I*Inf, and I*-0 is corrupted + * to -0.0+I*0.0. + * + * In C11, a CMPLX(x,y) macro was added to circumvent this limitation, + * and gcc 4.7 added a __builtin_complex feature to simplify implementation + * of CMPLX in libc, so we can take advantage of these features if they + * are available. Clang simply allows complex values to be constructed + * using a compound literal. + * + * If __builtin_complex is not available, resort to using inline + * functions instead. These can unfortunately not be used to construct + * compile-time constants. + * + * C99 specifies that complex numbers have the same representation as + * an array of two elements, where the first element is the real part + * and the second element is the imaginary part. + */ + +#ifdef __clang__ +# define CMPLXF(x, y) ((float complex){x, y}) +# define CMPLX(x, y) ((double complex){x, y}) +# define CMPLXL(x, y) ((long double complex){x, y}) +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) && !defined(__INTEL_COMPILER) +# define CMPLXF(x,y) __builtin_complex ((float) (x), (float) (y)) +# define CMPLX(x,y) __builtin_complex ((double) (x), (double) (y)) +# define CMPLXL(x,y) __builtin_complex ((long double) (x), (long double) (y)) +#else +static inline float complex +CMPLXF(float x, float y) +{ + union { + float a[2]; + float complex f; + } z = {{ x, y }}; + + return (z.f); +} + +static inline double complex +CMPLX(double x, double y) +{ + union { + double a[2]; + double complex f; + } z = {{ x, y }}; + + return (z.f); +} + +static inline long double complex +CMPLXL(long double x, long double y) +{ + union { + long double a[2]; + long double complex f; + } z = {{ x, y }}; + + return (z.f); +} +#endif + +/* + * Double versions of C99 functions + */ +double complex cacos(double complex); +double complex casin(double complex); +double complex catan(double complex); +double complex ccos(double complex); +double complex csin(double complex); +double complex ctan(double complex); +double complex cacosh(double complex); +double complex casinh(double complex); +double complex catanh(double complex); +double complex ccosh(double complex); +double complex csinh(double complex); +double complex ctanh(double complex); +double complex cexp(double complex); +double complex clog(double complex); +double cabs(double complex); +double complex cpow(double complex, double complex); +double complex csqrt(double complex); +double carg(double complex); +double cimag(double complex); +double complex conj(double complex); +double complex cproj(double complex); +double creal(double complex); + +/* + * Float versions of C99 functions + */ +float complex cacosf(float complex); +float complex casinf(float complex); +float complex catanf(float complex); +float complex ccosf(float complex); +float complex csinf(float complex); +float complex ctanf(float complex); +float complex cacoshf(float complex); +float complex casinhf(float complex); +float complex catanhf(float complex); +float complex ccoshf(float complex); +float complex csinhf(float complex); +float complex ctanhf(float complex); +float complex cexpf(float complex); +float complex clogf(float complex); +float cabsf(float complex); +float complex cpowf(float complex, float complex); +float complex csqrtf(float complex); +float cargf(float complex); +float cimagf(float complex); +float complex conjf(float complex); +float complex cprojf(float complex); +float crealf(float complex); + +/* + * Long double versions of C99 functions + */ +long double complex cacosl(long double complex); +long double complex casinl(long double complex); +long double complex catanl(long double complex); +long double complex ccosl(long double complex); +long double complex csinl(long double complex); +long double complex ctanl(long double complex); +long double complex cacoshl(long double complex); +long double complex casinhl(long double complex); +long double complex catanhl(long double complex); +long double complex ccoshl(long double complex); +long double complex csinhl(long double complex); +long double complex ctanhl(long double complex); +long double complex cexpl(long double complex); +long double complex clogl(long double complex); +long double cabsl(long double complex); +long double complex cpowl(long double complex, + long double complex); +long double complex csqrtl(long double complex); +long double cargl(long double complex); +long double cimagl(long double complex); +long double complex conjl(long double complex); +long double complex cprojl(long double complex); +long double creall(long double complex); + +#endif /* !OPENLIBM_COMPLEX_H */ + +#endif /* OPENLIBM_USE_HOST_COMPLEX_H */ diff --git a/openlibm/include/openlibm_defs.h b/openlibm/include/openlibm_defs.h new file mode 100644 index 0000000000..8a6fa6b5bc --- /dev/null +++ b/openlibm/include/openlibm_defs.h @@ -0,0 +1,14 @@ +#ifndef OPENLIBM_DEFS_H_ +#define OPENLIBM_DEFS_H_ + +#ifdef _WIN32 +# ifdef IMPORT_EXPORTS +# define OLM_DLLEXPORT __declspec(dllimport) +# else +# define OLM_DLLEXPORT __declspec(dllexport) +# endif +#else +#define OLM_DLLEXPORT __attribute__ ((visibility("default"))) +#endif + +#endif // OPENLIBM_DEFS_H_ diff --git a/openlibm/include/openlibm_fenv.h b/openlibm/include/openlibm_fenv.h new file mode 100644 index 0000000000..35a5586f35 --- /dev/null +++ b/openlibm/include/openlibm_fenv.h @@ -0,0 +1,27 @@ +#ifdef OPENLIBM_USE_HOST_FENV_H +#include +#else /* !OPENLIBM_USE_HOST_FENV_H */ + +#if defined(__aarch64__) +#include +#elif defined(__arm__) +#include +#elif defined(__x86_64__) +#include +#elif defined(__i386__) +#include +#elif defined(__powerpc__) || defined(__POWERPC__) +#include +#elif defined(__mips__) +#include +#elif defined(__s390__) +#include +#elif defined(__riscv) +#include +#elif defined(__loongarch64) +#include +#else +#error "Unsupported platform" +#endif + +#endif /* OPENLIBM_USE_HOST_FENV_H */ diff --git a/openlibm/include/openlibm_fenv_aarch64.h b/openlibm/include/openlibm_fenv_aarch64.h new file mode 100644 index 0000000000..5cf0e49b0d --- /dev/null +++ b/openlibm/include/openlibm_fenv_aarch64.h @@ -0,0 +1,247 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include + +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +/* The high 32 bits contain fpcr, low 32 contain fpsr. */ +typedef uint64_t fenv_t; +typedef uint64_t fexcept_t; + +/* Exception flags */ +#define FE_INVALID 0x00000001 +#define FE_DIVBYZERO 0x00000002 +#define FE_OVERFLOW 0x00000004 +#define FE_UNDERFLOW 0x00000008 +#define FE_INEXACT 0x00000010 +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* + * Rounding modes + * + * We can't just use the hardware bit values here, because that would + * make FE_UPWARD and FE_DOWNWARD negative, which is not allowed. + */ +#define FE_TONEAREST 0x0 +#define FE_UPWARD 0x1 +#define FE_DOWNWARD 0x2 +#define FE_TOWARDZERO 0x3 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) +#define _ROUND_SHIFT 22 + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +/* We need to be able to map status flag positions to mask flag positions */ +#define _FPUSW_SHIFT 8 +#define _ENABLE_MASK (FE_ALL_EXCEPT << _FPUSW_SHIFT) + +#define __mrs_fpcr(__r) __asm __volatile("mrs %0, fpcr" : "=r" (__r)) +#define __msr_fpcr(__r) __asm __volatile("msr fpcr, %0" : : "r" (__r)) + +#define __mrs_fpsr(__r) __asm __volatile("mrs %0, fpsr" : "=r" (__r)) +#define __msr_fpsr(__r) __asm __volatile("msr fpsr, %0" : : "r" (__r)) + + +__fenv_static inline int +feclearexcept(int __excepts) +{ + fexcept_t __r; + + __mrs_fpsr(__r); + __r &= ~__excepts; + __msr_fpsr(__r); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + fexcept_t __r; + + __mrs_fpsr(__r); + *__flagp = __r & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + fexcept_t __r; + + __mrs_fpsr(__r); + __r &= ~__excepts; + __r |= *__flagp & __excepts; + __msr_fpsr(__r); + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + fexcept_t __r; + + __mrs_fpsr(__r); + __r |= __excepts; + __msr_fpsr(__r); + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + fexcept_t __r; + + __mrs_fpsr(__r); + return (__r & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + fenv_t __r; + + __mrs_fpcr(__r); + return ((__r >> _ROUND_SHIFT) & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + fenv_t __r; + + if (__round & ~_ROUND_MASK) + return (-1); + __mrs_fpcr(__r); + __r &= ~(_ROUND_MASK << _ROUND_SHIFT); + __r |= __round << _ROUND_SHIFT; + __msr_fpcr(__r); + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + uint64_t fpcr; + uint64_t fpsr; + + __mrs_fpcr(fpcr); + __mrs_fpsr(fpsr); + *__envp = fpsr | (fpcr << 32); + + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + fenv_t __r; + + __mrs_fpcr(__r); + *__envp = __r << 32; + __r &= ~(_ENABLE_MASK); + __msr_fpcr(__r); + + __mrs_fpsr(__r); + *__envp |= (uint32_t)__r; + __r &= ~(_ENABLE_MASK); + __msr_fpsr(__r); + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + + __msr_fpcr((*__envp) >> 32); + __msr_fpsr((fenv_t)(uint32_t)*__envp); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + fexcept_t __r; + + __mrs_fpsr(__r); + fesetenv(__envp); + feraiseexcept(__r & FE_ALL_EXCEPT); + return (0); +} + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +static inline int +feenableexcept(int __mask) +{ + fenv_t __old_r, __new_r; + + __mrs_fpcr(__old_r); + __new_r = __old_r | ((__mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT); + __msr_fpcr(__new_r); + return ((__old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT); +} + +static inline int +fedisableexcept(int __mask) +{ + fenv_t __old_r, __new_r; + + __mrs_fpcr(__old_r); + __new_r = __old_r & ~((__mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT); + __msr_fpcr(__new_r); + return ((__old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT); +} + +static inline int +fegetexcept(void) +{ + fenv_t __r; + + __mrs_fpcr(__r); + return ((__r & _ENABLE_MASK) >> _FPUSW_SHIFT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_amd64.h b/openlibm/include/openlibm_fenv_amd64.h new file mode 100644 index 0000000000..c6db210e49 --- /dev/null +++ b/openlibm/include/openlibm_fenv_amd64.h @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/amd64/fenv.h,v 1.8 2011/10/10 15:43:09 das Exp $ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include +#include "cdefs-compat.h" +#include "types-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef struct { + struct { + uint32_t __control; + uint32_t __status; + uint32_t __tag; + char __other[16]; + } __x87; + uint32_t __mxcsr; +} fenv_t; + +typedef uint16_t fexcept_t; + +/* Exception flags */ +#define FE_INVALID 0x01 +#define FE_DENORMAL 0x02 +#define FE_DIVBYZERO 0x04 +#define FE_OVERFLOW 0x08 +#define FE_UNDERFLOW 0x10 +#define FE_INEXACT 0x20 +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_DENORMAL | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_DOWNWARD 0x0400 +#define FE_UPWARD 0x0800 +#define FE_TOWARDZERO 0x0c00 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +/* + * As compared to the x87 control word, the SSE unit's control word + * has the rounding control bits offset by 3 and the exception mask + * bits offset by 7. + */ +#define _SSE_ROUND_SHIFT 3 +#define _SSE_EMASK_SHIFT 7 + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +#define __fldcw(__cw) __asm __volatile("fldcw %0" : : "m" (__cw)) +#define __fldenv(__env) __asm __volatile("fldenv %0" : : "m" (__env)) +#define __fldenvx(__env) __asm __volatile("fldenv %0" : : "m" (__env) \ + : "st", "st(1)", "st(2)", "st(3)", "st(4)", \ + "st(5)", "st(6)", "st(7)") +#define __fnclex() __asm __volatile("fnclex") +#define __fnstenv(__env) __asm __volatile("fnstenv %0" : "=m" (*(__env))) +#define __fnstcw(__cw) __asm __volatile("fnstcw %0" : "=m" (*(__cw))) +#define __fnstsw(__sw) __asm __volatile("fnstsw %0" : "=am" (*(__sw))) +#define __fwait() __asm __volatile("fwait") +#define __ldmxcsr(__csr) __asm __volatile("ldmxcsr %0" : : "m" (__csr)) +#define __stmxcsr(__csr) __asm __volatile("stmxcsr %0" : "=m" (*(__csr))) + +__fenv_static __attribute__((always_inline)) inline int +feclearexcept(int __excepts) +{ + fenv_t __env; + + if (__excepts == FE_ALL_EXCEPT) { + __fnclex(); + } else { + __fnstenv(&__env.__x87); + __env.__x87.__status &= ~__excepts; + __fldenv(__env.__x87); + } + __stmxcsr(&__env.__mxcsr); + __env.__mxcsr &= ~__excepts; + __ldmxcsr(__env.__mxcsr); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + uint32_t __mxcsr; + uint16_t __status; + + __stmxcsr(&__mxcsr); + __fnstsw(&__status); + *__flagp = (__mxcsr | __status) & __excepts; + return (0); +} + +OLM_DLLEXPORT int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +OLM_DLLEXPORT int feraiseexcept(int __excepts); + +__fenv_static __attribute__((always_inline)) inline int +fetestexcept(int __excepts) +{ + uint32_t __mxcsr; + uint16_t __status; + + __stmxcsr(&__mxcsr); + __fnstsw(&__status); + return ((__status | __mxcsr) & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + uint16_t __control; + + /* + * We assume that the x87 and the SSE unit agree on the + * rounding mode. Reading the control word on the x87 turns + * out to be about 5 times faster than reading it on the SSE + * unit on an Opteron 244. + */ + __fnstcw(&__control); + return (__control & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + uint32_t __mxcsr; + uint16_t __control; + + if (__round & ~_ROUND_MASK) + return (-1); + + __fnstcw(&__control); + __control &= ~_ROUND_MASK; + __control |= __round; + __fldcw(__control); + + __stmxcsr(&__mxcsr); + __mxcsr &= ~(_ROUND_MASK << _SSE_ROUND_SHIFT); + __mxcsr |= __round << _SSE_ROUND_SHIFT; + __ldmxcsr(__mxcsr); + + return (0); +} + +OLM_DLLEXPORT int fegetenv(fenv_t *__envp); +OLM_DLLEXPORT int feholdexcept(fenv_t *__envp); + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + + /* + * XXX Using fldenvx() instead of fldenv() tells the compiler that this + * instruction clobbers the i387 register stack. This happens because + * we restore the tag word from the saved environment. Normally, this + * would happen anyway and we wouldn't care, because the ABI allows + * function calls to clobber the i387 regs. However, fesetenv() is + * inlined, so we need to be more careful. + */ + __fldenvx(__envp->__x87); + __ldmxcsr(__envp->__mxcsr); + return (0); +} + +OLM_DLLEXPORT int feupdateenv(const fenv_t *__envp); + +#if __BSD_VISIBLE + +OLM_DLLEXPORT int feenableexcept(int __mask); +OLM_DLLEXPORT int fedisableexcept(int __mask); + +/* We currently provide no external definition of fegetexcept(). */ +static inline int +fegetexcept(void) +{ + uint16_t __control; + + /* + * We assume that the masks for the x87 and the SSE unit are + * the same. + */ + __fnstcw(&__control); + return (~__control & FE_ALL_EXCEPT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_arm.h b/openlibm/include/openlibm_fenv_arm.h new file mode 100644 index 0000000000..8223a01d8e --- /dev/null +++ b/openlibm/include/openlibm_fenv_arm.h @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/arm/fenv.h,v 1.6 2011/10/10 15:43:09 das Exp $ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include + +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef uint32_t fenv_t; +typedef uint32_t fexcept_t; + +/* Exception flags */ +#define FE_INVALID 0x0001 +#define FE_DIVBYZERO 0x0002 +#define FE_OVERFLOW 0x0004 +#define FE_UNDERFLOW 0x0008 +#define FE_INEXACT 0x0010 +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_TOWARDZERO 0x0001 +#define FE_UPWARD 0x0002 +#define FE_DOWNWARD 0x0003 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +/* We need to be able to map status flag positions to mask flag positions */ +#define _FPUSW_SHIFT 16 +#define _ENABLE_MASK (FE_ALL_EXCEPT << _FPUSW_SHIFT) + +/* Test for hardware support for ARM floating point operations, explicitly +checking for float and double support, see "ARM C Language Extensions", 6.5.1 */ +#if defined(__ARM_FP) && (__ARM_FP & 0x0C) != 0 +#define __rfs(__fpsr) __asm __volatile("vmrs %0,fpscr" : "=&r" (*(__fpsr))) +#define __wfs(__fpsr) __asm __volatile("vmsr fpscr,%0" : : "r" (__fpsr)) +#else +#define __rfs(__fpsr) (*(__fpsr) = 0) +#define __wfs(__fpsr) +#endif + +__fenv_static inline int +feclearexcept(int __excepts) +{ + fexcept_t __fpsr; + + __rfs(&__fpsr); + __fpsr &= ~__excepts; + __wfs(__fpsr); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + fexcept_t __fpsr; + + __rfs(&__fpsr); + *__flagp = __fpsr & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + fexcept_t __fpsr; + + __rfs(&__fpsr); + __fpsr &= ~__excepts; + __fpsr |= *__flagp & __excepts; + __wfs(__fpsr); + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + fexcept_t __ex = __excepts; + + fesetexceptflag(&__ex, __excepts); /* XXX */ + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + fexcept_t __fpsr; + + __rfs(&__fpsr); + return (__fpsr & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + + /* + * Apparently, the rounding mode is specified as part of the + * instruction format on ARM, so the dynamic rounding mode is + * indeterminate. Some FPUs may differ. + */ + return (-1); +} + +__fenv_static inline int +fesetround(int __round) +{ + + return (-1); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + + __rfs(__envp); + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + fenv_t __env; + + __rfs(&__env); + *__envp = __env; + __env &= ~(FE_ALL_EXCEPT | _ENABLE_MASK); + __wfs(__env); + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + + __wfs(*__envp); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + fexcept_t __fpsr; + + __rfs(&__fpsr); + __wfs(*__envp); + feraiseexcept(__fpsr & FE_ALL_EXCEPT); + return (0); +} + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +static inline int +feenableexcept(int __mask) +{ + fenv_t __old_fpsr, __new_fpsr; + + __rfs(&__old_fpsr); + __new_fpsr = __old_fpsr | (__mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT; + __wfs(__new_fpsr); + return ((__old_fpsr >> _FPUSW_SHIFT) & FE_ALL_EXCEPT); +} + +static inline int +fedisableexcept(int __mask) +{ + fenv_t __old_fpsr, __new_fpsr; + + __rfs(&__old_fpsr); + __new_fpsr = __old_fpsr & ~((__mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT); + __wfs(__new_fpsr); + return ((__old_fpsr >> _FPUSW_SHIFT) & FE_ALL_EXCEPT); +} + +static inline int +fegetexcept(void) +{ + fenv_t __fpsr; + + __rfs(&__fpsr); + return ((__fpsr & _ENABLE_MASK) >> _FPUSW_SHIFT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_i387.h b/openlibm/include/openlibm_fenv_i387.h new file mode 100644 index 0000000000..ec4eb87cab --- /dev/null +++ b/openlibm/include/openlibm_fenv_i387.h @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/i387/fenv.h,v 1.8 2011/10/10 15:43:09 das Exp $ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include "openlibm_defs.h" +#include "cdefs-compat.h" +#include "types-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +/* + * To preserve binary compatibility with FreeBSD 5.3, we pack the + * mxcsr into some reserved fields, rather than changing sizeof(fenv_t). + */ +typedef struct { + uint16_t __control; + uint16_t __mxcsr_hi; + uint16_t __status; + uint16_t __mxcsr_lo; + uint32_t __tag; + char __other[16]; +} fenv_t; + +#define __get_mxcsr(env) (((env).__mxcsr_hi << 16) | \ + ((env).__mxcsr_lo)) +#define __set_mxcsr(env, x) do { \ + (env).__mxcsr_hi = (uint32_t)(x) >> 16; \ + (env).__mxcsr_lo = (uint16_t)(x); \ +} while (0) + +typedef uint16_t fexcept_t; + +/* Exception flags */ +#define FE_INVALID 0x01 +#define FE_DENORMAL 0x02 +#define FE_DIVBYZERO 0x04 +#define FE_OVERFLOW 0x08 +#define FE_UNDERFLOW 0x10 +#define FE_INEXACT 0x20 +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_DENORMAL | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_DOWNWARD 0x0400 +#define FE_UPWARD 0x0800 +#define FE_TOWARDZERO 0x0c00 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +/* + * As compared to the x87 control word, the SSE unit's control word + * has the rounding control bits offset by 3 and the exception mask + * bits offset by 7. + */ +#define _SSE_ROUND_SHIFT 3 +#define _SSE_EMASK_SHIFT 7 + +__BEGIN_DECLS + +/* After testing for SSE support once, we cache the result in __has_sse. */ +enum __sse_support { __SSE_YES, __SSE_NO, __SSE_UNK }; +OLM_DLLEXPORT extern enum __sse_support __has_sse; +OLM_DLLEXPORT int __test_sse(void); +#ifdef __SSE__ +#define __HAS_SSE() 1 +#else +#define __HAS_SSE() (__has_sse == __SSE_YES || \ + (__has_sse == __SSE_UNK && __test_sse())) +#endif + +/* Default floating-point environment */ +OLM_DLLEXPORT extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +#define __fldcw(__cw) __asm __volatile("fldcw %0" : : "m" (__cw)) +#define __fldenv(__env) __asm __volatile("fldenv %0" : : "m" (__env)) +#define __fldenvx(__env) __asm __volatile("fldenv %0" : : "m" (__env) \ + : "st", "st(1)", "st(2)", "st(3)", "st(4)", \ + "st(5)", "st(6)", "st(7)") +#define __fnclex() __asm __volatile("fnclex") +#define __fnstenv(__env) __asm __volatile("fnstenv %0" : "=m" (*(__env))) +#define __fnstcw(__cw) __asm __volatile("fnstcw %0" : "=m" (*(__cw))) +#define __fnstsw(__sw) __asm __volatile("fnstsw %0" : "=am" (*(__sw))) +#define __fwait() __asm __volatile("fwait") +#define __ldmxcsr(__csr) __asm __volatile("ldmxcsr %0" : : "m" (__csr)) +#define __stmxcsr(__csr) __asm __volatile("stmxcsr %0" : "=m" (*(__csr))) + +__fenv_static inline int +feclearexcept(int __excepts) +{ + fenv_t __env; + uint32_t __mxcsr; + + if (__excepts == FE_ALL_EXCEPT) { + __fnclex(); + } else { + __fnstenv(&__env); + __env.__status &= ~__excepts; + __fldenv(__env); + } + if (__HAS_SSE()) { + __stmxcsr(&__mxcsr); + __mxcsr &= ~__excepts; + __ldmxcsr(__mxcsr); + } + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + uint32_t __mxcsr; + uint16_t __status; + + __fnstsw(&__status); + if (__HAS_SSE()) + __stmxcsr(&__mxcsr); + else + __mxcsr = 0; + *__flagp = (__mxcsr | __status) & __excepts; + return (0); +} + +OLM_DLLEXPORT int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +OLM_DLLEXPORT int feraiseexcept(int __excepts); + +__fenv_static inline int +fetestexcept(int __excepts) +{ + uint32_t __mxcsr; + uint16_t __status; + + __fnstsw(&__status); + if (__HAS_SSE()) + __stmxcsr(&__mxcsr); + else + __mxcsr = 0; + return ((__status | __mxcsr) & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + uint16_t __control; + + /* + * We assume that the x87 and the SSE unit agree on the + * rounding mode. Reading the control word on the x87 turns + * out to be about 5 times faster than reading it on the SSE + * unit on an Opteron 244. + */ + __fnstcw(&__control); + return (__control & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + uint32_t __mxcsr; + uint16_t __control; + + if (__round & ~_ROUND_MASK) + return (-1); + + __fnstcw(&__control); + __control &= ~_ROUND_MASK; + __control |= __round; + __fldcw(__control); + + if (__HAS_SSE()) { + __stmxcsr(&__mxcsr); + __mxcsr &= ~(_ROUND_MASK << _SSE_ROUND_SHIFT); + __mxcsr |= __round << _SSE_ROUND_SHIFT; + __ldmxcsr(__mxcsr); + } + + return (0); +} + +OLM_DLLEXPORT int fegetenv(fenv_t *__envp); +OLM_DLLEXPORT int feholdexcept(fenv_t *__envp); + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + fenv_t __env = *__envp; + uint32_t __mxcsr; + + __mxcsr = __get_mxcsr(__env); + __set_mxcsr(__env, 0xffffffff); + /* + * XXX Using fldenvx() instead of fldenv() tells the compiler that this + * instruction clobbers the i387 register stack. This happens because + * we restore the tag word from the saved environment. Normally, this + * would happen anyway and we wouldn't care, because the ABI allows + * function calls to clobber the i387 regs. However, fesetenv() is + * inlined, so we need to be more careful. + */ + __fldenvx(__env); + if (__HAS_SSE()) + __ldmxcsr(__mxcsr); + return (0); +} + +OLM_DLLEXPORT int feupdateenv(const fenv_t *__envp); + +#if __BSD_VISIBLE + +OLM_DLLEXPORT int feenableexcept(int __mask); +OLM_DLLEXPORT int fedisableexcept(int __mask); + +/* We currently provide no external definition of fegetexcept(). */ +static inline int +fegetexcept(void) +{ + uint16_t __control; + + /* + * We assume that the masks for the x87 and the SSE unit are + * the same. + */ + __fnstcw(&__control); + return (~__control & FE_ALL_EXCEPT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_loongarch64.h b/openlibm/include/openlibm_fenv_loongarch64.h new file mode 100644 index 0000000000..cfaf020755 --- /dev/null +++ b/openlibm/include/openlibm_fenv_loongarch64.h @@ -0,0 +1,226 @@ +/*- + * Copyright (c) 2023 Yifan An + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef uint32_t fenv_t; +typedef uint32_t fexcept_t; + +/* Exception flags */ +#define FE_INVALID 0x100000 +#define FE_DIVBYZERO 0x080000 +#define FE_OVERFLOW 0x040000 +#define FE_UNDERFLOW 0x020000 +#define FE_INEXACT 0x010000 +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_TOWARDZERO 0x0100 +#define FE_DOWNWARD 0x0200 +#define FE_UPWARD 0x0300 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +#define _FPU_MASK_V 0x10 +#define _FPU_MASK_Z 0x08 +#define _FPU_MASK_O 0x04 +#define _FPU_MASK_U 0x02 +#define _FPU_MASK_I 0x01 + +#define _FPUSW_SHIFT 16 +#define _ENABLE_MASK (_FPU_MASK_V | _FPU_MASK_Z | _FPU_MASK_O | _FPU_MASK_U | _FPU_MASK_I) + +#define __rfs(__fpsr) __asm __volatile("movfcsr2gr %0,$r0" : "=r"(__fpsr)) +#define __wfs(__fpsr) __asm __volatile("movgr2fcsr $r0,%0" : : "r"(__fpsr)) + +__fenv_static inline int +feclearexcept(int __excepts) +{ + fexcept_t __fpsr; + + __rfs(__fpsr); + __fpsr &= ~__excepts; + __wfs(__fpsr); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + fexcept_t __fpsr; + + __rfs(__fpsr); + *__flagp = __fpsr & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + fexcept_t __fpsr; + + __rfs(__fpsr); + __fpsr &= ~__excepts; + __fpsr |= *__flagp & __excepts; + __wfs(__fpsr); + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + fexcept_t __ex = __excepts; + + fesetexceptflag(&__ex, __excepts); /* XXX */ + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + fexcept_t __fpsr; + + __rfs(__fpsr); + return (__fpsr & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + fexcept_t __fpsr; + + __rfs(__fpsr); + return __fpsr & _ROUND_MASK; +} + +__fenv_static inline int +fesetround(int __round) +{ + fexcept_t __fpsr; + if ((__round & ~_ROUND_MASK) != 0) + return 1; + + __rfs(__fpsr); + __fpsr &= ~_ROUND_MASK; + __fpsr |= __round; + __wfs(__fpsr); + + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + __rfs(*__envp); + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + fenv_t __env; + + __rfs(__env); + *__envp = __env; + __env &= ~(FE_ALL_EXCEPT | _FPU_MASK_V | _FPU_MASK_Z | _FPU_MASK_O | _FPU_MASK_U | _FPU_MASK_I); + __wfs(__env); + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + __wfs(*__envp); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + fexcept_t __fpsr; + + __rfs(__fpsr); + __wfs(*__envp); + feraiseexcept(__fpsr & FE_ALL_EXCEPT); + return (0); +} + +#if __BSD_VISIBLE + +static inline int +feenableexcept(int __mask) +{ + fenv_t __old_fpsr, __new_fpsr; + + __rfs(__new_fpsr); + __old_fpsr = (__new_fpsr & _ENABLE_MASK) << _FPUSW_SHIFT; + __new_fpsr |= (__mask & FE_ALL_EXCEPT) >> _FPUSW_SHIFT; + __wfs(__new_fpsr); + return __old_fpsr; +} + +static inline int +fedisableexcept(int __mask) +{ + fenv_t __old_fpsr, __new_fpsr; + + __rfs(__new_fpsr); + __old_fpsr = (__new_fpsr & _ENABLE_MASK) << _FPUSW_SHIFT; + __new_fpsr &= ~((__mask & FE_ALL_EXCEPT) >> _FPUSW_SHIFT); + __wfs(__new_fpsr); + return __old_fpsr; +} + +static inline int +fegetexcept(void) +{ + fenv_t __fpsr; + + __rfs(__fpsr); + return ((__fpsr & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ \ No newline at end of file diff --git a/openlibm/include/openlibm_fenv_mips.h b/openlibm/include/openlibm_fenv_mips.h new file mode 100644 index 0000000000..980252f7ed --- /dev/null +++ b/openlibm/include/openlibm_fenv_mips.h @@ -0,0 +1,278 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef uint32_t fenv_t; +typedef uint32_t fexcept_t; + +/* Exception flags */ +#ifdef __mips_soft_float +#define _FPUSW_SHIFT 16 +#define FE_INVALID 0x0001 +#define FE_DIVBYZERO 0x0002 +#define FE_OVERFLOW 0x0004 +#define FE_UNDERFLOW 0x0008 +#define FE_INEXACT 0x0010 +#else +#define _FCSR_CAUSE_SHIFT 10 +#define FE_INVALID 0x0040 +#define FE_DIVBYZERO 0x0020 +#define FE_OVERFLOW 0x0010 +#define FE_UNDERFLOW 0x0008 +#define FE_INEXACT 0x0004 +#endif +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_TOWARDZERO 0x0001 +#define FE_UPWARD 0x0002 +#define FE_DOWNWARD 0x0003 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +/* We need to be able to map status flag positions to mask flag positions */ +#define _ENABLE_SHIFT 5 +#define _ENABLE_MASK (FE_ALL_EXCEPT << _ENABLE_SHIFT) + +#ifndef __mips_soft_float +#define __cfc1(__fcsr) __asm __volatile("cfc1 %0, $31" : "=r" (__fcsr)) +#define __ctc1(__fcsr) __asm __volatile("ctc1 %0, $31" :: "r" (__fcsr)) +#endif + +#ifdef __mips_soft_float +int feclearexcept(int __excepts); +int fegetexceptflag(fexcept_t *__flagp, int __excepts); +int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +int feraiseexcept(int __excepts); +int fetestexcept(int __excepts); +int fegetround(void); +int fesetround(int __round); +int fegetenv(fenv_t *__envp); +int feholdexcept(fenv_t *__envp); +int fesetenv(const fenv_t *__envp); +int feupdateenv(const fenv_t *__envp); +#else +__fenv_static inline int +feclearexcept(int __excepts) +{ + fexcept_t fcsr; + + __excepts &= FE_ALL_EXCEPT; + __cfc1(fcsr); + fcsr &= ~(__excepts | (__excepts << _FCSR_CAUSE_SHIFT)); + __ctc1(fcsr); + + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + fexcept_t fcsr; + + __excepts &= FE_ALL_EXCEPT; + __cfc1(fcsr); + *__flagp = fcsr & __excepts; + + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + fexcept_t fcsr; + + __excepts &= FE_ALL_EXCEPT; + __cfc1(fcsr); + fcsr &= ~__excepts; + fcsr |= *__flagp & __excepts; + __ctc1(fcsr); + + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + fexcept_t fcsr; + + __excepts &= FE_ALL_EXCEPT; + __cfc1(fcsr); + fcsr |= __excepts | (__excepts << _FCSR_CAUSE_SHIFT); + __ctc1(fcsr); + + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + fexcept_t fcsr; + + __excepts &= FE_ALL_EXCEPT; + __cfc1(fcsr); + + return (fcsr & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + fexcept_t fcsr; + + __cfc1(fcsr); + + return (fcsr & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + fexcept_t fcsr; + + if (__round & ~_ROUND_MASK) + return (-1); + + __cfc1(fcsr); + fcsr &= ~_ROUND_MASK; + fcsr |= __round; + __ctc1(fcsr); + + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + + __cfc1(*__envp); + + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + fexcept_t fcsr; + + __cfc1(fcsr); + *__envp = fcsr; + fcsr &= ~(FE_ALL_EXCEPT | _ENABLE_MASK); + __ctc1(fcsr); + + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + + __ctc1(*__envp); + + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + fexcept_t fcsr; + + __cfc1(fcsr); + fesetenv(__envp); + feraiseexcept(fcsr); + + return (0); +} +#endif /* !__mips_soft_float */ + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +#ifdef __mips_soft_float +int feenableexcept(int __mask); +int fedisableexcept(int __mask); +int fegetexcept(void); +#else +static inline int +feenableexcept(int __mask) +{ + fenv_t __old_fcsr, __new_fcsr; + + __cfc1(__old_fcsr); + __new_fcsr = __old_fcsr | (__mask & FE_ALL_EXCEPT) << _ENABLE_SHIFT; + __ctc1(__new_fcsr); + + return ((__old_fcsr >> _ENABLE_SHIFT) & FE_ALL_EXCEPT); +} + +static inline int +fedisableexcept(int __mask) +{ + fenv_t __old_fcsr, __new_fcsr; + + __cfc1(__old_fcsr); + __new_fcsr = __old_fcsr & ~((__mask & FE_ALL_EXCEPT) << _ENABLE_SHIFT); + __ctc1(__new_fcsr); + + return ((__old_fcsr >> _ENABLE_SHIFT) & FE_ALL_EXCEPT); +} + +static inline int +fegetexcept(void) +{ + fexcept_t fcsr; + + __cfc1(fcsr); + + return ((fcsr & _ENABLE_MASK) >> _ENABLE_SHIFT); +} + +#endif /* !__mips_soft_float */ + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_powerpc.h b/openlibm/include/openlibm_fenv_powerpc.h new file mode 100644 index 0000000000..7592de86c5 --- /dev/null +++ b/openlibm/include/openlibm_fenv_powerpc.h @@ -0,0 +1,281 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include +#include +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef uint32_t fenv_t; +typedef uint32_t fexcept_t; + +/* Exception flags */ +#define FE_INEXACT 0x02000000 +#define FE_DIVBYZERO 0x04000000 +#define FE_UNDERFLOW 0x08000000 +#define FE_OVERFLOW 0x10000000 +#define FE_INVALID 0x20000000 /* all types of invalid FP ops */ + +/* + * The PowerPC architecture has extra invalid flags that indicate the + * specific type of invalid operation occurred. These flags may be + * tested, set, and cleared---but not masked---separately. All of + * these bits are cleared when FE_INVALID is cleared, but only + * FE_VXSOFT is set when FE_INVALID is explicitly set in software. + */ +#define FE_VXCVI 0x00000100 /* invalid integer convert */ +#define FE_VXSQRT 0x00000200 /* square root of a negative */ +#define FE_VXSOFT 0x00000400 /* software-requested exception */ +#define FE_VXVC 0x00080000 /* ordered comparison involving NaN */ +#define FE_VXIMZ 0x00100000 /* inf * 0 */ +#define FE_VXZDZ 0x00200000 /* 0 / 0 */ +#define FE_VXIDI 0x00400000 /* inf / inf */ +#define FE_VXISI 0x00800000 /* inf - inf */ +#define FE_VXSNAN 0x01000000 /* operation on a signalling NaN */ +#define FE_ALL_INVALID (FE_VXCVI | FE_VXSQRT | FE_VXSOFT | FE_VXVC | \ + FE_VXIMZ | FE_VXZDZ | FE_VXIDI | FE_VXISI | \ + FE_VXSNAN | FE_INVALID) +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_ALL_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_TOWARDZERO 0x0001 +#define FE_UPWARD 0x0002 +#define FE_DOWNWARD 0x0003 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +/* We need to be able to map status flag positions to mask flag positions */ +#define _FPUSW_SHIFT 22 +#define _ENABLE_MASK ((FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) >> _FPUSW_SHIFT) + +#ifndef _SOFT_FLOAT +#define __mffs(__env) __asm __volatile("mffs %0" : "=f" (*(__env))) +#define __mtfsf(__env) __asm __volatile("mtfsf 255,%0" : : "f" (__env)) +#else +#define __mffs(__env) +#define __mtfsf(__env) +#endif + +union __fpscr { + double __d; + struct { +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + fenv_t __reg; + uint32_t __junk; +#else + uint32_t __junk; + fenv_t __reg; +#endif + } __bits; +}; + +__fenv_static inline int +feclearexcept(int __excepts) +{ + union __fpscr __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_ALL_INVALID; + __mffs(&__r.__d); + __r.__bits.__reg &= ~__excepts; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + union __fpscr __r; + + __mffs(&__r.__d); + *__flagp = __r.__bits.__reg & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + union __fpscr __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_ALL_EXCEPT; + __mffs(&__r.__d); + __r.__bits.__reg &= ~__excepts; + __r.__bits.__reg |= *__flagp & __excepts; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + union __fpscr __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_VXSOFT; + __mffs(&__r.__d); + __r.__bits.__reg |= __excepts; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + union __fpscr __r; + + __mffs(&__r.__d); + return (__r.__bits.__reg & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + union __fpscr __r; + + __mffs(&__r.__d); + return (__r.__bits.__reg & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + union __fpscr __r; + + if (__round & ~_ROUND_MASK) + return (-1); + __mffs(&__r.__d); + __r.__bits.__reg &= ~_ROUND_MASK; + __r.__bits.__reg |= __round; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + union __fpscr __r; + + __mffs(&__r.__d); + *__envp = __r.__bits.__reg; + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + union __fpscr __r; + + __mffs(&__r.__d); + *__envp = __r.__d; + __r.__bits.__reg &= ~(FE_ALL_EXCEPT | _ENABLE_MASK); + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + union __fpscr __r; + + __r.__bits.__reg = *__envp; + __mtfsf(__r.__d); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + union __fpscr __r; + + __mffs(&__r.__d); + __r.__bits.__reg &= FE_ALL_EXCEPT; + __r.__bits.__reg |= *__envp; + __mtfsf(__r.__d); + return (0); +} + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +static inline int +feenableexcept(int __mask) +{ + union __fpscr __r; + fenv_t __oldmask; + + __mffs(&__r.__d); + __oldmask = __r.__bits.__reg; + __r.__bits.__reg |= (__mask & FE_ALL_EXCEPT) >> _FPUSW_SHIFT; + __mtfsf(__r.__d); + return ((__oldmask & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +static inline int +fedisableexcept(int __mask) +{ + union __fpscr __r; + fenv_t __oldmask; + + __mffs(&__r.__d); + __oldmask = __r.__bits.__reg; + __r.__bits.__reg &= ~((__mask & FE_ALL_EXCEPT) >> _FPUSW_SHIFT); + __mtfsf(__r.__d); + return ((__oldmask & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +static inline int +fegetexcept(void) +{ + union __fpscr __r; + + __mffs(&__r.__d); + return ((__r.__bits.__reg & _ENABLE_MASK) << _FPUSW_SHIFT); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_riscv.h b/openlibm/include/openlibm_fenv_riscv.h new file mode 100644 index 0000000000..e8ce78e711 --- /dev/null +++ b/openlibm/include/openlibm_fenv_riscv.h @@ -0,0 +1,261 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * Copyright (c) 2015-2016 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/lib/msun/riscv/fenv.h 332792 2018-04-19 20:36:15Z brooks $ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef uint64_t fenv_t; +typedef uint64_t fexcept_t; + +/* Exception flags */ +#define FE_INVALID 0x0010 +#define FE_DIVBYZERO 0x0008 +#define FE_OVERFLOW 0x0004 +#define FE_UNDERFLOW 0x0002 +#define FE_INEXACT 0x0001 +#define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_INEXACT | \ + FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) + +/* + * RISC-V Rounding modes + */ +#define _ROUND_SHIFT 5 +#define FE_TONEAREST (0x00 << _ROUND_SHIFT) +#define FE_TOWARDZERO (0x01 << _ROUND_SHIFT) +#define FE_DOWNWARD (0x02 << _ROUND_SHIFT) +#define FE_UPWARD (0x03 << _ROUND_SHIFT) +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +#if !defined(__riscv_float_abi_soft) && !defined(__riscv_float_abi_double) +#if defined(__riscv_float_abi_single) +#error single precision floating point ABI not supported +#else +#error compiler did not set soft/hard float macros +#endif +#endif + +#ifndef __riscv_float_abi_soft +#define __rfs(__fcsr) __asm __volatile("csrr %0, fcsr" : "=r" (__fcsr)) +#define __wfs(__fcsr) __asm __volatile("csrw fcsr, %0" :: "r" (__fcsr)) +#endif + +#ifdef __riscv_float_abi_soft +int feclearexcept(int __excepts); +int fegetexceptflag(fexcept_t *__flagp, int __excepts); +int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +int feraiseexcept(int __excepts); +int fetestexcept(int __excepts); +int fegetround(void); +int fesetround(int __round); +int fegetenv(fenv_t *__envp); +int feholdexcept(fenv_t *__envp); +int fesetenv(const fenv_t *__envp); +int feupdateenv(const fenv_t *__envp); +#else +__fenv_static inline int +feclearexcept(int __excepts) +{ + + __asm __volatile("csrc fflags, %0" :: "r"(__excepts)); + + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + fexcept_t __fcsr; + + __rfs(__fcsr); + *__flagp = __fcsr & __excepts; + + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + fexcept_t __fcsr; + + __fcsr = *__flagp; + __asm __volatile("csrc fflags, %0" :: "r"(__excepts)); + __asm __volatile("csrs fflags, %0" :: "r"(__fcsr & __excepts)); + + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + + __asm __volatile("csrs fflags, %0" :: "r"(__excepts)); + + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + fexcept_t __fcsr; + + __rfs(__fcsr); + + return (__fcsr & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + fexcept_t __fcsr; + + __rfs(__fcsr); + + return (__fcsr & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + fexcept_t __fcsr; + + if (__round & ~_ROUND_MASK) + return (-1); + + __rfs(__fcsr); + __fcsr &= ~_ROUND_MASK; + __fcsr |= __round; + __wfs(__fcsr); + + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + + __rfs(*__envp); + + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + + /* No exception traps. */ + + return (-1); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + + __wfs(*__envp); + + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + fexcept_t __fcsr; + + __rfs(__fcsr); + __wfs(*__envp); + feraiseexcept(__fcsr & FE_ALL_EXCEPT); + + return (0); +} +#endif /* !__riscv_float_abi_soft */ + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +#ifdef __riscv_float_abi_soft +int feenableexcept(int __mask); +int fedisableexcept(int __mask); +int fegetexcept(void); +#else +static inline int +feenableexcept(int __mask) +{ + + /* No exception traps. */ + + return (-1); +} + +static inline int +fedisableexcept(int __mask) +{ + + /* No exception traps. */ + + return (0); +} + +static inline int +fegetexcept(void) +{ + + /* No exception traps. */ + + return (0); +} +#endif /* !__riscv_float_abi_soft */ + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_fenv_s390.h b/openlibm/include/openlibm_fenv_s390.h new file mode 100644 index 0000000000..a21b9ee6af --- /dev/null +++ b/openlibm/include/openlibm_fenv_s390.h @@ -0,0 +1,237 @@ +/*- + * Copyright (c) 2016 Dan Horák + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include +#include +#include "cdefs-compat.h" + +#ifndef __fenv_static +#define __fenv_static static +#endif + +typedef uint32_t fenv_t; +typedef uint32_t fexcept_t; + +/* Exception flags */ +#define FE_INEXACT 0x080000 +#define FE_UNDERFLOW 0x100000 +#define FE_OVERFLOW 0x200000 +#define FE_DIVBYZERO 0x400000 +#define FE_INVALID 0x800000 /* all types of invalid FP ops */ + +#define FE_ALL_EXCEPT (FE_INVALID | FE_DIVBYZERO | FE_INEXACT | FE_OVERFLOW | FE_UNDERFLOW) + +/* Rounding modes */ +#define FE_TONEAREST 0x0000 +#define FE_TOWARDZERO 0x0001 +#define FE_UPWARD 0x0002 +#define FE_DOWNWARD 0x0003 +#define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ + FE_UPWARD | FE_TOWARDZERO) + +__BEGIN_DECLS + +/* Default floating-point environment */ +extern const fenv_t __fe_dfl_env; +#define FE_DFL_ENV (&__fe_dfl_env) + +/* We need to be able to map status flag positions to mask flag positions */ +#define _FPC_EXC_MASK_SHIFT 8 +#define _ENABLE_MASK ((FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) << _FPC_EXC_MASK_SHIFT) + +/* Macros for accessing the hardware control word. */ +#define _FPU_GETCW(cw) __asm__ __volatile__ ("efpc %0,0" : "=d" (cw)) +#define _FPU_SETCW(cw) __asm__ __volatile__ ("sfpc %0,0" : : "d" (cw)) + +__fenv_static inline int +feclearexcept(int __excepts) +{ + fexcept_t __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_ALL_EXCEPT; + _FPU_GETCW(__r); + __r &= ~__excepts; + _FPU_SETCW(__r); + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + *__flagp = __r & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + fexcept_t __r; + + if (__excepts & FE_INVALID) + __excepts |= FE_ALL_EXCEPT; + _FPU_GETCW(__r); + __r &= ~__excepts; + __r |= *__flagp & __excepts; + _FPU_SETCW(__r); + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + __r |= __excepts; + _FPU_SETCW(__r); + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + return (__r & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + return (__r & _ROUND_MASK); +} + +__fenv_static inline int +fesetround(int __round) +{ + fexcept_t __r; + + if (__round & ~_ROUND_MASK) + return (-1); + + _FPU_GETCW(__r); + __r &= ~_ROUND_MASK; + __r |= __round; + _FPU_SETCW(__r); + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + _FPU_GETCW(*__envp); + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + *__envp = __r; + __r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK); + _FPU_SETCW(__r); + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + _FPU_SETCW(*__envp); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + __r &= FE_ALL_EXCEPT; + __r |= *__envp; + _FPU_SETCW(__r); + return (0); +} + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +static inline int +feenableexcept(int __mask) +{ + fenv_t __r; + fenv_t __oldmask; + + _FPU_GETCW(__r); + __oldmask = __r; + __r |= (__mask & FE_ALL_EXCEPT) << _FPC_EXC_MASK_SHIFT; + _FPU_SETCW(__r); + return ((__oldmask & _ENABLE_MASK) >> _FPC_EXC_MASK_SHIFT); +} + +static inline int +fedisableexcept(int __mask) +{ + fenv_t __r; + fenv_t __oldmask; + + _FPU_GETCW(__r); + __oldmask = __r; + __r &= ~((__mask & FE_ALL_EXCEPT) << _FPC_EXC_MASK_SHIFT); + _FPU_SETCW(__r); + return ((__oldmask & _ENABLE_MASK) >> _FPC_EXC_MASK_SHIFT); +} + +static inline int +fegetexcept(void) +{ + fexcept_t __r; + + _FPU_GETCW(__r); + return (__r & (_ENABLE_MASK >> _FPC_EXC_MASK_SHIFT)); +} + +#endif /* __BSD_VISIBLE */ + +__END_DECLS + +#endif /* !_FENV_H_ */ diff --git a/openlibm/include/openlibm_math.h b/openlibm/include/openlibm_math.h new file mode 100644 index 0000000000..701ad70557 --- /dev/null +++ b/openlibm/include/openlibm_math.h @@ -0,0 +1,493 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * from: @(#)fdlibm.h 5.1 93/09/24 + * $FreeBSD: src/lib/msun/src/openlibm.h,v 1.82 2011/11/12 19:55:48 theraven Exp $ + */ + +#ifdef OPENLIBM_USE_HOST_MATH_H +#include +#else /* !OPENLIBM_USE_HOST_MATH_H */ + +#include + +#ifndef OPENLIBM_MATH_H +#define OPENLIBM_MATH_H + +#if (defined(_WIN32) || defined (_MSC_VER)) && !defined(__WIN32__) + #define __WIN32__ +#endif + +#if !defined(__arm__) && !defined(__wasm__) +#define OLM_LONG_DOUBLE +#endif + +#ifndef __pure2 +#define __pure2 +#endif + +/* + * ANSI/POSIX + */ +extern const union __infinity_un { + unsigned char __uc[8]; + double __ud; +} __infinity; + +extern const union __nan_un { + unsigned char __uc[sizeof(float)]; + float __uf; +} __nan; + +/* VBS +#if __GNUC_PREREQ__(3, 3) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 800) +#define __MATH_BUILTIN_CONSTANTS +#endif + +#if __GNUC_PREREQ__(3, 0) && !defined(__INTEL_COMPILER) +#define __MATH_BUILTIN_RELOPS +#endif +*/ + +//VBS begin +#define __MATH_BUILTIN_CONSTANTS +#define __MATH_BUILTIN_RELOPS +#ifndef __ISO_C_VISIBLE +#define __ISO_C_VISIBLE 1999 +#endif +//VBS end + +#ifdef __MATH_BUILTIN_CONSTANTS +#define HUGE_VAL __builtin_huge_val() +#else +#define HUGE_VAL (__infinity.__ud) +#endif + +#if __ISO_C_VISIBLE >= 1999 +#define FP_ILOGB0 (-INT_MAX) +#define FP_ILOGBNAN INT_MAX + +#ifdef __MATH_BUILTIN_CONSTANTS +#define HUGE_VALF __builtin_huge_valf() +#define HUGE_VALL __builtin_huge_vall() +#define INFINITY __builtin_inff() +#define NAN __builtin_nanf("") +#else +#define HUGE_VALF (float)HUGE_VAL +#define HUGE_VALL (long double)HUGE_VAL +#define INFINITY HUGE_VALF +#define NAN (__nan.__uf) +#endif /* __MATH_BUILTIN_CONSTANTS */ + +#define MATH_ERRNO 1 +#define MATH_ERREXCEPT 2 +#define math_errhandling MATH_ERREXCEPT + +#define FP_FAST_FMAF 1 +#ifdef __ia64__ +#define FP_FAST_FMA 1 +#define FP_FAST_FMAL 1 +#endif + +/* Symbolic constants to classify floating point numbers. */ +#define FP_INFINITE 0x01 +#define FP_NAN 0x02 +#define FP_NORMAL 0x04 +#define FP_SUBNORMAL 0x08 +#define FP_ZERO 0x10 +#define fpclassify(x) \ + ((sizeof (x) == sizeof (float)) ? __fpclassifyf(x) \ + : (sizeof (x) == sizeof (double)) ? __fpclassifyd(x) \ + : __fpclassifyl(x)) + +#define isfinite(x) \ + ((sizeof (x) == sizeof (float)) ? __isfinitef(x) \ + : (sizeof (x) == sizeof (double)) ? __isfinite(x) \ + : __isfinitel(x)) +#define isinf(x) \ + ((sizeof (x) == sizeof (float)) ? __isinff(x) \ + : (sizeof (x) == sizeof (double)) ? isinf(x) \ + : __isinfl(x)) +#define isnan(x) \ + ((sizeof (x) == sizeof (float)) ? __isnanf(x) \ + : (sizeof (x) == sizeof (double)) ? isnan(x) \ + : __isnanl(x)) +#define isnormal(x) \ + ((sizeof (x) == sizeof (float)) ? __isnormalf(x) \ + : (sizeof (x) == sizeof (double)) ? __isnormal(x) \ + : __isnormall(x)) + +#ifdef __MATH_BUILTIN_RELOPS +#define isgreater(x, y) __builtin_isgreater((x), (y)) +#define isgreaterequal(x, y) __builtin_isgreaterequal((x), (y)) +#define isless(x, y) __builtin_isless((x), (y)) +#define islessequal(x, y) __builtin_islessequal((x), (y)) +#define islessgreater(x, y) __builtin_islessgreater((x), (y)) +#define isunordered(x, y) __builtin_isunordered((x), (y)) +#else +#define isgreater(x, y) (!isunordered((x), (y)) && (x) > (y)) +#define isgreaterequal(x, y) (!isunordered((x), (y)) && (x) >= (y)) +#define isless(x, y) (!isunordered((x), (y)) && (x) < (y)) +#define islessequal(x, y) (!isunordered((x), (y)) && (x) <= (y)) +#define islessgreater(x, y) (!isunordered((x), (y)) && \ + ((x) > (y) || (y) > (x))) +#define isunordered(x, y) (isnan(x) || isnan(y)) +#endif /* __MATH_BUILTIN_RELOPS */ + +#define signbit(x) \ + ((sizeof (x) == sizeof (float)) ? __signbitf(x) \ + : (sizeof (x) == sizeof (double)) ? __signbit(x) \ + : __signbitl(x)) + +//VBS +//typedef __double_t double_t; +//typedef __float_t float_t; +#endif /* __ISO_C_VISIBLE >= 1999 */ + +/* + * XOPEN/SVID + */ +#if __BSD_VISIBLE || __XSI_VISIBLE +#define M_E 2.7182818284590452354 /* e */ +#define M_LOG2E 1.4426950408889634074 /* log 2e */ +#define M_LOG10E 0.43429448190325182765 /* log 10e */ +#define M_LN2 0.69314718055994530942 /* log e2 */ +#define M_LN10 2.30258509299404568402 /* log e10 */ +#define M_PI 3.14159265358979323846 /* pi */ +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#define M_PI_4 0.78539816339744830962 /* pi/4 */ +#define M_1_PI 0.31830988618379067154 /* 1/pi */ +#define M_2_PI 0.63661977236758134308 /* 2/pi */ +#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ + +#define MAXFLOAT ((float)3.40282346638528860e+38) + +#ifndef OPENLIBM_ONLY_THREAD_SAFE +OLM_DLLEXPORT extern int signgam; +#endif +#endif /* __BSD_VISIBLE || __XSI_VISIBLE */ + +#if __BSD_VISIBLE +#if 0 +/* Old value from 4.4BSD-Lite openlibm.h; this is probably better. */ +#define HUGE HUGE_VAL +#else +#define HUGE MAXFLOAT +#endif +#endif /* __BSD_VISIBLE */ + +/* + * Most of these functions depend on the rounding mode and have the side + * effect of raising floating-point exceptions, so they are not declared + * as __pure2. In C99, FENV_ACCESS affects the purity of these functions. + */ + +#if defined(__cplusplus) +extern "C" { +#endif +/* Symbol present when OpenLibm is used. */ +int isopenlibm(void); + +/* + * ANSI/POSIX + */ +OLM_DLLEXPORT int __fpclassifyd(double) __pure2; +OLM_DLLEXPORT int __fpclassifyf(float) __pure2; +OLM_DLLEXPORT int __fpclassifyl(long double) __pure2; +OLM_DLLEXPORT int __isfinitef(float) __pure2; +OLM_DLLEXPORT int __isfinite(double) __pure2; +OLM_DLLEXPORT int __isfinitel(long double) __pure2; +OLM_DLLEXPORT int __isinff(float) __pure2; +OLM_DLLEXPORT int __isinfl(long double) __pure2; +OLM_DLLEXPORT int __isnanf(float) __pure2; +OLM_DLLEXPORT int __isnanl(long double) __pure2; +OLM_DLLEXPORT int __isnormalf(float) __pure2; +OLM_DLLEXPORT int __isnormal(double) __pure2; +OLM_DLLEXPORT int __isnormall(long double) __pure2; +OLM_DLLEXPORT int __signbit(double) __pure2; +OLM_DLLEXPORT int __signbitf(float) __pure2; +OLM_DLLEXPORT int __signbitl(long double) __pure2; + +OLM_DLLEXPORT double acos(double); +OLM_DLLEXPORT double asin(double); +OLM_DLLEXPORT double atan(double); +OLM_DLLEXPORT double atan2(double, double); +OLM_DLLEXPORT double cos(double); +OLM_DLLEXPORT double sin(double); +OLM_DLLEXPORT double tan(double); + +OLM_DLLEXPORT double cosh(double); +OLM_DLLEXPORT double sinh(double); +OLM_DLLEXPORT double tanh(double); + +OLM_DLLEXPORT double exp(double); +OLM_DLLEXPORT double frexp(double, int *); /* fundamentally !__pure2 */ +OLM_DLLEXPORT double ldexp(double, int); +OLM_DLLEXPORT double log(double); +OLM_DLLEXPORT double log10(double); +OLM_DLLEXPORT double modf(double, double *); /* fundamentally !__pure2 */ + +OLM_DLLEXPORT double pow(double, double); +OLM_DLLEXPORT double sqrt(double); + +OLM_DLLEXPORT double ceil(double); +OLM_DLLEXPORT double fabs(double) __pure2; +OLM_DLLEXPORT double floor(double); +OLM_DLLEXPORT double fmod(double, double); + +/* + * These functions are not in C90. + */ +#if __BSD_VISIBLE || __ISO_C_VISIBLE >= 1999 || __XSI_VISIBLE +OLM_DLLEXPORT double acosh(double); +OLM_DLLEXPORT double asinh(double); +OLM_DLLEXPORT double atanh(double); +OLM_DLLEXPORT double cbrt(double); +OLM_DLLEXPORT double erf(double); +OLM_DLLEXPORT double erfc(double); +OLM_DLLEXPORT double exp2(double); +OLM_DLLEXPORT double expm1(double); +OLM_DLLEXPORT double fma(double, double, double); +OLM_DLLEXPORT double hypot(double, double); +OLM_DLLEXPORT int ilogb(double) __pure2; +OLM_DLLEXPORT int (isinf)(double) __pure2; +OLM_DLLEXPORT int (isnan)(double) __pure2; +OLM_DLLEXPORT double lgamma(double); +OLM_DLLEXPORT long long llrint(double); +OLM_DLLEXPORT long long llround(double); +OLM_DLLEXPORT double log1p(double); +OLM_DLLEXPORT double log2(double); +OLM_DLLEXPORT double logb(double); +OLM_DLLEXPORT long lrint(double); +OLM_DLLEXPORT long lround(double); +OLM_DLLEXPORT double nan(const char *) __pure2; +OLM_DLLEXPORT double nextafter(double, double); +OLM_DLLEXPORT double remainder(double, double); +OLM_DLLEXPORT double remquo(double, double, int *); +OLM_DLLEXPORT double rint(double); +#endif /* __BSD_VISIBLE || __ISO_C_VISIBLE >= 1999 || __XSI_VISIBLE */ + +#if __BSD_VISIBLE || __XSI_VISIBLE +OLM_DLLEXPORT double j0(double); +OLM_DLLEXPORT double j1(double); +OLM_DLLEXPORT double jn(int, double); +OLM_DLLEXPORT double y0(double); +OLM_DLLEXPORT double y1(double); +OLM_DLLEXPORT double yn(int, double); +#endif /* __BSD_VISIBLE || __XSI_VISIBLE */ + +#if __BSD_VISIBLE || __ISO_C_VISIBLE >= 1999 +OLM_DLLEXPORT double copysign(double, double) __pure2; +OLM_DLLEXPORT double fdim(double, double); +OLM_DLLEXPORT double fmax(double, double) __pure2; +OLM_DLLEXPORT double fmin(double, double) __pure2; +OLM_DLLEXPORT double nearbyint(double); +OLM_DLLEXPORT double round(double); +OLM_DLLEXPORT double scalbln(double, long); +OLM_DLLEXPORT double scalbn(double, int); +OLM_DLLEXPORT double tgamma(double); +OLM_DLLEXPORT double trunc(double); +#endif + +/* + * BSD math library entry points + */ +#if __BSD_VISIBLE +OLM_DLLEXPORT int isinff(float) __pure2; +OLM_DLLEXPORT int isnanf(float) __pure2; + +/* + * Reentrant version of lgamma; passes signgam back by reference as the + * second argument; user must allocate space for signgam. + */ +OLM_DLLEXPORT double lgamma_r(double, int *); + +/* + * Single sine/cosine function. + */ +OLM_DLLEXPORT void sincos(double, double *, double *); +#endif /* __BSD_VISIBLE */ + +/* float versions of ANSI/POSIX functions */ +#if __ISO_C_VISIBLE >= 1999 +OLM_DLLEXPORT float acosf(float); +OLM_DLLEXPORT float asinf(float); +OLM_DLLEXPORT float atanf(float); +OLM_DLLEXPORT float atan2f(float, float); +OLM_DLLEXPORT float cosf(float); +OLM_DLLEXPORT float sinf(float); +OLM_DLLEXPORT float tanf(float); + +OLM_DLLEXPORT float coshf(float); +OLM_DLLEXPORT float sinhf(float); +OLM_DLLEXPORT float tanhf(float); + +OLM_DLLEXPORT float exp2f(float); +OLM_DLLEXPORT float expf(float); +OLM_DLLEXPORT float expm1f(float); +OLM_DLLEXPORT float frexpf(float, int *); /* fundamentally !__pure2 */ +OLM_DLLEXPORT int ilogbf(float) __pure2; +OLM_DLLEXPORT float ldexpf(float, int); +OLM_DLLEXPORT float log10f(float); +OLM_DLLEXPORT float log1pf(float); +OLM_DLLEXPORT float log2f(float); +OLM_DLLEXPORT float logf(float); +OLM_DLLEXPORT float modff(float, float *); /* fundamentally !__pure2 */ + +OLM_DLLEXPORT float powf(float, float); +OLM_DLLEXPORT float sqrtf(float); + +OLM_DLLEXPORT float ceilf(float); +OLM_DLLEXPORT float fabsf(float) __pure2; +OLM_DLLEXPORT float floorf(float); +OLM_DLLEXPORT float fmodf(float, float); +OLM_DLLEXPORT float roundf(float); + +OLM_DLLEXPORT float erff(float); +OLM_DLLEXPORT float erfcf(float); +OLM_DLLEXPORT float hypotf(float, float); +OLM_DLLEXPORT float lgammaf(float); +OLM_DLLEXPORT float tgammaf(float); + +OLM_DLLEXPORT float acoshf(float); +OLM_DLLEXPORT float asinhf(float); +OLM_DLLEXPORT float atanhf(float); +OLM_DLLEXPORT float cbrtf(float); +OLM_DLLEXPORT float logbf(float); +OLM_DLLEXPORT float copysignf(float, float) __pure2; +OLM_DLLEXPORT long long llrintf(float); +OLM_DLLEXPORT long long llroundf(float); +OLM_DLLEXPORT long lrintf(float); +OLM_DLLEXPORT long lroundf(float); +OLM_DLLEXPORT float nanf(const char *) __pure2; +OLM_DLLEXPORT float nearbyintf(float); +OLM_DLLEXPORT float nextafterf(float, float); +OLM_DLLEXPORT float remainderf(float, float); +OLM_DLLEXPORT float remquof(float, float, int *); +OLM_DLLEXPORT float rintf(float); +OLM_DLLEXPORT float scalblnf(float, long); +OLM_DLLEXPORT float scalbnf(float, int); +OLM_DLLEXPORT float truncf(float); + +OLM_DLLEXPORT float fdimf(float, float); +OLM_DLLEXPORT float fmaf(float, float, float); +OLM_DLLEXPORT float fmaxf(float, float) __pure2; +OLM_DLLEXPORT float fminf(float, float) __pure2; +#endif + +/* + * float versions of BSD math library entry points + */ +#if __BSD_VISIBLE +OLM_DLLEXPORT float dremf(float, float); +OLM_DLLEXPORT float j0f(float); +OLM_DLLEXPORT float j1f(float); +OLM_DLLEXPORT float jnf(int, float); +OLM_DLLEXPORT float y0f(float); +OLM_DLLEXPORT float y1f(float); +OLM_DLLEXPORT float ynf(int, float); + +/* + * Float versions of reentrant version of lgamma; passes signgam back by + * reference as the second argument; user must allocate space for signgam. + */ +OLM_DLLEXPORT float lgammaf_r(float, int *); + +/* + * Single sine/cosine function. + */ +OLM_DLLEXPORT void sincosf(float, float *, float *); +#endif /* __BSD_VISIBLE */ + +/* + * long double versions of ISO/POSIX math functions + */ +#if __ISO_C_VISIBLE >= 1999 +OLM_DLLEXPORT long double acoshl(long double); +OLM_DLLEXPORT long double acosl(long double); +OLM_DLLEXPORT long double asinhl(long double); +OLM_DLLEXPORT long double asinl(long double); +OLM_DLLEXPORT long double atan2l(long double, long double); +OLM_DLLEXPORT long double atanhl(long double); +OLM_DLLEXPORT long double atanl(long double); +OLM_DLLEXPORT long double cbrtl(long double); +OLM_DLLEXPORT long double ceill(long double); +OLM_DLLEXPORT long double copysignl(long double, long double) __pure2; +OLM_DLLEXPORT long double coshl(long double); +OLM_DLLEXPORT long double cosl(long double); +OLM_DLLEXPORT long double erfcl(long double); +OLM_DLLEXPORT long double erfl(long double); +OLM_DLLEXPORT long double exp2l(long double); +OLM_DLLEXPORT long double expl(long double); +OLM_DLLEXPORT long double expm1l(long double); +OLM_DLLEXPORT long double fabsl(long double) __pure2; +OLM_DLLEXPORT long double fdiml(long double, long double); +OLM_DLLEXPORT long double floorl(long double); +OLM_DLLEXPORT long double fmal(long double, long double, long double); +OLM_DLLEXPORT long double fmaxl(long double, long double) __pure2; +OLM_DLLEXPORT long double fminl(long double, long double) __pure2; +OLM_DLLEXPORT long double fmodl(long double, long double); +OLM_DLLEXPORT long double frexpl(long double value, int *); /* fundamentally !__pure2 */ +OLM_DLLEXPORT long double hypotl(long double, long double); +OLM_DLLEXPORT int ilogbl(long double) __pure2; +OLM_DLLEXPORT long double ldexpl(long double, int); +OLM_DLLEXPORT long double lgammal(long double); +OLM_DLLEXPORT long long llrintl(long double); +OLM_DLLEXPORT long long llroundl(long double); +OLM_DLLEXPORT long double log10l(long double); +OLM_DLLEXPORT long double log1pl(long double); +OLM_DLLEXPORT long double log2l(long double); +OLM_DLLEXPORT long double logbl(long double); +OLM_DLLEXPORT long double logl(long double); +OLM_DLLEXPORT long lrintl(long double); +OLM_DLLEXPORT long lroundl(long double); +OLM_DLLEXPORT long double modfl(long double, long double *); /* fundamentally !__pure2 */ +OLM_DLLEXPORT long double nanl(const char *) __pure2; +OLM_DLLEXPORT long double nearbyintl(long double); +OLM_DLLEXPORT long double nextafterl(long double, long double); +OLM_DLLEXPORT double nexttoward(double, long double); +OLM_DLLEXPORT float nexttowardf(float, long double); +OLM_DLLEXPORT long double nexttowardl(long double, long double); +OLM_DLLEXPORT long double powl(long double, long double); +OLM_DLLEXPORT long double remainderl(long double, long double); +OLM_DLLEXPORT long double remquol(long double, long double, int *); +OLM_DLLEXPORT long double rintl(long double); +OLM_DLLEXPORT long double roundl(long double); +OLM_DLLEXPORT long double scalblnl(long double, long); +OLM_DLLEXPORT long double scalbnl(long double, int); +OLM_DLLEXPORT long double sinhl(long double); +OLM_DLLEXPORT long double sinl(long double); +OLM_DLLEXPORT long double sqrtl(long double); +OLM_DLLEXPORT long double tanhl(long double); +OLM_DLLEXPORT long double tanl(long double); +OLM_DLLEXPORT long double tgammal(long double); +OLM_DLLEXPORT long double truncl(long double); +#endif /* __ISO_C_VISIBLE >= 1999 */ + +/* Reentrant version of lgammal. */ +#if __BSD_VISIBLE +OLM_DLLEXPORT long double lgammal_r(long double, int *); + +/* + * Single sine/cosine function. + */ +OLM_DLLEXPORT void sincosl(long double, long double *, long double *); +#endif /* __BSD_VISIBLE */ + +#if defined(__cplusplus) +} +#endif +#endif /* !OPENLIBM_MATH_H */ + +#endif /* OPENLIBM_USE_HOST_MATH_H */ diff --git a/openlibm/ld128/Make.files b/openlibm/ld128/Make.files new file mode 100644 index 0000000000..1198cfbc40 --- /dev/null +++ b/openlibm/ld128/Make.files @@ -0,0 +1,13 @@ +$(CUR_SRCS) += invtrig.c \ + e_acoshl.c e_powl.c k_tanl.c s_exp2l.c \ + e_atanhl.c e_lgammal_r.c e_sinhl.c s_asinhl.c s_expm1l.c \ + e_coshl.c e_log10l.c e_tgammal.c \ + e_expl.c e_log2l.c k_cosl.c s_log1pl.c s_tanhl.c \ + e_logl.c k_sinl.c s_erfl.c + +# s_remquol.c e_fmodl.c s_truncl.c +# e_hypotl.c s_floorl.c s_nextafterl.c s_ceill.c s_modfl.c + +ifneq ($(OS), WINNT) +$(CUR_SRCS) += s_nanl.c +endif diff --git a/openlibm/ld128/e_acoshl.c b/openlibm/ld128/e_acoshl.c new file mode 100644 index 0000000000..e7cfd034dc --- /dev/null +++ b/openlibm/ld128/e_acoshl.c @@ -0,0 +1,58 @@ +/* @(#)e_acosh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* acoshl(x) + * Method : + * Based on + * acoshl(x) = logl [ x + sqrtl(x*x-1) ] + * we have + * acoshl(x) := logl(x)+ln2, if x is large; else + * acoshl(x) := logl(2x-1/(sqrtl(x*x-1)+x)) if x>2; else + * acoshl(x) := log1pl(t+sqrtl(2.0*t+t*t)); where t=x-1. + * + * Special cases: + * acoshl(x) is NaN with signal if x<1. + * acoshl(NaN) is NaN without signal. + */ + +#include + +#include "math_private.h" + +static const long double +one = 1.0, +ln2 = 0.6931471805599453094172321214581766L; + +long double +acoshl(long double x) +{ + long double t; + u_int64_t lx; + int64_t hx; + GET_LDOUBLE_WORDS64(hx,lx,x); + if(hx<0x3fff000000000000LL) { /* x < 1 */ + return (x-x)/(x-x); + } else if(hx >=0x4035000000000000LL) { /* x > 2**54 */ + if(hx >=0x7fff000000000000LL) { /* x is inf of NaN */ + return x+x; + } else + return logl(x)+ln2; /* acoshl(huge)=logl(2x) */ + } else if(((hx-0x3fff000000000000LL)|lx)==0) { + return 0.0L; /* acosh(1) = 0 */ + } else if (hx > 0x4000000000000000LL) { /* 2**28 > x > 2 */ + t=x*x; + return logl(2.0L*x-one/(x+sqrtl(t-one))); + } else { /* 1=0.5 + * 1 2x x + * atanhl(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) + * 2 1 - x 1 - x + * + * For x<0.5 + * atanhl(x) = 0.5*log1pl(2x+2x*x/(1-x)) + * + * Special cases: + * atanhl(x) is NaN if |x| > 1 with signal; + * atanhl(NaN) is that NaN with no signal; + * atanhl(+-1) is +-INF with signal. + * + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0L, huge = 1e4900L; + +static const long double zero = 0.0L; + +long double +atanhl(long double x) +{ + long double t; + u_int32_t jx, ix; + ieee_quad_shape_type u; + + u.value = x; + jx = u.parts32.mswhi; + ix = jx & 0x7fffffff; + u.parts32.mswhi = ix; + if (ix >= 0x3fff0000) /* |x| >= 1.0 or infinity or NaN */ + { + if (u.value == one) + return x/zero; + else + return (x-x)/(x-x); + } + if(ix<0x3fc60000 && (huge+x)>zero) return x; /* x < 2^-57 */ + + if(ix<0x3ffe0000) { /* x < 0.5 */ + t = u.value+u.value; + t = 0.5*log1pl(t+t*u.value/(one-u.value)); + } else + t = 0.5*log1pl((u.value+u.value)/(one-u.value)); + if(jx & 0x80000000) return -t; else return t; +} diff --git a/openlibm/ld128/e_coshl.c b/openlibm/ld128/e_coshl.c new file mode 100644 index 0000000000..955c378a67 --- /dev/null +++ b/openlibm/ld128/e_coshl.c @@ -0,0 +1,105 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* coshl(x) + * Method : + * mathematically coshl(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (coshl(x) = coshl(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : coshl(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : coshl(x) := ------------------- + * 2 + * 22 <= x <= lnovft : coshl(x) := expl(x)/2 + * lnovft <= x <= ln2ovft: coshl(x) := expl(x/2)/2 * expl(x/2) + * ln2ovft < x : coshl(x) := huge*huge (overflow) + * + * Special cases: + * coshl(x) is |x| if x is +INF, -INF, or NaN. + * only coshl(0)=1 is exact for finite x. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, half = 0.5, huge = 1.0e4900L, +ovf_thresh = 1.1357216553474703894801348310092223067821E4L; + +long double +coshl(long double x) +{ + long double t, w; + int32_t ex; + ieee_quad_shape_type u; + + u.value = x; + ex = u.parts32.mswhi & 0x7fffffff; + + /* Absolute value of x. */ + u.parts32.mswhi = ex; + + /* x is INF or NaN */ + if (ex >= 0x7fff0000) + return x * x; + + /* |x| in [0,0.5*ln2], return 1+expm1l(|x|)^2/(2*expl(|x|)) */ + if (ex < 0x3ffd62e4) /* 0.3465728759765625 */ + { + t = expm1l (u.value); + w = one + t; + if (ex < 0x3fb80000) /* |x| < 2^-116 */ + return w; /* cosh(tiny) = 1 */ + + return one + (t * t) / (w + w); + } + + /* |x| in [0.5*ln2,40], return (exp(|x|)+1/exp(|x|)/2; */ + if (ex < 0x40044000) + { + t = expl (u.value); + return half * t + half / t; + } + + /* |x| in [22, ln(maxdouble)] return half*exp(|x|) */ + if (ex <= 0x400c62e3) /* 11356.375 */ + return half * expl (u.value); + + /* |x| in [log(maxdouble), overflowthresold] */ + if (u.value <= ovf_thresh) + { + w = expl (half * u.value); + t = half * w; + return t * w; + } + + /* |x| > overflowthresold, cosh(x) overflow */ + return huge * huge; +} diff --git a/openlibm/ld128/e_expl.c b/openlibm/ld128/e_expl.c new file mode 100644 index 0000000000..24c7b50ce5 --- /dev/null +++ b/openlibm/ld128/e_expl.c @@ -0,0 +1,145 @@ +/* $OpenBSD: e_expl.c,v 1.3 2013/11/12 20:35:18 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* expl.c + * + * Exponential function, 128-bit long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, expl(); + * + * y = expl( x ); + * + * + * + * DESCRIPTION: + * + * Returns e (2.71828...) raised to the x power. + * + * Range reduction is accomplished by separating the argument + * into an integer k and fraction f such that + * + * x k f + * e = 2 e. + * + * A Pade' form of degree 2/3 is used to approximate exp(f) - 1 + * in the basic range [-0.5 ln 2, 0.5 ln 2]. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE +-MAXLOG 100,000 2.6e-34 8.6e-35 + * + * + * Error amplification in the exponential function can be + * a serious matter. The error propagation involves + * exp( X(1+delta) ) = exp(X) ( 1 + X*delta + ... ), + * which shows that a 1 lsb error in representing X produces + * a relative error of X times 1 lsb in the function. + * While the routine gives an accurate result for arguments + * that are exactly represented by a long double precision + * computer number, the result contains amplified roundoff + * error for large arguments not exactly represented. + * + * + * ERROR MESSAGES: + * + * message condition value returned + * exp underflow x < MINLOG 0.0 + * exp overflow x > MAXLOG MAXNUM + * + */ + +/* Exponential function */ + +#include +#include + +#include "math_private.h" + +/* Pade' coefficients for exp(x) - 1 + Theoretical peak relative error = 2.2e-37, + relative peak error spread = 9.2e-38 + */ +static long double P[5] = { + 3.279723985560247033712687707263393506266E-10L, + 6.141506007208645008909088812338454698548E-7L, + 2.708775201978218837374512615596512792224E-4L, + 3.508710990737834361215404761139478627390E-2L, + 9.999999999999999999999999999999999998502E-1L +}; +static long double Q[6] = { + 2.980756652081995192255342779918052538681E-12L, + 1.771372078166251484503904874657985291164E-8L, + 1.504792651814944826817779302637284053660E-5L, + 3.611828913847589925056132680618007270344E-3L, + 2.368408864814233538909747618894558968880E-1L, + 2.000000000000000000000000000000000000150E0L +}; +/* C1 + C2 = ln 2 */ +static const long double C1 = -6.93145751953125E-1L; +static const long double C2 = -1.428606820309417232121458176568075500134E-6L; + +static const long double LOG2EL = 1.442695040888963407359924681001892137426646L; +static const long double MAXLOGL = 1.1356523406294143949491931077970764891253E4L; +static const long double MINLOGL = -1.143276959615573793352782661133116431383730e4L; +static const long double huge = 0x1p10000L; +#if 0 /* XXX Prevent gcc from erroneously constant folding this. */ +static const long double twom10000 = 0x1p-10000L; +#else +static volatile long double twom10000 = 0x1p-10000L; +#endif + +long double +expl(long double x) +{ +long double px, xx; +int n; + +if( x > MAXLOGL) + return (huge*huge); /* overflow */ + +if( x < MINLOGL ) + return (twom10000*twom10000); /* underflow */ + +/* Express e**x = e**g 2**n + * = e**g e**( n loge(2) ) + * = e**( g + n loge(2) ) + */ +px = floorl( LOG2EL * x + 0.5L ); /* floor() truncates toward -infinity. */ +n = px; +x += px * C1; +x += px * C2; +/* rational approximation for exponential + * of the fractional part: + * e**x = 1 + 2x P(x**2)/( Q(x**2) - P(x**2) ) + */ +xx = x * x; +px = x * __polevll( xx, P, 4 ); +xx = __polevll( xx, Q, 5 ); +x = px/( xx - px ); +x = 1.0L + x + x; + +x = ldexpl( x, n ); +return(x); +} diff --git a/openlibm/ld128/e_fmodl.c b/openlibm/ld128/e_fmodl.c new file mode 100644 index 0000000000..698fa3ab91 --- /dev/null +++ b/openlibm/ld128/e_fmodl.c @@ -0,0 +1,129 @@ +/* @(#)e_fmod.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fmodl(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, Zero[] = {0.0, -0.0,}; + +long double +fmodl(long double x, long double y) +{ + int64_t n,hx,hy,hz,ix,iy,sx,i; + u_int64_t lx,ly,lz; + + GET_LDOUBLE_WORDS64(hx,lx,x); + GET_LDOUBLE_WORDS64(hy,ly,y); + sx = hx&0x8000000000000000ULL; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffffffffffffLL; /* |y| */ + + /* purge off exception values */ + if((hy|ly)==0||(hx>=0x7fff000000000000LL)|| /* y=0,or x not finite */ + ((hy|((ly|-ly)>>63))>0x7fff000000000000LL)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<=hy) { + if((hx>63]; /* |x|=|y| return x*0*/ + } + + /* determine ix = ilogb(x) */ + if(hx<0x0001000000000000LL) { /* subnormal x */ + if(hx==0) { + for (ix = -16431, i=lx; i>0; i<<=1) ix -=1; + } else { + for (ix = -16382, i=hx<<15; i>0; i<<=1) ix -=1; + } + } else ix = (hx>>48)-0x3fff; + + /* determine iy = ilogb(y) */ + if(hy<0x0001000000000000LL) { /* subnormal y */ + if(hy==0) { + for (iy = -16431, i=ly; i>0; i<<=1) iy -=1; + } else { + for (iy = -16382, i=hy<<15; i>0; i<<=1) iy -=1; + } + } else iy = (hy>>48)-0x3fff; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -16382) + hx = 0x0001000000000000LL|(0x0000ffffffffffffLL&hx); + else { /* subnormal x, shift x to normal */ + n = -16382-ix; + if(n<=63) { + hx = (hx<>(64-n)); + lx <<= n; + } else { + hx = lx<<(n-64); + lx = 0; + } + } + if(iy >= -16382) + hy = 0x0001000000000000LL|(0x0000ffffffffffffLL&hy); + else { /* subnormal y, shift y to normal */ + n = -16382-iy; + if(n<=63) { + hy = (hy<>(64-n)); + ly <<= n; + } else { + hy = ly<<(n-64); + ly = 0; + } + } + + /* fix point fmod */ + n = ix - iy; + while(n--) { + hz=hx-hy;lz=lx-ly; if(lx>63); lx = lx+lx;} + else { + if((hz|lz)==0) /* return sign(x)*0 */ + return Zero[(u_int64_t)sx>>63]; + hx = hz+hz+(lz>>63); lx = lz+lz; + } + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) /* return sign(x)*0 */ + return Zero[(u_int64_t)sx>>63]; + while(hx<0x0001000000000000LL) { /* normalize x */ + hx = hx+hx+(lx>>63); lx = lx+lx; + iy -= 1; + } + if(iy>= -16382) { /* normalize output */ + hx = ((hx-0x0001000000000000LL)|((iy+16383)<<48)); + SET_LDOUBLE_WORDS64(x,hx|sx,lx); + } else { /* subnormal output */ + n = -16382 - iy; + if(n<=48) { + lx = (lx>>n)|((u_int64_t)hx<<(64-n)); + hx >>= n; + } else if (n<=63) { + lx = (hx<<(64-n))|(lx>>n); hx = sx; + } else { + lx = hx>>(n-64); hx = sx; + } + SET_LDOUBLE_WORDS64(x,hx|sx,lx); + x *= one; /* create necessary signal */ + } + return x; /* exact output */ +} diff --git a/openlibm/ld128/e_hypotl.c b/openlibm/ld128/e_hypotl.c new file mode 100644 index 0000000000..1cdf3d8ceb --- /dev/null +++ b/openlibm/ld128/e_hypotl.c @@ -0,0 +1,122 @@ +/* @(#)e_hypot.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* hypotl(x,y) + * + * Method : + * If (assume round-to-nearest) z=x*x+y*y + * has error less than sqrtl(2)/2 ulp, than + * sqrtl(z) has error less than 1 ulp (exercise). + * + * So, compute sqrtl(x*x+y*y) with some care as + * follows to get the error below 1 ulp: + * + * Assume x>y>0; + * (if possible, set rounding to round-to-nearest) + * 1. if x > 2y use + * x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y + * where x1 = x with lower 64 bits cleared, x2 = x-x1; else + * 2. if x <= 2y use + * t1*yy1+((x-y)*(x-y)+(t1*y2+t2*y)) + * where t1 = 2x with lower 64 bits cleared, t2 = 2x-t1, + * yy1= y with lower 64 bits chopped, y2 = y-yy1. + * + * NOTE: scaling may be necessary if some argument is too + * large or too tiny + * + * Special cases: + * hypotl(x,y) is INF if x or y is +INF or -INF; else + * hypotl(x,y) is NAN if x or y is NAN. + * + * Accuracy: + * hypotl(x,y) returns sqrtl(x^2+y^2) with error less + * than 1 ulps (units in the last place) + */ + +#include + +#include "math_private.h" + +long double +hypotl(long double x, long double y) +{ + long double a,b,t1,t2,yy1,y2,w; + int64_t j,k,ha,hb; + + GET_LDOUBLE_MSW64(ha,x); + ha &= 0x7fffffffffffffffLL; + GET_LDOUBLE_MSW64(hb,y); + hb &= 0x7fffffffffffffffLL; + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + SET_LDOUBLE_MSW64(a,ha); /* a <- |a| */ + SET_LDOUBLE_MSW64(b,hb); /* b <- |b| */ + if((ha-hb)>0x78000000000000LL) {return a+b;} /* x/y > 2**120 */ + k=0; + if(ha > 0x5f3f000000000000LL) { /* a>2**8000 */ + if(ha >= 0x7fff000000000000LL) { /* Inf or NaN */ + u_int64_t low; + w = a+b; /* for sNaN */ + GET_LDOUBLE_LSW64(low,a); + if(((ha&0xffffffffffffLL)|low)==0) w = a; + GET_LDOUBLE_LSW64(low,b); + if(((hb^0x7fff000000000000LL)|low)==0) w = b; + return w; + } + /* scale a and b by 2**-9600 */ + ha -= 0x2580000000000000LL; + hb -= 0x2580000000000000LL; k += 9600; + SET_LDOUBLE_MSW64(a,ha); + SET_LDOUBLE_MSW64(b,hb); + } + if(hb < 0x20bf000000000000LL) { /* b < 2**-8000 */ + if(hb <= 0x0000ffffffffffffLL) { /* subnormal b or 0 */ + u_int64_t low; + GET_LDOUBLE_LSW64(low,b); + if((hb|low)==0) return a; + t1=0; + SET_LDOUBLE_MSW64(t1,0x7ffd000000000000LL); /* t1=2^16382 */ + b *= t1; + a *= t1; + k -= 16382; + } else { /* scale a and b by 2^9600 */ + ha += 0x2580000000000000LL; /* a *= 2^9600 */ + hb += 0x2580000000000000LL; /* b *= 2^9600 */ + k -= 9600; + SET_LDOUBLE_MSW64(a,ha); + SET_LDOUBLE_MSW64(b,hb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + t1 = 0; + SET_LDOUBLE_MSW64(t1,ha); + t2 = a-t1; + w = sqrtl(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + yy1 = 0; + SET_LDOUBLE_MSW64(yy1,hb); + y2 = b - yy1; + t1 = 0; + SET_LDOUBLE_MSW64(t1,ha+0x0001000000000000LL); + t2 = a - t1; + w = sqrtl(t1*yy1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + u_int64_t high; + t1 = 1.0L; + GET_LDOUBLE_MSW64(high,t1); + SET_LDOUBLE_MSW64(t1,high+(k<<48)); + return t1*w; + } else return w; +} diff --git a/openlibm/ld128/e_lgammal_r.c b/openlibm/ld128/e_lgammal_r.c new file mode 100644 index 0000000000..dd8ea5d46d --- /dev/null +++ b/openlibm/ld128/e_lgammal_r.c @@ -0,0 +1,1037 @@ +/* $OpenBSD: e_lgammal.c,v 1.3 2011/07/09 05:29:06 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* lgammal_r + * + * Natural logarithm of gamma function + * + * + * + * SYNOPSIS: + * + * long double x, y, lgammal_r(); + * int signgam; + * + * y = lgammal_r(x, &signgam); + * + * + * + * DESCRIPTION: + * + * Returns the base e (2.718...) logarithm of the absolute + * value of the gamma function of the argument. + * The sign (+1 or -1) of the gamma function is returned through signgamp. + * + * The positive domain is partitioned into numerous segments for approximation. + * For x > 10, + * log gamma(x) = (x - 0.5) log(x) - x + log sqrt(2 pi) + 1/x R(1/x^2) + * Near the minimum at x = x0 = 1.46... the approximation is + * log gamma(x0 + z) = log gamma(x0) + z^2 P(z)/Q(z) + * for small z. + * Elsewhere between 0 and 10, + * log gamma(n + z) = log gamma(n) + z P(z)/Q(z) + * for various selected n and small z. + * + * The cosecant reflection formula is employed for negative arguments. + * + * + * + * ACCURACY: + * + * + * arithmetic domain # trials peak rms + * Relative error: + * IEEE 10, 30 100000 3.9e-34 9.8e-35 + * IEEE 0, 10 100000 3.8e-34 5.3e-35 + * Absolute error: + * IEEE -10, 0 100000 8.0e-34 8.0e-35 + * IEEE -30, -10 100000 4.4e-34 1.0e-34 + * IEEE -100, 100 100000 1.0e-34 + * + * The absolute error criterion is the same as relative error + * when the function magnitude is greater than one but it is absolute + * when the magnitude is less than one. + * + */ + +#include + +#include "math_private.h" + +static const long double PIL = 3.1415926535897932384626433832795028841972E0L; +static const long double MAXLGM = 1.0485738685148938358098967157129705071571E4928L; +static const long double one = 1.0L; +static const long double huge = 1.0e4000L; + +/* log gamma(x) = ( x - 0.5 ) * log(x) - x + LS2PI + 1/x P(1/x^2) + 1/x <= 0.0741 (x >= 13.495...) + Peak relative error 1.5e-36 */ +static const long double ls2pi = 9.1893853320467274178032973640561763986140E-1L; +#define NRASY 12 +static const long double RASY[NRASY + 1] = +{ + 8.333333333333333333333333333310437112111E-2L, + -2.777777777777777777777774789556228296902E-3L, + 7.936507936507936507795933938448586499183E-4L, + -5.952380952380952041799269756378148574045E-4L, + 8.417508417507928904209891117498524452523E-4L, + -1.917526917481263997778542329739806086290E-3L, + 6.410256381217852504446848671499409919280E-3L, + -2.955064066900961649768101034477363301626E-2L, + 1.796402955865634243663453415388336954675E-1L, + -1.391522089007758553455753477688592767741E0L, + 1.326130089598399157988112385013829305510E1L, + -1.420412699593782497803472576479997819149E2L, + 1.218058922427762808938869872528846787020E3L +}; + + +/* log gamma(x+13) = log gamma(13) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 12.5 <= x+13 <= 13.5 + Peak relative error 1.1e-36 */ +static const long double lgam13a = 1.9987213134765625E1L; +static const long double lgam13b = 1.3608962611495173623870550785125024484248E-6L; +#define NRN13 7 +static const long double RN13[NRN13 + 1] = +{ + 8.591478354823578150238226576156275285700E11L, + 2.347931159756482741018258864137297157668E11L, + 2.555408396679352028680662433943000804616E10L, + 1.408581709264464345480765758902967123937E9L, + 4.126759849752613822953004114044451046321E7L, + 6.133298899622688505854211579222889943778E5L, + 3.929248056293651597987893340755876578072E3L, + 6.850783280018706668924952057996075215223E0L +}; +#define NRD13 6 +static const long double RD13[NRD13 + 1] = +{ + 3.401225382297342302296607039352935541669E11L, + 8.756765276918037910363513243563234551784E10L, + 8.873913342866613213078554180987647243903E9L, + 4.483797255342763263361893016049310017973E8L, + 1.178186288833066430952276702931512870676E7L, + 1.519928623743264797939103740132278337476E5L, + 7.989298844938119228411117593338850892311E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+12) = log gamma(12) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 11.5 <= x+12 <= 12.5 + Peak relative error 4.1e-36 */ +static const long double lgam12a = 1.75023040771484375E1L; +static const long double lgam12b = 3.7687254483392876529072161996717039575982E-6L; +#define NRN12 7 +static const long double RN12[NRN12 + 1] = +{ + 4.709859662695606986110997348630997559137E11L, + 1.398713878079497115037857470168777995230E11L, + 1.654654931821564315970930093932954900867E10L, + 9.916279414876676861193649489207282144036E8L, + 3.159604070526036074112008954113411389879E7L, + 5.109099197547205212294747623977502492861E5L, + 3.563054878276102790183396740969279826988E3L, + 6.769610657004672719224614163196946862747E0L +}; +#define NRD12 6 +static const long double RD12[NRD12 + 1] = +{ + 1.928167007860968063912467318985802726613E11L, + 5.383198282277806237247492369072266389233E10L, + 5.915693215338294477444809323037871058363E9L, + 3.241438287570196713148310560147925781342E8L, + 9.236680081763754597872713592701048455890E6L, + 1.292246897881650919242713651166596478850E5L, + 7.366532445427159272584194816076600211171E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+11) = log gamma(11) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 10.5 <= x+11 <= 11.5 + Peak relative error 1.8e-35 */ +static const long double lgam11a = 1.5104400634765625E1L; +static const long double lgam11b = 1.1938309890295225709329251070371882250744E-5L; +#define NRN11 7 +static const long double RN11[NRN11 + 1] = +{ + 2.446960438029415837384622675816736622795E11L, + 7.955444974446413315803799763901729640350E10L, + 1.030555327949159293591618473447420338444E10L, + 6.765022131195302709153994345470493334946E8L, + 2.361892792609204855279723576041468347494E7L, + 4.186623629779479136428005806072176490125E5L, + 3.202506022088912768601325534149383594049E3L, + 6.681356101133728289358838690666225691363E0L +}; +#define NRD11 6 +static const long double RD11[NRD11 + 1] = +{ + 1.040483786179428590683912396379079477432E11L, + 3.172251138489229497223696648369823779729E10L, + 3.806961885984850433709295832245848084614E9L, + 2.278070344022934913730015420611609620171E8L, + 7.089478198662651683977290023829391596481E6L, + 1.083246385105903533237139380509590158658E5L, + 6.744420991491385145885727942219463243597E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+10) = log gamma(10) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 9.5 <= x+10 <= 10.5 + Peak relative error 5.4e-37 */ +static const long double lgam10a = 1.280181884765625E1L; +static const long double lgam10b = 8.6324252196112077178745667061642811492557E-6L; +#define NRN10 7 +static const long double RN10[NRN10 + 1] = +{ + -1.239059737177249934158597996648808363783E14L, + -4.725899566371458992365624673357356908719E13L, + -7.283906268647083312042059082837754850808E12L, + -5.802855515464011422171165179767478794637E11L, + -2.532349691157548788382820303182745897298E10L, + -5.884260178023777312587193693477072061820E8L, + -6.437774864512125749845840472131829114906E6L, + -2.350975266781548931856017239843273049384E4L +}; +#define NRD10 7 +static const long double RD10[NRD10 + 1] = +{ + -5.502645997581822567468347817182347679552E13L, + -1.970266640239849804162284805400136473801E13L, + -2.819677689615038489384974042561531409392E12L, + -2.056105863694742752589691183194061265094E11L, + -8.053670086493258693186307810815819662078E9L, + -1.632090155573373286153427982504851867131E8L, + -1.483575879240631280658077826889223634921E6L, + -4.002806669713232271615885826373550502510E3L + /* 1.0E0L */ +}; + + +/* log gamma(x+9) = log gamma(9) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 8.5 <= x+9 <= 9.5 + Peak relative error 3.6e-36 */ +static const long double lgam9a = 1.06045989990234375E1L; +static const long double lgam9b = 3.9037218127284172274007216547549861681400E-6L; +#define NRN9 7 +static const long double RN9[NRN9 + 1] = +{ + -4.936332264202687973364500998984608306189E13L, + -2.101372682623700967335206138517766274855E13L, + -3.615893404644823888655732817505129444195E12L, + -3.217104993800878891194322691860075472926E11L, + -1.568465330337375725685439173603032921399E10L, + -4.073317518162025744377629219101510217761E8L, + -4.983232096406156139324846656819246974500E6L, + -2.036280038903695980912289722995505277253E4L +}; +#define NRD9 7 +static const long double RD9[NRD9 + 1] = +{ + -2.306006080437656357167128541231915480393E13L, + -9.183606842453274924895648863832233799950E12L, + -1.461857965935942962087907301194381010380E12L, + -1.185728254682789754150068652663124298303E11L, + -5.166285094703468567389566085480783070037E9L, + -1.164573656694603024184768200787835094317E8L, + -1.177343939483908678474886454113163527909E6L, + -3.529391059783109732159524500029157638736E3L + /* 1.0E0L */ +}; + + +/* log gamma(x+8) = log gamma(8) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 7.5 <= x+8 <= 8.5 + Peak relative error 2.4e-37 */ +static const long double lgam8a = 8.525146484375E0L; +static const long double lgam8b = 1.4876690414300165531036347125050759667737E-5L; +#define NRN8 8 +static const long double RN8[NRN8 + 1] = +{ + 6.600775438203423546565361176829139703289E11L, + 3.406361267593790705240802723914281025800E11L, + 7.222460928505293914746983300555538432830E10L, + 8.102984106025088123058747466840656458342E9L, + 5.157620015986282905232150979772409345927E8L, + 1.851445288272645829028129389609068641517E7L, + 3.489261702223124354745894067468953756656E5L, + 2.892095396706665774434217489775617756014E3L, + 6.596977510622195827183948478627058738034E0L +}; +#define NRD8 7 +static const long double RD8[NRD8 + 1] = +{ + 3.274776546520735414638114828622673016920E11L, + 1.581811207929065544043963828487733970107E11L, + 3.108725655667825188135393076860104546416E10L, + 3.193055010502912617128480163681842165730E9L, + 1.830871482669835106357529710116211541839E8L, + 5.790862854275238129848491555068073485086E6L, + 9.305213264307921522842678835618803553589E4L, + 6.216974105861848386918949336819572333622E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+7) = log gamma(7) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 6.5 <= x+7 <= 7.5 + Peak relative error 3.2e-36 */ +static const long double lgam7a = 6.5792388916015625E0L; +static const long double lgam7b = 1.2320408538495060178292903945321122583007E-5L; +#define NRN7 8 +static const long double RN7[NRN7 + 1] = +{ + 2.065019306969459407636744543358209942213E11L, + 1.226919919023736909889724951708796532847E11L, + 2.996157990374348596472241776917953749106E10L, + 3.873001919306801037344727168434909521030E9L, + 2.841575255593761593270885753992732145094E8L, + 1.176342515359431913664715324652399565551E7L, + 2.558097039684188723597519300356028511547E5L, + 2.448525238332609439023786244782810774702E3L, + 6.460280377802030953041566617300902020435E0L +}; +#define NRD7 7 +static const long double RD7[NRD7 + 1] = +{ + 1.102646614598516998880874785339049304483E11L, + 6.099297512712715445879759589407189290040E10L, + 1.372898136289611312713283201112060238351E10L, + 1.615306270420293159907951633566635172343E9L, + 1.061114435798489135996614242842561967459E8L, + 3.845638971184305248268608902030718674691E6L, + 7.081730675423444975703917836972720495507E4L, + 5.423122582741398226693137276201344096370E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+6) = log gamma(6) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 5.5 <= x+6 <= 6.5 + Peak relative error 6.2e-37 */ +static const long double lgam6a = 4.7874908447265625E0L; +static const long double lgam6b = 8.9805548349424770093452324304839959231517E-7L; +#define NRN6 8 +static const long double RN6[NRN6 + 1] = +{ + -3.538412754670746879119162116819571823643E13L, + -2.613432593406849155765698121483394257148E13L, + -8.020670732770461579558867891923784753062E12L, + -1.322227822931250045347591780332435433420E12L, + -1.262809382777272476572558806855377129513E11L, + -7.015006277027660872284922325741197022467E9L, + -2.149320689089020841076532186783055727299E8L, + -3.167210585700002703820077565539658995316E6L, + -1.576834867378554185210279285358586385266E4L +}; +#define NRD6 8 +static const long double RD6[NRD6 + 1] = +{ + -2.073955870771283609792355579558899389085E13L, + -1.421592856111673959642750863283919318175E13L, + -4.012134994918353924219048850264207074949E12L, + -6.013361045800992316498238470888523722431E11L, + -5.145382510136622274784240527039643430628E10L, + -2.510575820013409711678540476918249524123E9L, + -6.564058379709759600836745035871373240904E7L, + -7.861511116647120540275354855221373571536E5L, + -2.821943442729620524365661338459579270561E3L + /* 1.0E0L */ +}; + + +/* log gamma(x+5) = log gamma(5) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 4.5 <= x+5 <= 5.5 + Peak relative error 3.4e-37 */ +static const long double lgam5a = 3.17803955078125E0L; +static const long double lgam5b = 1.4279566695619646941601297055408873990961E-5L; +#define NRN5 9 +static const long double RN5[NRN5 + 1] = +{ + 2.010952885441805899580403215533972172098E11L, + 1.916132681242540921354921906708215338584E11L, + 7.679102403710581712903937970163206882492E10L, + 1.680514903671382470108010973615268125169E10L, + 2.181011222911537259440775283277711588410E9L, + 1.705361119398837808244780667539728356096E8L, + 7.792391565652481864976147945997033946360E6L, + 1.910741381027985291688667214472560023819E5L, + 2.088138241893612679762260077783794329559E3L, + 6.330318119566998299106803922739066556550E0L +}; +#define NRD5 8 +static const long double RD5[NRD5 + 1] = +{ + 1.335189758138651840605141370223112376176E11L, + 1.174130445739492885895466097516530211283E11L, + 4.308006619274572338118732154886328519910E10L, + 8.547402888692578655814445003283720677468E9L, + 9.934628078575618309542580800421370730906E8L, + 6.847107420092173812998096295422311820672E7L, + 2.698552646016599923609773122139463150403E6L, + 5.526516251532464176412113632726150253215E4L, + 4.772343321713697385780533022595450486932E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+4) = log gamma(4) + x P(x)/Q(x) + -0.5 <= x <= 0.5 + 3.5 <= x+4 <= 4.5 + Peak relative error 6.7e-37 */ +static const long double lgam4a = 1.791748046875E0L; +static const long double lgam4b = 1.1422353055000812477358380702272722990692E-5L; +#define NRN4 9 +static const long double RN4[NRN4 + 1] = +{ + -1.026583408246155508572442242188887829208E13L, + -1.306476685384622809290193031208776258809E13L, + -7.051088602207062164232806511992978915508E12L, + -2.100849457735620004967624442027793656108E12L, + -3.767473790774546963588549871673843260569E11L, + -4.156387497364909963498394522336575984206E10L, + -2.764021460668011732047778992419118757746E9L, + -1.036617204107109779944986471142938641399E8L, + -1.895730886640349026257780896972598305443E6L, + -1.180509051468390914200720003907727988201E4L +}; +#define NRD4 9 +static const long double RD4[NRD4 + 1] = +{ + -8.172669122056002077809119378047536240889E12L, + -9.477592426087986751343695251801814226960E12L, + -4.629448850139318158743900253637212801682E12L, + -1.237965465892012573255370078308035272942E12L, + -1.971624313506929845158062177061297598956E11L, + -1.905434843346570533229942397763361493610E10L, + -1.089409357680461419743730978512856675984E9L, + -3.416703082301143192939774401370222822430E7L, + -4.981791914177103793218433195857635265295E5L, + -2.192507743896742751483055798411231453733E3L + /* 1.0E0L */ +}; + + +/* log gamma(x+3) = log gamma(3) + x P(x)/Q(x) + -0.25 <= x <= 0.5 + 2.75 <= x+3 <= 3.5 + Peak relative error 6.0e-37 */ +static const long double lgam3a = 6.93145751953125E-1L; +static const long double lgam3b = 1.4286068203094172321214581765680755001344E-6L; + +#define NRN3 9 +static const long double RN3[NRN3 + 1] = +{ + -4.813901815114776281494823863935820876670E11L, + -8.425592975288250400493910291066881992620E11L, + -6.228685507402467503655405482985516909157E11L, + -2.531972054436786351403749276956707260499E11L, + -6.170200796658926701311867484296426831687E10L, + -9.211477458528156048231908798456365081135E9L, + -8.251806236175037114064561038908691305583E8L, + -4.147886355917831049939930101151160447495E7L, + -1.010851868928346082547075956946476932162E6L, + -8.333374463411801009783402800801201603736E3L +}; +#define NRD3 9 +static const long double RD3[NRD3 + 1] = +{ + -5.216713843111675050627304523368029262450E11L, + -8.014292925418308759369583419234079164391E11L, + -5.180106858220030014546267824392678611990E11L, + -1.830406975497439003897734969120997840011E11L, + -3.845274631904879621945745960119924118925E10L, + -4.891033385370523863288908070309417710903E9L, + -3.670172254411328640353855768698287474282E8L, + -1.505316381525727713026364396635522516989E7L, + -2.856327162923716881454613540575964890347E5L, + -1.622140448015769906847567212766206894547E3L + /* 1.0E0L */ +}; + + +/* log gamma(x+2.5) = log gamma(2.5) + x P(x)/Q(x) + -0.125 <= x <= 0.25 + 2.375 <= x+2.5 <= 2.75 */ +static const long double lgam2r5a = 2.8466796875E-1L; +static const long double lgam2r5b = 1.4901722919159632494669682701924320137696E-5L; +#define NRN2r5 8 +static const long double RN2r5[NRN2r5 + 1] = +{ + -4.676454313888335499356699817678862233205E9L, + -9.361888347911187924389905984624216340639E9L, + -7.695353600835685037920815799526540237703E9L, + -3.364370100981509060441853085968900734521E9L, + -8.449902011848163568670361316804900559863E8L, + -1.225249050950801905108001246436783022179E8L, + -9.732972931077110161639900388121650470926E6L, + -3.695711763932153505623248207576425983573E5L, + -4.717341584067827676530426007495274711306E3L +}; +#define NRD2r5 8 +static const long double RD2r5[NRD2r5 + 1] = +{ + -6.650657966618993679456019224416926875619E9L, + -1.099511409330635807899718829033488771623E10L, + -7.482546968307837168164311101447116903148E9L, + -2.702967190056506495988922973755870557217E9L, + -5.570008176482922704972943389590409280950E8L, + -6.536934032192792470926310043166993233231E7L, + -4.101991193844953082400035444146067511725E6L, + -1.174082735875715802334430481065526664020E5L, + -9.932840389994157592102947657277692978511E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+2) = x P(x)/Q(x) + -0.125 <= x <= +0.375 + 1.875 <= x+2 <= 2.375 + Peak relative error 4.6e-36 */ +#define NRN2 9 +static const long double RN2[NRN2 + 1] = +{ + -3.716661929737318153526921358113793421524E9L, + -1.138816715030710406922819131397532331321E10L, + -1.421017419363526524544402598734013569950E10L, + -9.510432842542519665483662502132010331451E9L, + -3.747528562099410197957514973274474767329E9L, + -8.923565763363912474488712255317033616626E8L, + -1.261396653700237624185350402781338231697E8L, + -9.918402520255661797735331317081425749014E6L, + -3.753996255897143855113273724233104768831E5L, + -4.778761333044147141559311805999540765612E3L +}; +#define NRD2 9 +static const long double RD2[NRD2 + 1] = +{ + -8.790916836764308497770359421351673950111E9L, + -2.023108608053212516399197678553737477486E10L, + -1.958067901852022239294231785363504458367E10L, + -1.035515043621003101254252481625188704529E10L, + -3.253884432621336737640841276619272224476E9L, + -6.186383531162456814954947669274235815544E8L, + -6.932557847749518463038934953605969951466E7L, + -4.240731768287359608773351626528479703758E6L, + -1.197343995089189188078944689846348116630E5L, + -1.004622911670588064824904487064114090920E3L +/* 1.0E0 */ +}; + + +/* log gamma(x+1.75) = log gamma(1.75) + x P(x)/Q(x) + -0.125 <= x <= +0.125 + 1.625 <= x+1.75 <= 1.875 + Peak relative error 9.2e-37 */ +static const long double lgam1r75a = -8.441162109375E-2L; +static const long double lgam1r75b = 1.0500073264444042213965868602268256157604E-5L; +#define NRN1r75 8 +static const long double RN1r75[NRN1r75 + 1] = +{ + -5.221061693929833937710891646275798251513E7L, + -2.052466337474314812817883030472496436993E8L, + -2.952718275974940270675670705084125640069E8L, + -2.132294039648116684922965964126389017840E8L, + -8.554103077186505960591321962207519908489E7L, + -1.940250901348870867323943119132071960050E7L, + -2.379394147112756860769336400290402208435E6L, + -1.384060879999526222029386539622255797389E5L, + -2.698453601378319296159355612094598695530E3L +}; +#define NRD1r75 8 +static const long double RD1r75[NRD1r75 + 1] = +{ + -2.109754689501705828789976311354395393605E8L, + -5.036651829232895725959911504899241062286E8L, + -4.954234699418689764943486770327295098084E8L, + -2.589558042412676610775157783898195339410E8L, + -7.731476117252958268044969614034776883031E7L, + -1.316721702252481296030801191240867486965E7L, + -1.201296501404876774861190604303728810836E6L, + -5.007966406976106636109459072523610273928E4L, + -6.155817990560743422008969155276229018209E2L + /* 1.0E0L */ +}; + + +/* log gamma(x+x0) = y0 + x^2 P(x)/Q(x) + -0.0867 <= x <= +0.1634 + 1.374932... <= x+x0 <= 1.625032... + Peak relative error 4.0e-36 */ +static const long double x0a = 1.4616241455078125L; +static const long double x0b = 7.9994605498412626595423257213002588621246E-6L; +static const long double y0a = -1.21490478515625E-1L; +static const long double y0b = 4.1879797753919044854428223084178486438269E-6L; +#define NRN1r5 8 +static const long double RN1r5[NRN1r5 + 1] = +{ + 6.827103657233705798067415468881313128066E5L, + 1.910041815932269464714909706705242148108E6L, + 2.194344176925978377083808566251427771951E6L, + 1.332921400100891472195055269688876427962E6L, + 4.589080973377307211815655093824787123508E5L, + 8.900334161263456942727083580232613796141E4L, + 9.053840838306019753209127312097612455236E3L, + 4.053367147553353374151852319743594873771E2L, + 5.040631576303952022968949605613514584950E0L +}; +#define NRD1r5 8 +static const long double RD1r5[NRD1r5 + 1] = +{ + 1.411036368843183477558773688484699813355E6L, + 4.378121767236251950226362443134306184849E6L, + 5.682322855631723455425929877581697918168E6L, + 3.999065731556977782435009349967042222375E6L, + 1.653651390456781293163585493620758410333E6L, + 4.067774359067489605179546964969435858311E5L, + 5.741463295366557346748361781768833633256E4L, + 4.226404539738182992856094681115746692030E3L, + 1.316980975410327975566999780608618774469E2L, + /* 1.0E0L */ +}; + + +/* log gamma(x+1.25) = log gamma(1.25) + x P(x)/Q(x) + -.125 <= x <= +.125 + 1.125 <= x+1.25 <= 1.375 + Peak relative error = 4.9e-36 */ +static const long double lgam1r25a = -9.82818603515625E-2L; +static const long double lgam1r25b = 1.0023929749338536146197303364159774377296E-5L; +#define NRN1r25 9 +static const long double RN1r25[NRN1r25 + 1] = +{ + -9.054787275312026472896002240379580536760E4L, + -8.685076892989927640126560802094680794471E4L, + 2.797898965448019916967849727279076547109E5L, + 6.175520827134342734546868356396008898299E5L, + 5.179626599589134831538516906517372619641E5L, + 2.253076616239043944538380039205558242161E5L, + 5.312653119599957228630544772499197307195E4L, + 6.434329437514083776052669599834938898255E3L, + 3.385414416983114598582554037612347549220E2L, + 4.907821957946273805080625052510832015792E0L +}; +#define NRD1r25 8 +static const long double RD1r25[NRD1r25 + 1] = +{ + 3.980939377333448005389084785896660309000E5L, + 1.429634893085231519692365775184490465542E6L, + 2.145438946455476062850151428438668234336E6L, + 1.743786661358280837020848127465970357893E6L, + 8.316364251289743923178092656080441655273E5L, + 2.355732939106812496699621491135458324294E5L, + 3.822267399625696880571810137601310855419E4L, + 3.228463206479133236028576845538387620856E3L, + 1.152133170470059555646301189220117965514E2L + /* 1.0E0L */ +}; + + +/* log gamma(x + 1) = x P(x)/Q(x) + 0.0 <= x <= +0.125 + 1.0 <= x+1 <= 1.125 + Peak relative error 1.1e-35 */ +#define NRN1 8 +static const long double RN1[NRN1 + 1] = +{ + -9.987560186094800756471055681088744738818E3L, + -2.506039379419574361949680225279376329742E4L, + -1.386770737662176516403363873617457652991E4L, + 1.439445846078103202928677244188837130744E4L, + 2.159612048879650471489449668295139990693E4L, + 1.047439813638144485276023138173676047079E4L, + 2.250316398054332592560412486630769139961E3L, + 1.958510425467720733041971651126443864041E2L, + 4.516830313569454663374271993200291219855E0L +}; +#define NRD1 7 +static const long double RD1[NRD1 + 1] = +{ + 1.730299573175751778863269333703788214547E4L, + 6.807080914851328611903744668028014678148E4L, + 1.090071629101496938655806063184092302439E5L, + 9.124354356415154289343303999616003884080E4L, + 4.262071638655772404431164427024003253954E4L, + 1.096981664067373953673982635805821283581E4L, + 1.431229503796575892151252708527595787588E3L, + 7.734110684303689320830401788262295992921E1L + /* 1.0E0 */ +}; + + +/* log gamma(x + 1) = x P(x)/Q(x) + -0.125 <= x <= 0 + 0.875 <= x+1 <= 1.0 + Peak relative error 7.0e-37 */ +#define NRNr9 8 +static const long double RNr9[NRNr9 + 1] = +{ + 4.441379198241760069548832023257571176884E5L, + 1.273072988367176540909122090089580368732E6L, + 9.732422305818501557502584486510048387724E5L, + -5.040539994443998275271644292272870348684E5L, + -1.208719055525609446357448132109723786736E6L, + -7.434275365370936547146540554419058907156E5L, + -2.075642969983377738209203358199008185741E5L, + -2.565534860781128618589288075109372218042E4L, + -1.032901669542994124131223797515913955938E3L, +}; +#define NRDr9 8 +static const long double RDr9[NRDr9 + 1] = +{ + -7.694488331323118759486182246005193998007E5L, + -3.301918855321234414232308938454112213751E6L, + -5.856830900232338906742924836032279404702E6L, + -5.540672519616151584486240871424021377540E6L, + -3.006530901041386626148342989181721176919E6L, + -9.350378280513062139466966374330795935163E5L, + -1.566179100031063346901755685375732739511E5L, + -1.205016539620260779274902967231510804992E4L, + -2.724583156305709733221564484006088794284E2L +/* 1.0E0 */ +}; + + +/* Evaluate P[n] x^n + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +neval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + +/* Evaluate x^n+1 + P[n] x^(n) + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +deval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = x + *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + +long double +lgammal_r(long double x, int *signgamp) +{ + long double p, q, w, z, nx; + int i, nn; + + *signgamp = 1; + + if (!isfinite (x)) + return x * x; + + if (x == 0.0L) + { + if (signbit (x)) + *signgamp = -1; + return one / fabsl (x); + } + + if (x < 0.0L) + { + q = -x; + p = floorl (q); + if (p == q) + return (one / (p - p)); + i = p; + if ((i & 1) == 0) + *signgamp = -1; + else + *signgamp = 1; + z = q - p; + if (z > 0.5L) + { + p += 1.0L; + z = p - q; + } + z = q * sinl (PIL * z); + if (z == 0.0L) + return (*signgamp * huge * huge); + w = lgammal (q); + z = logl (PIL / z) - w; + return (z); + } + + if (x < 13.5L) + { + p = 0.0L; + nx = floorl (x + 0.5L); + nn = nx; + switch (nn) + { + case 0: + /* log gamma (x + 1) = log(x) + log gamma(x) */ + if (x <= 0.125) + { + p = x * neval (x, RN1, NRN1) / deval (x, RD1, NRD1); + } + else if (x <= 0.375) + { + z = x - 0.25L; + p = z * neval (z, RN1r25, NRN1r25) / deval (z, RD1r25, NRD1r25); + p += lgam1r25b; + p += lgam1r25a; + } + else if (x <= 0.625) + { + z = x + (1.0L - x0a); + z = z - x0b; + p = neval (z, RN1r5, NRN1r5) / deval (z, RD1r5, NRD1r5); + p = p * z * z; + p = p + y0b; + p = p + y0a; + } + else if (x <= 0.875) + { + z = x - 0.75L; + p = z * neval (z, RN1r75, NRN1r75) / deval (z, RD1r75, NRD1r75); + p += lgam1r75b; + p += lgam1r75a; + } + else + { + z = x - 1.0L; + p = z * neval (z, RN2, NRN2) / deval (z, RD2, NRD2); + } + p = p - logl (x); + break; + + case 1: + if (x < 0.875L) + { + if (x <= 0.625) + { + z = x + (1.0L - x0a); + z = z - x0b; + p = neval (z, RN1r5, NRN1r5) / deval (z, RD1r5, NRD1r5); + p = p * z * z; + p = p + y0b; + p = p + y0a; + } + else if (x <= 0.875) + { + z = x - 0.75L; + p = z * neval (z, RN1r75, NRN1r75) + / deval (z, RD1r75, NRD1r75); + p += lgam1r75b; + p += lgam1r75a; + } + else + { + z = x - 1.0L; + p = z * neval (z, RN2, NRN2) / deval (z, RD2, NRD2); + } + p = p - logl (x); + } + else if (x < 1.0L) + { + z = x - 1.0L; + p = z * neval (z, RNr9, NRNr9) / deval (z, RDr9, NRDr9); + } + else if (x == 1.0L) + p = 0.0L; + else if (x <= 1.125L) + { + z = x - 1.0L; + p = z * neval (z, RN1, NRN1) / deval (z, RD1, NRD1); + } + else if (x <= 1.375) + { + z = x - 1.25L; + p = z * neval (z, RN1r25, NRN1r25) / deval (z, RD1r25, NRD1r25); + p += lgam1r25b; + p += lgam1r25a; + } + else + { + /* 1.375 <= x+x0 <= 1.625 */ + z = x - x0a; + z = z - x0b; + p = neval (z, RN1r5, NRN1r5) / deval (z, RD1r5, NRD1r5); + p = p * z * z; + p = p + y0b; + p = p + y0a; + } + break; + + case 2: + if (x < 1.625L) + { + z = x - x0a; + z = z - x0b; + p = neval (z, RN1r5, NRN1r5) / deval (z, RD1r5, NRD1r5); + p = p * z * z; + p = p + y0b; + p = p + y0a; + } + else if (x < 1.875L) + { + z = x - 1.75L; + p = z * neval (z, RN1r75, NRN1r75) / deval (z, RD1r75, NRD1r75); + p += lgam1r75b; + p += lgam1r75a; + } + else if (x == 2.0L) + p = 0.0L; + else if (x < 2.375L) + { + z = x - 2.0L; + p = z * neval (z, RN2, NRN2) / deval (z, RD2, NRD2); + } + else + { + z = x - 2.5L; + p = z * neval (z, RN2r5, NRN2r5) / deval (z, RD2r5, NRD2r5); + p += lgam2r5b; + p += lgam2r5a; + } + break; + + case 3: + if (x < 2.75) + { + z = x - 2.5L; + p = z * neval (z, RN2r5, NRN2r5) / deval (z, RD2r5, NRD2r5); + p += lgam2r5b; + p += lgam2r5a; + } + else + { + z = x - 3.0L; + p = z * neval (z, RN3, NRN3) / deval (z, RD3, NRD3); + p += lgam3b; + p += lgam3a; + } + break; + + case 4: + z = x - 4.0L; + p = z * neval (z, RN4, NRN4) / deval (z, RD4, NRD4); + p += lgam4b; + p += lgam4a; + break; + + case 5: + z = x - 5.0L; + p = z * neval (z, RN5, NRN5) / deval (z, RD5, NRD5); + p += lgam5b; + p += lgam5a; + break; + + case 6: + z = x - 6.0L; + p = z * neval (z, RN6, NRN6) / deval (z, RD6, NRD6); + p += lgam6b; + p += lgam6a; + break; + + case 7: + z = x - 7.0L; + p = z * neval (z, RN7, NRN7) / deval (z, RD7, NRD7); + p += lgam7b; + p += lgam7a; + break; + + case 8: + z = x - 8.0L; + p = z * neval (z, RN8, NRN8) / deval (z, RD8, NRD8); + p += lgam8b; + p += lgam8a; + break; + + case 9: + z = x - 9.0L; + p = z * neval (z, RN9, NRN9) / deval (z, RD9, NRD9); + p += lgam9b; + p += lgam9a; + break; + + case 10: + z = x - 10.0L; + p = z * neval (z, RN10, NRN10) / deval (z, RD10, NRD10); + p += lgam10b; + p += lgam10a; + break; + + case 11: + z = x - 11.0L; + p = z * neval (z, RN11, NRN11) / deval (z, RD11, NRD11); + p += lgam11b; + p += lgam11a; + break; + + case 12: + z = x - 12.0L; + p = z * neval (z, RN12, NRN12) / deval (z, RD12, NRD12); + p += lgam12b; + p += lgam12a; + break; + + case 13: + z = x - 13.0L; + p = z * neval (z, RN13, NRN13) / deval (z, RD13, NRD13); + p += lgam13b; + p += lgam13a; + break; + } + return p; + } + + if (x > MAXLGM) + return (*signgamp * huge * huge); + + q = ls2pi - x; + q = (x - 0.5L) * logl (x) + q; + if (x > 1.0e18L) + return (q); + + p = 1.0L / (x * x); + q += neval (p, RASY, NRASY) / x; + return (q); +} diff --git a/openlibm/ld128/e_log10l.c b/openlibm/ld128/e_log10l.c new file mode 100644 index 0000000000..9448101140 --- /dev/null +++ b/openlibm/ld128/e_log10l.c @@ -0,0 +1,255 @@ +/* $OpenBSD: e_log10l.c,v 1.1 2011/07/06 00:02:42 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* log10l.c + * + * Common logarithm, 128-bit long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, log10l(); + * + * y = log10l( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base 10 logarithm of x. + * + * The argument is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the logarithm + * of the fraction is approximated by + * + * log(1+x) = x - 0.5 x^2 + x^3 P(x)/Q(x). + * + * Otherwise, setting z = 2(x-1)/x+1), + * + * log(x) = z + z^3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE 0.5, 2.0 30000 2.3e-34 4.9e-35 + * IEEE exp(+-10000) 30000 1.0e-34 4.1e-35 + * + * In the tests over the interval exp(+-10000), the logarithms + * of the random arguments were uniformly distributed over + * [-10000, +10000]. + * + */ + +#include + +#include "math_private.h" + +/* Coefficients for ln(1+x) = x - x**2/2 + x**3 P(x)/Q(x) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 5.3e-37, + * relative peak error spread = 2.3e-14 + */ +static const long double P[13] = +{ + 1.313572404063446165910279910527789794488E4L, + 7.771154681358524243729929227226708890930E4L, + 2.014652742082537582487669938141683759923E5L, + 3.007007295140399532324943111654767187848E5L, + 2.854829159639697837788887080758954924001E5L, + 1.797628303815655343403735250238293741397E5L, + 7.594356839258970405033155585486712125861E4L, + 2.128857716871515081352991964243375186031E4L, + 3.824952356185897735160588078446136783779E3L, + 4.114517881637811823002128927449878962058E2L, + 2.321125933898420063925789532045674660756E1L, + 4.998469661968096229986658302195402690910E-1L, + 1.538612243596254322971797716843006400388E-6L +}; +static const long double Q[12] = +{ + 3.940717212190338497730839731583397586124E4L, + 2.626900195321832660448791748036714883242E5L, + 7.777690340007566932935753241556479363645E5L, + 1.347518538384329112529391120390701166528E6L, + 1.514882452993549494932585972882995548426E6L, + 1.158019977462989115839826904108208787040E6L, + 6.132189329546557743179177159925690841200E5L, + 2.248234257620569139969141618556349415120E5L, + 5.605842085972455027590989944010492125825E4L, + 9.147150349299596453976674231612674085381E3L, + 9.104928120962988414618126155557301584078E2L, + 4.839208193348159620282142911143429644326E1L +/* 1.000000000000000000000000000000000000000E0L, */ +}; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 1.1e-35, + * relative peak error spread 1.1e-9 + */ +static const long double R[6] = +{ + 1.418134209872192732479751274970992665513E5L, + -8.977257995689735303686582344659576526998E4L, + 2.048819892795278657810231591630928516206E4L, + -2.024301798136027039250415126250455056397E3L, + 8.057002716646055371965756206836056074715E1L, + -8.828896441624934385266096344596648080902E-1L +}; +static const long double S[6] = +{ + 1.701761051846631278975701529965589676574E6L, + -1.332535117259762928288745111081235577029E6L, + 4.001557694070773974936904547424676279307E5L, + -5.748542087379434595104154610899551484314E4L, + 3.998526750980007367835804959888064681098E3L, + -1.186359407982897997337150403816839480438E2L +/* 1.000000000000000000000000000000000000000E0L, */ +}; + +static const long double +/* log10(2) */ +L102A = 0.3125L, +L102B = -1.14700043360188047862611052755069732318101185E-2L, +/* log10(e) */ +L10EA = 0.5L, +L10EB = -6.570551809674817234887108108339491770560299E-2L, +/* sqrt(2)/2 */ +SQRTH = 7.071067811865475244008443621048490392848359E-1L; + + + +/* Evaluate P[n] x^n + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +neval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + +/* Evaluate x^n+1 + P[n] x^(n) + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +deval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = x + *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + + +long double +log10l(long double x) +{ + long double z; + long double y; + int e; + int64_t hx, lx; + +/* Test for domain */ + GET_LDOUBLE_WORDS64 (hx, lx, x); + if (((hx & 0x7fffffffffffffffLL) | lx) == 0) + return (-1.0L / (x - x)); + if (hx < 0) + return (x - x) / (x - x); + if (hx >= 0x7fff000000000000LL) + return (x + x); + +/* separate mantissa from exponent */ + +/* Note, frexp is used so that denormal numbers + * will be handled properly. + */ + x = frexpl (x, &e); + + +/* logarithm using log(x) = z + z**3 P(z)/Q(z), + * where z = 2(x-1)/x+1) + */ + if ((e > 2) || (e < -2)) + { + if (x < SQRTH) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } + else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } + x = z / y; + z = x * x; + y = x * (z * neval (z, R, 5) / deval (z, S, 5)); + goto done; + } + + +/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */ + + if (x < SQRTH) + { + e -= 1; + x = 2.0 * x - 1.0L; /* 2x - 1 */ + } + else + { + x = x - 1.0L; + } + z = x * x; + y = x * (z * neval (x, P, 12) / deval (x, Q, 11)); + y = y - 0.5 * z; + +done: + + /* Multiply log of fraction by log10(e) + * and base 2 exponent by log10(2). + */ + z = y * L10EB; + z += x * L10EB; + z += e * L102B; + z += y * L10EA; + z += x * L10EA; + z += e * L102A; + return (z); +} diff --git a/openlibm/ld128/e_log2l.c b/openlibm/ld128/e_log2l.c new file mode 100644 index 0000000000..2ce4d943c1 --- /dev/null +++ b/openlibm/ld128/e_log2l.c @@ -0,0 +1,248 @@ +/* $OpenBSD: e_log2l.c,v 1.1 2011/07/06 00:02:42 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* log2l.c + * Base 2 logarithm, 128-bit long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, log2l(); + * + * y = log2l( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base 2 logarithm of x. + * + * The argument is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the (natural) + * logarithm of the fraction is approximated by + * + * log(1+x) = x - 0.5 x^2 + x^3 P(x)/Q(x). + * + * Otherwise, setting z = 2(x-1)/x+1), + * + * log(x) = z + z^3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE 0.5, 2.0 100,000 2.6e-34 4.9e-35 + * IEEE exp(+-10000) 100,000 9.6e-35 4.0e-35 + * + * In the tests over the interval exp(+-10000), the logarithms + * of the random arguments were uniformly distributed over + * [-10000, +10000]. + * + */ + +#include + +#include "math_private.h" + +/* Coefficients for ln(1+x) = x - x**2/2 + x**3 P(x)/Q(x) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 5.3e-37, + * relative peak error spread = 2.3e-14 + */ +static const long double P[13] = +{ + 1.313572404063446165910279910527789794488E4L, + 7.771154681358524243729929227226708890930E4L, + 2.014652742082537582487669938141683759923E5L, + 3.007007295140399532324943111654767187848E5L, + 2.854829159639697837788887080758954924001E5L, + 1.797628303815655343403735250238293741397E5L, + 7.594356839258970405033155585486712125861E4L, + 2.128857716871515081352991964243375186031E4L, + 3.824952356185897735160588078446136783779E3L, + 4.114517881637811823002128927449878962058E2L, + 2.321125933898420063925789532045674660756E1L, + 4.998469661968096229986658302195402690910E-1L, + 1.538612243596254322971797716843006400388E-6L +}; +static const long double Q[12] = +{ + 3.940717212190338497730839731583397586124E4L, + 2.626900195321832660448791748036714883242E5L, + 7.777690340007566932935753241556479363645E5L, + 1.347518538384329112529391120390701166528E6L, + 1.514882452993549494932585972882995548426E6L, + 1.158019977462989115839826904108208787040E6L, + 6.132189329546557743179177159925690841200E5L, + 2.248234257620569139969141618556349415120E5L, + 5.605842085972455027590989944010492125825E4L, + 9.147150349299596453976674231612674085381E3L, + 9.104928120962988414618126155557301584078E2L, + 4.839208193348159620282142911143429644326E1L +/* 1.000000000000000000000000000000000000000E0L, */ +}; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 1.1e-35, + * relative peak error spread 1.1e-9 + */ +static const long double R[6] = +{ + 1.418134209872192732479751274970992665513E5L, + -8.977257995689735303686582344659576526998E4L, + 2.048819892795278657810231591630928516206E4L, + -2.024301798136027039250415126250455056397E3L, + 8.057002716646055371965756206836056074715E1L, + -8.828896441624934385266096344596648080902E-1L +}; +static const long double S[6] = +{ + 1.701761051846631278975701529965589676574E6L, + -1.332535117259762928288745111081235577029E6L, + 4.001557694070773974936904547424676279307E5L, + -5.748542087379434595104154610899551484314E4L, + 3.998526750980007367835804959888064681098E3L, + -1.186359407982897997337150403816839480438E2L +/* 1.000000000000000000000000000000000000000E0L, */ +}; + +static const long double +/* log2(e) - 1 */ +LOG2EA = 4.4269504088896340735992468100189213742664595E-1L, +/* sqrt(2)/2 */ +SQRTH = 7.071067811865475244008443621048490392848359E-1L; + + +/* Evaluate P[n] x^n + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +neval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + +/* Evaluate x^n+1 + P[n] x^(n) + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +deval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = x + *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + + +long double +log2l(long double x) +{ + long double z; + long double y; + int e; + int64_t hx, lx; + +/* Test for domain */ + GET_LDOUBLE_WORDS64 (hx, lx, x); + if (((hx & 0x7fffffffffffffffLL) | lx) == 0) + return (-1.0L / (x - x)); + if (hx < 0) + return (x - x) / (x - x); + if (hx >= 0x7fff000000000000LL) + return (x + x); + +/* separate mantissa from exponent */ + +/* Note, frexp is used so that denormal numbers + * will be handled properly. + */ + x = frexpl (x, &e); + + +/* logarithm using log(x) = z + z**3 P(z)/Q(z), + * where z = 2(x-1)/x+1) + */ + if ((e > 2) || (e < -2)) + { + if (x < SQRTH) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } + else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } + x = z / y; + z = x * x; + y = x * (z * neval (z, R, 5) / deval (z, S, 5)); + goto done; + } + + +/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */ + + if (x < SQRTH) + { + e -= 1; + x = 2.0 * x - 1.0L; /* 2x - 1 */ + } + else + { + x = x - 1.0L; + } + z = x * x; + y = x * (z * neval (x, P, 12) / deval (x, Q, 11)); + y = y - 0.5 * z; + +done: + +/* Multiply log of fraction by log2(e) + * and base 2 exponent by 1 + */ + z = y * LOG2EA; + z += x * LOG2EA; + z += y; + z += x; + z += e; + return (z); +} diff --git a/openlibm/ld128/e_logl.c b/openlibm/ld128/e_logl.c new file mode 100644 index 0000000000..f6da91f4f4 --- /dev/null +++ b/openlibm/ld128/e_logl.c @@ -0,0 +1,283 @@ +/* $OpenBSD: e_logl.c,v 1.1 2011/07/06 00:02:42 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* logl.c + * + * Natural logarithm for 128-bit long double precision. + * + * + * + * SYNOPSIS: + * + * long double x, y, logl(); + * + * y = logl( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base e (2.718...) logarithm of x. + * + * The argument is separated into its exponent and fractional + * parts. Use of a lookup table increases the speed of the routine. + * The program uses logarithms tabulated at intervals of 1/128 to + * cover the domain from approximately 0.7 to 1.4. + * + * On the interval [-1/128, +1/128] the logarithm of 1+x is approximated by + * log(1+x) = x - 0.5 x^2 + x^3 P(x) . + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE 0.875, 1.125 100000 1.2e-34 4.1e-35 + * IEEE 0.125, 8 100000 1.2e-34 4.1e-35 + * + * + * WARNING: + * + * This program uses integer operations on bit fields of floating-point + * numbers. It does not work with data structures other than the + * structure assumed. + * + */ + +#include + +#include "math_private.h" + +/* log(1+x) = x - .5 x^2 + x^3 l(x) + -.0078125 <= x <= +.0078125 + peak relative error 1.2e-37 */ +static const long double +l3 = 3.333333333333333333333333333333336096926E-1L, +l4 = -2.499999999999999999999999999486853077002E-1L, +l5 = 1.999999999999999999999999998515277861905E-1L, +l6 = -1.666666666666666666666798448356171665678E-1L, +l7 = 1.428571428571428571428808945895490721564E-1L, +l8 = -1.249999999999999987884655626377588149000E-1L, +l9 = 1.111111111111111093947834982832456459186E-1L, +l10 = -1.000000000000532974938900317952530453248E-1L, +l11 = 9.090909090915566247008015301349979892689E-2L, +l12 = -8.333333211818065121250921925397567745734E-2L, +l13 = 7.692307559897661630807048686258659316091E-2L, +l14 = -7.144242754190814657241902218399056829264E-2L, +l15 = 6.668057591071739754844678883223432347481E-2L; + +/* Lookup table of ln(t) - (t-1) + t = 0.5 + (k+26)/128) + k = 0, ..., 91 */ +static const long double logtbl[92] = { +-5.5345593589352099112142921677820359632418E-2L, +-5.2108257402767124761784665198737642086148E-2L, +-4.8991686870576856279407775480686721935120E-2L, +-4.5993270766361228596215288742353061431071E-2L, +-4.3110481649613269682442058976885699556950E-2L, +-4.0340872319076331310838085093194799765520E-2L, +-3.7682072451780927439219005993827431503510E-2L, +-3.5131785416234343803903228503274262719586E-2L, +-3.2687785249045246292687241862699949178831E-2L, +-3.0347913785027239068190798397055267411813E-2L, +-2.8110077931525797884641940838507561326298E-2L, +-2.5972247078357715036426583294246819637618E-2L, +-2.3932450635346084858612873953407168217307E-2L, +-2.1988775689981395152022535153795155900240E-2L, +-2.0139364778244501615441044267387667496733E-2L, +-1.8382413762093794819267536615342902718324E-2L, +-1.6716169807550022358923589720001638093023E-2L, +-1.5138929457710992616226033183958974965355E-2L, +-1.3649036795397472900424896523305726435029E-2L, +-1.2244881690473465543308397998034325468152E-2L, +-1.0924898127200937840689817557742469105693E-2L, +-9.6875626072830301572839422532631079809328E-3L, +-8.5313926245226231463436209313499745894157E-3L, +-7.4549452072765973384933565912143044991706E-3L, +-6.4568155251217050991200599386801665681310E-3L, +-5.5356355563671005131126851708522185605193E-3L, +-4.6900728132525199028885749289712348829878E-3L, +-3.9188291218610470766469347968659624282519E-3L, +-3.2206394539524058873423550293617843896540E-3L, +-2.5942708080877805657374888909297113032132E-3L, +-2.0385211375711716729239156839929281289086E-3L, +-1.5522183228760777967376942769773768850872E-3L, +-1.1342191863606077520036253234446621373191E-3L, +-7.8340854719967065861624024730268350459991E-4L, +-4.9869831458030115699628274852562992756174E-4L, +-2.7902661731604211834685052867305795169688E-4L, +-1.2335696813916860754951146082826952093496E-4L, +-3.0677461025892873184042490943581654591817E-5L, +#define ZERO logtbl[38] + 0.0000000000000000000000000000000000000000E0L, +-3.0359557945051052537099938863236321874198E-5L, +-1.2081346403474584914595395755316412213151E-4L, +-2.7044071846562177120083903771008342059094E-4L, +-4.7834133324631162897179240322783590830326E-4L, +-7.4363569786340080624467487620270965403695E-4L, +-1.0654639687057968333207323853366578860679E-3L, +-1.4429854811877171341298062134712230604279E-3L, +-1.8753781835651574193938679595797367137975E-3L, +-2.3618380914922506054347222273705859653658E-3L, +-2.9015787624124743013946600163375853631299E-3L, +-3.4938307889254087318399313316921940859043E-3L, +-4.1378413103128673800485306215154712148146E-3L, +-4.8328735414488877044289435125365629849599E-3L, +-5.5782063183564351739381962360253116934243E-3L, +-6.3731336597098858051938306767880719015261E-3L, +-7.2169643436165454612058905294782949315193E-3L, +-8.1090214990427641365934846191367315083867E-3L, +-9.0486422112807274112838713105168375482480E-3L, +-1.0035177140880864314674126398350812606841E-2L, +-1.1067990155502102718064936259435676477423E-2L, +-1.2146457974158024928196575103115488672416E-2L, +-1.3269969823361415906628825374158424754308E-2L, +-1.4437927104692837124388550722759686270765E-2L, +-1.5649743073340777659901053944852735064621E-2L, +-1.6904842527181702880599758489058031645317E-2L, +-1.8202661505988007336096407340750378994209E-2L, +-1.9542647000370545390701192438691126552961E-2L, +-2.0924256670080119637427928803038530924742E-2L, +-2.2346958571309108496179613803760727786257E-2L, +-2.3810230892650362330447187267648486279460E-2L, +-2.5313561699385640380910474255652501521033E-2L, +-2.6856448685790244233704909690165496625399E-2L, +-2.8438398935154170008519274953860128449036E-2L, +-3.0058928687233090922411781058956589863039E-2L, +-3.1717563112854831855692484086486099896614E-2L, +-3.3413836095418743219397234253475252001090E-2L, +-3.5147290019036555862676702093393332533702E-2L, +-3.6917475563073933027920505457688955423688E-2L, +-3.8723951502862058660874073462456610731178E-2L, +-4.0566284516358241168330505467000838017425E-2L, +-4.2444048996543693813649967076598766917965E-2L, +-4.4356826869355401653098777649745233339196E-2L, +-4.6304207416957323121106944474331029996141E-2L, +-4.8285787106164123613318093945035804818364E-2L, +-5.0301169421838218987124461766244507342648E-2L, +-5.2349964705088137924875459464622098310997E-2L, +-5.4431789996103111613753440311680967840214E-2L, +-5.6546268881465384189752786409400404404794E-2L, +-5.8693031345788023909329239565012647817664E-2L, +-6.0871713627532018185577188079210189048340E-2L, +-6.3081958078862169742820420185833800925568E-2L, +-6.5323413029406789694910800219643791556918E-2L, +-6.7595732653791419081537811574227049288168E-2L +}; + +/* ln(2) = ln2a + ln2b with extended precision. */ +static const long double + ln2a = 6.93145751953125e-1L, + ln2b = 1.4286068203094172321214581765680755001344E-6L; + +long double +logl(long double x) +{ + long double z, y, w; + ieee_quad_shape_type u, t; + unsigned int m; + int k, e; + + u.value = x; + m = u.parts32.mswhi; + + /* Check for IEEE special cases. */ + k = m & 0x7fffffff; + /* log(0) = -infinity. */ + if ((k | u.parts32.mswlo | u.parts32.lswhi | u.parts32.lswlo) == 0) + { + return -0.5L / ZERO; + } + /* log ( x < 0 ) = NaN */ + if (m & 0x80000000) + { + return (x - x) / ZERO; + } + /* log (infinity or NaN) */ + if (k >= 0x7fff0000) + { + return x + x; + } + + /* Extract exponent and reduce domain to 0.703125 <= u < 1.40625 */ + e = (int) (m >> 16) - (int) 0x3ffe; + m &= 0xffff; + u.parts32.mswhi = m | 0x3ffe0000; + m |= 0x10000; + /* Find lookup table index k from high order bits of the significand. */ + if (m < 0x16800) + { + k = (m - 0xff00) >> 9; + /* t is the argument 0.5 + (k+26)/128 + of the nearest item to u in the lookup table. */ + t.parts32.mswhi = 0x3fff0000 + (k << 9); + t.parts32.mswlo = 0; + t.parts32.lswhi = 0; + t.parts32.lswlo = 0; + u.parts32.mswhi += 0x10000; + e -= 1; + k += 64; + } + else + { + k = (m - 0xfe00) >> 10; + t.parts32.mswhi = 0x3ffe0000 + (k << 10); + t.parts32.mswlo = 0; + t.parts32.lswhi = 0; + t.parts32.lswlo = 0; + } + /* On this interval the table is not used due to cancellation error. */ + if ((x <= 1.0078125L) && (x >= 0.9921875L)) + { + z = x - 1.0L; + k = 64; + t.value = 1.0L; + e = 0; + } + else + { + /* log(u) = log( t u/t ) = log(t) + log(u/t) + log(t) is tabulated in the lookup table. + Express log(u/t) = log(1+z), where z = u/t - 1 = (u-t)/t. + cf. Cody & Waite. */ + z = (u.value - t.value) / t.value; + } + /* Series expansion of log(1+z). */ + w = z * z; + y = ((((((((((((l15 * z + + l14) * z + + l13) * z + + l12) * z + + l11) * z + + l10) * z + + l9) * z + + l8) * z + + l7) * z + + l6) * z + + l5) * z + + l4) * z + + l3) * z * w; + y -= 0.5 * w; + y += e * ln2b; /* Base 2 exponent offset times ln(2). */ + y += z; + y += logtbl[k-26]; /* log(t) - (t-1) */ + y += (t.value - 1.0L); + y += e * ln2a; + return y; +} diff --git a/openlibm/ld128/e_powl.c b/openlibm/ld128/e_powl.c new file mode 100644 index 0000000000..430b26102c --- /dev/null +++ b/openlibm/ld128/e_powl.c @@ -0,0 +1,439 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* powl(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 113-53 = 60 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + */ + +#include + +#include "math_private.h" + +static const long double bp[] = { + 1.0L, + 1.5L, +}; + +/* log_2(1.5) */ +static const long double dp_h[] = { + 0.0, + 5.8496250072115607565592654282227158546448E-1L +}; + +/* Low part of log_2(1.5) */ +static const long double dp_l[] = { + 0.0, + 1.0579781240112554492329533686862998106046E-16L +}; + +static const long double zero = 0.0L, + one = 1.0L, + two = 2.0L, + two113 = 1.0384593717069655257060992658440192E34L, + huge = 1.0e3000L, + tiny = 1.0e-3000L; + +/* 3/2 log x = 3 z + z^3 + z^3 (z^2 R(z^2)) + z = (x-1)/(x+1) + 1 <= x <= 1.25 + Peak relative error 2.3e-37 */ +static const long double LN[] = +{ + -3.0779177200290054398792536829702930623200E1L, + 6.5135778082209159921251824580292116201640E1L, + -4.6312921812152436921591152809994014413540E1L, + 1.2510208195629420304615674658258363295208E1L, + -9.9266909031921425609179910128531667336670E-1L +}; +static const long double LD[] = +{ + -5.129862866715009066465422805058933131960E1L, + 1.452015077564081884387441590064272782044E2L, + -1.524043275549860505277434040464085593165E2L, + 7.236063513651544224319663428634139768808E1L, + -1.494198912340228235853027849917095580053E1L + /* 1.0E0 */ +}; + +/* exp(x) = 1 + x - x / (1 - 2 / (x - x^2 R(x^2))) + 0 <= x <= 0.5 + Peak relative error 5.7e-38 */ +static const long double PN[] = +{ + 5.081801691915377692446852383385968225675E8L, + 9.360895299872484512023336636427675327355E6L, + 4.213701282274196030811629773097579432957E4L, + 5.201006511142748908655720086041570288182E1L, + 9.088368420359444263703202925095675982530E-3L, +}; +static const long double PD[] = +{ + 3.049081015149226615468111430031590411682E9L, + 1.069833887183886839966085436512368982758E8L, + 8.259257717868875207333991924545445705394E5L, + 1.872583833284143212651746812884298360922E3L, + /* 1.0E0 */ +}; + +static const long double + /* ln 2 */ + lg2 = 6.9314718055994530941723212145817656807550E-1L, + lg2_h = 6.9314718055994528622676398299518041312695E-1L, + lg2_l = 2.3190468138462996154948554638754786504121E-17L, + ovt = 8.0085662595372944372e-0017L, + /* 2/(3*log(2)) */ + cp = 9.6179669392597560490661645400126142495110E-1L, + cp_h = 9.6179669392597555432899980587535537779331E-1L, + cp_l = 5.0577616648125906047157785230014751039424E-17L; + +long double +powl(long double x, long double y) +{ + long double z, ax, z_h, z_l, p_h, p_l; + long double yy1, t1, t2, r, s, t, u, v, w; + long double s2, s_h, s_l, t_h, t_l; + int32_t i, j, k, yisint, n; + u_int32_t ix, iy; + int32_t hx, hy; + ieee_quad_shape_type o, p, q; + + p.value = x; + hx = p.parts32.mswhi; + ix = hx & 0x7fffffff; + + q.value = y; + hy = q.parts32.mswhi; + iy = hy & 0x7fffffff; + + + /* y==zero: x**0 = 1 */ + if ((iy | q.parts32.mswlo | q.parts32.lswhi | q.parts32.lswlo) == 0) + return one; + + /* 1.0**y = 1; -1.0**+-Inf = 1 */ + if (x == one) + return one; + if (x == -1.0L && iy == 0x7fff0000 + && (q.parts32.mswlo | q.parts32.lswhi | q.parts32.lswlo) == 0) + return one; + + /* +-NaN return x+y */ + if ((ix > 0x7fff0000) + || ((ix == 0x7fff0000) + && ((p.parts32.mswlo | p.parts32.lswhi | p.parts32.lswlo) != 0)) + || (iy > 0x7fff0000) + || ((iy == 0x7fff0000) + && ((q.parts32.mswlo | q.parts32.lswhi | q.parts32.lswlo) != 0))) + return x + y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if (hx < 0) + { + if (iy >= 0x40700000) /* 2^113 */ + yisint = 2; /* even integer y */ + else if (iy >= 0x3fff0000) /* 1.0 */ + { + if (floorl (y) == y) + { + z = 0.5 * y; + if (floorl (z) == z) + yisint = 2; + else + yisint = 1; + } + } + } + + /* special value of y */ + if ((q.parts32.mswlo | q.parts32.lswhi | q.parts32.lswlo) == 0) + { + if (iy == 0x7fff0000) /* y is +-inf */ + { + if (((ix - 0x3fff0000) | p.parts32.mswlo | p.parts32.lswhi | + p.parts32.lswlo) == 0) + return y - y; /* +-1**inf is NaN */ + else if (ix >= 0x3fff0000) /* (|x|>1)**+-inf = inf,0 */ + return (hy >= 0) ? y : zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy < 0) ? -y : zero; + } + if (iy == 0x3fff0000) + { /* y is +-1 */ + if (hy < 0) + return one / x; + else + return x; + } + if (hy == 0x40000000) + return x * x; /* y is 2 */ + if (hy == 0x3ffe0000) + { /* y is 0.5 */ + if (hx >= 0) /* x >= +0 */ + return sqrtl (x); + } + } + + ax = fabsl (x); + /* special value of x */ + if ((p.parts32.mswlo | p.parts32.lswhi | p.parts32.lswlo) == 0) + { + if (ix == 0x7fff0000 || ix == 0 || ix == 0x3fff0000) + { + z = ax; /*x is +-0,+-inf,+-1 */ + if (hy < 0) + z = one / z; /* z = (1/|x|) */ + if (hx < 0) + { + if (((ix - 0x3fff0000) | yisint) == 0) + { + z = (z - z) / (z - z); /* (-1)**non-int is NaN */ + } + else if (yisint == 1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + /* (x<0)**(non-int) is NaN */ + if (((((u_int32_t) hx >> 31) - 1) | yisint) == 0) + return (x - x) / (x - x); + + /* |y| is huge. + 2^-16495 = 1/2 of smallest representable value. + If (1 - 1/131072)^y underflows, y > 1.4986e9 */ + if (iy > 0x401d654b) + { + /* if (1 - 2^-113)^y underflows, y > 1.1873e38 */ + if (iy > 0x407d654b) + { + if (ix <= 0x3ffeffff) + return (hy < 0) ? huge * huge : tiny * tiny; + if (ix >= 0x3fff0000) + return (hy > 0) ? huge * huge : tiny * tiny; + } + /* over/underflow if x is not close to one */ + if (ix < 0x3ffeffff) + return (hy < 0) ? huge * huge : tiny * tiny; + if (ix > 0x3fff0000) + return (hy > 0) ? huge * huge : tiny * tiny; + } + + n = 0; + /* take care subnormal number */ + if (ix < 0x00010000) + { + ax *= two113; + n -= 113; + o.value = ax; + ix = o.parts32.mswhi; + } + n += ((ix) >> 16) - 0x3fff; + j = ix & 0x0000ffff; + /* determine interval */ + ix = j | 0x3fff0000; /* normalize ix */ + if (j <= 0x3988) + k = 0; /* |x|> 31) - 1) | (yisint - 1)) == 0) + s = -one; /* (-ve)**(odd int) */ + + /* split up y into yy1+y2 and compute (yy1+y2)*(t1+t2) */ + yy1 = y; + o.value = yy1; + o.parts32.lswlo = 0; + o.parts32.lswhi &= 0xf8000000; + yy1 = o.value; + p_l = (y - yy1) * t1 + y * t2; + p_h = yy1 * t1; + z = p_l + p_h; + o.value = z; + j = o.parts32.mswhi; + if (j >= 0x400d0000) /* z >= 16384 */ + { + /* if z > 16384 */ + if (((j - 0x400d0000) | o.parts32.mswlo | o.parts32.lswhi | + o.parts32.lswlo) != 0) + return s * huge * huge; /* overflow */ + else + { + if (p_l + ovt > z - p_h) + return s * huge * huge; /* overflow */ + } + } + else if ((j & 0x7fffffff) >= 0x400d01b9) /* z <= -16495 */ + { + /* z < -16495 */ + if (((j - 0xc00d01bc) | o.parts32.mswlo | o.parts32.lswhi | + o.parts32.lswlo) + != 0) + return s * tiny * tiny; /* underflow */ + else + { + if (p_l <= z - p_h) + return s * tiny * tiny; /* underflow */ + } + } + /* compute 2**(p_h+p_l) */ + i = j & 0x7fffffff; + k = (i >> 16) - 0x3fff; + n = 0; + if (i > 0x3ffe0000) + { /* if |z| > 0.5, set n = [z+0.5] */ + n = floorl (z + 0.5L); + t = n; + p_h -= t; + } + t = p_l + p_h; + o.value = t; + o.parts32.lswlo = 0; + o.parts32.lswhi &= 0xf8000000; + t = o.value; + u = t * lg2_h; + v = (p_l - (t - p_h)) * lg2 + t * lg2_l; + z = u + v; + w = v - (z - u); + /* exp(z) */ + t = z * z; + u = PN[0] + t * (PN[1] + t * (PN[2] + t * (PN[3] + t * PN[4]))); + v = PD[0] + t * (PD[1] + t * (PD[2] + t * (PD[3] + t))); + t1 = z - t * u / v; + r = (z * t1) / (t1 - two) - (w + z * w); + z = one - (r - z); + o.value = z; + j = o.parts32.mswhi; + j += (n << 16); + if ((j >> 16) <= 0) + z = scalbnl (z, n); /* subnormal output */ + else + { + o.parts32.mswhi = j; + z = o.value; + } + return s * z; +} diff --git a/openlibm/ld128/e_rem_pio2l.h b/openlibm/ld128/e_rem_pio2l.h new file mode 100644 index 0000000000..5224245671 --- /dev/null +++ b/openlibm/ld128/e_rem_pio2l.h @@ -0,0 +1,144 @@ +/* From: @(#)e_rem_pio2.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * Optimized by Bruce D. Evans. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld128/e_rem_pio2l.h,v 1.2 2011/05/30 19:41:28 kargl Exp $"); + +/* ld128 version of __ieee754_rem_pio2l(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2() + */ + +#include +#include + +#include "math_private.h" +#include "fpmath.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +/* + * XXX need to verify that nonzero integer multiples of pi/2 within the + * range get no closer to a long double than 2**-140, or that + * ilogb(x) + ilogb(min_delta) < 45 - -140. + */ +/* + * invpio2: 113 bits of 2/pi + * pio2_1: first 68 bits of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 68 bits of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 68 bits of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const double +zero = 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ +two24 = 1.67772160000000000000e+07; /* 0x41700000, 0x00000000 */ + +static const long double +invpio2 = 6.3661977236758134307553505349005747e-01L, /* 0x145f306dc9c882a53f84eafa3ea6a.0p-113 */ +pio2_1 = 1.5707963267948966192292994253909555e+00L, /* 0x1921fb54442d18469800000000000.0p-112 */ +pio2_1t = 2.0222662487959507323996846200947577e-21L, /* 0x13198a2e03707344a4093822299f3.0p-181 */ +pio2_2 = 2.0222662487959507323994779168837751e-21L, /* 0x13198a2e03707344a400000000000.0p-181 */ +pio2_2t = 2.0670321098263988236496903051604844e-43L, /* 0x127044533e63a0105df531d89cd91.0p-254 */ +pio2_3 = 2.0670321098263988236499468110329591e-43L, /* 0x127044533e63a0105e00000000000.0p-254 */ +pio2_3t = -2.5650587247459238361625433492959285e-65L; /* -0x159c4ec64ddaeb5f78671cbfb2210.0p-327 */ + +//VBS +//static inline __always_inline int +//__ieee754_rem_pio2l(long double x, long double *y) + +static inline int +__ieee754_rem_pio2l(long double x, long double *y) +{ + union IEEEl2bits u,u1; + long double z,w,t,r,fn; + double tx[5],ty[3]; + int64_t n; + int e0,ex,i,j,nx; + int16_t expsign; + + u.e = x; + expsign = u.xbits.expsign; + ex = expsign & 0x7fff; + if (ex < BIAS + 45 || ex == BIAS + 45 && + u.bits.manh < 0x921fb54442d1LL) { + /* |x| ~< 2^45*(pi/2), medium size */ + /* Use a specialized rint() to get fn. Assume round-to-nearest. */ + fn = x*invpio2+0x1.8p112; + fn = fn-0x1.8p112; +#ifdef HAVE_EFFICIENT_I64RINT + n = i64rint(fn); +#else + n = fn; +#endif + r = x-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 180 bit */ + { + union IEEEl2bits u2; + int ex1; + j = ex; + y[0] = r-w; + u2.e = y[0]; + ex1 = u2.xbits.expsign & 0x7fff; + i = j-ex1; + if(i>51) { /* 2nd iteration needed, good to 248 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + u2.e = y[0]; + ex1 = u2.xbits.expsign & 0x7fff; + i = j-ex1; + if(i>119) { /* 3rd iteration need, 316 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + return n; + } + /* + * all other (large) arguments + */ + if(ex==0x7fff) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-23) */ + u1.e = x; + e0 = ex - BIAS - 23; /* e0 = ilogb(|x|)-23; */ + u1.xbits.expsign = ex - e0; + z = u1.e; + for(i=0;i<4;i++) { + tx[i] = (double)((int32_t)(z)); + z = (z-tx[i])*two24; + } + tx[4] = z; + nx = 5; + while(tx[nx-1]==zero) nx--; /* skip zero term */ + n = __kernel_rem_pio2(tx,ty,e0,nx,3); + t = (long double)ty[2] + ty[1]; + r = t + ty[0]; + w = ty[0] - (r - t); + if(expsign<0) {y[0] = -r; y[1] = -w; return -n;} + y[0] = r; y[1] = w; return n; +} diff --git a/openlibm/ld128/e_sinhl.c b/openlibm/ld128/e_sinhl.c new file mode 100644 index 0000000000..3bfdb5d346 --- /dev/null +++ b/openlibm/ld128/e_sinhl.c @@ -0,0 +1,104 @@ +/* @(#)e_sinh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* sinhl(x) + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinhl(-x) = -sinhl(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 25 : sinhl(x) := --------------, E=expm1l(x) + * 2 + * + * 25 <= x <= lnovft : sinhl(x) := expl(x)/2 + * lnovft <= x <= ln2ovft: sinhl(x) := expl(x/2)/2 * expl(x/2) + * ln2ovft < x : sinhl(x) := x*shuge (overflow) + * + * Special cases: + * sinhl(x) is |x| if x is +INF, -INF, or NaN. + * only sinhl(0)=0 is exact for finite x. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, shuge = 1.0e4931L, +ovf_thresh = 1.1357216553474703894801348310092223067821E4L; + +long double +sinhl(long double x) +{ + long double t, w, h; + u_int32_t jx, ix; + ieee_quad_shape_type u; + + /* Words of |x|. */ + u.value = x; + jx = u.parts32.mswhi; + ix = jx & 0x7fffffff; + + /* x is INF or NaN */ + if (ix >= 0x7fff0000) + return x + x; + + h = 0.5; + if (jx & 0x80000000) + h = -h; + + /* Absolute value of x. */ + u.parts32.mswhi = ix; + + /* |x| in [0,40], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix <= 0x40044000) + { + if (ix < 0x3fc60000) /* |x| < 2^-57 */ + if (shuge + x > one) + return x; /* sinh(tiny) = tiny with inexact */ + t = expm1l (u.value); + if (ix < 0x3fff0000) + return h * (2.0 * t - t * t / (t + one)); + return h * (t + t / (t + one)); + } + + /* |x| in [40, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix <= 0x400c62e3) /* 11356.375 */ + return h * expl (u.value); + + /* |x| in [log(maxdouble), overflowthreshold] + Overflow threshold is log(2 * maxdouble). */ + if (u.value <= ovf_thresh) + { + w = expl (0.5 * u.value); + t = h * w; + return t * w; + } + + /* |x| > overflowthreshold, sinhl(x) overflow */ + return x * shuge; +} diff --git a/openlibm/ld128/e_tgammal.c b/openlibm/ld128/e_tgammal.c new file mode 100644 index 0000000000..6c78c244f3 --- /dev/null +++ b/openlibm/ld128/e_tgammal.c @@ -0,0 +1,39 @@ +/* $OpenBSD: e_tgammal.c,v 1.1 2011/07/06 00:02:42 martynas Exp $ */ + +/* + * Copyright (c) 2011 Martynas Venckus + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "math_private.h" + +long double +tgammal(long double x) +{ + int64_t i0,i1; + + GET_LDOUBLE_WORDS64(i0,i1,x); + if (((i0&0x7fffffffffffffffLL)|i1) == 0) + return (1.0/x); + + if (i0<0 && (u_int64_t)i0<0xffff000000000000ULL && rintl(x)==x) + return (x-x)/(x-x); + + if (i0==0xffff000000000000ULL && i1==0) + return (x-x); + + return expl(lgammal(x)); +} diff --git a/openlibm/ld128/invtrig.c b/openlibm/ld128/invtrig.c new file mode 100644 index 0000000000..5a869c2caa --- /dev/null +++ b/openlibm/ld128/invtrig.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld128/invtrig.c,v 1.1 2008/07/31 22:41:26 das Exp $"); + +#include "ld128/invtrig.h" + +/* + * asinl() and acosl() + */ +const long double +pS0 = 1.66666666666666666666666666666700314e-01L, +pS1 = -7.32816946414566252574527475428622708e-01L, +pS2 = 1.34215708714992334609030036562143589e+00L, +pS3 = -1.32483151677116409805070261790752040e+00L, +pS4 = 7.61206183613632558824485341162121989e-01L, +pS5 = -2.56165783329023486777386833928147375e-01L, +pS6 = 4.80718586374448793411019434585413855e-02L, +pS7 = -4.42523267167024279410230886239774718e-03L, +pS8 = 1.44551535183911458253205638280410064e-04L, +pS9 = -2.10558957916600254061591040482706179e-07L, +qS1 = -4.84690167848739751544716485245697428e+00L, +qS2 = 9.96619113536172610135016921140206980e+00L, +qS3 = -1.13177895428973036660836798461641458e+01L, +qS4 = 7.74004374389488266169304117714658761e+00L, +qS5 = -3.25871986053534084709023539900339905e+00L, +qS6 = 8.27830318881232209752469022352928864e-01L, +qS7 = -1.18768052702942805423330715206348004e-01L, +qS8 = 8.32600764660522313269101537926539470e-03L, +qS9 = -1.99407384882605586705979504567947007e-04L; + +/* + * atanl() + */ +const long double atanhi[] = { + 4.63647609000806116214256231461214397e-01L, + 7.85398163397448309615660845819875699e-01L, + 9.82793723247329067985710611014666038e-01L, + 1.57079632679489661923132169163975140e+00L, +}; + +const long double atanlo[] = { + 4.89509642257333492668618435220297706e-36L, + 2.16795253253094525619926100651083806e-35L, + -2.31288434538183565909319952098066272e-35L, + 4.33590506506189051239852201302167613e-35L, +}; + +const long double aT[] = { + 3.33333333333333333333333333333333125e-01L, + -1.99999999999999999999999999999180430e-01L, + 1.42857142857142857142857142125269827e-01L, + -1.11111111111111111111110834490810169e-01L, + 9.09090909090909090908522355708623681e-02L, + -7.69230769230769230696553844935357021e-02L, + 6.66666666666666660390096773046256096e-02L, + -5.88235294117646671706582985209643694e-02L, + 5.26315789473666478515847092020327506e-02L, + -4.76190476189855517021024424991436144e-02L, + 4.34782608678695085948531993458097026e-02L, + -3.99999999632663469330634215991142368e-02L, + 3.70370363987423702891250829918659723e-02L, + -3.44827496515048090726669907612335954e-02L, + 3.22579620681420149871973710852268528e-02L, + -3.03020767654269261041647570626778067e-02L, + 2.85641979882534783223403715930946138e-02L, + -2.69824879726738568189929461383741323e-02L, + 2.54194698498808542954187110873675769e-02L, + -2.35083879708189059926183138130183215e-02L, + 2.04832358998165364349957325067131428e-02L, + -1.54489555488544397858507248612362957e-02L, + 8.64492360989278761493037861575248038e-03L, + -2.58521121597609872727919154569765469e-03L, +}; + +const long double pi_lo = 8.67181013012378102479704402604335225e-35L; diff --git a/openlibm/ld128/invtrig.h b/openlibm/ld128/invtrig.h new file mode 100644 index 0000000000..c85a615efd --- /dev/null +++ b/openlibm/ld128/invtrig.h @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/ld128/invtrig.h,v 1.1 2008/07/31 22:41:26 das Exp $ + */ + +#include + +#include "fpmath.h" + +#define BIAS (LDBL_MAX_EXP - 1) +#define MANH_SIZE (LDBL_MANH_SIZE + 1) + +/* Approximation thresholds. */ +#define ASIN_LINEAR (BIAS - 56) /* 2**-56 */ +#define ACOS_CONST (BIAS - 113) /* 2**-113 */ +#define ATAN_CONST (BIAS + 113) /* 2**113 */ +#define ATAN_LINEAR (BIAS - 56) /* 2**-56 */ + +/* 0.95 */ +#define THRESH ((0xe666666666666666ULL>>(64-(MANH_SIZE-1)))|LDBL_NBIT) + +/* Constants shared by the long double inverse trig functions. */ +#define pS0 _ItL_pS0 +#define pS1 _ItL_pS1 +#define pS2 _ItL_pS2 +#define pS3 _ItL_pS3 +#define pS4 _ItL_pS4 +#define pS5 _ItL_pS5 +#define pS6 _ItL_pS6 +#define pS7 _ItL_pS7 +#define pS8 _ItL_pS8 +#define pS9 _ItL_pS9 +#define qS1 _ItL_qS1 +#define qS2 _ItL_qS2 +#define qS3 _ItL_qS3 +#define qS4 _ItL_qS4 +#define qS5 _ItL_qS5 +#define qS6 _ItL_qS6 +#define qS7 _ItL_qS7 +#define qS8 _ItL_qS8 +#define qS9 _ItL_qS9 +#define atanhi _ItL_atanhi +#define atanlo _ItL_atanlo +#define aT _ItL_aT +#define pi_lo _ItL_pi_lo + +#define pio2_hi atanhi[3] +#define pio2_lo atanlo[3] +#define pio4_hi atanhi[1] + +/* Constants shared by the long double inverse trig functions. */ +extern const long double pS0, pS1, pS2, pS3, pS4, pS5, pS6, pS7, pS8, pS9; +extern const long double qS1, qS2, qS3, qS4, qS5, qS6, qS7, qS8, qS9; +extern const long double atanhi[], atanlo[], aT[]; +extern const long double pi_lo; + +static inline long double +P(long double x) +{ + + return (x * (pS0 + x * (pS1 + x * (pS2 + x * (pS3 + x * \ + (pS4 + x * (pS5 + x * (pS6 + x * (pS7 + x * (pS8 + x * \ + pS9)))))))))); +} + +static inline long double +Q(long double x) +{ + + return (1.0 + x * (qS1 + x * (qS2 + x * (qS3 + x * (qS4 + x * \ + (qS5 + x * (qS6 + x * (qS7 + x * (qS8 + x * qS9))))))))); +} + +static inline long double +T_even(long double x) +{ + + return (aT[0] + x * (aT[2] + x * (aT[4] + x * (aT[6] + x * \ + (aT[8] + x * (aT[10] + x * (aT[12] + x * (aT[14] + x * \ + (aT[16] + x * (aT[18] + x * (aT[20] + x * aT[22]))))))))))); +} + +static inline long double +T_odd(long double x) +{ + + return (aT[1] + x * (aT[3] + x * (aT[5] + x * (aT[7] + x * \ + (aT[9] + x * (aT[11] + x * (aT[13] + x * (aT[15] + x * \ + (aT[17] + x * (aT[19] + x * (aT[21] + x * aT[23]))))))))))); +} diff --git a/openlibm/ld128/k_cosl.c b/openlibm/ld128/k_cosl.c new file mode 100644 index 0000000000..37666dcb8e --- /dev/null +++ b/openlibm/ld128/k_cosl.c @@ -0,0 +1,61 @@ +/* From: @(#)k_cos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld128/k_cosl.c,v 1.1 2008/02/17 07:32:31 das Exp $"); + +/* + * ld128 version of k_cos.c. See ../src/k_cos.c for most comments. + */ + +#include "math_private.h" + +/* + * Domain [-0.7854, 0.7854], range ~[-1.80e-37, 1.79e-37]: + * |cos(x) - c(x))| < 2**-122.0 + * + * 113-bit precision requires more care than 64-bit precision, since + * simple methods give a minimax polynomial with coefficient for x^2 + * that is 1 ulp below 0.5, but we want it to be precisely 0.5. See + * ../ld80/k_cosl.c for more details. + */ +static const double +one = 1.0; + +static const long double +C1 = 0.04166666666666666666666666666666658424671L, +C2 = -0.001388888888888888888888888888863490893732L, +C3 = 0.00002480158730158730158730158600795304914210L, +C4 = -0.2755731922398589065255474947078934284324e-6L, +C5 = 0.2087675698786809897659225313136400793948e-8L, +C6 = -0.1147074559772972315817149986812031204775e-10L, +C7 = 0.4779477332386808976875457937252120293400e-13L; + +static const double +C8 = -0.1561920696721507929516718307820958119868e-15, +C9 = 0.4110317413744594971475941557607804508039e-18, +C10 = -0.8896592467191938803288521958313920156409e-21, +C11 = 0.1601061435794535138244346256065192782581e-23; + +long double +__kernel_cosl(long double x, long double y) +{ + long double hz,z,r,w; + + z = x*x; + r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*(C6+z*(C7+ + z*(C8+z*(C9+z*(C10+z*C11)))))))))); + hz = 0.5*z; + w = one-hz; + return w + (((one-w)-hz) + (z*r-x*y)); +} diff --git a/openlibm/ld128/k_sinl.c b/openlibm/ld128/k_sinl.c new file mode 100644 index 0000000000..10791598b5 --- /dev/null +++ b/openlibm/ld128/k_sinl.c @@ -0,0 +1,59 @@ +/* From: @(#)k_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld128/k_sinl.c,v 1.1 2008/02/17 07:32:31 das Exp $"); + +/* + * ld128 version of k_sin.c. See ../src/k_sin.c for most comments. + */ + +#include "math_private.h" + +static const double +half = 0.5; + +/* + * Domain [-0.7854, 0.7854], range ~[-1.53e-37, 1.659e-37] + * |sin(x)/x - s(x)| < 2**-122.1 + * + * See ../ld80/k_cosl.c for more details about the polynomial. + */ +static const long double +S1 = -0.16666666666666666666666666666666666606732416116558L, +S2 = 0.0083333333333333333333333333333331135404851288270047L, +S3 = -0.00019841269841269841269841269839935785325638310428717L, +S4 = 0.27557319223985890652557316053039946268333231205686e-5L, +S5 = -0.25052108385441718775048214826384312253862930064745e-7L, +S6 = 0.16059043836821614596571832194524392581082444805729e-9L, +S7 = -0.76471637318198151807063387954939213287488216303768e-12L, +S8 = 0.28114572543451292625024967174638477283187397621303e-14L; + +static const double +S9 = -0.82206352458348947812512122163446202498005154296863e-17, +S10 = 0.19572940011906109418080609928334380560135358385256e-19, +S11 = -0.38680813379701966970673724299207480965452616911420e-22, +S12 = 0.64038150078671872796678569586315881020659912139412e-25; + +long double +__kernel_sinl(long double x, long double y, int iy) +{ + long double z,r,v; + + z = x*x; + v = z*x; + r = S2+z*(S3+z*(S4+z*(S5+z*(S6+z*(S7+z*(S8+ + z*(S9+z*(S10+z*(S11+z*S12))))))))); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} diff --git a/openlibm/ld128/k_tanl.c b/openlibm/ld128/k_tanl.c new file mode 100644 index 0000000000..72550765fe --- /dev/null +++ b/openlibm/ld128/k_tanl.c @@ -0,0 +1,120 @@ +/* From: @(#)k_tan.c 1.5 04/04/22 SMI */ + +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld128/k_tanl.c,v 1.1 2008/02/17 07:32:31 das Exp $"); + +/* + * ld128 version of k_tan.c. See ../src/k_tan.c for most comments. + */ + +#include + +#include "math_private.h" + +/* + * Domain [-0.67434, 0.67434], range ~[-3.37e-36, 1.982e-37] + * |tan(x)/x - t(x)| < 2**-117.8 (XXX should be ~1e-37) + * + * See ../ld80/k_cosl.c for more details about the polynomial. + */ +static const long double +T3 = 0x1.5555555555555555555555555553p-2L, +T5 = 0x1.1111111111111111111111111eb5p-3L, +T7 = 0x1.ba1ba1ba1ba1ba1ba1ba1b694cd6p-5L, +T9 = 0x1.664f4882c10f9f32d6bbe09d8bcdp-6L, +T11 = 0x1.226e355e6c23c8f5b4f5762322eep-7L, +T13 = 0x1.d6d3d0e157ddfb5fed8e84e27b37p-9L, +T15 = 0x1.7da36452b75e2b5fce9ee7c2c92ep-10L, +T17 = 0x1.355824803674477dfcf726649efep-11L, +T19 = 0x1.f57d7734d1656e0aceb716f614c2p-13L, +T21 = 0x1.967e18afcb180ed942dfdc518d6cp-14L, +T23 = 0x1.497d8eea21e95bc7e2aa79b9f2cdp-15L, +T25 = 0x1.0b132d39f055c81be49eff7afd50p-16L, +T27 = 0x1.b0f72d33eff7bfa2fbc1059d90b6p-18L, +T29 = 0x1.5ef2daf21d1113df38d0fbc00267p-19L, +T31 = 0x1.1c77d6eac0234988cdaa04c96626p-20L, +T33 = 0x1.cd2a5a292b180e0bdd701057dfe3p-22L, +T35 = 0x1.75c7357d0298c01a31d0a6f7d518p-23L, +T37 = 0x1.2f3190f4718a9a520f98f50081fcp-24L, +pio4 = 0x1.921fb54442d18469898cc51701b8p-1L, +pio4lo = 0x1.cd129024e088a67cc74020bbea60p-116L; + +static const double +T39 = 0.000000028443389121318352, /* 0x1e8a7592977938.0p-78 */ +T41 = 0.000000011981013102001973, /* 0x19baa1b1223219.0p-79 */ +T43 = 0.0000000038303578044958070, /* 0x107385dfb24529.0p-80 */ +T45 = 0.0000000034664378216909893, /* 0x1dc6c702a05262.0p-81 */ +T47 = -0.0000000015090641701997785, /* -0x19ecef3569ebb6.0p-82 */ +T49 = 0.0000000029449552300483952, /* 0x194c0668da786a.0p-81 */ +T51 = -0.0000000022006995706097711, /* -0x12e763b8845268.0p-81 */ +T53 = 0.0000000015468200913196612, /* 0x1a92fc98c29554.0p-82 */ +T55 = -0.00000000061311613386849674, /* -0x151106cbc779a9.0p-83 */ +T57 = 1.4912469681508012e-10; /* 0x147edbdba6f43a.0p-85 */ + +long double +__kernel_tanl(long double x, long double y, int iy) { + long double z, r, v, w, s; + long double osign; + int i; + + iy = (iy == 1 ? -1 : 1); /* XXX recover original interface */ + osign = (x >= 0 ? 1.0 : -1.0); /* XXX slow, probably wrong for -0 */ + if (fabsl(x) >= 0.67434) { + if (x < 0) { + x = -x; + y = -y; + } + z = pio4 - x; + w = pio4lo - y; + x = z + w; + y = 0.0; + i = 1; + } else + i = 0; + z = x * x; + w = z * z; + r = T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 + + w * (T25 + w * (T29 + w * (T33 + + w * (T37 + w * (T41 + w * (T45 + w * (T49 + w * (T53 + + w * T57)))))))))))); + v = z * (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 + + w * (T27 + w * (T31 + w * (T35 + + w * (T39 + w * (T43 + w * (T47 + w * (T51 + w * T55)))))))))))); + s = z * x; + r = y + z * (s * (r + v) + y); + r += T3 * s; + w = x + r; + if (i == 1) { + v = (long double) iy; + return osign * + (v - 2.0 * (x - (w * w / (w + v) - r))); + } + if (iy == 1) + return w; + else { + /* + * if allow error up to 2 ulp, simply return + * -1.0 / (x+r) here + */ + /* compute -1.0 / (x+r) accurately */ + long double a, t; + z = w; + z = z + 0x1p32 - 0x1p32; + v = r - (z - x); /* z+v = r+x */ + t = a = -1.0 / w; /* a = -1.0/w */ + t = t + 0x1p32 - 0x1p32; + s = 1.0 + t * z; + return t + a * (s + t * v); + } +} diff --git a/openlibm/ld128/s_asinhl.c b/openlibm/ld128/s_asinhl.c new file mode 100644 index 0000000000..29791e8af1 --- /dev/null +++ b/openlibm/ld128/s_asinhl.c @@ -0,0 +1,69 @@ +/* @(#)s_asinh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* asinhl(x) + * Method : + * Based on + * asinhl(x) = signl(x) * logl [ |x| + sqrtl(x*x+1) ] + * we have + * asinhl(x) := x if 1+x*x=1, + * := signl(x)*(logl(x)+ln2)) for large |x|, else + * := signl(x)*logl(2|x|+1/(|x|+sqrtl(x*x+1))) if|x|>2, else + * := signl(x)*log1pl(|x| + x^2/(1 + sqrtl(1+x^2))) + */ + +#include + +#include "math_private.h" + +static const long double + one = 1.0L, + ln2 = 6.931471805599453094172321214581765681e-1L, + huge = 1.0e+4900L; + +long double +asinhl(long double x) +{ + long double t, w; + int32_t ix, sign; + ieee_quad_shape_type u; + + u.value = x; + sign = u.parts32.mswhi; + ix = sign & 0x7fffffff; + if (ix == 0x7fff0000) + return x + x; /* x is inf or NaN */ + if (ix < 0x3fc70000) + { /* |x| < 2^ -56 */ + if (huge + x > one) + return x; /* return x inexact except 0 */ + } + u.parts32.mswhi = ix; + if (ix > 0x40350000) + { /* |x| > 2 ^ 54 */ + w = logl (u.value) + ln2; + } + else if (ix >0x40000000) + { /* 2^ 54 > |x| > 2.0 */ + t = u.value; + w = logl (2.0 * t + one / (sqrtl (x * x + one) + t)); + } + else + { /* 2.0 > |x| > 2 ^ -56 */ + t = x * x; + w = log1pl (u.value + t / (one + sqrtl (one + t))); + } + if (sign & 0x80000000) + return -w; + else + return w; +} diff --git a/openlibm/ld128/s_ceill.c b/openlibm/ld128/s_ceill.c new file mode 100644 index 0000000000..719e7fc64d --- /dev/null +++ b/openlibm/ld128/s_ceill.c @@ -0,0 +1,69 @@ +/* @(#)s_ceil.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * ceill(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to ceil(x). + */ + +#include + +#include "math_private.h" + +static const long double huge = 1.0e4930L; + +long double +ceill(long double x) +{ + int64_t i0,i1,jj0; + u_int64_t i,j; + GET_LDOUBLE_WORDS64(i0,i1,x); + jj0 = ((i0>>48)&0x7fff)-0x3fff; + if(jj0<48) { + if(jj0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x8000000000000000ULL;i1=0;} + else if((i0|i1)!=0) { i0=0x3fff000000000000ULL;i1=0;} + } + } else { + i = (0x0000ffffffffffffULL)>>jj0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x0001000000000000LL)>>jj0; + i0 &= (~i); i1=0; + } + } + } else if (jj0>111) { + if(jj0==0x4000) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = -1ULL>>(jj0-48); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) { + if(jj0==48) i0+=1; + else { + j = i1+(1LL<<(112-jj0)); + if(j + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* double erf(double x) + * double erfc(double x) + * x + * 2 |\ + * erf(x) = --------- | exp(-t*t)dt + * sqrt(pi) \| + * 0 + * + * erfc(x) = 1-erf(x) + * Note that + * erf(-x) = -erf(x) + * erfc(-x) = 2 - erfc(x) + * + * Method: + * 1. erf(x) = x + x*R(x^2) for |x| in [0, 7/8] + * Remark. The formula is derived by noting + * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) + * and that + * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 + * is close to one. + * + * 1a. erf(x) = 1 - erfc(x), for |x| > 1.0 + * erfc(x) = 1 - erf(x) if |x| < 1/4 + * + * 2. For |x| in [7/8, 1], let s = |x| - 1, and + * c = 0.84506291151 rounded to single (24 bits) + * erf(s + c) = sign(x) * (c + P1(s)/Q1(s)) + * Remark: here we use the taylor series expansion at x=1. + * erf(1+s) = erf(1) + s*Poly(s) + * = 0.845.. + P1(s)/Q1(s) + * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] + * + * 3. For x in [1/4, 5/4], + * erfc(s + const) = erfc(const) + s P1(s)/Q1(s) + * for const = 1/4, 3/8, ..., 9/8 + * and 0 <= s <= 1/8 . + * + * 4. For x in [5/4, 107], + * erfc(x) = (1/x)*exp(-x*x-0.5625 + R(z)) + * z=1/x^2 + * The interval is partitioned into several segments + * of width 1/8 in 1/x. + * + * Note1: + * To compute exp(-x*x-0.5625+R/S), let s be a single + * precision number and s := x; then + * -x*x = -s*s + (s-x)*(s+x) + * exp(-x*x-0.5626+R/S) = + * exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S); + * Note2: + * Here 4 and 5 make use of the asymptotic series + * exp(-x*x) + * erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) ) + * x*sqrt(pi) + * + * 5. For inf > x >= 107 + * erf(x) = sign(x) *(1 - tiny) (raise inexact) + * erfc(x) = tiny*tiny (raise underflow) if x > 0 + * = 2 - tiny if x<0 + * + * 7. Special case: + * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, + * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, + * erfc/erf(NaN) is NaN + */ + +#include + +#include "math_private.h" + +/* Evaluate P[n] x^n + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +neval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + +/* Evaluate x^n+1 + P[n] x^(n) + P[n-1] x^(n-1) + ... + P[0] */ + +static long double +deval (long double x, const long double *p, int n) +{ + long double y; + + p += n; + y = x + *p--; + do + { + y = y * x + *p--; + } + while (--n > 0); + return y; +} + + + +static const long double +tiny = 1e-4931L, + one = 1.0L, + two = 2.0L, + /* 2/sqrt(pi) - 1 */ + efx = 1.2837916709551257389615890312154517168810E-1L, + /* 8 * (2/sqrt(pi) - 1) */ + efx8 = 1.0270333367641005911692712249723613735048E0L; + + +/* erf(x) = x + x R(x^2) + 0 <= x <= 7/8 + Peak relative error 1.8e-35 */ +#define NTN1 8 +static const long double TN1[NTN1 + 1] = +{ + -3.858252324254637124543172907442106422373E10L, + 9.580319248590464682316366876952214879858E10L, + 1.302170519734879977595901236693040544854E10L, + 2.922956950426397417800321486727032845006E9L, + 1.764317520783319397868923218385468729799E8L, + 1.573436014601118630105796794840834145120E7L, + 4.028077380105721388745632295157816229289E5L, + 1.644056806467289066852135096352853491530E4L, + 3.390868480059991640235675479463287886081E1L +}; +#define NTD1 8 +static const long double TD1[NTD1 + 1] = +{ + -3.005357030696532927149885530689529032152E11L, + -1.342602283126282827411658673839982164042E11L, + -2.777153893355340961288511024443668743399E10L, + -3.483826391033531996955620074072768276974E9L, + -2.906321047071299585682722511260895227921E8L, + -1.653347985722154162439387878512427542691E7L, + -6.245520581562848778466500301865173123136E5L, + -1.402124304177498828590239373389110545142E4L, + -1.209368072473510674493129989468348633579E2L +/* 1.0E0 */ +}; + + +/* erf(z+1) = erf_const + P(z)/Q(z) + -.125 <= z <= 0 + Peak relative error 7.3e-36 */ +static const long double erf_const = 0.845062911510467529296875L; +#define NTN2 8 +static const long double TN2[NTN2 + 1] = +{ + -4.088889697077485301010486931817357000235E1L, + 7.157046430681808553842307502826960051036E3L, + -2.191561912574409865550015485451373731780E3L, + 2.180174916555316874988981177654057337219E3L, + 2.848578658049670668231333682379720943455E2L, + 1.630362490952512836762810462174798925274E2L, + 6.317712353961866974143739396865293596895E0L, + 2.450441034183492434655586496522857578066E1L, + 5.127662277706787664956025545897050896203E-1L +}; +#define NTD2 8 +static const long double TD2[NTD2 + 1] = +{ + 1.731026445926834008273768924015161048885E4L, + 1.209682239007990370796112604286048173750E4L, + 1.160950290217993641320602282462976163857E4L, + 5.394294645127126577825507169061355698157E3L, + 2.791239340533632669442158497532521776093E3L, + 8.989365571337319032943005387378993827684E2L, + 2.974016493766349409725385710897298069677E2L, + 6.148192754590376378740261072533527271947E1L, + 1.178502892490738445655468927408440847480E1L + /* 1.0E0 */ +}; + + +/* erfc(x + 0.25) = erfc(0.25) + x R(x) + 0 <= x < 0.125 + Peak relative error 1.4e-35 */ +#define NRNr13 8 +static const long double RNr13[NRNr13 + 1] = +{ + -2.353707097641280550282633036456457014829E3L, + 3.871159656228743599994116143079870279866E2L, + -3.888105134258266192210485617504098426679E2L, + -2.129998539120061668038806696199343094971E1L, + -8.125462263594034672468446317145384108734E1L, + 8.151549093983505810118308635926270319660E0L, + -5.033362032729207310462422357772568553670E0L, + -4.253956621135136090295893547735851168471E-2L, + -8.098602878463854789780108161581050357814E-2L +}; +#define NRDr13 7 +static const long double RDr13[NRDr13 + 1] = +{ + 2.220448796306693503549505450626652881752E3L, + 1.899133258779578688791041599040951431383E2L, + 1.061906712284961110196427571557149268454E3L, + 7.497086072306967965180978101974566760042E1L, + 2.146796115662672795876463568170441327274E2L, + 1.120156008362573736664338015952284925592E1L, + 2.211014952075052616409845051695042741074E1L, + 6.469655675326150785692908453094054988938E-1L + /* 1.0E0 */ +}; +/* erfc(0.25) = C13a + C13b to extra precision. */ +static const long double C13a = 0.723663330078125L; +static const long double C13b = 1.0279753638067014931732235184287934646022E-5L; + + +/* erfc(x + 0.375) = erfc(0.375) + x R(x) + 0 <= x < 0.125 + Peak relative error 1.2e-35 */ +#define NRNr14 8 +static const long double RNr14[NRNr14 + 1] = +{ + -2.446164016404426277577283038988918202456E3L, + 6.718753324496563913392217011618096698140E2L, + -4.581631138049836157425391886957389240794E2L, + -2.382844088987092233033215402335026078208E1L, + -7.119237852400600507927038680970936336458E1L, + 1.313609646108420136332418282286454287146E1L, + -6.188608702082264389155862490056401365834E0L, + -2.787116601106678287277373011101132659279E-2L, + -2.230395570574153963203348263549700967918E-2L +}; +#define NRDr14 7 +static const long double RDr14[NRDr14 + 1] = +{ + 2.495187439241869732696223349840963702875E3L, + 2.503549449872925580011284635695738412162E2L, + 1.159033560988895481698051531263861842461E3L, + 9.493751466542304491261487998684383688622E1L, + 2.276214929562354328261422263078480321204E2L, + 1.367697521219069280358984081407807931847E1L, + 2.276988395995528495055594829206582732682E1L, + 7.647745753648996559837591812375456641163E-1L + /* 1.0E0 */ +}; +/* erfc(0.375) = C14a + C14b to extra precision. */ +static const long double C14a = 0.5958709716796875L; +static const long double C14b = 1.2118885490201676174914080878232469565953E-5L; + +/* erfc(x + 0.5) = erfc(0.5) + x R(x) + 0 <= x < 0.125 + Peak relative error 4.7e-36 */ +#define NRNr15 8 +static const long double RNr15[NRNr15 + 1] = +{ + -2.624212418011181487924855581955853461925E3L, + 8.473828904647825181073831556439301342756E2L, + -5.286207458628380765099405359607331669027E2L, + -3.895781234155315729088407259045269652318E1L, + -6.200857908065163618041240848728398496256E1L, + 1.469324610346924001393137895116129204737E1L, + -6.961356525370658572800674953305625578903E0L, + 5.145724386641163809595512876629030548495E-3L, + 1.990253655948179713415957791776180406812E-2L +}; +#define NRDr15 7 +static const long double RDr15[NRDr15 + 1] = +{ + 2.986190760847974943034021764693341524962E3L, + 5.288262758961073066335410218650047725985E2L, + 1.363649178071006978355113026427856008978E3L, + 1.921707975649915894241864988942255320833E2L, + 2.588651100651029023069013885900085533226E2L, + 2.628752920321455606558942309396855629459E1L, + 2.455649035885114308978333741080991380610E1L, + 1.378826653595128464383127836412100939126E0L + /* 1.0E0 */ +}; +/* erfc(0.5) = C15a + C15b to extra precision. */ +static const long double C15a = 0.4794921875L; +static const long double C15b = 7.9346869534623172533461080354712635484242E-6L; + +/* erfc(x + 0.625) = erfc(0.625) + x R(x) + 0 <= x < 0.125 + Peak relative error 5.1e-36 */ +#define NRNr16 8 +static const long double RNr16[NRNr16 + 1] = +{ + -2.347887943200680563784690094002722906820E3L, + 8.008590660692105004780722726421020136482E2L, + -5.257363310384119728760181252132311447963E2L, + -4.471737717857801230450290232600243795637E1L, + -4.849540386452573306708795324759300320304E1L, + 1.140885264677134679275986782978655952843E1L, + -6.731591085460269447926746876983786152300E0L, + 1.370831653033047440345050025876085121231E-1L, + 2.022958279982138755020825717073966576670E-2L, +}; +#define NRDr16 7 +static const long double RDr16[NRDr16 + 1] = +{ + 3.075166170024837215399323264868308087281E3L, + 8.730468942160798031608053127270430036627E2L, + 1.458472799166340479742581949088453244767E3L, + 3.230423687568019709453130785873540386217E2L, + 2.804009872719893612081109617983169474655E2L, + 4.465334221323222943418085830026979293091E1L, + 2.612723259683205928103787842214809134746E1L, + 2.341526751185244109722204018543276124997E0L, + /* 1.0E0 */ +}; +/* erfc(0.625) = C16a + C16b to extra precision. */ +static const long double C16a = 0.3767547607421875L; +static const long double C16b = 4.3570693945275513594941232097252997287766E-6L; + +/* erfc(x + 0.75) = erfc(0.75) + x R(x) + 0 <= x < 0.125 + Peak relative error 1.7e-35 */ +#define NRNr17 8 +static const long double RNr17[NRNr17 + 1] = +{ + -1.767068734220277728233364375724380366826E3L, + 6.693746645665242832426891888805363898707E2L, + -4.746224241837275958126060307406616817753E2L, + -2.274160637728782675145666064841883803196E1L, + -3.541232266140939050094370552538987982637E1L, + 6.988950514747052676394491563585179503865E0L, + -5.807687216836540830881352383529281215100E0L, + 3.631915988567346438830283503729569443642E-1L, + -1.488945487149634820537348176770282391202E-2L +}; +#define NRDr17 7 +static const long double RDr17[NRDr17 + 1] = +{ + 2.748457523498150741964464942246913394647E3L, + 1.020213390713477686776037331757871252652E3L, + 1.388857635935432621972601695296561952738E3L, + 3.903363681143817750895999579637315491087E2L, + 2.784568344378139499217928969529219886578E2L, + 5.555800830216764702779238020065345401144E1L, + 2.646215470959050279430447295801291168941E1L, + 2.984905282103517497081766758550112011265E0L, + /* 1.0E0 */ +}; +/* erfc(0.75) = C17a + C17b to extra precision. */ +static const long double C17a = 0.2888336181640625L; +static const long double C17b = 1.0748182422368401062165408589222625794046E-5L; + + +/* erfc(x + 0.875) = erfc(0.875) + x R(x) + 0 <= x < 0.125 + Peak relative error 2.2e-35 */ +#define NRNr18 8 +static const long double RNr18[NRNr18 + 1] = +{ + -1.342044899087593397419622771847219619588E3L, + 6.127221294229172997509252330961641850598E2L, + -4.519821356522291185621206350470820610727E2L, + 1.223275177825128732497510264197915160235E1L, + -2.730789571382971355625020710543532867692E1L, + 4.045181204921538886880171727755445395862E0L, + -4.925146477876592723401384464691452700539E0L, + 5.933878036611279244654299924101068088582E-1L, + -5.557645435858916025452563379795159124753E-2L +}; +#define NRDr18 7 +static const long double RDr18[NRDr18 + 1] = +{ + 2.557518000661700588758505116291983092951E3L, + 1.070171433382888994954602511991940418588E3L, + 1.344842834423493081054489613250688918709E3L, + 4.161144478449381901208660598266288188426E2L, + 2.763670252219855198052378138756906980422E2L, + 5.998153487868943708236273854747564557632E1L, + 2.657695108438628847733050476209037025318E1L, + 3.252140524394421868923289114410336976512E0L, + /* 1.0E0 */ +}; +/* erfc(0.875) = C18a + C18b to extra precision. */ +static const long double C18a = 0.215911865234375L; +static const long double C18b = 1.3073705765341685464282101150637224028267E-5L; + +/* erfc(x + 1.0) = erfc(1.0) + x R(x) + 0 <= x < 0.125 + Peak relative error 1.6e-35 */ +#define NRNr19 8 +static const long double RNr19[NRNr19 + 1] = +{ + -1.139180936454157193495882956565663294826E3L, + 6.134903129086899737514712477207945973616E2L, + -4.628909024715329562325555164720732868263E2L, + 4.165702387210732352564932347500364010833E1L, + -2.286979913515229747204101330405771801610E1L, + 1.870695256449872743066783202326943667722E0L, + -4.177486601273105752879868187237000032364E0L, + 7.533980372789646140112424811291782526263E-1L, + -8.629945436917752003058064731308767664446E-2L +}; +#define NRDr19 7 +static const long double RDr19[NRDr19 + 1] = +{ + 2.744303447981132701432716278363418643778E3L, + 1.266396359526187065222528050591302171471E3L, + 1.466739461422073351497972255511919814273E3L, + 4.868710570759693955597496520298058147162E2L, + 2.993694301559756046478189634131722579643E2L, + 6.868976819510254139741559102693828237440E1L, + 2.801505816247677193480190483913753613630E1L, + 3.604439909194350263552750347742663954481E0L, + /* 1.0E0 */ +}; +/* erfc(1.0) = C19a + C19b to extra precision. */ +static const long double C19a = 0.15728759765625L; +static const long double C19b = 1.1609394035130658779364917390740703933002E-5L; + +/* erfc(x + 1.125) = erfc(1.125) + x R(x) + 0 <= x < 0.125 + Peak relative error 3.6e-36 */ +#define NRNr20 8 +static const long double RNr20[NRNr20 + 1] = +{ + -9.652706916457973956366721379612508047640E2L, + 5.577066396050932776683469951773643880634E2L, + -4.406335508848496713572223098693575485978E2L, + 5.202893466490242733570232680736966655434E1L, + -1.931311847665757913322495948705563937159E1L, + -9.364318268748287664267341457164918090611E-2L, + -3.306390351286352764891355375882586201069E0L, + 7.573806045289044647727613003096916516475E-1L, + -9.611744011489092894027478899545635991213E-2L +}; +#define NRDr20 7 +static const long double RDr20[NRDr20 + 1] = +{ + 3.032829629520142564106649167182428189014E3L, + 1.659648470721967719961167083684972196891E3L, + 1.703545128657284619402511356932569292535E3L, + 6.393465677731598872500200253155257708763E2L, + 3.489131397281030947405287112726059221934E2L, + 8.848641738570783406484348434387611713070E1L, + 3.132269062552392974833215844236160958502E1L, + 4.430131663290563523933419966185230513168E0L + /* 1.0E0 */ +}; +/* erfc(1.125) = C20a + C20b to extra precision. */ +static const long double C20a = 0.111602783203125L; +static const long double C20b = 8.9850951672359304215530728365232161564636E-6L; + +/* erfc(1/x) = 1/x exp (-1/x^2 - 0.5625 + R(1/x^2)) + 7/8 <= 1/x < 1 + Peak relative error 1.4e-35 */ +#define NRNr8 9 +static const long double RNr8[NRNr8 + 1] = +{ + 3.587451489255356250759834295199296936784E1L, + 5.406249749087340431871378009874875889602E2L, + 2.931301290625250886238822286506381194157E3L, + 7.359254185241795584113047248898753470923E3L, + 9.201031849810636104112101947312492532314E3L, + 5.749697096193191467751650366613289284777E3L, + 1.710415234419860825710780802678697889231E3L, + 2.150753982543378580859546706243022719599E2L, + 8.740953582272147335100537849981160931197E0L, + 4.876422978828717219629814794707963640913E-2L +}; +#define NRDr8 8 +static const long double RDr8[NRDr8 + 1] = +{ + 6.358593134096908350929496535931630140282E1L, + 9.900253816552450073757174323424051765523E2L, + 5.642928777856801020545245437089490805186E3L, + 1.524195375199570868195152698617273739609E4L, + 2.113829644500006749947332935305800887345E4L, + 1.526438562626465706267943737310282977138E4L, + 5.561370922149241457131421914140039411782E3L, + 9.394035530179705051609070428036834496942E2L, + 6.147019596150394577984175188032707343615E1L + /* 1.0E0 */ +}; + +/* erfc(1/x) = 1/x exp (-1/x^2 - 0.5625 + R(1/x^2)) + 0.75 <= 1/x <= 0.875 + Peak relative error 2.0e-36 */ +#define NRNr7 9 +static const long double RNr7[NRNr7 + 1] = +{ + 1.686222193385987690785945787708644476545E1L, + 1.178224543567604215602418571310612066594E3L, + 1.764550584290149466653899886088166091093E4L, + 1.073758321890334822002849369898232811561E5L, + 3.132840749205943137619839114451290324371E5L, + 4.607864939974100224615527007793867585915E5L, + 3.389781820105852303125270837910972384510E5L, + 1.174042187110565202875011358512564753399E5L, + 1.660013606011167144046604892622504338313E4L, + 6.700393957480661937695573729183733234400E2L +}; +#define NRDr7 9 +static const long double RDr7[NRDr7 + 1] = +{ +-1.709305024718358874701575813642933561169E3L, +-3.280033887481333199580464617020514788369E4L, +-2.345284228022521885093072363418750835214E5L, +-8.086758123097763971926711729242327554917E5L, +-1.456900414510108718402423999575992450138E6L, +-1.391654264881255068392389037292702041855E6L, +-6.842360801869939983674527468509852583855E5L, +-1.597430214446573566179675395199807533371E5L, +-1.488876130609876681421645314851760773480E4L, +-3.511762950935060301403599443436465645703E2L + /* 1.0E0 */ +}; + +/* erfc(1/x) = 1/x exp(-1/x^2 - 0.5625 + R(1/x^2)) + 5/8 <= 1/x < 3/4 + Peak relative error 1.9e-35 */ +#define NRNr6 9 +static const long double RNr6[NRNr6 + 1] = +{ + 1.642076876176834390623842732352935761108E0L, + 1.207150003611117689000664385596211076662E2L, + 2.119260779316389904742873816462800103939E3L, + 1.562942227734663441801452930916044224174E4L, + 5.656779189549710079988084081145693580479E4L, + 1.052166241021481691922831746350942786299E5L, + 9.949798524786000595621602790068349165758E4L, + 4.491790734080265043407035220188849562856E4L, + 8.377074098301530326270432059434791287601E3L, + 4.506934806567986810091824791963991057083E2L +}; +#define NRDr6 9 +static const long double RDr6[NRDr6 + 1] = +{ +-1.664557643928263091879301304019826629067E2L, +-3.800035902507656624590531122291160668452E3L, +-3.277028191591734928360050685359277076056E4L, +-1.381359471502885446400589109566587443987E5L, +-3.082204287382581873532528989283748656546E5L, +-3.691071488256738343008271448234631037095E5L, +-2.300482443038349815750714219117566715043E5L, +-6.873955300927636236692803579555752171530E4L, +-8.262158817978334142081581542749986845399E3L, +-2.517122254384430859629423488157361983661E2L + /* 1.00 */ +}; + +/* erfc(1/x) = 1/x exp(-1/x^2 - 0.5625 + R(1/x^2)) + 1/2 <= 1/x < 5/8 + Peak relative error 4.6e-36 */ +#define NRNr5 10 +static const long double RNr5[NRNr5 + 1] = +{ +-3.332258927455285458355550878136506961608E-3L, +-2.697100758900280402659586595884478660721E-1L, +-6.083328551139621521416618424949137195536E0L, +-6.119863528983308012970821226810162441263E1L, +-3.176535282475593173248810678636522589861E2L, +-8.933395175080560925809992467187963260693E2L, +-1.360019508488475978060917477620199499560E3L, +-1.075075579828188621541398761300910213280E3L, +-4.017346561586014822824459436695197089916E2L, +-5.857581368145266249509589726077645791341E1L, +-2.077715925587834606379119585995758954399E0L +}; +#define NRDr5 9 +static const long double RDr5[NRDr5 + 1] = +{ + 3.377879570417399341550710467744693125385E-1L, + 1.021963322742390735430008860602594456187E1L, + 1.200847646592942095192766255154827011939E2L, + 7.118915528142927104078182863387116942836E2L, + 2.318159380062066469386544552429625026238E3L, + 4.238729853534009221025582008928765281620E3L, + 4.279114907284825886266493994833515580782E3L, + 2.257277186663261531053293222591851737504E3L, + 5.570475501285054293371908382916063822957E2L, + 5.142189243856288981145786492585432443560E1L + /* 1.0E0 */ +}; + +/* erfc(1/x) = 1/x exp(-1/x^2 - 0.5625 + R(1/x^2)) + 3/8 <= 1/x < 1/2 + Peak relative error 2.0e-36 */ +#define NRNr4 10 +static const long double RNr4[NRNr4 + 1] = +{ + 3.258530712024527835089319075288494524465E-3L, + 2.987056016877277929720231688689431056567E-1L, + 8.738729089340199750734409156830371528862E0L, + 1.207211160148647782396337792426311125923E2L, + 8.997558632489032902250523945248208224445E2L, + 3.798025197699757225978410230530640879762E3L, + 9.113203668683080975637043118209210146846E3L, + 1.203285891339933238608683715194034900149E4L, + 8.100647057919140328536743641735339740855E3L, + 2.383888249907144945837976899822927411769E3L, + 2.127493573166454249221983582495245662319E2L +}; +#define NRDr4 10 +static const long double RDr4[NRDr4 + 1] = +{ +-3.303141981514540274165450687270180479586E-1L, +-1.353768629363605300707949368917687066724E1L, +-2.206127630303621521950193783894598987033E2L, +-1.861800338758066696514480386180875607204E3L, +-8.889048775872605708249140016201753255599E3L, +-2.465888106627948210478692168261494857089E4L, +-3.934642211710774494879042116768390014289E4L, +-3.455077258242252974937480623730228841003E4L, +-1.524083977439690284820586063729912653196E4L, +-2.810541887397984804237552337349093953857E3L, +-1.343929553541159933824901621702567066156E2L + /* 1.0E0 */ +}; + +/* erfc(1/x) = 1/x exp(-1/x^2 - 0.5625 + R(1/x^2)) + 1/4 <= 1/x < 3/8 + Peak relative error 8.4e-37 */ +#define NRNr3 11 +static const long double RNr3[NRNr3 + 1] = +{ +-1.952401126551202208698629992497306292987E-6L, +-2.130881743066372952515162564941682716125E-4L, +-8.376493958090190943737529486107282224387E-3L, +-1.650592646560987700661598877522831234791E-1L, +-1.839290818933317338111364667708678163199E0L, +-1.216278715570882422410442318517814388470E1L, +-4.818759344462360427612133632533779091386E1L, +-1.120994661297476876804405329172164436784E2L, +-1.452850765662319264191141091859300126931E2L, +-9.485207851128957108648038238656777241333E1L, +-2.563663855025796641216191848818620020073E1L, +-1.787995944187565676837847610706317833247E0L +}; +#define NRDr3 10 +static const long double RDr3[NRDr3 + 1] = +{ + 1.979130686770349481460559711878399476903E-4L, + 1.156941716128488266238105813374635099057E-2L, + 2.752657634309886336431266395637285974292E-1L, + 3.482245457248318787349778336603569327521E0L, + 2.569347069372696358578399521203959253162E1L, + 1.142279000180457419740314694631879921561E2L, + 3.056503977190564294341422623108332700840E2L, + 4.780844020923794821656358157128719184422E2L, + 4.105972727212554277496256802312730410518E2L, + 1.724072188063746970865027817017067646246E2L, + 2.815939183464818198705278118326590370435E1L + /* 1.0E0 */ +}; + +/* erfc(1/x) = 1/x exp(-1/x^2 - 0.5625 + R(1/x^2)) + 1/8 <= 1/x < 1/4 + Peak relative error 1.5e-36 */ +#define NRNr2 11 +static const long double RNr2[NRNr2 + 1] = +{ +-2.638914383420287212401687401284326363787E-8L, +-3.479198370260633977258201271399116766619E-6L, +-1.783985295335697686382487087502222519983E-4L, +-4.777876933122576014266349277217559356276E-3L, +-7.450634738987325004070761301045014986520E-2L, +-7.068318854874733315971973707247467326619E-1L, +-4.113919921935944795764071670806867038732E0L, +-1.440447573226906222417767283691888875082E1L, +-2.883484031530718428417168042141288943905E1L, +-2.990886974328476387277797361464279931446E1L, +-1.325283914915104866248279787536128997331E1L, +-1.572436106228070195510230310658206154374E0L +}; +#define NRDr2 10 +static const long double RDr2[NRDr2 + 1] = +{ + 2.675042728136731923554119302571867799673E-6L, + 2.170997868451812708585443282998329996268E-4L, + 7.249969752687540289422684951196241427445E-3L, + 1.302040375859768674620410563307838448508E-1L, + 1.380202483082910888897654537144485285549E0L, + 8.926594113174165352623847870299170069350E0L, + 3.521089584782616472372909095331572607185E1L, + 8.233547427533181375185259050330809105570E1L, + 1.072971579885803033079469639073292840135E2L, + 6.943803113337964469736022094105143158033E1L, + 1.775695341031607738233608307835017282662E1L + /* 1.0E0 */ +}; + +/* erfc(1/x) = 1/x exp(-1/x^2 - 0.5625 + R(1/x^2)) + 1/128 <= 1/x < 1/8 + Peak relative error 2.2e-36 */ +#define NRNr1 9 +static const long double RNr1[NRNr1 + 1] = +{ +-4.250780883202361946697751475473042685782E-8L, +-5.375777053288612282487696975623206383019E-6L, +-2.573645949220896816208565944117382460452E-4L, +-6.199032928113542080263152610799113086319E-3L, +-8.262721198693404060380104048479916247786E-2L, +-6.242615227257324746371284637695778043982E-1L, +-2.609874739199595400225113299437099626386E0L, +-5.581967563336676737146358534602770006970E0L, +-5.124398923356022609707490956634280573882E0L, +-1.290865243944292370661544030414667556649E0L +}; +#define NRDr1 8 +static const long double RDr1[NRDr1 + 1] = +{ + 4.308976661749509034845251315983612976224E-6L, + 3.265390126432780184125233455960049294580E-4L, + 9.811328839187040701901866531796570418691E-3L, + 1.511222515036021033410078631914783519649E-1L, + 1.289264341917429958858379585970225092274E0L, + 6.147640356182230769548007536914983522270E0L, + 1.573966871337739784518246317003956180750E1L, + 1.955534123435095067199574045529218238263E1L, + 9.472613121363135472247929109615785855865E0L + /* 1.0E0 */ +}; + + +long double +erfl(long double x) +{ + long double a, y, z; + int32_t i, ix, sign; + ieee_quad_shape_type u; + + u.value = x; + sign = u.parts32.mswhi; + ix = sign & 0x7fffffff; + + if (ix >= 0x7fff0000) + { /* erf(nan)=nan */ + i = ((sign & 0xffff0000) >> 31) << 1; + return (long double) (1 - i) + one / x; /* erf(+-inf)=+-1 */ + } + + if (ix >= 0x3fff0000) /* |x| >= 1.0 */ + { + y = erfcl (x); + return (one - y); + /* return (one - erfcl (x)); */ + } + u.parts32.mswhi = ix; + a = u.value; + z = x * x; + if (ix < 0x3ffec000) /* a < 0.875 */ + { + if (ix < 0x3fc60000) /* |x|<2**-57 */ + { + if (ix < 0x00080000) + return 0.125 * (8.0 * x + efx8 * x); /*avoid underflow */ + return x + efx * x; + } + y = a + a * neval (z, TN1, NTN1) / deval (z, TD1, NTD1); + } + else + { + a = a - one; + y = erf_const + neval (a, TN2, NTN2) / deval (a, TD2, NTD2); + } + + if (sign & 0x80000000) /* x < 0 */ + y = -y; + return( y ); +} + +long double +erfcl(long double x) +{ + long double y, z, p, r; + int32_t i, ix, sign; + ieee_quad_shape_type u; + + u.value = x; + sign = u.parts32.mswhi; + ix = sign & 0x7fffffff; + u.parts32.mswhi = ix; + + if (ix >= 0x7fff0000) + { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (long double) (((u_int32_t) sign >> 31) << 1) + one / x; + } + + if (ix < 0x3ffd0000) /* |x| <1/4 */ + { + if (ix < 0x3f8d0000) /* |x|<2**-114 */ + return one - x; + return one - erfl (x); + } + if (ix < 0x3fff4000) /* 1.25 */ + { + x = u.value; + i = 8.0 * x; + switch (i) + { + case 2: + z = x - 0.25L; + y = C13b + z * neval (z, RNr13, NRNr13) / deval (z, RDr13, NRDr13); + y += C13a; + break; + case 3: + z = x - 0.375L; + y = C14b + z * neval (z, RNr14, NRNr14) / deval (z, RDr14, NRDr14); + y += C14a; + break; + case 4: + z = x - 0.5L; + y = C15b + z * neval (z, RNr15, NRNr15) / deval (z, RDr15, NRDr15); + y += C15a; + break; + case 5: + z = x - 0.625L; + y = C16b + z * neval (z, RNr16, NRNr16) / deval (z, RDr16, NRDr16); + y += C16a; + break; + case 6: + z = x - 0.75L; + y = C17b + z * neval (z, RNr17, NRNr17) / deval (z, RDr17, NRDr17); + y += C17a; + break; + case 7: + z = x - 0.875L; + y = C18b + z * neval (z, RNr18, NRNr18) / deval (z, RDr18, NRDr18); + y += C18a; + break; + case 8: + z = x - 1.0L; + y = C19b + z * neval (z, RNr19, NRNr19) / deval (z, RDr19, NRDr19); + y += C19a; + break; + case 9: + z = x - 1.125L; + y = C20b + z * neval (z, RNr20, NRNr20) / deval (z, RDr20, NRDr20); + y += C20a; + break; + } + if (sign & 0x80000000) + y = 2.0L - y; + return y; + } + /* 1.25 < |x| < 107 */ + if (ix < 0x4005ac00) + { + /* x < -9 */ + if ((ix >= 0x40022000) && (sign & 0x80000000)) + return two - tiny; + + x = fabsl (x); + z = one / (x * x); + i = 8.0 / x; + switch (i) + { + default: + case 0: + p = neval (z, RNr1, NRNr1) / deval (z, RDr1, NRDr1); + break; + case 1: + p = neval (z, RNr2, NRNr2) / deval (z, RDr2, NRDr2); + break; + case 2: + p = neval (z, RNr3, NRNr3) / deval (z, RDr3, NRDr3); + break; + case 3: + p = neval (z, RNr4, NRNr4) / deval (z, RDr4, NRDr4); + break; + case 4: + p = neval (z, RNr5, NRNr5) / deval (z, RDr5, NRDr5); + break; + case 5: + p = neval (z, RNr6, NRNr6) / deval (z, RDr6, NRDr6); + break; + case 6: + p = neval (z, RNr7, NRNr7) / deval (z, RDr7, NRDr7); + break; + case 7: + p = neval (z, RNr8, NRNr8) / deval (z, RDr8, NRDr8); + break; + } + u.value = x; + u.parts32.lswlo = 0; + u.parts32.lswhi &= 0xfe000000; + z = u.value; + r = expl (-z * z - 0.5625) * + expl ((z - x) * (z + x) + p); + if ((sign & 0x80000000) == 0) + return r / x; + else + return two - r / x; + } + else + { + if ((sign & 0x80000000) == 0) + return tiny * tiny; + else + return two - tiny; + } +} diff --git a/openlibm/ld128/s_exp2l.c b/openlibm/ld128/s_exp2l.c new file mode 100644 index 0000000000..c5f997f772 --- /dev/null +++ b/openlibm/ld128/s_exp2l.c @@ -0,0 +1,431 @@ +/*- + * Copyright (c) 2005-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld128/s_exp2l.c,v 1.3 2008/02/13 10:44:44 bde Exp $"); + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#define TBLBITS 7 +#define TBLSIZE (1 << TBLBITS) + +#define BIAS (LDBL_MAX_EXP - 1) +#define EXPMASK (BIAS + LDBL_MAX_EXP) + +#if 0 /* XXX Prevent gcc from erroneously constant folding this. */ +static const long double twom10000 = 0x1p-10000L; +#else +static volatile long double twom10000 = 0x1p-10000L; +#endif + +static const long double + huge = 0x1p10000L, + P1 = 0x1.62e42fefa39ef35793c7673007e6p-1L, + P2 = 0x1.ebfbdff82c58ea86f16b06ec9736p-3L, + P3 = 0x1.c6b08d704a0bf8b33a762bad3459p-5L, + P4 = 0x1.3b2ab6fba4e7729ccbbe0b4f3fc2p-7L, + P5 = 0x1.5d87fe78a67311071dee13fd11d9p-10L, + P6 = 0x1.430912f86c7876f4b663b23c5fe5p-13L; + +static const double + P7 = 0x1.ffcbfc588b041p-17, + P8 = 0x1.62c0223a5c7c7p-20, + P9 = 0x1.b52541ff59713p-24, + P10 = 0x1.e4cf56a391e22p-28, + redux = 0x1.8p112 / TBLSIZE; + +static const long double tbl[TBLSIZE] = { + 0x1.6a09e667f3bcc908b2fb1366dfeap-1L, + 0x1.6c012750bdabeed76a99800f4edep-1L, + 0x1.6dfb23c651a2ef220e2cbe1bc0d4p-1L, + 0x1.6ff7df9519483cf87e1b4f3e1e98p-1L, + 0x1.71f75e8ec5f73dd2370f2ef0b148p-1L, + 0x1.73f9a48a58173bd5c9a4e68ab074p-1L, + 0x1.75feb564267c8bf6e9aa33a489a8p-1L, + 0x1.780694fde5d3f619ae02808592a4p-1L, + 0x1.7a11473eb0186d7d51023f6ccb1ap-1L, + 0x1.7c1ed0130c1327c49334459378dep-1L, + 0x1.7e2f336cf4e62105d02ba1579756p-1L, + 0x1.80427543e1a11b60de67649a3842p-1L, + 0x1.82589994cce128acf88afab34928p-1L, + 0x1.8471a4623c7acce52f6b97c6444cp-1L, + 0x1.868d99b4492ec80e41d90ac2556ap-1L, + 0x1.88ac7d98a669966530bcdf2d4cc0p-1L, + 0x1.8ace5422aa0db5ba7c55a192c648p-1L, + 0x1.8cf3216b5448bef2aa1cd161c57ap-1L, + 0x1.8f1ae991577362b982745c72eddap-1L, + 0x1.9145b0b91ffc588a61b469f6b6a0p-1L, + 0x1.93737b0cdc5e4f4501c3f2540ae8p-1L, + 0x1.95a44cbc8520ee9b483695a0e7fep-1L, + 0x1.97d829fde4e4f8b9e920f91e8eb6p-1L, + 0x1.9a0f170ca07b9ba3109b8c467844p-1L, + 0x1.9c49182a3f0901c7c46b071f28dep-1L, + 0x1.9e86319e323231824ca78e64c462p-1L, + 0x1.a0c667b5de564b29ada8b8cabbacp-1L, + 0x1.a309bec4a2d3358c171f770db1f4p-1L, + 0x1.a5503b23e255c8b424491caf88ccp-1L, + 0x1.a799e1330b3586f2dfb2b158f31ep-1L, + 0x1.a9e6b5579fdbf43eb243bdff53a2p-1L, + 0x1.ac36bbfd3f379c0db966a3126988p-1L, + 0x1.ae89f995ad3ad5e8734d17731c80p-1L, + 0x1.b0e07298db66590842acdfc6fb4ep-1L, + 0x1.b33a2b84f15faf6bfd0e7bd941b0p-1L, + 0x1.b59728de559398e3881111648738p-1L, + 0x1.b7f76f2fb5e46eaa7b081ab53ff6p-1L, + 0x1.ba5b030a10649840cb3c6af5b74cp-1L, + 0x1.bcc1e904bc1d2247ba0f45b3d06cp-1L, + 0x1.bf2c25bd71e088408d7025190cd0p-1L, + 0x1.c199bdd85529c2220cb12a0916bap-1L, + 0x1.c40ab5fffd07a6d14df820f17deap-1L, + 0x1.c67f12e57d14b4a2137fd20f2a26p-1L, + 0x1.c8f6d9406e7b511acbc48805c3f6p-1L, + 0x1.cb720dcef90691503cbd1e949d0ap-1L, + 0x1.cdf0b555dc3f9c44f8958fac4f12p-1L, + 0x1.d072d4a07897b8d0f22f21a13792p-1L, + 0x1.d2f87080d89f18ade123989ea50ep-1L, + 0x1.d5818dcfba48725da05aeb66dff8p-1L, + 0x1.d80e316c98397bb84f9d048807a0p-1L, + 0x1.da9e603db3285708c01a5b6d480cp-1L, + 0x1.dd321f301b4604b695de3c0630c0p-1L, + 0x1.dfc97337b9b5eb968cac39ed284cp-1L, + 0x1.e264614f5a128a12761fa17adc74p-1L, + 0x1.e502ee78b3ff6273d130153992d0p-1L, + 0x1.e7a51fbc74c834b548b2832378a4p-1L, + 0x1.ea4afa2a490d9858f73a18f5dab4p-1L, + 0x1.ecf482d8e67f08db0312fb949d50p-1L, + 0x1.efa1bee615a27771fd21a92dabb6p-1L, + 0x1.f252b376bba974e8696fc3638f24p-1L, + 0x1.f50765b6e4540674f84b762861a6p-1L, + 0x1.f7bfdad9cbe138913b4bfe72bd78p-1L, + 0x1.fa7c1819e90d82e90a7e74b26360p-1L, + 0x1.fd3c22b8f71f10975ba4b32bd006p-1L, + 0x1.0000000000000000000000000000p+0L, + 0x1.0163da9fb33356d84a66ae336e98p+0L, + 0x1.02c9a3e778060ee6f7caca4f7a18p+0L, + 0x1.04315e86e7f84bd738f9a20da442p+0L, + 0x1.059b0d31585743ae7c548eb68c6ap+0L, + 0x1.0706b29ddf6ddc6dc403a9d87b1ep+0L, + 0x1.0874518759bc808c35f25d942856p+0L, + 0x1.09e3ecac6f3834521e060c584d5cp+0L, + 0x1.0b5586cf9890f6298b92b7184200p+0L, + 0x1.0cc922b7247f7407b705b893dbdep+0L, + 0x1.0e3ec32d3d1a2020742e4f8af794p+0L, + 0x1.0fb66affed31af232091dd8a169ep+0L, + 0x1.11301d0125b50a4ebbf1aed9321cp+0L, + 0x1.12abdc06c31cbfb92bad324d6f84p+0L, + 0x1.1429aaea92ddfb34101943b2588ep+0L, + 0x1.15a98c8a58e512480d573dd562aep+0L, + 0x1.172b83c7d517adcdf7c8c50eb162p+0L, + 0x1.18af9388c8de9bbbf70b9a3c269cp+0L, + 0x1.1a35beb6fcb753cb698f692d2038p+0L, + 0x1.1bbe084045cd39ab1e72b442810ep+0L, + 0x1.1d4873168b9aa7805b8028990be8p+0L, + 0x1.1ed5022fcd91cb8819ff61121fbep+0L, + 0x1.2063b88628cd63b8eeb0295093f6p+0L, + 0x1.21f49917ddc962552fd29294bc20p+0L, + 0x1.2387a6e75623866c1fadb1c159c0p+0L, + 0x1.251ce4fb2a63f3582ab7de9e9562p+0L, + 0x1.26b4565e27cdd257a673281d3068p+0L, + 0x1.284dfe1f5638096cf15cf03c9fa0p+0L, + 0x1.29e9df51fdee12c25d15f5a25022p+0L, + 0x1.2b87fd0dad98ffddea46538fca24p+0L, + 0x1.2d285a6e4030b40091d536d0733ep+0L, + 0x1.2ecafa93e2f5611ca0f45d5239a4p+0L, + 0x1.306fe0a31b7152de8d5a463063bep+0L, + 0x1.32170fc4cd8313539cf1c3009330p+0L, + 0x1.33c08b26416ff4c9c8610d96680ep+0L, + 0x1.356c55f929ff0c94623476373be4p+0L, + 0x1.371a7373aa9caa7145502f45452ap+0L, + 0x1.38cae6d05d86585a9cb0d9bed530p+0L, + 0x1.3a7db34e59ff6ea1bc9299e0a1fep+0L, + 0x1.3c32dc313a8e484001f228b58cf0p+0L, + 0x1.3dea64c12342235b41223e13d7eep+0L, + 0x1.3fa4504ac801ba0bf701aa417b9cp+0L, + 0x1.4160a21f72e29f84325b8f3dbacap+0L, + 0x1.431f5d950a896dc704439410b628p+0L, + 0x1.44e086061892d03136f409df0724p+0L, + 0x1.46a41ed1d005772512f459229f0ap+0L, + 0x1.486a2b5c13cd013c1a3b69062f26p+0L, + 0x1.4a32af0d7d3de672d8bcf46f99b4p+0L, + 0x1.4bfdad5362a271d4397afec42e36p+0L, + 0x1.4dcb299fddd0d63b36ef1a9e19dep+0L, + 0x1.4f9b2769d2ca6ad33d8b69aa0b8cp+0L, + 0x1.516daa2cf6641c112f52c84d6066p+0L, + 0x1.5342b569d4f81df0a83c49d86bf4p+0L, + 0x1.551a4ca5d920ec52ec620243540cp+0L, + 0x1.56f4736b527da66ecb004764e61ep+0L, + 0x1.58d12d497c7fd252bc2b7343d554p+0L, + 0x1.5ab07dd48542958c93015191e9a8p+0L, + 0x1.5c9268a5946b701c4b1b81697ed4p+0L, + 0x1.5e76f15ad21486e9be4c20399d12p+0L, + 0x1.605e1b976dc08b076f592a487066p+0L, + 0x1.6247eb03a5584b1f0fa06fd2d9eap+0L, + 0x1.6434634ccc31fc76f8714c4ee122p+0L, + 0x1.66238825522249127d9e29b92ea2p+0L, + 0x1.68155d44ca973081c57227b9f69ep+0L, +}; + +static const float eps[TBLSIZE] = { + -0x1.5c50p-101, + -0x1.5d00p-106, + 0x1.8e90p-102, + -0x1.5340p-103, + 0x1.1bd0p-102, + -0x1.4600p-105, + -0x1.7a40p-104, + 0x1.d590p-102, + -0x1.d590p-101, + 0x1.b100p-103, + -0x1.0d80p-105, + 0x1.6b00p-103, + -0x1.9f00p-105, + 0x1.c400p-103, + 0x1.e120p-103, + -0x1.c100p-104, + -0x1.9d20p-103, + 0x1.a800p-108, + 0x1.4c00p-106, + -0x1.9500p-106, + 0x1.6900p-105, + -0x1.29d0p-100, + 0x1.4c60p-103, + 0x1.13a0p-102, + -0x1.5b60p-103, + -0x1.1c40p-103, + 0x1.db80p-102, + 0x1.91a0p-102, + 0x1.dc00p-105, + 0x1.44c0p-104, + 0x1.9710p-102, + 0x1.8760p-103, + -0x1.a720p-103, + 0x1.ed20p-103, + -0x1.49c0p-102, + -0x1.e000p-111, + 0x1.86a0p-103, + 0x1.2b40p-103, + -0x1.b400p-108, + 0x1.1280p-99, + -0x1.02d8p-102, + -0x1.e3d0p-103, + -0x1.b080p-105, + -0x1.f100p-107, + -0x1.16c0p-105, + -0x1.1190p-103, + -0x1.a7d2p-100, + 0x1.3450p-103, + -0x1.67c0p-105, + 0x1.4b80p-104, + -0x1.c4e0p-103, + 0x1.6000p-108, + -0x1.3f60p-105, + 0x1.93f0p-104, + 0x1.5fe0p-105, + 0x1.6f80p-107, + -0x1.7600p-106, + 0x1.21e0p-106, + -0x1.3a40p-106, + -0x1.40c0p-104, + -0x1.9860p-105, + -0x1.5d40p-108, + -0x1.1d70p-106, + 0x1.2760p-105, + 0x0.0000p+0, + 0x1.21e2p-104, + -0x1.9520p-108, + -0x1.5720p-106, + -0x1.4810p-106, + -0x1.be00p-109, + 0x1.0080p-105, + -0x1.5780p-108, + -0x1.d460p-105, + -0x1.6140p-105, + 0x1.4630p-104, + 0x1.ad50p-103, + 0x1.82e0p-105, + 0x1.1d3cp-101, + 0x1.6100p-107, + 0x1.ec30p-104, + 0x1.f200p-108, + 0x1.0b40p-103, + 0x1.3660p-102, + 0x1.d9d0p-103, + -0x1.02d0p-102, + 0x1.b070p-103, + 0x1.b9c0p-104, + -0x1.01c0p-103, + -0x1.dfe0p-103, + 0x1.1b60p-104, + -0x1.ae94p-101, + -0x1.3340p-104, + 0x1.b3d8p-102, + -0x1.6e40p-105, + -0x1.3670p-103, + 0x1.c140p-104, + 0x1.1840p-101, + 0x1.1ab0p-102, + -0x1.a400p-104, + 0x1.1f00p-104, + -0x1.7180p-103, + 0x1.4ce0p-102, + 0x1.9200p-107, + -0x1.54c0p-103, + 0x1.1b80p-105, + -0x1.1828p-101, + 0x1.5720p-102, + -0x1.a060p-100, + 0x1.9160p-102, + 0x1.a280p-104, + 0x1.3400p-107, + 0x1.2b20p-102, + 0x1.7800p-108, + 0x1.cfd0p-101, + 0x1.2ef0p-102, + -0x1.2760p-99, + 0x1.b380p-104, + 0x1.0048p-101, + -0x1.60b0p-102, + 0x1.a1ccp-100, + -0x1.a640p-104, + -0x1.08a0p-101, + 0x1.7e60p-102, + 0x1.22c0p-103, + -0x1.7200p-106, + 0x1.f0f0p-102, + 0x1.eb4ep-99, + 0x1.c6e0p-103, +}; + +/* + * exp2l(x): compute the base 2 exponential of x + * + * Accuracy: Peak error < 0.502 ulp. + * + * Method: (accurate tables) + * + * Reduce x: + * x = 2**k + y, for integer k and |y| <= 1/2. + * Thus we have exp2(x) = 2**k * exp2(y). + * + * Reduce y: + * y = i/TBLSIZE + z - eps[i] for integer i near y * TBLSIZE. + * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z - eps[i]), + * with |z - eps[i]| <= 2**-8 + 2**-98 for the table used. + * + * We compute exp2(i/TBLSIZE) via table lookup and exp2(z - eps[i]) via + * a degree-10 minimax polynomial with maximum error under 2**-120. + * The values in exp2t[] and eps[] are chosen such that + * exp2t[i] = exp2(i/TBLSIZE + eps[i]), and eps[i] is a small offset such + * that exp2t[i] is accurate to 2**-122. + * + * Note that the range of i is +-TBLSIZE/2, so we actually index the tables + * by i0 = i + TBLSIZE/2. + * + * This method is due to Gal, with many details due to Gal and Bachelis: + * + * Gal, S. and Bachelis, B. An Accurate Elementary Mathematical Library + * for the IEEE Floating Point Standard. TOMS 17(1), 26-46 (1991). + */ +OLM_DLLEXPORT long double +exp2l(long double x) +{ + union IEEEl2bits u, v; + long double r, t, twopk, twopkp10000, z; + uint32_t hx, ix, i0; + int k; + + u.e = x; + + /* Filter out exceptional cases. */ + hx = u.xbits.expsign; + ix = hx & EXPMASK; + if (ix >= BIAS + 14) { /* |x| >= 16384 */ + if (ix == BIAS + LDBL_MAX_EXP) { + if (u.xbits.manh != 0 + || u.xbits.manl != 0 + || (hx & 0x8000) == 0) + return (x + x); /* x is NaN or +Inf */ + else + return (0.0); /* x is -Inf */ + } + if (x >= 16384) + return (huge * huge); /* overflow */ + if (x <= -16495) + return (twom10000 * twom10000); /* underflow */ + } else if (ix <= BIAS - 115) { /* |x| < 0x1p-115 */ + return (1.0 + x); + } + + /* + * Reduce x, computing z, i0, and k. The low bits of x + redux + * contain the 16-bit integer part of the exponent (k) followed by + * TBLBITS fractional bits (i0). We use bit tricks to extract these + * as integers, then set z to the remainder. + * + * Example: Suppose x is 0xabc.123456p0 and TBLBITS is 8. + * Then the low-order word of x + redux is 0x000abc12, + * We split this into k = 0xabc and i0 = 0x12 (adjusted to + * index into the table), then we compute z = 0x0.003456p0. + * + * XXX If the exponent is negative, the computation of k depends on + * '>>' doing sign extension. + */ + u.e = x + redux; + i0 = (u.bits.manl & 0xffffffff) + TBLSIZE / 2; + k = (int)i0 >> TBLBITS; + i0 = i0 & (TBLSIZE - 1); + u.e -= redux; + z = x - u.e; + v.xbits.manh = 0; + v.xbits.manl = 0; + if (k >= LDBL_MIN_EXP) { + v.xbits.expsign = LDBL_MAX_EXP - 1 + k; + twopk = v.e; + } else { + v.xbits.expsign = LDBL_MAX_EXP - 1 + k + 10000; + twopkp10000 = v.e; + } + + /* Compute r = exp2(y) = exp2t[i0] * p(z - eps[i]). */ + t = tbl[i0]; /* exp2t[i0] */ + z -= eps[i0]; /* eps[i0] */ + r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * (P5 + z * (P6 + + z * (P7 + z * (P8 + z * (P9 + z * P10))))))))); + + /* Scale by 2**k. */ + if(k >= LDBL_MIN_EXP) { + if (k == LDBL_MAX_EXP) + return (r * 2.0 * 0x1p16383L); + return (r * twopk); + } else { + return (r * twopkp10000 * twom10000); + } +} diff --git a/openlibm/ld128/s_expm1l.c b/openlibm/ld128/s_expm1l.c new file mode 100644 index 0000000000..1c22696337 --- /dev/null +++ b/openlibm/ld128/s_expm1l.c @@ -0,0 +1,162 @@ +/* $OpenBSD: s_expm1l.c,v 1.1 2011/07/06 00:02:42 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* expm1l.c + * + * Exponential function, minus 1 + * 128-bit long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, expm1l(); + * + * y = expm1l( x ); + * + * + * + * DESCRIPTION: + * + * Returns e (2.71828...) raised to the x power, minus one. + * + * Range reduction is accomplished by separating the argument + * into an integer k and fraction f such that + * + * x k f + * e = 2 e. + * + * An expansion x + .5 x^2 + x^3 R(x) approximates exp(f) - 1 + * in the basic range [-0.5 ln 2, 0.5 ln 2]. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -79,+MAXLOG 100,000 1.7e-34 4.5e-35 + * + */ + +#include +#include + +#include "math_private.h" + +/* exp(x) - 1 = x + 0.5 x^2 + x^3 P(x)/Q(x) + -.5 ln 2 < x < .5 ln 2 + Theoretical peak relative error = 8.1e-36 */ + +static const long double + P0 = 2.943520915569954073888921213330863757240E8L, + P1 = -5.722847283900608941516165725053359168840E7L, + P2 = 8.944630806357575461578107295909719817253E6L, + P3 = -7.212432713558031519943281748462837065308E5L, + P4 = 4.578962475841642634225390068461943438441E4L, + P5 = -1.716772506388927649032068540558788106762E3L, + P6 = 4.401308817383362136048032038528753151144E1L, + P7 = -4.888737542888633647784737721812546636240E-1L, + Q0 = 1.766112549341972444333352727998584753865E9L, + Q1 = -7.848989743695296475743081255027098295771E8L, + Q2 = 1.615869009634292424463780387327037251069E8L, + Q3 = -2.019684072836541751428967854947019415698E7L, + Q4 = 1.682912729190313538934190635536631941751E6L, + Q5 = -9.615511549171441430850103489315371768998E4L, + Q6 = 3.697714952261803935521187272204485251835E3L, + Q7 = -8.802340681794263968892934703309274564037E1L, + /* Q8 = 1.000000000000000000000000000000000000000E0 */ +/* C1 + C2 = ln 2 */ + + C1 = 6.93145751953125E-1L, + C2 = 1.428606820309417232121458176568075500134E-6L, +/* ln (2^16384 * (1 - 2^-113)) */ + maxlog = 1.1356523406294143949491931077970764891253E4L, +/* ln 2^-114 */ + minarg = -7.9018778583833765273564461846232128760607E1L, big = 1e4932L; + + +long double +expm1l(long double x) +{ + long double px, qx, xx; + int32_t ix, sign; + ieee_quad_shape_type u; + int k; + + /* Detect infinity and NaN. */ + u.value = x; + ix = u.parts32.mswhi; + sign = ix & 0x80000000; + ix &= 0x7fffffff; + if (ix >= 0x7fff0000) + { + /* Infinity. */ + if (((ix & 0xffff) | u.parts32.mswlo | u.parts32.lswhi | + u.parts32.lswlo) == 0) + { + if (sign) + return -1.0L; + else + return x; + } + /* NaN. No invalid exception. */ + return x; + } + + /* expm1(+- 0) = +- 0. */ + if ((ix == 0) && (u.parts32.mswlo | u.parts32.lswhi | u.parts32.lswlo) == 0) + return x; + + /* Overflow. */ + if (x > maxlog) + return (big * big); + + /* Minimum value. */ + if (x < minarg) + return (4.0/big - 1.0L); + + /* Express x = ln 2 (k + remainder), remainder not exceeding 1/2. */ + xx = C1 + C2; /* ln 2. */ + px = floorl (0.5 + x / xx); + k = px; + /* remainder times ln 2 */ + x -= px * C1; + x -= px * C2; + + /* Approximate exp(remainder ln 2). */ + px = (((((((P7 * x + + P6) * x + + P5) * x + P4) * x + P3) * x + P2) * x + P1) * x + P0) * x; + + qx = (((((((x + + Q7) * x + + Q6) * x + Q5) * x + Q4) * x + Q3) * x + Q2) * x + Q1) * x + Q0; + + xx = x * x; + qx = x + (0.5 * xx + xx * px / qx); + + /* exp(x) = exp(k ln 2) exp(remainder ln 2) = 2^k exp(remainder ln 2). + + We have qx = exp(remainder ln 2) - 1, so + exp(x) - 1 = 2^k (qx + 1) - 1 + = 2^k qx + 2^k - 1. */ + + px = ldexpl (1.0L, k); + x = px * qx + (px - 1.0); + return x; +} diff --git a/openlibm/ld128/s_floorl.c b/openlibm/ld128/s_floorl.c new file mode 100644 index 0000000000..f00dcfa1ce --- /dev/null +++ b/openlibm/ld128/s_floorl.c @@ -0,0 +1,71 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * floorl(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floor(x). + */ + +#include + +#include "math_private.h" + +static const long double huge = 1.0e4930L; + +long double +floorl(long double x) +{ + int64_t i0,i1,jj0; + u_int64_t i,j; + GET_LDOUBLE_WORDS64(i0,i1,x); + jj0 = ((i0>>48)&0x7fff)-0x3fff; + if(jj0<48) { + if(jj0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) { + if(i0>=0) + return 0.0L; + else if(((i0&0x7fffffffffffffffLL)|i1)!=0) + return -1.0L; + } + } else { + i = (0x0000ffffffffffffULL)>>jj0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x0001000000000000LL)>>jj0; + i0 &= (~i); i1=0; + } + } + } else if (jj0>111) { + if(jj0==0x4000) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = -1ULL>>(jj0-48); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) { + if(jj0==48) i0+=1; + else { + j = i1+(1LL<<(112-jj0)); + if(j + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* log1pl.c + * + * Relative error logarithm + * Natural logarithm of 1+x, 128-bit long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, log1pl(); + * + * y = log1pl( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base e (2.718...) logarithm of 1+x. + * + * The argument 1+x is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the logarithm + * of the fraction is approximated by + * + * log(1+x) = x - 0.5 x^2 + x^3 P(x)/Q(x). + * + * Otherwise, setting z = 2(w-1)/(w+1), + * + * log(w) = z + z^3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -1, 8 100000 1.9e-34 4.3e-35 + */ + +#include + +#include "math_private.h" + +/* Coefficients for log(1+x) = x - x^2 / 2 + x^3 P(x)/Q(x) + * 1/sqrt(2) <= 1+x < sqrt(2) + * Theoretical peak relative error = 5.3e-37, + * relative peak error spread = 2.3e-14 + */ +static const long double + P12 = 1.538612243596254322971797716843006400388E-6L, + P11 = 4.998469661968096229986658302195402690910E-1L, + P10 = 2.321125933898420063925789532045674660756E1L, + P9 = 4.114517881637811823002128927449878962058E2L, + P8 = 3.824952356185897735160588078446136783779E3L, + P7 = 2.128857716871515081352991964243375186031E4L, + P6 = 7.594356839258970405033155585486712125861E4L, + P5 = 1.797628303815655343403735250238293741397E5L, + P4 = 2.854829159639697837788887080758954924001E5L, + P3 = 3.007007295140399532324943111654767187848E5L, + P2 = 2.014652742082537582487669938141683759923E5L, + P1 = 7.771154681358524243729929227226708890930E4L, + P0 = 1.313572404063446165910279910527789794488E4L, + /* Q12 = 1.000000000000000000000000000000000000000E0L, */ + Q11 = 4.839208193348159620282142911143429644326E1L, + Q10 = 9.104928120962988414618126155557301584078E2L, + Q9 = 9.147150349299596453976674231612674085381E3L, + Q8 = 5.605842085972455027590989944010492125825E4L, + Q7 = 2.248234257620569139969141618556349415120E5L, + Q6 = 6.132189329546557743179177159925690841200E5L, + Q5 = 1.158019977462989115839826904108208787040E6L, + Q4 = 1.514882452993549494932585972882995548426E6L, + Q3 = 1.347518538384329112529391120390701166528E6L, + Q2 = 7.777690340007566932935753241556479363645E5L, + Q1 = 2.626900195321832660448791748036714883242E5L, + Q0 = 3.940717212190338497730839731583397586124E4L; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 1.1e-35, + * relative peak error spread 1.1e-9 + */ +static const long double + R5 = -8.828896441624934385266096344596648080902E-1L, + R4 = 8.057002716646055371965756206836056074715E1L, + R3 = -2.024301798136027039250415126250455056397E3L, + R2 = 2.048819892795278657810231591630928516206E4L, + R1 = -8.977257995689735303686582344659576526998E4L, + R0 = 1.418134209872192732479751274970992665513E5L, + /* S6 = 1.000000000000000000000000000000000000000E0L, */ + S5 = -1.186359407982897997337150403816839480438E2L, + S4 = 3.998526750980007367835804959888064681098E3L, + S3 = -5.748542087379434595104154610899551484314E4L, + S2 = 4.001557694070773974936904547424676279307E5L, + S1 = -1.332535117259762928288745111081235577029E6L, + S0 = 1.701761051846631278975701529965589676574E6L; + +/* C1 + C2 = ln 2 */ +static const long double C1 = 6.93145751953125E-1L; +static const long double C2 = 1.428606820309417232121458176568075500134E-6L; + +static const long double sqrth = 0.7071067811865475244008443621048490392848L; +/* ln (2^16384 * (1 - 2^-113)) */ +static const long double zero = 0.0L; + +long double +log1pl(long double xm1) +{ + long double x, y, z, r, s; + ieee_quad_shape_type u; + int32_t hx; + int e; + + /* Test for NaN or infinity input. */ + u.value = xm1; + hx = u.parts32.mswhi; + if (hx >= 0x7fff0000) + return xm1; + + /* log1p(+- 0) = +- 0. */ + if (((hx & 0x7fffffff) == 0) + && (u.parts32.mswlo | u.parts32.lswhi | u.parts32.lswlo) == 0) + return xm1; + + x = xm1 + 1.0L; + + /* log1p(-1) = -inf */ + if (x <= 0.0L) + { + if (x == 0.0L) + return (-1.0L / (x - x)); + else + return (zero / (x - x)); + } + + /* Separate mantissa from exponent. */ + + /* Use frexp used so that denormal numbers will be handled properly. */ + x = frexpl (x, &e); + + /* Logarithm using log(x) = z + z^3 P(z^2)/Q(z^2), + where z = 2(x-1)/x+1). */ + if ((e > 2) || (e < -2)) + { + if (x < sqrth) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } + else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } + x = z / y; + z = x * x; + r = ((((R5 * z + + R4) * z + + R3) * z + + R2) * z + + R1) * z + + R0; + s = (((((z + + S5) * z + + S4) * z + + S3) * z + + S2) * z + + S1) * z + + S0; + z = x * (z * r / s); + z = z + e * C2; + z = z + x; + z = z + e * C1; + return (z); + } + + + /* Logarithm using log(1+x) = x - .5x^2 + x^3 P(x)/Q(x). */ + + if (x < sqrth) + { + e -= 1; + if (e != 0) + x = 2.0L * x - 1.0L; /* 2x - 1 */ + else + x = xm1; + } + else + { + if (e != 0) + x = x - 1.0L; + else + x = xm1; + } + z = x * x; + r = (((((((((((P12 * x + + P11) * x + + P10) * x + + P9) * x + + P8) * x + + P7) * x + + P6) * x + + P5) * x + + P4) * x + + P3) * x + + P2) * x + + P1) * x + + P0; + s = (((((((((((x + + Q11) * x + + Q10) * x + + Q9) * x + + Q8) * x + + Q7) * x + + Q6) * x + + Q5) * x + + Q4) * x + + Q3) * x + + Q2) * x + + Q1) * x + + Q0; + y = x * (z * r / s); + y = y + e * C2; + z = y - 0.5L * z; + z = z + x; + z = z + e * C1; + return (z); +} diff --git a/openlibm/ld128/s_modfl.c b/openlibm/ld128/s_modfl.c new file mode 100644 index 0000000000..bd18cbab26 --- /dev/null +++ b/openlibm/ld128/s_modfl.c @@ -0,0 +1,73 @@ +/* @(#)s_modf.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * modfl(long double x, long double *iptr) + * return fraction part of x, and return x's integral part in *iptr. + * Method: + * Bit twiddling. + * + * Exception: + * No exception. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0; + +long double +modfl(long double x, long double *iptr) +{ + int64_t i0,i1,jj0; + u_int64_t i; + GET_LDOUBLE_WORDS64(i0,i1,x); + jj0 = ((i0>>48)&0x7fff)-0x3fff; /* exponent of x */ + if(jj0<48) { /* integer part in high x */ + if(jj0<0) { /* |x|<1 */ + /* *iptr = +-0 */ + SET_LDOUBLE_WORDS64(*iptr,i0&0x8000000000000000ULL,0); + return x; + } else { + i = (0x0000ffffffffffffLL)>>jj0; + if(((i0&i)|i1)==0) { /* x is integral */ + *iptr = x; + /* return +-0 */ + SET_LDOUBLE_WORDS64(x,i0&0x8000000000000000ULL,0); + return x; + } else { + SET_LDOUBLE_WORDS64(*iptr,i0&(~i),0); + return x - *iptr; + } + } + } else if (jj0>111) { /* no fraction part */ + *iptr = x*one; + /* We must handle NaNs separately. */ + if (jj0 == 0x4000 && ((i0 & 0x0000ffffffffffffLL) | i1)) + return x*one; + /* return +-0 */ + SET_LDOUBLE_WORDS64(x,i0&0x8000000000000000ULL,0); + return x; + } else { /* fraction part in low x */ + i = -1ULL>>(jj0-48); + if((i1&i)==0) { /* x is integral */ + *iptr = x; + /* return +-0 */ + SET_LDOUBLE_WORDS64(x,i0&0x8000000000000000ULL,0); + return x; + } else { + SET_LDOUBLE_WORDS64(*iptr,i0,i1&(~i)); + return x - *iptr; + } + } +} diff --git a/openlibm/ld128/s_nanl.c b/openlibm/ld128/s_nanl.c new file mode 100644 index 0000000000..4dcdb4e79f --- /dev/null +++ b/openlibm/ld128/s_nanl.c @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/ld128/s_nanl.c,v 1.3 2008/03/02 20:16:55 das Exp $ + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT long double +nanl(const char *s) +{ + union { + union IEEEl2bits ieee; + uint32_t bits[4]; + } u; + + __scan_nan(u.bits, 4, s); + u.ieee.bits.exp = 0x7fff; + u.ieee.bits.manh |= 1ULL << 47; /* make it a quiet NaN */ + return (u.ieee.e); +} diff --git a/openlibm/ld128/s_nextafterl.c b/openlibm/ld128/s_nextafterl.c new file mode 100644 index 0000000000..af667a0ff4 --- /dev/null +++ b/openlibm/ld128/s_nextafterl.c @@ -0,0 +1,72 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* IEEE functions + * nextafterl(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include + +#include "math_private.h" + +long double +nextafterl(long double x, long double y) +{ + int64_t hx,hy,ix,iy; + u_int64_t lx,ly; + + GET_LDOUBLE_WORDS64(hx,lx,x); + GET_LDOUBLE_WORDS64(hy,ly,y); + ix = hx&0x7fffffffffffffffLL; /* |x| */ + iy = hy&0x7fffffffffffffffLL; /* |y| */ + + if(((ix>=0x7fff000000000000LL)&&((ix-0x7fff000000000000LL)|lx)!=0) || /* x is nan */ + ((iy>=0x7fff000000000000LL)&&((iy-0x7fff000000000000LL)|ly)!=0)) /* y is nan */ + return x+y; + if(x==y) return y; /* x=y, return y */ + if((ix|lx)==0) { /* x == 0 */ + volatile long double u; + SET_LDOUBLE_WORDS64(x,hy&0x8000000000000000ULL,1);/* return +-minsubnormal */ + u = x; + u = u * u; /* raise underflow flag */ + return x; + } + if(hx>=0) { /* x > 0 */ + if(hx>hy||((hx==hy)&&(lx>ly))) { /* x > y, x -= ulp */ + if(lx==0) hx--; + lx--; + } else { /* x < y, x += ulp */ + lx++; + if(lx==0) hx++; + } + } else { /* x < 0 */ + if(hy>=0||hx>hy||((hx==hy)&&(lx>ly))){/* x < y, x -= ulp */ + if(lx==0) hx--; + lx--; + } else { /* x > y, x += ulp */ + lx++; + if(lx==0) hx++; + } + } + hy = hx&0x7fff000000000000LL; + if(hy==0x7fff000000000000LL) return x+x;/* overflow */ + if(hy==0) { + volatile long double u = x*x; /* underflow */ + } + SET_LDOUBLE_WORDS64(x,hx,lx); + return x; +} + +__strong_alias(nexttowardl, nextafterl); diff --git a/openlibm/ld128/s_nexttoward.c b/openlibm/ld128/s_nexttoward.c new file mode 100644 index 0000000000..a9ff797068 --- /dev/null +++ b/openlibm/ld128/s_nexttoward.c @@ -0,0 +1,85 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* IEEE functions + * nexttoward(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include +#include + +#include "math_private.h" + +double +nexttoward(double x, long double y) +{ + int32_t hx,ix; + int64_t hy,iy; + u_int32_t lx; + u_int64_t ly; + + EXTRACT_WORDS(hx,lx,x); + GET_LDOUBLE_WORDS64(hy,ly,y); + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffffffffffffLL; /* |y| */ + + if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) || /* x is nan */ + ((iy>=0x7fff000000000000LL)&&((iy-0x7fff000000000000LL)|ly)!=0)) + /* y is nan */ + return x+y; + if((long double) x==y) return y; /* x=y, return y */ + if((ix|lx)==0) { /* x == 0 */ + volatile double u; + INSERT_WORDS(x,(u_int32_t)((hy>>32)&0x80000000),1);/* return +-minsub */ + u = x; + u = u * u; /* raise underflow flag */ + return x; + } + if(hx>=0) { /* x > 0 */ + if (hy<0||(ix>>20)>(iy>>48)-0x3c00 + || ((ix>>20)==(iy>>48)-0x3c00 + && (((((int64_t)hx)<<28)|(lx>>4))>(hy&0x0000ffffffffffffLL) + || (((((int64_t)hx)<<28)|(lx>>4))==(hy&0x0000ffffffffffffLL) + && (lx&0xf)>(ly>>60))))) { /* x > y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x < y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } else { /* x < 0 */ + if (hy>=0||(ix>>20)>(iy>>48)-0x3c00 + || ((ix>>20)==(iy>>48)-0x3c00 + && (((((int64_t)hx)<<28)|(lx>>4))>(hy&0x0000ffffffffffffLL) + || (((((int64_t)hx)<<28)|(lx>>4))==(hy&0x0000ffffffffffffLL) + && (lx&0xf)>(ly>>60))))) { /* x < y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x > y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } + hy = hx&0x7ff00000; + if(hy>=0x7ff00000) { + x = x+x; /* overflow */ + return x; + } + if(hy<0x00100000) { + volatile double u = x*x; /* underflow */ + } + INSERT_WORDS(x,hx,lx); + return x; +} diff --git a/openlibm/ld128/s_nexttowardf.c b/openlibm/ld128/s_nexttowardf.c new file mode 100644 index 0000000000..61387ac16a --- /dev/null +++ b/openlibm/ld128/s_nexttowardf.c @@ -0,0 +1,65 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include + +#include "math_private.h" + +float +nexttowardf(float x, long double y) +{ + int32_t hx,ix; + int64_t hy,iy; + u_int64_t ly; + + GET_FLOAT_WORD(hx,x); + GET_LDOUBLE_WORDS64(hy,ly,y); + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffffffffffffLL; /* |y| */ + + if((ix>0x7f800000) || /* x is nan */ + ((iy>=0x7fff000000000000LL)&&((iy-0x7fff000000000000LL)|ly)!=0)) + /* y is nan */ + return x+y; + if((long double) x==y) return y; /* x=y, return y */ + if(ix==0) { /* x == 0 */ + volatile float u; + SET_FLOAT_WORD(x,(u_int32_t)((hy>>32)&0x80000000)|1);/* return +-minsub*/ + u = x; + u = u * u; /* raise underflow flag */ + return x; + } + if(hx>=0) { /* x > 0 */ + if(hy<0||(ix>>23)>(iy>>48)-0x3f80 + || ((ix>>23)==(iy>>48)-0x3f80 + && (ix&0x7fffff)>((hy>>25)&0x7fffff))) {/* x > y, x -= ulp */ + hx -= 1; + } else { /* x < y, x += ulp */ + hx += 1; + } + } else { /* x < 0 */ + if(hy>=0||(ix>>23)>(iy>>48)-0x3f80 + || ((ix>>23)==(iy>>48)-0x3f80 + && (ix&0x7fffff)>((hy>>25)&0x7fffff))) {/* x < y, x -= ulp */ + hx -= 1; + } else { /* x > y, x += ulp */ + hx += 1; + } + } + hy = hx&0x7f800000; + if(hy>=0x7f800000) return x+x; /* overflow */ + if(hy<0x00800000) { + volatile float u = x*x; /* underflow */ + } + SET_FLOAT_WORD(x,hx); + return x; +} diff --git a/openlibm/ld128/s_remquol.c b/openlibm/ld128/s_remquol.c new file mode 100644 index 0000000000..f56ce4f877 --- /dev/null +++ b/openlibm/ld128/s_remquol.c @@ -0,0 +1,168 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +#include + +#include +#include +#include + +#include "math_private.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +/* + * These macros add and remove an explicit integer bit in front of the + * fractional mantissa, if the architecture doesn't have such a bit by + * default already. + */ +#ifdef LDBL_IMPLICIT_NBIT +#define LDBL_NBIT 0 +#define SET_NBIT(hx) ((hx) | (1ULL << LDBL_MANH_SIZE)) +#define HFRAC_BITS (EXT_FRACHBITS + EXT_FRACHMBITS) +#else +#define LDBL_NBIT 0x80000000 +#define SET_NBIT(hx) (hx) +#define HFRAC_BITS (EXT_FRACHBITS + EXT_FRACHMBITS - 1) +#endif + +#define MANL_SHIFT (EXT_FRACLMBITS + EXT_FRACLBITS - 1) + +static const long double Zero[] = {0.0L, -0.0L}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + * + * Assumptions: + * - The low part of the mantissa fits in a manl_t exactly. + * - The high part of the mantissa fits in an int64_t with enough room + * for an explicit integer bit in front of the fractional bits. + */ +long double +remquol(long double x, long double y, int *quo) +{ + int64_t hx,hz,hy,_hx; + uint64_t lx,ly,lz; + uint64_t sx,sxy; + int ix,iy,n,q; + + GET_LDOUBLE_WORDS64(hx,lx,x); + GET_LDOUBLE_WORDS64(hy,ly,y); + sx = (hx>>48)&0x8000; + sxy = sx ^ ((hy>>48)&0x8000); + hx &= 0x7fffffffffffffffLL; /* |x| */ + hy &= 0x7fffffffffffffffLL; /* |y| */ + SET_LDOUBLE_WORDS64(x,hx,lx); + SET_LDOUBLE_WORDS64(y,hy,ly); + + /* purge off exception values */ + if((hy|ly)==0 || /* y=0 */ + ((hx>>48) == BIAS + LDBL_MAX_EXP) || /* or x not finite */ + ((hy>>48) == BIAS + LDBL_MAX_EXP && + (((hy&0x0000ffffffffffffLL)&~LDBL_NBIT)|ly)!=0)) /* or y is NaN */ + return (x*y)/(x*y); + if((hx>>48)<=(hy>>48)) { + if(((hx>>48)<(hy>>48)) || + ((hx&0x0000ffffffffffffLL)<=(hy&0x0000ffffffffffffLL) && + ((hx&0x0000ffffffffffffLL)<(hy&0x0000ffffffffffffLL) || + lx>48) == 0) { /* subnormal x */ + x *= 0x1.0p512; + GET_LDOUBLE_WORDS64(hx,lx,x); + ix = (hx>>48) - (BIAS + 512); + } else { + ix = (hx>>48) - BIAS; + } + + /* determine iy = ilogb(y) */ + if((hy>>48) == 0) { /* subnormal y */ + y *= 0x1.0p512; + GET_LDOUBLE_WORDS64(hy,ly,y); + iy = (hy>>48) - (BIAS + 512); + } else { + iy = (hy>>48) - BIAS; + } + + /* set up {hx,lx}, {hy,ly} and align y to x */ + _hx = SET_NBIT(hx) & 0x0000ffffffffffffLL; + hy = SET_NBIT(hy); + + /* fix point fmod */ + n = ix - iy; + q = 0; + + while(n--) { + hz=_hx-hy;lz=lx-ly; if(lx>MANL_SHIFT); lx = lx+lx;} + else {_hx = hz+hz+(lz>>MANL_SHIFT); lx = lz+lz; q++;} + q <<= 1; + } + hz=_hx-hy;lz=lx-ly; if(lx=0) {_hx=hz;lx=lz;q++;} + + /* convert back to floating value and restore the sign */ + if((_hx|lx)==0) { /* return sign(x)*0 */ + *quo = (sxy ? -q : q); + return Zero[sx!=0]; + } + while(_hx<(1ULL<>MANL_SHIFT); lx = lx+lx; + iy -= 1; + } + hx = (hx&0xffff000000000000LL) | (_hx&0x0000ffffffffffffLL); + if (iy < LDBL_MIN_EXP) { + hx = (hx&0x0000ffffffffffffLL) | (uint64_t)(iy + BIAS + 512)<<48; + SET_LDOUBLE_WORDS64(x,hx,lx); + x *= 0x1p-512; + GET_LDOUBLE_WORDS64(hx,lx,x); + } else { + hx = (hx&0x0000ffffffffffffLL) | (uint64_t)(iy + BIAS)<<48; + } + hx &= 0x7fffffffffffffffLL; + SET_LDOUBLE_WORDS64(x,hx,lx); +fixup: + y = fabsl(y); + if (y < LDBL_MIN * 2) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5*y || (x==0.5*y && (q & 1))) { + q++; + x-=y; + } + + GET_LDOUBLE_MSW64(hx,x); + hx ^= sx; + SET_LDOUBLE_MSW64(x,hx); + + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} diff --git a/openlibm/ld128/s_tanhl.c b/openlibm/ld128/s_tanhl.c new file mode 100644 index 0000000000..f8e62914e0 --- /dev/null +++ b/openlibm/ld128/s_tanhl.c @@ -0,0 +1,105 @@ +/* @(#)s_tanh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* tanhl(x) + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanhl(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanhl(-x) = -tanhl(x). + * 2. 0 <= x <= 2**-57 : tanhl(x) := x*(one+x) + * -t + * 2**-57 < x <= 1 : tanhl(x) := -----; t = expm1l(-2x) + * t + 2 + * 2 + * 1 <= x <= 40.0 : tanhl(x) := 1- ----- ; t=expm1l(2x) + * t + 2 + * 40.0 < x <= INF : tanhl(x) := 1. + * + * Special cases: + * tanhl(NaN) is NaN; + * only tanhl(0)=0 is exact for finite argument. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, two = 2.0, tiny = 1.0e-4900L; + +long double +tanhl(long double x) +{ + long double t, z; + u_int32_t jx, ix; + ieee_quad_shape_type u; + + /* Words of |x|. */ + u.value = x; + jx = u.parts32.mswhi; + ix = jx & 0x7fffffff; + /* x is INF or NaN */ + if (ix >= 0x7fff0000) + { + /* for NaN it's not important which branch: tanhl(NaN) = NaN */ + if (jx & 0x80000000) + return one / x - one; /* tanhl(-inf)= -1; */ + else + return one / x + one; /* tanhl(+inf)=+1 */ + } + + /* |x| < 40 */ + if (ix < 0x40044000) + { + if (u.value == 0) + return x; /* x == +- 0 */ + if (ix < 0x3fc60000) /* |x| < 2^-57 */ + return x * (one + tiny); /* tanh(small) = small */ + u.parts32.mswhi = ix; /* Absolute value of x. */ + if (ix >= 0x3fff0000) + { /* |x| >= 1 */ + t = expm1l (two * u.value); + z = one - two / (t + two); + } + else + { + t = expm1l (-two * u.value); + z = -t / (t + two); + } + /* |x| > 40, return +-1 */ + } + else + { + z = one - tiny; /* raised inexact flag */ + } + return (jx & 0x80000000) ? -z : z; +} diff --git a/openlibm/ld128/s_truncl.c b/openlibm/ld128/s_truncl.c new file mode 100644 index 0000000000..a0d8bd5d6c --- /dev/null +++ b/openlibm/ld128/s_truncl.c @@ -0,0 +1,72 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * From: @(#)s_floor.c 5.1 93/09/24 + */ + +/* + * truncl(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to truncl(x). + */ + +#include +#include + +#include +#include +#include + +#include "math_private.h" + +#ifdef LDBL_IMPLICIT_NBIT +#define MANH_SIZE (EXT_FRACHBITS + EXT_FRACHMBITS + 1) +#else +#define MANH_SIZE (EXT_FRACHBITS + EXT_FRACHMBITS) +#endif + +static const long double huge = 1.0e300; +static const float zero[] = { 0.0, -0.0 }; + +long double +truncl(long double x) +{ + int e; + int64_t ix0, ix1; + + GET_LDOUBLE_WORDS64(ix0,ix1,x); + e = ((ix0>>48)&0x7fff) - LDBL_MAX_EXP + 1; + + if (e < MANH_SIZE - 1) { + if (e < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) + return (zero[((ix0>>48)&0x8000)!=0]); + } else { + uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1); + if (((ix0 & m) | ix1) == 0) + return (x); /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + ix0 &= ~m; + ix1 = 0; + } + } + } else if (e < LDBL_MANT_DIG - 1) { + uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1); + if ((ix1 & m) == 0) + return (x); /* x is integral */ + if (huge + x > 0.0) /* raise inexact flag */ + ix1 &= ~m; + } + SET_LDOUBLE_WORDS64(x,ix0,ix1); + return (x); +} diff --git a/openlibm/ld80/Make.files b/openlibm/ld80/Make.files new file mode 100644 index 0000000000..1198cfbc40 --- /dev/null +++ b/openlibm/ld80/Make.files @@ -0,0 +1,13 @@ +$(CUR_SRCS) += invtrig.c \ + e_acoshl.c e_powl.c k_tanl.c s_exp2l.c \ + e_atanhl.c e_lgammal_r.c e_sinhl.c s_asinhl.c s_expm1l.c \ + e_coshl.c e_log10l.c e_tgammal.c \ + e_expl.c e_log2l.c k_cosl.c s_log1pl.c s_tanhl.c \ + e_logl.c k_sinl.c s_erfl.c + +# s_remquol.c e_fmodl.c s_truncl.c +# e_hypotl.c s_floorl.c s_nextafterl.c s_ceill.c s_modfl.c + +ifneq ($(OS), WINNT) +$(CUR_SRCS) += s_nanl.c +endif diff --git a/openlibm/ld80/e_acoshl.c b/openlibm/ld80/e_acoshl.c new file mode 100644 index 0000000000..35758bba58 --- /dev/null +++ b/openlibm/ld80/e_acoshl.c @@ -0,0 +1,57 @@ +/* @(#)e_acosh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* acoshl(x) + * Method : + * Based on + * acoshl(x) = logl [ x + sqrtl(x*x-1) ] + * we have + * acoshl(x) := logl(x)+ln2, if x is large; else + * acoshl(x) := logl(2x-1/(sqrtl(x*x-1)+x)) if x>2; else + * acoshl(x) := log1pl(t+sqrtl(2.0*t+t*t)); where t=x-1. + * + * Special cases: + * acoshl(x) is NaN with signal if x<1. + * acoshl(NaN) is NaN without signal. + */ + +#include + +#include "math_private.h" + +static const long double +one = 1.0, +ln2 = 6.931471805599453094287e-01L; /* 0x3FFE, 0xB17217F7, 0xD1CF79AC */ + +long double +acoshl(long double x) +{ + long double t; + u_int32_t se,i0,i1; + GET_LDOUBLE_WORDS(se,i0,i1,x); + if(se<0x3fff || se & 0x8000) { /* x < 1 */ + return (x-x)/(x-x); + } else if(se >=0x401d) { /* x > 2**30 */ + if(se >=0x7fff) { /* x is inf of NaN */ + return x+x; + } else + return logl(x)+ln2; /* acoshl(huge)=logl(2x) */ + } else if(((se-0x3fff)|i0|i1)==0) { + return 0.0; /* acosh(1) = 0 */ + } else if (se > 0x4000) { /* 2**28 > x > 2 */ + t=x*x; + return logl(2.0*x-one/(x+sqrtl(t-one))); + } else { /* 1=0.5 + * 1 2x x + * atanhl(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) + * 2 1 - x 1 - x + * + * For x<0.5 + * atanhl(x) = 0.5*log1pl(2x+2x*x/(1-x)) + * + * Special cases: + * atanhl(x) is NaN if |x| > 1 with signal; + * atanhl(NaN) is that NaN with no signal; + * atanhl(+-1) is +-INF with signal. + * + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, huge = 1e4900L; + +static const long double zero = 0.0; + +long double +atanhl(long double x) +{ + long double t; + int32_t ix; + u_int32_t se,i0,i1; + GET_LDOUBLE_WORDS(se,i0,i1,x); + ix = se&0x7fff; + if ((ix+((((i0&0x7fffffff)|i1)|(-((i0&0x7fffffff)|i1)))>>31))>0x3fff) + /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3fff) + return x/zero; + if(ix<0x3fe3&&(huge+x)>zero) return x; /* x<2**-28 */ + SET_LDOUBLE_EXP(x,ix); + if(ix<0x3ffe) { /* x < 0.5 */ + t = x+x; + t = 0.5*log1pl(t+t*x/(one-x)); + } else + t = 0.5*log1pl((x+x)/(one-x)); + if(se<=0x7fff) return t; else return -t; +} diff --git a/openlibm/ld80/e_coshl.c b/openlibm/ld80/e_coshl.c new file mode 100644 index 0000000000..a0fb9b5399 --- /dev/null +++ b/openlibm/ld80/e_coshl.c @@ -0,0 +1,83 @@ +/* @(#)e_cosh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* coshl(x) + * Method : + * mathematically coshl(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (coshl(x) = coshl(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : coshl(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : coshl(x) := ------------------- + * 2 + * 22 <= x <= lnovft : coshl(x) := expl(x)/2 + * lnovft <= x <= ln2ovft: coshl(x) := expl(x/2)/2 * expl(x/2) + * ln2ovft < x : coshl(x) := huge*huge (overflow) + * + * Special cases: + * coshl(x) is |x| if x is +INF, -INF, or NaN. + * only coshl(0)=1 is exact for finite x. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, half=0.5, huge = 1.0e4900L; + +long double +coshl(long double x) +{ + long double t,w; + int32_t ex; + u_int32_t mx,lx; + + /* High word of |x|. */ + GET_LDOUBLE_WORDS(ex,mx,lx,x); + ex &= 0x7fff; + + /* x is INF or NaN */ + if(ex==0x7fff) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1l(|x|)^2/(2*expl(|x|)) */ + if(ex < 0x3ffd || (ex == 0x3ffd && mx < 0xb17217f7u)) { + t = expm1l(fabsl(x)); + w = one+t; + if (ex<0x3fbc) return w; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */ + if (ex < 0x4003 || (ex == 0x4003 && mx < 0xb0000000u)) { + t = expl(fabsl(x)); + return half*t+half/t; + } + + /* |x| in [22, ln(maxdouble)] return half*exp(|x|) */ + if (ex < 0x400c || (ex == 0x400c && mx < 0xb1700000u)) + return half*expl(fabsl(x)); + + /* |x| in [log(maxdouble), log(2*maxdouble)) */ + if (ex == 0x400c && (mx < 0xb174ddc0u + || (mx == 0xb174ddc0u && lx < 0x31aec0ebu))) + { + w = expl(half*fabsl(x)); + t = half*w; + return t*w; + } + + /* |x| >= log(2*maxdouble), cosh(x) overflow */ + return huge*huge; +} diff --git a/openlibm/ld80/e_expl.c b/openlibm/ld80/e_expl.c new file mode 100644 index 0000000000..db0d1cae1f --- /dev/null +++ b/openlibm/ld80/e_expl.c @@ -0,0 +1,131 @@ +/* $OpenBSD: e_expl.c,v 1.3 2013/11/12 20:35:19 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* expl.c + * + * Exponential function, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, expl(); + * + * y = expl( x ); + * + * + * + * DESCRIPTION: + * + * Returns e (2.71828...) raised to the x power. + * + * Range reduction is accomplished by separating the argument + * into an integer k and fraction f such that + * + * x k f + * e = 2 e. + * + * A Pade' form of degree 2/3 is used to approximate exp(f) - 1 + * in the basic range [-0.5 ln 2, 0.5 ln 2]. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE +-10000 50000 1.12e-19 2.81e-20 + * + * + * Error amplification in the exponential function can be + * a serious matter. The error propagation involves + * exp( X(1+delta) ) = exp(X) ( 1 + X*delta + ... ), + * which shows that a 1 lsb error in representing X produces + * a relative error of X times 1 lsb in the function. + * While the routine gives an accurate result for arguments + * that are exactly represented by a long double precision + * computer number, the result contains amplified roundoff + * error for large arguments not exactly represented. + * + * + * ERROR MESSAGES: + * + * message condition value returned + * exp underflow x < MINLOG 0.0 + * exp overflow x > MAXLOG MAXNUM + * + */ + +/* Exponential function */ + +#include + +#include "math_private.h" + +static long double P[3] = { + 1.2617719307481059087798E-4L, + 3.0299440770744196129956E-2L, + 9.9999999999999999991025E-1L, +}; +static long double Q[4] = { + 3.0019850513866445504159E-6L, + 2.5244834034968410419224E-3L, + 2.2726554820815502876593E-1L, + 2.0000000000000000000897E0L, +}; +static const long double C1 = 6.9314575195312500000000E-1L; +static const long double C2 = 1.4286068203094172321215E-6L; +static const long double MAXLOGL = 1.1356523406294143949492E4L; +static const long double MINLOGL = -1.13994985314888605586758E4L; +static const long double LOG2EL = 1.4426950408889634073599E0L; + +long double +expl(long double x) +{ +long double px, xx; +int n; + +if( isnan(x) ) + return(x); +if( x > MAXLOGL) + return( INFINITY ); + +if( x < MINLOGL ) + return(0.0L); + +/* Express e**x = e**g 2**n + * = e**g e**( n loge(2) ) + * = e**( g + n loge(2) ) + */ +px = floorl( LOG2EL * x + 0.5L ); /* floor() truncates toward -infinity. */ +n = px; +x -= px * C1; +x -= px * C2; + + +/* rational approximation for exponential + * of the fractional part: + * e**x = 1 + 2x P(x**2)/( Q(x**2) - P(x**2) ) + */ +xx = x * x; +px = x * __polevll( xx, P, 2 ); +x = px/( __polevll( xx, Q, 3 ) - px ); +x = 1.0L + ldexpl( x, 1 ); + +x = ldexpl( x, n ); +return(x); +} diff --git a/openlibm/ld80/e_fmodl.c b/openlibm/ld80/e_fmodl.c new file mode 100644 index 0000000000..ec792e3341 --- /dev/null +++ b/openlibm/ld80/e_fmodl.c @@ -0,0 +1,142 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +//#include + +#include +#include +#include + +#include "math_private.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +/* + * These macros add and remove an explicit integer bit in front of the + * fractional mantissa, if the architecture doesn't have such a bit by + * default already. + */ +#ifdef LDBL_IMPLICIT_NBIT +#define LDBL_NBIT 0 +#define SET_NBIT(hx) ((hx) | (1ULL << LDBL_MANH_SIZE)) +#define HFRAC_BITS EXT_FRACHBITS +#else +#define LDBL_NBIT 0x80000000 +#define SET_NBIT(hx) (hx) +#define HFRAC_BITS (EXT_FRACHBITS - 1) +#endif + +#define MANL_SHIFT (EXT_FRACLBITS - 1) + +static const long double one = 1.0, Zero[] = {0.0, -0.0,}; + +/* + * fmodl(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + * + * Assumptions: + * - The low part of the mantissa fits in a manl_t exactly. + * - The high part of the mantissa fits in an int64_t with enough room + * for an explicit integer bit in front of the fractional bits. + */ +long double +fmodl(long double x, long double y) +{ + union { + long double e; + struct ieee_ext bits; + } ux, uy; + int64_t hx,hz; /* We need a carry bit even if LDBL_MANH_SIZE is 32. */ + uint32_t hy; + uint32_t lx,ly,lz; + int ix,iy,n,sx; + + ux.e = x; + uy.e = y; + sx = ux.bits.ext_sign; + + /* purge off exception values */ + if((uy.bits.ext_exp|uy.bits.ext_frach|uy.bits.ext_fracl)==0 || /* y=0 */ + (ux.bits.ext_exp == BIAS + LDBL_MAX_EXP) || /* or x not finite */ + (uy.bits.ext_exp == BIAS + LDBL_MAX_EXP && + ((uy.bits.ext_frach&~LDBL_NBIT)|uy.bits.ext_fracl)!=0)) /* or y is NaN */ + return (x*y)/(x*y); + if(ux.bits.ext_exp<=uy.bits.ext_exp) { + if((ux.bits.ext_exp>MANL_SHIFT); lx = lx+lx;} + else { + if ((hz|lz)==0) /* return sign(x)*0 */ + return Zero[sx]; + hx = hz+hz+(lz>>MANL_SHIFT); lx = lz+lz; + } + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) /* return sign(x)*0 */ + return Zero[sx]; + while(hx<(1ULL<>MANL_SHIFT); lx = lx+lx; + iy -= 1; + } + ux.bits.ext_frach = hx; /* The mantissa is truncated here if needed. */ + ux.bits.ext_fracl = lx; + if (iy < LDBL_MIN_EXP) { + ux.bits.ext_exp = iy + (BIAS + 512); + ux.e *= 0x1p-512; + } else { + ux.bits.ext_exp = iy + BIAS; + } + x = ux.e * one; /* create necessary signal */ + return x; /* exact output */ +} diff --git a/openlibm/ld80/e_hypotl.c b/openlibm/ld80/e_hypotl.c new file mode 100644 index 0000000000..e6faa22867 --- /dev/null +++ b/openlibm/ld80/e_hypotl.c @@ -0,0 +1,122 @@ +/* @(#)e_hypot.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* hypotl(x,y) + * + * Method : + * If (assume round-to-nearest) z=x*x+y*y + * has error less than sqrt(2)/2 ulp, than + * sqrt(z) has error less than 1 ulp (exercise). + * + * So, compute sqrt(x*x+y*y) with some care as + * follows to get the error below 1 ulp: + * + * Assume x>y>0; + * (if possible, set rounding to round-to-nearest) + * 1. if x > 2y use + * x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y + * where x1 = x with lower 32 bits cleared, x2 = x-x1; else + * 2. if x <= 2y use + * t1*yy1+((x-y)*(x-y)+(t1*y2+t2*y)) + * where t1 = 2x with lower 32 bits cleared, t2 = 2x-t1, + * yy1= y with lower 32 bits chopped, y2 = y-yy1. + * + * NOTE: scaling may be necessary if some argument is too + * large or too tiny + * + * Special cases: + * hypot(x,y) is INF if x or y is +INF or -INF; else + * hypot(x,y) is NAN if x or y is NAN. + * + * Accuracy: + * hypot(x,y) returns sqrt(x^2+y^2) with error less + * than 1 ulps (units in the last place) + */ + +#include + +#include "math_private.h" + +long double +hypotl(long double x, long double y) +{ + long double a,b,t1,t2,yy1,y2,w; + u_int32_t j,k,ea,eb; + + GET_LDOUBLE_EXP(ea,x); + ea &= 0x7fff; + GET_LDOUBLE_EXP(eb,y); + eb &= 0x7fff; + if(eb > ea) {a=y;b=x;j=ea; ea=eb;eb=j;} else {a=x;b=y;} + SET_LDOUBLE_EXP(a,ea); /* a <- |a| */ + SET_LDOUBLE_EXP(b,eb); /* b <- |b| */ + if((ea-eb)>0x46) {return a+b;} /* x/y > 2**70 */ + k=0; + if(ea > 0x5f3f) { /* a>2**8000 */ + if(ea == 0x7fff) { /* Inf or NaN */ + u_int32_t es,high,low; + w = a+b; /* for sNaN */ + GET_LDOUBLE_WORDS(es,high,low,a); + if(((high&0x7fffffff)|low)==0) w = a; + GET_LDOUBLE_WORDS(es,high,low,b); + if(((eb^0x7fff)|(high&0x7fffffff)|low)==0) w = b; + return w; + } + /* scale a and b by 2**-9600 */ + ea -= 0x2580; eb -= 0x2580; k += 9600; + SET_LDOUBLE_EXP(a,ea); + SET_LDOUBLE_EXP(b,eb); + } + if(eb < 0x20bf) { /* b < 2**-8000 */ + if(eb == 0) { /* subnormal b or 0 */ + u_int32_t es,high,low; + GET_LDOUBLE_WORDS(es,high,low,b); + if((high|low)==0) return a; + SET_LDOUBLE_WORDS(t1, 0x7ffd, 0, 0); /* t1=2^16382 */ + b *= t1; + a *= t1; + k -= 16382; + } else { /* scale a and b by 2^9600 */ + ea += 0x2580; /* a *= 2^9600 */ + eb += 0x2580; /* b *= 2^9600 */ + k -= 9600; + SET_LDOUBLE_EXP(a,ea); + SET_LDOUBLE_EXP(b,eb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + u_int32_t high; + GET_LDOUBLE_MSW(high,a); + SET_LDOUBLE_WORDS(t1,ea,high,0); + t2 = a-t1; + w = sqrtl(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + u_int32_t high; + GET_LDOUBLE_MSW(high,b); + a = a+a; + SET_LDOUBLE_WORDS(yy1,eb,high,0); + y2 = b - yy1; + GET_LDOUBLE_MSW(high,a); + SET_LDOUBLE_WORDS(t1,ea+1,high,0); + t2 = a - t1; + w = sqrtl(t1*yy1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + u_int32_t es; + t1 = 1.0; + GET_LDOUBLE_EXP(es,t1); + SET_LDOUBLE_EXP(t1,es+k); + return t1*w; + } else return w; +} diff --git a/openlibm/ld80/e_lgammal_r.c b/openlibm/ld80/e_lgammal_r.c new file mode 100644 index 0000000000..0af5420997 --- /dev/null +++ b/openlibm/ld80/e_lgammal_r.c @@ -0,0 +1,425 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* lgammal_r(x, signgamp) + * Reentrant version of the logarithm of the Gamma function + * with user provide pointer for the sign of Gamma(x). + * + * Method: + * 1. Argument Reduction for 0 < x <= 8 + * Since gamma(1+s)=s*gamma(s), for x in [0,8], we may + * reduce x to a number in [1.5,2.5] by + * lgamma(1+s) = log(s) + lgamma(s) + * for example, + * lgamma(7.3) = log(6.3) + lgamma(6.3) + * = log(6.3*5.3) + lgamma(5.3) + * = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3) + * 2. Polynomial approximation of lgamma around its + * minimun ymin=1.461632144968362245 to maintain monotonicity. + * On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use + * Let z = x-ymin; + * lgamma(x) = -1.214862905358496078218 + z^2*poly(z) + * 2. Rational approximation in the primary interval [2,3] + * We use the following approximation: + * s = x-2.0; + * lgamma(x) = 0.5*s + s*P(s)/Q(s) + * Our algorithms are based on the following observation + * + * zeta(2)-1 2 zeta(3)-1 3 + * lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ... + * 2 3 + * + * where Euler = 0.5771... is the Euler constant, which is very + * close to 0.5. + * + * 3. For x>=8, we have + * lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+.... + * (better formula: + * lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...) + * Let z = 1/x, then we approximation + * f(z) = lgamma(x) - (x-0.5)(log(x)-1) + * by + * 3 5 11 + * w = w0 + w1*z + w2*z + w3*z + ... + w6*z + * + * 4. For negative x, since (G is gamma function) + * -x*G(-x)*G(x) = pi/sin(pi*x), + * we have + * G(x) = pi/(sin(pi*x)*(-x)*G(-x)) + * since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0 + * Hence, for x<0, signgam = sign(sin(pi*x)) and + * lgamma(x) = log(|Gamma(x)|) + * = log(pi/(|x*sin(pi*x)|)) - lgamma(-x); + * Note: one should avoid compute pi*(-x) directly in the + * computation of sin(pi*(-x)). + * + * 5. Special Cases + * lgamma(2+s) ~ s*(1-Euler) for tiny s + * lgamma(1)=lgamma(2)=0 + * lgamma(x) ~ -log(x) for tiny x + * lgamma(0) = lgamma(inf) = inf + * lgamma(-integer) = +-inf + * + */ + +#include + +#include "math_private.h" + +static const long double + half = 0.5L, + one = 1.0L, + pi = 3.14159265358979323846264L, + two63 = 9.223372036854775808e18L, + + /* lgam(1+x) = 0.5 x + x a(x)/b(x) + -0.268402099609375 <= x <= 0 + peak relative error 6.6e-22 */ + a0 = -6.343246574721079391729402781192128239938E2L, + a1 = 1.856560238672465796768677717168371401378E3L, + a2 = 2.404733102163746263689288466865843408429E3L, + a3 = 8.804188795790383497379532868917517596322E2L, + a4 = 1.135361354097447729740103745999661157426E2L, + a5 = 3.766956539107615557608581581190400021285E0L, + + b0 = 8.214973713960928795704317259806842490498E3L, + b1 = 1.026343508841367384879065363925870888012E4L, + b2 = 4.553337477045763320522762343132210919277E3L, + b3 = 8.506975785032585797446253359230031874803E2L, + b4 = 6.042447899703295436820744186992189445813E1L, + /* b5 = 1.000000000000000000000000000000000000000E0 */ + + + tc = 1.4616321449683623412626595423257213284682E0L, + tf = -1.2148629053584961146050602565082954242826E-1,/* double precision */ +/* tt = (tail of tf), i.e. tf + tt has extended precision. */ + tt = 3.3649914684731379602768989080467587736363E-18L, + /* lgam ( 1.4616321449683623412626595423257213284682E0 ) = +-1.2148629053584960809551455717769158215135617312999903886372437313313530E-1 */ + + /* lgam (x + tc) = tf + tt + x g(x)/h(x) + - 0.230003726999612341262659542325721328468 <= x + <= 0.2699962730003876587373404576742786715318 + peak relative error 2.1e-21 */ + g0 = 3.645529916721223331888305293534095553827E-18L, + g1 = 5.126654642791082497002594216163574795690E3L, + g2 = 8.828603575854624811911631336122070070327E3L, + g3 = 5.464186426932117031234820886525701595203E3L, + g4 = 1.455427403530884193180776558102868592293E3L, + g5 = 1.541735456969245924860307497029155838446E2L, + g6 = 4.335498275274822298341872707453445815118E0L, + + h0 = 1.059584930106085509696730443974495979641E4L, + h1 = 2.147921653490043010629481226937850618860E4L, + h2 = 1.643014770044524804175197151958100656728E4L, + h3 = 5.869021995186925517228323497501767586078E3L, + h4 = 9.764244777714344488787381271643502742293E2L, + h5 = 6.442485441570592541741092969581997002349E1L, + /* h6 = 1.000000000000000000000000000000000000000E0 */ + + + /* lgam (x+1) = -0.5 x + x u(x)/v(x) + -0.100006103515625 <= x <= 0.231639862060546875 + peak relative error 1.3e-21 */ + u0 = -8.886217500092090678492242071879342025627E1L, + u1 = 6.840109978129177639438792958320783599310E2L, + u2 = 2.042626104514127267855588786511809932433E3L, + u3 = 1.911723903442667422201651063009856064275E3L, + u4 = 7.447065275665887457628865263491667767695E2L, + u5 = 1.132256494121790736268471016493103952637E2L, + u6 = 4.484398885516614191003094714505960972894E0L, + + v0 = 1.150830924194461522996462401210374632929E3L, + v1 = 3.399692260848747447377972081399737098610E3L, + v2 = 3.786631705644460255229513563657226008015E3L, + v3 = 1.966450123004478374557778781564114347876E3L, + v4 = 4.741359068914069299837355438370682773122E2L, + v5 = 4.508989649747184050907206782117647852364E1L, + /* v6 = 1.000000000000000000000000000000000000000E0 */ + + + /* lgam (x+2) = .5 x + x s(x)/r(x) + 0 <= x <= 1 + peak relative error 7.2e-22 */ + s0 = 1.454726263410661942989109455292824853344E6L, + s1 = -3.901428390086348447890408306153378922752E6L, + s2 = -6.573568698209374121847873064292963089438E6L, + s3 = -3.319055881485044417245964508099095984643E6L, + s4 = -7.094891568758439227560184618114707107977E5L, + s5 = -6.263426646464505837422314539808112478303E4L, + s6 = -1.684926520999477529949915657519454051529E3L, + + r0 = -1.883978160734303518163008696712983134698E7L, + r1 = -2.815206082812062064902202753264922306830E7L, + r2 = -1.600245495251915899081846093343626358398E7L, + r3 = -4.310526301881305003489257052083370058799E6L, + r4 = -5.563807682263923279438235987186184968542E5L, + r5 = -3.027734654434169996032905158145259713083E4L, + r6 = -4.501995652861105629217250715790764371267E2L, + /* r6 = 1.000000000000000000000000000000000000000E0 */ + + +/* lgam(x) = ( x - 0.5 ) * log(x) - x + LS2PI + 1/x w(1/x^2) + x >= 8 + Peak relative error 1.51e-21 + w0 = LS2PI - 0.5 */ + w0 = 4.189385332046727417803e-1L, + w1 = 8.333333333333331447505E-2L, + w2 = -2.777777777750349603440E-3L, + w3 = 7.936507795855070755671E-4L, + w4 = -5.952345851765688514613E-4L, + w5 = 8.412723297322498080632E-4L, + w6 = -1.880801938119376907179E-3L, + w7 = 4.885026142432270781165E-3L; + +static const long double zero = 0.0L; + +static long double +sin_pi(long double x) +{ + long double y, z; + int n, ix; + u_int32_t se, i0, i1; + + GET_LDOUBLE_WORDS (se, i0, i1, x); + ix = se & 0x7fff; + ix = (ix << 16) | (i0 >> 16); + if (ix < 0x3ffd8000) /* 0.25 */ + return sinl (pi * x); + y = -x; /* x is assume negative */ + + /* + * argument reduction, make sure inexact flag not raised if input + * is an integer + */ + z = floorl (y); + if (z != y) + { /* inexact anyway */ + y *= 0.5; + y = 2.0*(y - floorl(y)); /* y = |x| mod 2.0 */ + n = (int) (y*4.0); + } + else + { + if (ix >= 0x403f8000) /* 2^64 */ + { + y = zero; n = 0; /* y must be even */ + } + else + { + if (ix < 0x403e8000) /* 2^63 */ + z = y + two63; /* exact */ + GET_LDOUBLE_WORDS (se, i0, i1, z); + n = i1 & 1; + y = n; + n <<= 2; + } + } + + switch (n) + { + case 0: + y = sinl (pi * y); + break; + case 1: + case 2: + y = cosl (pi * (half - y)); + break; + case 3: + case 4: + y = sinl (pi * (one - y)); + break; + case 5: + case 6: + y = -cosl (pi * (y - 1.5)); + break; + default: + y = sinl (pi * (y - 2.0)); + break; + } + return -y; +} + + +long double +lgammal_r(long double x, int *signgamp) +{ + long double t, y, z, nadj, p, p1, p2, q, r, w; + int i, ix; + u_int32_t se, i0, i1; + + *signgamp = 1; + GET_LDOUBLE_WORDS (se, i0, i1, x); + ix = se & 0x7fff; + + if ((ix | i0 | i1) == 0) + { + if (se & 0x8000) + *signgamp = -1; + return one / fabsl (x); + } + + ix = (ix << 16) | (i0 >> 16); + + /* purge off +-inf, NaN, +-0, and negative arguments */ + if (ix >= 0x7fff0000) + return x * x; + + if (ix < 0x3fc08000) /* 2^-63 */ + { /* |x|<2**-63, return -log(|x|) */ + if (se & 0x8000) + { + *signgamp = -1; + return -logl (-x); + } + else + return -logl (x); + } + if (se & 0x8000) + { + t = sin_pi (x); + if (t == zero) + return one / fabsl (t); /* -integer */ + nadj = logl (pi / fabsl (t * x)); + if (t < zero) + *signgamp = -1; + x = -x; + } + + /* purge off 1 and 2 */ + if ((((ix - 0x3fff8000) | i0 | i1) == 0) + || (((ix - 0x40008000) | i0 | i1) == 0)) + r = 0; + else if (ix < 0x40008000) /* 2.0 */ + { + /* x < 2.0 */ + if (ix <= 0x3ffee666) /* 8.99993896484375e-1 */ + { + /* lgamma(x) = lgamma(x+1) - log(x) */ + r = -logl (x); + if (ix >= 0x3ffebb4a) /* 7.31597900390625e-1 */ + { + y = x - one; + i = 0; + } + else if (ix >= 0x3ffced33)/* 2.31639862060546875e-1 */ + { + y = x - (tc - one); + i = 1; + } + else + { + /* x < 0.23 */ + y = x; + i = 2; + } + } + else + { + r = zero; + if (ix >= 0x3fffdda6) /* 1.73162841796875 */ + { + /* [1.7316,2] */ + y = x - 2.0; + i = 0; + } + else if (ix >= 0x3fff9da6)/* 1.23162841796875 */ + { + /* [1.23,1.73] */ + y = x - tc; + i = 1; + } + else + { + /* [0.9, 1.23] */ + y = x - one; + i = 2; + } + } + switch (i) + { + case 0: + p1 = a0 + y * (a1 + y * (a2 + y * (a3 + y * (a4 + y * a5)))); + p2 = b0 + y * (b1 + y * (b2 + y * (b3 + y * (b4 + y)))); + r += half * y + y * p1/p2; + break; + case 1: + p1 = g0 + y * (g1 + y * (g2 + y * (g3 + y * (g4 + y * (g5 + y * g6))))); + p2 = h0 + y * (h1 + y * (h2 + y * (h3 + y * (h4 + y * (h5 + y))))); + p = tt + y * p1/p2; + r += (tf + p); + break; + case 2: + p1 = y * (u0 + y * (u1 + y * (u2 + y * (u3 + y * (u4 + y * (u5 + y * u6)))))); + p2 = v0 + y * (v1 + y * (v2 + y * (v3 + y * (v4 + y * (v5 + y))))); + r += (-half * y + p1 / p2); + } + } + else if (ix < 0x40028000) /* 8.0 */ + { + /* x < 8.0 */ + i = (int) x; + t = zero; + y = x - (double) i; + p = y * + (s0 + y * (s1 + y * (s2 + y * (s3 + y * (s4 + y * (s5 + y * s6)))))); + q = r0 + y * (r1 + y * (r2 + y * (r3 + y * (r4 + y * (r5 + y * (r6 + y)))))); + r = half * y + p / q; + z = one; /* lgamma(1+s) = log(s) + lgamma(s) */ + switch (i) + { + case 7: + z *= (y + 6.0); /* FALLTHRU */ + case 6: + z *= (y + 5.0); /* FALLTHRU */ + case 5: + z *= (y + 4.0); /* FALLTHRU */ + case 4: + z *= (y + 3.0); /* FALLTHRU */ + case 3: + z *= (y + 2.0); /* FALLTHRU */ + r += logl (z); + break; + } + } + else if (ix < 0x40418000) /* 2^66 */ + { + /* 8.0 <= x < 2**66 */ + t = logl (x); + z = one / x; + y = z * z; + w = w0 + z * (w1 + + y * (w2 + y * (w3 + y * (w4 + y * (w5 + y * (w6 + y * w7)))))); + r = (x - half) * (t - one) + w; + } + else + /* 2**66 <= x <= inf */ + r = x * (logl (x) - one); + if (se & 0x8000) + r = nadj - r; + return r; +} diff --git a/openlibm/ld80/e_log10l.c b/openlibm/ld80/e_log10l.c new file mode 100644 index 0000000000..bb6cd7d70d --- /dev/null +++ b/openlibm/ld80/e_log10l.c @@ -0,0 +1,205 @@ +/* $OpenBSD: e_log10l.c,v 1.2 2013/11/12 20:35:19 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* log10l.c + * + * Common logarithm, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, log10l(); + * + * y = log10l( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base 10 logarithm of x. + * + * The argument is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the logarithm + * of the fraction is approximated by + * + * log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x). + * + * Otherwise, setting z = 2(x-1)/x+1), + * + * log(x) = z + z**3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE 0.5, 2.0 30000 9.0e-20 2.6e-20 + * IEEE exp(+-10000) 30000 6.0e-20 2.3e-20 + * + * In the tests over the interval exp(+-10000), the logarithms + * of the random arguments were uniformly distributed over + * [-10000, +10000]. + * + * ERROR MESSAGES: + * + * log singularity: x = 0; returns MINLOG + * log domain: x < 0; returns MINLOG + */ + +#include + +#include "math_private.h" + +/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 6.2e-22 + */ +static long double P[] = { + 4.9962495940332550844739E-1L, + 1.0767376367209449010438E1L, + 7.7671073698359539859595E1L, + 2.5620629828144409632571E2L, + 4.2401812743503691187826E2L, + 3.4258224542413922935104E2L, + 1.0747524399916215149070E2L, +}; +static long double Q[] = { +/* 1.0000000000000000000000E0,*/ + 2.3479774160285863271658E1L, + 1.9444210022760132894510E2L, + 7.7952888181207260646090E2L, + 1.6911722418503949084863E3L, + 2.0307734695595183428202E3L, + 1.2695660352705325274404E3L, + 3.2242573199748645407652E2L, +}; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 6.16e-22 + */ + +static long double R[4] = { + 1.9757429581415468984296E-3L, +-7.1990767473014147232598E-1L, + 1.0777257190312272158094E1L, +-3.5717684488096787370998E1L, +}; +static long double S[4] = { +/* 1.00000000000000000000E0L,*/ +-2.6201045551331104417768E1L, + 1.9361891836232102174846E2L, +-4.2861221385716144629696E2L, +}; +/* log10(2) */ +#define L102A 0.3125L +#define L102B -1.1470004336018804786261e-2L +/* log10(e) */ +#define L10EA 0.5L +#define L10EB -6.5705518096748172348871e-2L + +#define SQRTH 0.70710678118654752440L + +long double +log10l(long double x) +{ +long double y; +volatile long double z; +int e; + +if( isnan(x) ) + return(x); +/* Test for domain */ +if( x <= 0.0L ) + { + if( x == 0.0L ) + return (-1.0L / (x - x)); + else + return (x - x) / (x - x); + } +if( x == INFINITY ) + return(INFINITY); +/* separate mantissa from exponent */ + +/* Note, frexp is used so that denormal numbers + * will be handled properly. + */ +x = frexpl( x, &e ); + + +/* logarithm using log(x) = z + z**3 P(z)/Q(z), + * where z = 2(x-1)/x+1) + */ +if( (e > 2) || (e < -2) ) +{ +if( x < SQRTH ) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } +else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } +x = z / y; +z = x*x; +y = x * ( z * __polevll( z, R, 3 ) / __p1evll( z, S, 3 ) ); +goto done; +} + + +/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */ + +if( x < SQRTH ) + { + e -= 1; + x = ldexpl( x, 1 ) - 1.0L; /* 2x - 1 */ + } +else + { + x = x - 1.0L; + } +z = x*x; +y = x * ( z * __polevll( x, P, 6 ) / __p1evll( x, Q, 7 ) ); +y = y - ldexpl( z, -1 ); /* -0.5x^2 + ... */ + +done: + +/* Multiply log of fraction by log10(e) + * and base 2 exponent by log10(2). + * + * ***CAUTION*** + * + * This sequence of operations is critical and it may + * be horribly defeated by some compiler optimizers. + */ +z = y * (L10EB); +z += x * (L10EB); +z += e * (L102B); +z += y * (L10EA); +z += x * (L10EA); +z += e * (L102A); + +return( z ); +} diff --git a/openlibm/ld80/e_log2l.c b/openlibm/ld80/e_log2l.c new file mode 100644 index 0000000000..6ff6fe99ec --- /dev/null +++ b/openlibm/ld80/e_log2l.c @@ -0,0 +1,199 @@ +/* $OpenBSD: e_log2l.c,v 1.2 2013/11/12 20:35:19 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* log2l.c + * + * Base 2 logarithm, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, log2l(); + * + * y = log2l( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base 2 logarithm of x. + * + * The argument is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the (natural) + * logarithm of the fraction is approximated by + * + * log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x). + * + * Otherwise, setting z = 2(x-1)/x+1), + * + * log(x) = z + z**3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE 0.5, 2.0 30000 9.8e-20 2.7e-20 + * IEEE exp(+-10000) 70000 5.4e-20 2.3e-20 + * + * In the tests over the interval exp(+-10000), the logarithms + * of the random arguments were uniformly distributed over + * [-10000, +10000]. + * + * ERROR MESSAGES: + * + * log singularity: x = 0; returns -INFINITY + * log domain: x < 0; returns NAN + */ + +#include + +#include "math_private.h" + +/* Coefficients for ln(1+x) = x - x**2/2 + x**3 P(x)/Q(x) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 6.2e-22 + */ +static long double P[] = { + 4.9962495940332550844739E-1L, + 1.0767376367209449010438E1L, + 7.7671073698359539859595E1L, + 2.5620629828144409632571E2L, + 4.2401812743503691187826E2L, + 3.4258224542413922935104E2L, + 1.0747524399916215149070E2L, +}; +static long double Q[] = { +/* 1.0000000000000000000000E0,*/ + 2.3479774160285863271658E1L, + 1.9444210022760132894510E2L, + 7.7952888181207260646090E2L, + 1.6911722418503949084863E3L, + 2.0307734695595183428202E3L, + 1.2695660352705325274404E3L, + 3.2242573199748645407652E2L, +}; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 6.16e-22 + */ +static long double R[4] = { + 1.9757429581415468984296E-3L, +-7.1990767473014147232598E-1L, + 1.0777257190312272158094E1L, +-3.5717684488096787370998E1L, +}; +static long double S[4] = { +/* 1.00000000000000000000E0L,*/ +-2.6201045551331104417768E1L, + 1.9361891836232102174846E2L, +-4.2861221385716144629696E2L, +}; +/* log2(e) - 1 */ +#define LOG2EA 4.4269504088896340735992e-1L + +#define SQRTH 0.70710678118654752440L + +long double +log2l(long double x) +{ +volatile long double z; +long double y; +int e; + +if( isnan(x) ) + return(x); +if( x == INFINITY ) + return(x); +/* Test for domain */ +if( x <= 0.0L ) + { + if( x == 0.0L ) + return( -INFINITY ); + else + return( NAN ); + } + +/* separate mantissa from exponent */ + +/* Note, frexp is used so that denormal numbers + * will be handled properly. + */ +x = frexpl( x, &e ); + + +/* logarithm using log(x) = z + z**3 P(z)/Q(z), + * where z = 2(x-1)/x+1) + */ +if( (e > 2) || (e < -2) ) +{ +if( x < SQRTH ) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } +else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } +x = z / y; +z = x*x; +y = x * ( z * __polevll( z, R, 3 ) / __p1evll( z, S, 3 ) ); +goto done; +} + + +/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */ + +if( x < SQRTH ) + { + e -= 1; + x = ldexpl( x, 1 ) - 1.0L; /* 2x - 1 */ + } +else + { + x = x - 1.0L; + } +z = x*x; +y = x * ( z * __polevll( x, P, 6 ) / __p1evll( x, Q, 7 ) ); +y = y - ldexpl( z, -1 ); /* -0.5x^2 + ... */ + +done: + +/* Multiply log of fraction by log2(e) + * and base 2 exponent by 1 + * + * ***CAUTION*** + * + * This sequence of operations is critical and it may + * be horribly defeated by some compiler optimizers. + */ +z = y * LOG2EA; +z += x * LOG2EA; +z += y; +z += x; +z += e; +return( z ); +} diff --git a/openlibm/ld80/e_logl.c b/openlibm/ld80/e_logl.c new file mode 100644 index 0000000000..4722014ced --- /dev/null +++ b/openlibm/ld80/e_logl.c @@ -0,0 +1,190 @@ +/* $OpenBSD: e_logl.c,v 1.3 2013/11/12 20:35:19 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* logl.c + * + * Natural logarithm, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, logl(); + * + * y = logl( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base e (2.718...) logarithm of x. + * + * The argument is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the logarithm + * of the fraction is approximated by + * + * log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x). + * + * Otherwise, setting z = 2(x-1)/x+1), + * + * log(x) = z + z**3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE 0.5, 2.0 150000 8.71e-20 2.75e-20 + * IEEE exp(+-10000) 100000 5.39e-20 2.34e-20 + * + * In the tests over the interval exp(+-10000), the logarithms + * of the random arguments were uniformly distributed over + * [-10000, +10000]. + * + * ERROR MESSAGES: + * + * log singularity: x = 0; returns -INFINITY + * log domain: x < 0; returns NAN + */ + +#include + +#include "math_private.h" + +/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 2.32e-20 + */ +static long double P[] = { + 4.5270000862445199635215E-5L, + 4.9854102823193375972212E-1L, + 6.5787325942061044846969E0L, + 2.9911919328553073277375E1L, + 6.0949667980987787057556E1L, + 5.7112963590585538103336E1L, + 2.0039553499201281259648E1L, +}; +static long double Q[] = { +/* 1.0000000000000000000000E0,*/ + 1.5062909083469192043167E1L, + 8.3047565967967209469434E1L, + 2.2176239823732856465394E2L, + 3.0909872225312059774938E2L, + 2.1642788614495947685003E2L, + 6.0118660497603843919306E1L, +}; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 6.16e-22 + */ + +static long double R[4] = { + 1.9757429581415468984296E-3L, +-7.1990767473014147232598E-1L, + 1.0777257190312272158094E1L, +-3.5717684488096787370998E1L, +}; +static long double S[4] = { +/* 1.00000000000000000000E0L,*/ +-2.6201045551331104417768E1L, + 1.9361891836232102174846E2L, +-4.2861221385716144629696E2L, +}; +static const long double C1 = 6.9314575195312500000000E-1L; +static const long double C2 = 1.4286068203094172321215E-6L; + +#define SQRTH 0.70710678118654752440L + +long double +logl(long double x) +{ +long double y, z; +int e; + +if( isnan(x) ) + return(x); +if( x == INFINITY ) + return(x); +/* Test for domain */ +if( x <= 0.0L ) + { + if( x == 0.0L ) + return( -INFINITY ); + else + return( NAN ); + } + +/* separate mantissa from exponent */ + +/* Note, frexp is used so that denormal numbers + * will be handled properly. + */ +x = frexpl( x, &e ); + +/* logarithm using log(x) = z + z**3 P(z)/Q(z), + * where z = 2(x-1)/x+1) + */ +if( (e > 2) || (e < -2) ) +{ +if( x < SQRTH ) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } +else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } +x = z / y; +z = x*x; +z = x * ( z * __polevll( z, R, 3 ) / __p1evll( z, S, 3 ) ); +z = z + e * C2; +z = z + x; +z = z + e * C1; +return( z ); +} + + +/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */ + +if( x < SQRTH ) + { + e -= 1; + x = ldexpl( x, 1 ) - 1.0L; /* 2x - 1 */ + } +else + { + x = x - 1.0L; + } +z = x*x; +y = x * ( z * __polevll( x, P, 6 ) / __p1evll( x, Q, 6 ) ); +y = y + e * C2; +z = y - ldexpl( z, -1 ); /* y - 0.5 * z */ +/* Note, the sum of above terms does not exceed x/4, + * so it contributes at most about 1/4 lsb to the error. + */ +z = z + x; +z = z + e * C1; /* This sum has an error of 1/2 lsb. */ +return( z ); +} diff --git a/openlibm/ld80/e_powl.c b/openlibm/ld80/e_powl.c new file mode 100644 index 0000000000..dcb2b455ec --- /dev/null +++ b/openlibm/ld80/e_powl.c @@ -0,0 +1,615 @@ +/* $OpenBSD: e_powl.c,v 1.5 2013/11/12 20:35:19 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* powl.c + * + * Power function, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, z, powl(); + * + * z = powl( x, y ); + * + * + * + * DESCRIPTION: + * + * Computes x raised to the yth power. Analytically, + * + * x**y = exp( y log(x) ). + * + * Following Cody and Waite, this program uses a lookup table + * of 2**-i/32 and pseudo extended precision arithmetic to + * obtain several extra bits of accuracy in both the logarithm + * and the exponential. + * + * + * + * ACCURACY: + * + * The relative error of pow(x,y) can be estimated + * by y dl ln(2), where dl is the absolute error of + * the internally computed base 2 logarithm. At the ends + * of the approximation interval the logarithm equal 1/32 + * and its relative error is about 1 lsb = 1.1e-19. Hence + * the predicted relative error in the result is 2.3e-21 y . + * + * Relative error: + * arithmetic domain # trials peak rms + * + * IEEE +-1000 40000 2.8e-18 3.7e-19 + * .001 < x < 1000, with log(x) uniformly distributed. + * -1000 < y < 1000, y uniformly distributed. + * + * IEEE 0,8700 60000 6.5e-18 1.0e-18 + * 0.99 < x < 1.01, 0 < y < 8700, uniformly distributed. + * + * + * ERROR MESSAGES: + * + * message condition value returned + * pow overflow x**y > MAXNUM INFINITY + * pow underflow x**y < 1/MAXNUM 0.0 + * pow domain x<0 and y noninteger 0.0 + * + */ + +#include +#include + +#include "math_private.h" + +/* Table size */ +#define NXT 32 +/* log2(Table size) */ +#define LNXT 5 + +/* log(1+x) = x - .5x^2 + x^3 * P(z)/Q(z) + * on the domain 2^(-1/32) - 1 <= x <= 2^(1/32) - 1 + */ +static long double P[] = { + 8.3319510773868690346226E-4L, + 4.9000050881978028599627E-1L, + 1.7500123722550302671919E0L, + 1.4000100839971580279335E0L, +}; +static long double Q[] = { +/* 1.0000000000000000000000E0L,*/ + 5.2500282295834889175431E0L, + 8.4000598057587009834666E0L, + 4.2000302519914740834728E0L, +}; +/* A[i] = 2^(-i/32), rounded to IEEE long double precision. + * If i is even, A[i] + B[i/2] gives additional accuracy. + */ +static long double A[33] = { + 1.0000000000000000000000E0L, + 9.7857206208770013448287E-1L, + 9.5760328069857364691013E-1L, + 9.3708381705514995065011E-1L, + 9.1700404320467123175367E-1L, + 8.9735453750155359320742E-1L, + 8.7812608018664974155474E-1L, + 8.5930964906123895780165E-1L, + 8.4089641525371454301892E-1L, + 8.2287773907698242225554E-1L, + 8.0524516597462715409607E-1L, + 7.8799042255394324325455E-1L, + 7.7110541270397041179298E-1L, + 7.5458221379671136985669E-1L, + 7.3841307296974965571198E-1L, + 7.2259040348852331001267E-1L, + 7.0710678118654752438189E-1L, + 6.9195494098191597746178E-1L, + 6.7712777346844636413344E-1L, + 6.6261832157987064729696E-1L, + 6.4841977732550483296079E-1L, + 6.3452547859586661129850E-1L, + 6.2092890603674202431705E-1L, + 6.0762367999023443907803E-1L, + 5.9460355750136053334378E-1L, + 5.8186242938878875689693E-1L, + 5.6939431737834582684856E-1L, + 5.5719337129794626814472E-1L, + 5.4525386633262882960438E-1L, + 5.3357020033841180906486E-1L, + 5.2213689121370692017331E-1L, + 5.1094857432705833910408E-1L, + 5.0000000000000000000000E-1L, +}; +static long double B[17] = { + 0.0000000000000000000000E0L, + 2.6176170809902549338711E-20L, +-1.0126791927256478897086E-20L, + 1.3438228172316276937655E-21L, + 1.2207982955417546912101E-20L, +-6.3084814358060867200133E-21L, + 1.3164426894366316434230E-20L, +-1.8527916071632873716786E-20L, + 1.8950325588932570796551E-20L, + 1.5564775779538780478155E-20L, + 6.0859793637556860974380E-21L, +-2.0208749253662532228949E-20L, + 1.4966292219224761844552E-20L, + 3.3540909728056476875639E-21L, +-8.6987564101742849540743E-22L, +-1.2327176863327626135542E-20L, + 0.0000000000000000000000E0L, +}; + +/* 2^x = 1 + x P(x), + * on the interval -1/32 <= x <= 0 + */ +static long double R[] = { + 1.5089970579127659901157E-5L, + 1.5402715328927013076125E-4L, + 1.3333556028915671091390E-3L, + 9.6181291046036762031786E-3L, + 5.5504108664798463044015E-2L, + 2.4022650695910062854352E-1L, + 6.9314718055994530931447E-1L, +}; + +#define douba(k) A[k] +#define doubb(k) B[k] +#define MEXP (NXT*16384.0L) +/* The following if denormal numbers are supported, else -MEXP: */ +#define MNEXP (-NXT*(16384.0L+64.0L)) +/* log2(e) - 1 */ +#define LOG2EA 0.44269504088896340735992L + +#define F W +#define Fa Wa +#define Fb Wb +#define G W +#define Ga Wa +#define Gb u +#define H W +#define Ha Wb +#define Hb Wb + +static const long double MAXLOGL = 1.1356523406294143949492E4L; +static const long double MINLOGL = -1.13994985314888605586758E4L; +static const long double LOGE2L = 6.9314718055994530941723E-1L; +static volatile long double z; +static long double w, W, Wa, Wb, ya, yb, u; +static const long double huge = 0x1p10000L; +#if 0 /* XXX Prevent gcc from erroneously constant folding this. */ +static const long double twom10000 = 0x1p-10000L; +#else +static volatile long double twom10000 = 0x1p-10000L; +#endif + +static long double reducl( long double ); +static long double powil ( long double, int ); + +long double +powl(long double x, long double y) +{ +/* double F, Fa, Fb, G, Ga, Gb, H, Ha, Hb */ +int i, nflg, iyflg, yoddint; +long e; + +if( y == 0.0L ) + return( 1.0L ); + +if( x == 1.0L ) + return( 1.0L ); + +if( isnan(x) ) + return( x ); +if( isnan(y) ) + return( y ); + +if( y == 1.0L ) + return( x ); + +if( !isfinite(y) && x == -1.0L ) + return( 1.0L ); + +if( y >= LDBL_MAX ) + { + if( x > 1.0L ) + return( INFINITY ); + if( x > 0.0L && x < 1.0L ) + return( 0.0L ); + if( x < -1.0L ) + return( INFINITY ); + if( x > -1.0L && x < 0.0L ) + return( 0.0L ); + } +if( y <= -LDBL_MAX ) + { + if( x > 1.0L ) + return( 0.0L ); + if( x > 0.0L && x < 1.0L ) + return( INFINITY ); + if( x < -1.0L ) + return( 0.0L ); + if( x > -1.0L && x < 0.0L ) + return( INFINITY ); + } +if( x >= LDBL_MAX ) + { + if( y > 0.0L ) + return( INFINITY ); + return( 0.0L ); + } + +w = floorl(y); +/* Set iyflg to 1 if y is an integer. */ +iyflg = 0; +if( w == y ) + iyflg = 1; + +/* Test for odd integer y. */ +yoddint = 0; +if( iyflg ) + { + ya = fabsl(y); + ya = floorl(0.5L * ya); + yb = 0.5L * fabsl(w); + if( ya != yb ) + yoddint = 1; + } + +if( x <= -LDBL_MAX ) + { + if( y > 0.0L ) + { + if( yoddint ) + return( -INFINITY ); + return( INFINITY ); + } + if( y < 0.0L ) + { + if( yoddint ) + return( -0.0L ); + return( 0.0 ); + } + } + + +nflg = 0; /* flag = 1 if x<0 raised to integer power */ +if( x <= 0.0L ) + { + if( x == 0.0L ) + { + if( y < 0.0 ) + { + if( signbit(x) && yoddint ) + return( -INFINITY ); + return( INFINITY ); + } + if( y > 0.0 ) + { + if( signbit(x) && yoddint ) + return( -0.0L ); + return( 0.0 ); + } + if( y == 0.0L ) + return( 1.0L ); /* 0**0 */ + else + return( 0.0L ); /* 0**y */ + } + else + { + if( iyflg == 0 ) + return (x - x) / (x - x); /* (x<0)**(non-int) is NaN */ + nflg = 1; + } + } + +/* Integer power of an integer. */ + +if( iyflg ) + { + i = w; + w = floorl(x); + if( (w == x) && (fabsl(y) < 32768.0) ) + { + w = powil( x, (int) y ); + return( w ); + } + } + + +if( nflg ) + x = fabsl(x); + +/* separate significand from exponent */ +x = frexpl( x, &i ); +e = i; + +/* find significand in antilog table A[] */ +i = 1; +if( x <= douba(17) ) + i = 17; +if( x <= douba(i+8) ) + i += 8; +if( x <= douba(i+4) ) + i += 4; +if( x <= douba(i+2) ) + i += 2; +if( x >= douba(1) ) + i = -1; +i += 1; + + +/* Find (x - A[i])/A[i] + * in order to compute log(x/A[i]): + * + * log(x) = log( a x/a ) = log(a) + log(x/a) + * + * log(x/a) = log(1+v), v = x/a - 1 = (x-a)/a + */ +x -= douba(i); +x -= doubb(i/2); +x /= douba(i); + + +/* rational approximation for log(1+v): + * + * log(1+v) = v - v**2/2 + v**3 P(v) / Q(v) + */ +z = x*x; +w = x * ( z * __polevll( x, P, 3 ) / __p1evll( x, Q, 3 ) ); +w = w - ldexpl( z, -1 ); /* w - 0.5 * z */ + +/* Convert to base 2 logarithm: + * multiply by log2(e) = 1 + LOG2EA + */ +z = LOG2EA * w; +z += w; +z += LOG2EA * x; +z += x; + +/* Compute exponent term of the base 2 logarithm. */ +w = -i; +w = ldexpl( w, -LNXT ); /* divide by NXT */ +w += e; +/* Now base 2 log of x is w + z. */ + +/* Multiply base 2 log by y, in extended precision. */ + +/* separate y into large part ya + * and small part yb less than 1/NXT + */ +ya = reducl(y); +yb = y - ya; + +/* (w+z)(ya+yb) + * = w*ya + w*yb + z*y + */ +F = z * y + w * yb; +Fa = reducl(F); +Fb = F - Fa; + +G = Fa + w * ya; +Ga = reducl(G); +Gb = G - Ga; + +H = Fb + Gb; +Ha = reducl(H); +w = ldexpl( Ga+Ha, LNXT ); + +/* Test the power of 2 for overflow */ +if( w > MEXP ) + return (huge * huge); /* overflow */ + +if( w < MNEXP ) + return (twom10000 * twom10000); /* underflow */ + +e = w; +Hb = H - Ha; + +if( Hb > 0.0L ) + { + e += 1; + Hb -= (1.0L/NXT); /*0.0625L;*/ + } + +/* Now the product y * log2(x) = Hb + e/NXT. + * + * Compute base 2 exponential of Hb, + * where -0.0625 <= Hb <= 0. + */ +z = Hb * __polevll( Hb, R, 6 ); /* z = 2**Hb - 1 */ + +/* Express e/NXT as an integer plus a negative number of (1/NXT)ths. + * Find lookup table entry for the fractional power of 2. + */ +if( e < 0 ) + i = 0; +else + i = 1; +i = e/NXT + i; +e = NXT*i - e; +w = douba( e ); +z = w * z; /* 2**-e * ( 1 + (2**Hb-1) ) */ +z = z + w; +z = ldexpl( z, i ); /* multiply by integer power of 2 */ + +if( nflg ) + { +/* For negative x, + * find out if the integer exponent + * is odd or even. + */ + w = ldexpl( y, -1 ); + w = floorl(w); + w = ldexpl( w, 1 ); + if( w != y ) + z = -z; /* odd exponent */ + } + +return( z ); +} + + +/* Find a multiple of 1/NXT that is within 1/NXT of x. */ +static long double +reducl(long double x) +{ +long double t; + +t = ldexpl( x, LNXT ); +t = floorl( t ); +t = ldexpl( t, -LNXT ); +return(t); +} + +/* powil.c + * + * Real raised to integer power, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, powil(); + * int n; + * + * y = powil( x, n ); + * + * + * + * DESCRIPTION: + * + * Returns argument x raised to the nth power. + * The routine efficiently decomposes n as a sum of powers of + * two. The desired power is a product of two-to-the-kth + * powers of x. Thus to compute the 32767 power of x requires + * 28 multiplications instead of 32767 multiplications. + * + * + * + * ACCURACY: + * + * + * Relative error: + * arithmetic x domain n domain # trials peak rms + * IEEE .001,1000 -1022,1023 50000 4.3e-17 7.8e-18 + * IEEE 1,2 -1022,1023 20000 3.9e-17 7.6e-18 + * IEEE .99,1.01 0,8700 10000 3.6e-16 7.2e-17 + * + * Returns MAXNUM on overflow, zero on underflow. + * + */ + +static long double +powil(long double x, int nn) +{ +long double ww, y; +long double s; +int n, e, sign, asign, lx; + +if( x == 0.0L ) + { + if( nn == 0 ) + return( 1.0L ); + else if( nn < 0 ) + return( LDBL_MAX ); + else + return( 0.0L ); + } + +if( nn == 0 ) + return( 1.0L ); + + +if( x < 0.0L ) + { + asign = -1; + x = -x; + } +else + asign = 0; + + +if( nn < 0 ) + { + sign = -1; + n = -nn; + } +else + { + sign = 1; + n = nn; + } + +/* Overflow detection */ + +/* Calculate approximate logarithm of answer */ +s = x; +s = frexpl( s, &lx ); +e = (lx - 1)*n; +if( (e == 0) || (e > 64) || (e < -64) ) + { + s = (s - 7.0710678118654752e-1L) / (s + 7.0710678118654752e-1L); + s = (2.9142135623730950L * s - 0.5L + lx) * nn * LOGE2L; + } +else + { + s = LOGE2L * e; + } + +if( s > MAXLOGL ) + return (huge * huge); /* overflow */ + +if( s < MINLOGL ) + return (twom10000 * twom10000); /* underflow */ +/* Handle tiny denormal answer, but with less accuracy + * since roundoff error in 1.0/x will be amplified. + * The precise demarcation should be the gradual underflow threshold. + */ +if( s < (-MAXLOGL+2.0L) ) + { + x = 1.0L/x; + sign = -sign; + } + +/* First bit of the power */ +if( n & 1 ) + y = x; + +else + { + y = 1.0L; + asign = 0; + } + +ww = x; +n >>= 1; +while( n ) + { + ww = ww * ww; /* arg to the 2-to-the-kth power */ + if( n & 1 ) /* if that bit is set, then include in product */ + y *= ww; + n >>= 1; + } + +if( asign ) + y = -y; /* odd power of negative number */ +if( sign < 0 ) + y = 1.0L/y; +return(y); +} diff --git a/openlibm/ld80/e_rem_pio2l.h b/openlibm/ld80/e_rem_pio2l.h new file mode 100644 index 0000000000..c44ad48ef7 --- /dev/null +++ b/openlibm/ld80/e_rem_pio2l.h @@ -0,0 +1,152 @@ +/* From: @(#)e_rem_pio2.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * Optimized by Bruce D. Evans. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld80/e_rem_pio2l.h,v 1.3 2011/06/18 13:56:33 benl Exp $"); + +/* ld80 version of __ieee754_rem_pio2l(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2() + */ + +#include +#include + +#include "math_private.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +/* + * invpio2: 64 bits of 2/pi + * pio2_1: first 39 bits of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 39 bits of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 39 bits of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const double +zero = 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +pio2_1 = 1.57079632679597125389e+00, /* 0x3FF921FB, 0x54444000 */ +pio2_2 = -1.07463465549783099519e-12, /* -0x12e7b967674000.0p-92 */ +pio2_3 = 6.36831716351370313614e-25; /* 0x18a2e037074000.0p-133 */ + +#if defined(__amd64__) || defined(__i386__) +/* Long double constants are slow on these arches, and broken on i386. */ +static const volatile double +invpio2hi = 6.3661977236758138e-01, /* 0x145f306dc9c883.0p-53 */ +invpio2lo = -3.9356538861223811e-17, /* -0x16b00000000000.0p-107 */ +pio2_1thi = -1.0746346554971943e-12, /* -0x12e7b9676733af.0p-92 */ +pio2_1tlo = 8.8451028997905949e-29, /* 0x1c080000000000.0p-146 */ +pio2_2thi = 6.3683171635109499e-25, /* 0x18a2e03707344a.0p-133 */ +pio2_2tlo = 2.3183081793789774e-41, /* 0x10280000000000.0p-187 */ +pio2_3thi = -2.7529965190440717e-37, /* -0x176b7ed8fbbacc.0p-174 */ +pio2_3tlo = -4.2006647512740502e-54; /* -0x19c00000000000.0p-230 */ +#define invpio2 ((long double)invpio2hi + invpio2lo) +#define pio2_1t ((long double)pio2_1thi + pio2_1tlo) +#define pio2_2t ((long double)pio2_2thi + pio2_2tlo) +#define pio2_3t ((long double)pio2_3thi + pio2_3tlo) +#else +static const long double +invpio2 = 6.36619772367581343076e-01L, /* 0xa2f9836e4e44152a.0p-64 */ +pio2_1t = -1.07463465549719416346e-12L, /* -0x973dcb3b399d747f.0p-103 */ +pio2_2t = 6.36831716351095013979e-25L, /* 0xc51701b839a25205.0p-144 */ +pio2_3t = -2.75299651904407171810e-37L; /* -0xbb5bf6c7ddd660ce.0p-185 */ +#endif + +//VBS +//static inline __always_inline int +//__ieee754_rem_pio2l(long double x, long double *y) + +static inline int +__ieee754_rem_pio2l(long double x, long double *y) +{ + union IEEEl2bits u,u1; + long double z,w,t,r,fn; + double tx[3],ty[2]; + int e0,ex,i,j,nx,n; + int16_t expsign; + + u.e = x; + expsign = u.xbits.expsign; + ex = expsign & 0x7fff; + if (ex < BIAS + 25 || (ex == BIAS + 25 && u.bits.manh < 0xc90fdaa2)) { + /* |x| ~< 2^25*(pi/2), medium size */ + /* Use a specialized rint() to get fn. Assume round-to-nearest. */ + fn = x*invpio2+0x1.8p63; + fn = fn-0x1.8p63; +#ifdef HAVE_EFFICIENT_IRINT + n = irint(fn); +#else + n = fn; +#endif + r = x-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 102 bit */ + { + union IEEEl2bits u2; + int ex1; + j = ex; + y[0] = r-w; + u2.e = y[0]; + ex1 = u2.xbits.expsign & 0x7fff; + i = j-ex1; + if(i>22) { /* 2nd iteration needed, good to 141 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + u2.e = y[0]; + ex1 = u2.xbits.expsign & 0x7fff; + i = j-ex1; + if(i>61) { /* 3rd iteration need, 180 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + return n; + } + /* + * all other (large) arguments + */ + if(ex==0x7fff) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-23) */ + u1.e = x; + e0 = ex - BIAS - 23; /* e0 = ilogb(|x|)-23; */ + u1.xbits.expsign = ex - e0; + z = u1.e; + for(i=0;i<2;i++) { + tx[i] = (double)((int32_t)(z)); + z = (z-tx[i])*two24; + } + tx[2] = z; + nx = 3; + while(tx[nx-1]==zero) nx--; /* skip zero term */ + n = __kernel_rem_pio2(tx,ty,e0,nx,2); + r = (long double)ty[0] + ty[1]; + w = ty[1] - (r - ty[0]); + if(expsign<0) {y[0] = -r; y[1] = -w; return -n;} + y[0] = r; y[1] = w; return n; +} diff --git a/openlibm/ld80/e_sinhl.c b/openlibm/ld80/e_sinhl.c new file mode 100644 index 0000000000..cb452041f9 --- /dev/null +++ b/openlibm/ld80/e_sinhl.c @@ -0,0 +1,76 @@ +/* @(#)e_sinh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* sinhl(x) + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinhl(-x) = -sinhl(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 25 : sinhl(x) := --------------, E=expm1l(x) + * 2 + * + * 25 <= x <= lnovft : sinhl(x) := expl(x)/2 + * lnovft <= x <= ln2ovft: sinhl(x) := expl(x/2)/2 * expl(x/2) + * ln2ovft < x : sinhl(x) := x*shuge (overflow) + * + * Special cases: + * sinhl(x) is |x| if x is +INF, -INF, or NaN. + * only sinhl(0)=0 is exact for finite x. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0, shuge = 1.0e4931L; + +long double +sinhl(long double x) +{ + long double t,w,h; + u_int32_t jx,ix,i0,i1; + + /* Words of |x|. */ + GET_LDOUBLE_WORDS(jx,i0,i1,x); + ix = jx&0x7fff; + + /* x is INF or NaN */ + if(ix==0x7fff) return x+x; + + h = 0.5; + if (jx & 0x8000) h = -h; + /* |x| in [0,25], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x4003 || (ix == 0x4003 && i0 <= 0xc8000000)) { /* |x|<25 */ + if (ix<0x3fdf) /* |x|<2**-32 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = expm1l(fabsl(x)); + if(ix<0x3fff) return h*(2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [25, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix < 0x400c || (ix == 0x400c && i0 < 0xb17217f7)) + return h*expl(fabsl(x)); + + /* |x| in [log(maxdouble), overflowthreshold] */ + if (ix<0x400c || (ix == 0x400c && (i0 < 0xb174ddc0 + || (i0 == 0xb174ddc0 + && i1 <= 0x31aec0ea)))) { + w = expl(0.5*fabsl(x)); + t = h*w; + return t*w; + } + + /* |x| > overflowthreshold, sinhl(x) overflow */ + return x*shuge; +} diff --git a/openlibm/ld80/e_tgammal.c b/openlibm/ld80/e_tgammal.c new file mode 100644 index 0000000000..036061c0b8 --- /dev/null +++ b/openlibm/ld80/e_tgammal.c @@ -0,0 +1,313 @@ +/* $OpenBSD: e_tgammal.c,v 1.4 2013/11/12 20:35:19 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* tgammal.c + * + * Gamma function + * + * + * + * SYNOPSIS: + * + * long double x, y, tgammal(); + * + * y = tgammal( x ); + * + * + * + * DESCRIPTION: + * + * Returns gamma function of the argument. The result is correctly + * signed. This variable is also filled in by the logarithmic gamma + * function lgamma(). + * + * Arguments |x| <= 13 are reduced by recurrence and the function + * approximated by a rational function of degree 7/8 in the + * interval (2,3). Large arguments are handled by Stirling's + * formula. Large negative arguments are made positive using + * a reflection formula. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -40,+40 10000 3.6e-19 7.9e-20 + * IEEE -1755,+1755 10000 4.8e-18 6.5e-19 + * + * Accuracy for large arguments is dominated by error in powl(). + * + */ + +#include +#include + +#include "math_private.h" + +/* +tgamma(x+2) = tgamma(x+2) P(x)/Q(x) +0 <= x <= 1 +Relative error +n=7, d=8 +Peak error = 1.83e-20 +Relative error spread = 8.4e-23 +*/ + +static long double P[8] = { + 4.212760487471622013093E-5L, + 4.542931960608009155600E-4L, + 4.092666828394035500949E-3L, + 2.385363243461108252554E-2L, + 1.113062816019361559013E-1L, + 3.629515436640239168939E-1L, + 8.378004301573126728826E-1L, + 1.000000000000000000009E0L, +}; +static long double Q[9] = { +-1.397148517476170440917E-5L, + 2.346584059160635244282E-4L, +-1.237799246653152231188E-3L, +-7.955933682494738320586E-4L, + 2.773706565840072979165E-2L, +-4.633887671244534213831E-2L, +-2.243510905670329164562E-1L, + 4.150160950588455434583E-1L, + 9.999999999999999999908E-1L, +}; + +/* +static long double P[] = { +-3.01525602666895735709e0L, +-3.25157411956062339893e1L, +-2.92929976820724030353e2L, +-1.70730828800510297666e3L, +-7.96667499622741999770e3L, +-2.59780216007146401957e4L, +-5.99650230220855581642e4L, +-7.15743521530849602425e4L +}; +static long double Q[] = { + 1.00000000000000000000e0L, +-1.67955233807178858919e1L, + 8.85946791747759881659e1L, + 5.69440799097468430177e1L, +-1.98526250512761318471e3L, + 3.31667508019495079814e3L, + 1.60577839621734713377e4L, +-2.97045081369399940529e4L, +-7.15743521530849602412e4L +}; +*/ +#define MAXGAML 1755.455L +/*static const long double LOGPI = 1.14472988584940017414L;*/ + +/* Stirling's formula for the gamma function +tgamma(x) = sqrt(2 pi) x^(x-.5) exp(-x) (1 + 1/x P(1/x)) +z(x) = x +13 <= x <= 1024 +Relative error +n=8, d=0 +Peak error = 9.44e-21 +Relative error spread = 8.8e-4 +*/ + +static long double STIR[9] = { + 7.147391378143610789273E-4L, +-2.363848809501759061727E-5L, +-5.950237554056330156018E-4L, + 6.989332260623193171870E-5L, + 7.840334842744753003862E-4L, +-2.294719747873185405699E-4L, +-2.681327161876304418288E-3L, + 3.472222222230075327854E-3L, + 8.333333333333331800504E-2L, +}; + +#define MAXSTIR 1024.0L +static const long double SQTPI = 2.50662827463100050242E0L; + +/* 1/tgamma(x) = z P(z) + * z(x) = 1/x + * 0 < x < 0.03125 + * Peak relative error 4.2e-23 + */ + +static long double S[9] = { +-1.193945051381510095614E-3L, + 7.220599478036909672331E-3L, +-9.622023360406271645744E-3L, +-4.219773360705915470089E-2L, + 1.665386113720805206758E-1L, +-4.200263503403344054473E-2L, +-6.558780715202540684668E-1L, + 5.772156649015328608253E-1L, + 1.000000000000000000000E0L, +}; + +/* 1/tgamma(-x) = z P(z) + * z(x) = 1/x + * 0 < x < 0.03125 + * Peak relative error 5.16e-23 + * Relative error spread = 2.5e-24 + */ + +static long double SN[9] = { + 1.133374167243894382010E-3L, + 7.220837261893170325704E-3L, + 9.621911155035976733706E-3L, +-4.219773343731191721664E-2L, +-1.665386113944413519335E-1L, +-4.200263503402112910504E-2L, + 6.558780715202536547116E-1L, + 5.772156649015328608727E-1L, +-1.000000000000000000000E0L, +}; + +static const long double PIL = 3.1415926535897932384626L; + +static long double stirf ( long double ); + +/* Gamma function computed by Stirling's formula. + */ +static long double stirf(long double x) +{ +long double y, w, v; + +w = 1.0L/x; +/* For large x, use rational coefficients from the analytical expansion. */ +if( x > 1024.0L ) + w = (((((6.97281375836585777429E-5L * w + + 7.84039221720066627474E-4L) * w + - 2.29472093621399176955E-4L) * w + - 2.68132716049382716049E-3L) * w + + 3.47222222222222222222E-3L) * w + + 8.33333333333333333333E-2L) * w + + 1.0L; +else + w = 1.0L + w * __polevll( w, STIR, 8 ); +y = expl(x); +if( x > MAXSTIR ) + { /* Avoid overflow in pow() */ + v = powl( x, 0.5L * x - 0.25L ); + y = v * (v / y); + } +else + { + y = powl( x, x - 0.5L ) / y; + } +y = SQTPI * y * w; +return( y ); +} + +long double +tgammal(long double x) +{ +long double p, q, z; +int i; + +if( isnan(x) ) + return(NAN); +if(x == INFINITY) + return(INFINITY); +if(x == -INFINITY) + return(x - x); +if( x == 0.0L ) + return( 1.0L / x ); +q = fabsl(x); + +if( q > 13.0L ) + { + int sign = 1; + if( q > MAXGAML ) + goto goverf; + if( x < 0.0L ) + { + p = floorl(q); + if( p == q ) + return (x - x) / (x - x); + i = p; + if( (i & 1) == 0 ) + sign = -1; + z = q - p; + if( z > 0.5L ) + { + p += 1.0L; + z = q - p; + } + z = q * sinl( PIL * z ); + z = fabsl(z) * stirf(q); + if( z <= PIL/LDBL_MAX ) + { +goverf: + return( sign * INFINITY); + } + z = PIL/z; + } + else + { + z = stirf(x); + } + return( sign * z ); + } + +z = 1.0L; +while( x >= 3.0L ) + { + x -= 1.0L; + z *= x; + } + +while( x < -0.03125L ) + { + z /= x; + x += 1.0L; + } + +if( x <= 0.03125L ) + goto small; + +while( x < 2.0L ) + { + z /= x; + x += 1.0L; + } + +if( x == 2.0L ) + return(z); + +x -= 2.0L; +p = __polevll( x, P, 7 ); +q = __polevll( x, Q, 8 ); +z = z * p / q; +return z; + +small: +if( x == 0.0L ) + return (x - x) / (x - x); +else + { + if( x < 0.0L ) + { + x = -x; + q = z / (x * __polevll( x, SN, 8 )); + } + else + q = z / (x * __polevll( x, S, 8 )); + } +return q; +} diff --git a/openlibm/ld80/invtrig.c b/openlibm/ld80/invtrig.c new file mode 100644 index 0000000000..15692ada47 --- /dev/null +++ b/openlibm/ld80/invtrig.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld80/invtrig.c,v 1.1 2008/07/31 22:41:26 das Exp $"); + +#include "ld80/invtrig.h" + +/* + * asinl() and acosl() + */ +const long double +pS0 = 1.66666666666666666631e-01L, +pS1 = -4.16313987993683104320e-01L, +pS2 = 3.69068046323246813704e-01L, +pS3 = -1.36213932016738603108e-01L, +pS4 = 1.78324189708471965733e-02L, +pS5 = -2.19216428382605211588e-04L, +pS6 = -7.10526623669075243183e-06L, +qS1 = -2.94788392796209867269e+00L, +qS2 = 3.27309890266528636716e+00L, +qS3 = -1.68285799854822427013e+00L, +qS4 = 3.90699412641738801874e-01L, +qS5 = -3.14365703596053263322e-02L; + +/* + * atanl() + */ +const long double atanhi[] = { + 4.63647609000806116202e-01L, + 7.85398163397448309628e-01L, + 9.82793723247329067960e-01L, + 1.57079632679489661926e+00L, +}; + +const long double atanlo[] = { + 1.18469937025062860669e-20L, + -1.25413940316708300586e-20L, + 2.55232234165405176172e-20L, + -2.50827880633416601173e-20L, +}; + +const long double aT[] = { + 3.33333333333333333017e-01L, + -1.99999999999999632011e-01L, + 1.42857142857046531280e-01L, + -1.11111111100562372733e-01L, + 9.09090902935647302252e-02L, + -7.69230552476207730353e-02L, + 6.66661718042406260546e-02L, + -5.88158892835030888692e-02L, + 5.25499891539726639379e-02L, + -4.70119845393155721494e-02L, + 4.03539201366454414072e-02L, + -2.91303858419364158725e-02L, + 1.24822046299269234080e-02L, +}; + +const long double pi_lo = -5.01655761266833202345e-20L; diff --git a/openlibm/ld80/invtrig.h b/openlibm/ld80/invtrig.h new file mode 100644 index 0000000000..3d543e9441 --- /dev/null +++ b/openlibm/ld80/invtrig.h @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/ld80/invtrig.h,v 1.2 2008/08/02 03:56:22 das Exp $ + */ + +#include + +#include + +#define BIAS (LDBL_MAX_EXP - 1) +#define MANH_SIZE LDBL_MANH_SIZE + +/* Approximation thresholds. */ +#define ASIN_LINEAR (BIAS - 32) /* 2**-32 */ +#define ACOS_CONST (BIAS - 65) /* 2**-65 */ +#define ATAN_CONST (BIAS + 65) /* 2**65 */ +#define ATAN_LINEAR (BIAS - 32) /* 2**-32 */ + +/* 0.95 */ +#define THRESH ((0xe666666666666666ULL>>(64-(MANH_SIZE-1)))|LDBL_NBIT) + +/* Constants shared by the long double inverse trig functions. */ +#define pS0 _ItL_pS0 +#define pS1 _ItL_pS1 +#define pS2 _ItL_pS2 +#define pS3 _ItL_pS3 +#define pS4 _ItL_pS4 +#define pS5 _ItL_pS5 +#define pS6 _ItL_pS6 +#define qS1 _ItL_qS1 +#define qS2 _ItL_qS2 +#define qS3 _ItL_qS3 +#define qS4 _ItL_qS4 +#define qS5 _ItL_qS5 +#define atanhi _ItL_atanhi +#define atanlo _ItL_atanlo +#define aT _ItL_aT +#define pi_lo _ItL_pi_lo + +#define pio2_hi atanhi[3] +#define pio2_lo atanlo[3] +#define pio4_hi atanhi[1] + +#ifdef STRUCT_DECLS +typedef struct longdouble { + uint64_t mant; + uint16_t expsign; +} LONGDOUBLE; +#else +typedef long double LONGDOUBLE; +#endif + +extern const LONGDOUBLE pS0, pS1, pS2, pS3, pS4, pS5, pS6; +extern const LONGDOUBLE qS1, qS2, qS3, qS4, qS5; +extern const LONGDOUBLE atanhi[], atanlo[], aT[]; +extern const LONGDOUBLE pi_lo; + +#ifndef STRUCT_DECLS + +static inline long double +P(long double x) +{ + + return (x * (pS0 + x * (pS1 + x * (pS2 + x * (pS3 + x * \ + (pS4 + x * (pS5 + x * pS6))))))); +} + +static inline long double +Q(long double x) +{ + + return (1.0 + x * (qS1 + x * (qS2 + x * (qS3 + x * (qS4 + x * qS5))))); +} + +static inline long double +T_even(long double x) +{ + + return (aT[0] + x * (aT[2] + x * (aT[4] + x * (aT[6] + x * \ + (aT[8] + x * (aT[10] + x * aT[12])))))); +} + +static inline long double +T_odd(long double x) +{ + + return (aT[1] + x * (aT[3] + x * (aT[5] + x * (aT[7] + x * \ + (aT[9] + x * aT[11]))))); +} + +#endif diff --git a/openlibm/ld80/k_cosl.c b/openlibm/ld80/k_cosl.c new file mode 100644 index 0000000000..403da9dd99 --- /dev/null +++ b/openlibm/ld80/k_cosl.c @@ -0,0 +1,78 @@ +/* From: @(#)k_cos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld80/k_cosl.c,v 1.1 2008/02/17 07:32:14 das Exp $"); + +/* + * ld80 version of k_cos.c. See ../src/k_cos.c for most comments. + */ + +#include "math_private.h" + +/* + * Domain [-0.7854, 0.7854], range ~[-2.43e-23, 2.425e-23]: + * |cos(x) - c(x)| < 2**-75.1 + * + * The coefficients of c(x) were generated by a pari-gp script using + * a Remez algorithm that searches for the best higher coefficients + * after rounding leading coefficients to a specified precision. + * + * Simpler methods like Chebyshev or basic Remez barely suffice for + * cos() in 64-bit precision, because we want the coefficient of x^2 + * to be precisely -0.5 so that multiplying by it is exact, and plain + * rounding of the coefficients of a good polynomial approximation only + * gives this up to about 64-bit precision. Plain rounding also gives + * a mediocre approximation for the coefficient of x^4, but a rounding + * error of 0.5 ulps for this coefficient would only contribute ~0.01 + * ulps to the final error, so this is unimportant. Rounding errors in + * higher coefficients are even less important. + * + * In fact, coefficients above the x^4 one only need to have 53-bit + * precision, and this is more efficient. We get this optimization + * almost for free from the complications needed to search for the best + * higher coefficients. + */ +static const double +one = 1.0; + +#if defined(__amd64__) || defined(__i386__) +/* Long double constants are slow on these arches, and broken on i386. */ +static const volatile double +C1hi = 0.041666666666666664, /* 0x15555555555555.0p-57 */ +C1lo = 2.2598839032744733e-18; /* 0x14d80000000000.0p-111 */ +#define C1 ((long double)C1hi + C1lo) +#else +static const long double +C1 = 0.0416666666666666666136L; /* 0xaaaaaaaaaaaaaa9b.0p-68 */ +#endif + +static const double +C2 = -0.0013888888888888874, /* -0x16c16c16c16c10.0p-62 */ +C3 = 0.000024801587301571716, /* 0x1a01a01a018e22.0p-68 */ +C4 = -0.00000027557319215507120, /* -0x127e4fb7602f22.0p-74 */ +C5 = 0.0000000020876754400407278, /* 0x11eed8caaeccf1.0p-81 */ +C6 = -1.1470297442401303e-11, /* -0x19393412bd1529.0p-89 */ +C7 = 4.7383039476436467e-14; /* 0x1aac9d9af5c43e.0p-97 */ + +long double +__kernel_cosl(long double x, long double y) +{ + long double hz,z,r,w; + + z = x*x; + r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*(C6+z*C7)))))); + hz = 0.5*z; + w = one-hz; + return w + (((one-w)-hz) + (z*r-x*y)); +} diff --git a/openlibm/ld80/k_sinl.c b/openlibm/ld80/k_sinl.c new file mode 100644 index 0000000000..792baf63f8 --- /dev/null +++ b/openlibm/ld80/k_sinl.c @@ -0,0 +1,62 @@ +/* From: @(#)k_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld80/k_sinl.c,v 1.1 2008/02/17 07:32:14 das Exp $"); + +/* + * ld80 version of k_sin.c. See ../src/k_sin.c for most comments. + */ + +#include "math_private.h" + +static const double +half = 0.5; + +/* + * Domain [-0.7854, 0.7854], range ~[-1.89e-22, 1.915e-22] + * |sin(x)/x - s(x)| < 2**-72.1 + * + * See ../ld80/k_cosl.c for more details about the polynomial. + */ +#if defined(__amd64__) || defined(__i386__) +/* Long double constants are slow on these arches, and broken on i386. */ +static const volatile double +S1hi = -0.16666666666666666, /* -0x15555555555555.0p-55 */ +S1lo = -9.2563760475949941e-18; /* -0x15580000000000.0p-109 */ +#define S1 ((long double)S1hi + S1lo) +#else +static const long double +S1 = -0.166666666666666666671L; /* -0xaaaaaaaaaaaaaaab.0p-66 */ +#endif + +static const double +S2 = 0.0083333333333333332, /* 0x11111111111111.0p-59 */ +S3 = -0.00019841269841269427, /* -0x1a01a01a019f81.0p-65 */ +S4 = 0.0000027557319223597490, /* 0x171de3a55560f7.0p-71 */ +S5 = -0.000000025052108218074604, /* -0x1ae64564f16cad.0p-78 */ +S6 = 1.6059006598854211e-10, /* 0x161242b90243b5.0p-85 */ +S7 = -7.6429779983024564e-13, /* -0x1ae42ebd1b2e00.0p-93 */ +S8 = 2.6174587166648325e-15; /* 0x179372ea0b3f64.0p-101 */ + +long double +__kernel_sinl(long double x, long double y, int iy) +{ + long double z,r,v; + + z = x*x; + v = z*x; + r = S2+z*(S3+z*(S4+z*(S5+z*(S6+z*(S7+z*S8))))); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} diff --git a/openlibm/ld80/k_tanl.c b/openlibm/ld80/k_tanl.c new file mode 100644 index 0000000000..691c952c15 --- /dev/null +++ b/openlibm/ld80/k_tanl.c @@ -0,0 +1,125 @@ +/* From: @(#)k_tan.c 1.5 04/04/22 SMI */ + +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2008 Steven G. Kargl, David Schultz, Bruce D. Evans. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld80/k_tanl.c,v 1.3 2008/02/18 15:39:52 bde Exp $"); + +/* + * ld80 version of k_tan.c. See ../src/k_tan.c for most comments. + */ + +#include + +#include "math_private.h" + +/* + * Domain [-0.67434, 0.67434], range ~[-2.25e-22, 1.921e-22] + * |tan(x)/x - t(x)| < 2**-71.9 + * + * See k_cosl.c for more details about the polynomial. + */ +#if defined(__amd64__) || defined(__i386__) +/* Long double constants are slow on these arches, and broken on i386. */ +static const volatile double +T3hi = 0.33333333333333331, /* 0x15555555555555.0p-54 */ +T3lo = 1.8350121769317163e-17, /* 0x15280000000000.0p-108 */ +T5hi = 0.13333333333333336, /* 0x11111111111112.0p-55 */ +T5lo = 1.3051083651294260e-17, /* 0x1e180000000000.0p-109 */ +T7hi = 0.053968253968250494, /* 0x1ba1ba1ba1b827.0p-57 */ +T7lo = 3.1509625637859973e-18, /* 0x1d100000000000.0p-111 */ +pio4_hi = 0.78539816339744828, /* 0x1921fb54442d18.0p-53 */ +pio4_lo = 3.0628711372715500e-17, /* 0x11a80000000000.0p-107 */ +pio4lo_hi = -1.2541394031670831e-20, /* -0x1d9cceba3f91f2.0p-119 */ +pio4lo_lo = 6.1493048227390915e-37; /* 0x1a280000000000.0p-173 */ +#define T3 ((long double)T3hi + T3lo) +#define T5 ((long double)T5hi + T5lo) +#define T7 ((long double)T7hi + T7lo) +#define pio4 ((long double)pio4_hi + pio4_lo) +#define pio4lo ((long double)pio4lo_hi + pio4lo_lo) +#else +static const long double +T3 = 0.333333333333333333180L, /* 0xaaaaaaaaaaaaaaa5.0p-65 */ +T5 = 0.133333333333333372290L, /* 0x88888888888893c3.0p-66 */ +T7 = 0.0539682539682504975744L, /* 0xdd0dd0dd0dc13ba2.0p-68 */ +pio4 = 0.785398163397448309628L, /* 0xc90fdaa22168c235.0p-64 */ +pio4lo = -1.25413940316708300586e-20L; /* -0xece675d1fc8f8cbb.0p-130 */ +#endif + +static const double +T9 = 0.021869488536312216, /* 0x1664f4882cc1c2.0p-58 */ +T11 = 0.0088632355256619590, /* 0x1226e355c17612.0p-59 */ +T13 = 0.0035921281113786528, /* 0x1d6d3d185d7ff8.0p-61 */ +T15 = 0.0014558334756312418, /* 0x17da354aa3f96b.0p-62 */ +T17 = 0.00059003538700862256, /* 0x13559358685b83.0p-63 */ +T19 = 0.00023907843576635544, /* 0x1f56242026b5be.0p-65 */ +T21 = 0.000097154625656538905, /* 0x1977efc26806f4.0p-66 */ +T23 = 0.000038440165747303162, /* 0x14275a09b3ceac.0p-67 */ +T25 = 0.000018082171885432524, /* 0x12f5e563e5487e.0p-68 */ +T27 = 0.0000024196006108814377, /* 0x144c0d80cc6896.0p-71 */ +T29 = 0.0000078293456938132840, /* 0x106b59141a6cb3.0p-69 */ +T31 = -0.0000032609076735050182, /* -0x1b5abef3ba4b59.0p-71 */ +T33 = 0.0000023261313142559411; /* 0x13835436c0c87f.0p-71 */ + +long double +__kernel_tanl(long double x, long double y, int iy) { + long double z, r, v, w, s; + long double osign; + int i; + + iy = (iy == 1 ? -1 : 1); /* XXX recover original interface */ + osign = (x >= 0 ? 1.0 : -1.0); /* XXX slow, probably wrong for -0 */ + if (fabsl(x) >= 0.67434) { + if (x < 0) { + x = -x; + y = -y; + } + z = pio4 - x; + w = pio4lo - y; + x = z + w; + y = 0.0; + i = 1; + } else + i = 0; + z = x * x; + w = z * z; + r = T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 + + w * (T25 + w * (T29 + w * T33)))))); + v = z * (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 + + w * (T27 + w * T31)))))); + s = z * x; + r = y + z * (s * (r + v) + y); + r += T3 * s; + w = x + r; + if (i == 1) { + v = (long double) iy; + return osign * + (v - 2.0 * (x - (w * w / (w + v) - r))); + } + if (iy == 1) + return w; + else { + /* + * if allow error up to 2 ulp, simply return + * -1.0 / (x+r) here + */ + /* compute -1.0 / (x+r) accurately */ + long double a, t; + z = w; + z = z + 0x1p32 - 0x1p32; + v = r - (z - x); /* z+v = r+x */ + t = a = -1.0 / w; /* a = -1.0/w */ + t = t + 0x1p32 - 0x1p32; + s = 1.0 + t * z; + return t + a * (s + t * v); + } +} diff --git a/openlibm/ld80/s_asinhl.c b/openlibm/ld80/s_asinhl.c new file mode 100644 index 0000000000..0453a9cdf9 --- /dev/null +++ b/openlibm/ld80/s_asinhl.c @@ -0,0 +1,54 @@ +/* @(#)s_asinh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* asinhl(x) + * Method : + * Based on + * asinhl(x) = signl(x) * logl [ |x| + sqrtl(x*x+1) ] + * we have + * asinhl(x) := x if 1+x*x=1, + * := signl(x)*(logl(x)+ln2)) for large |x|, else + * := signl(x)*logl(2|x|+1/(|x|+sqrtl(x*x+1))) if|x|>2, else + * := signl(x)*log1pl(|x| + x^2/(1 + sqrtl(1+x^2))) + */ + +#include + +#include "math_private.h" + +static const long double +one = 1.000000000000000000000e+00L, /* 0x3FFF, 0x00000000, 0x00000000 */ +ln2 = 6.931471805599453094287e-01L, /* 0x3FFE, 0xB17217F7, 0xD1CF79AC */ +huge= 1.000000000000000000e+4900L; + +long double +asinhl(long double x) +{ + long double t,w; + int32_t hx,ix; + GET_LDOUBLE_EXP(hx,x); + ix = hx&0x7fff; + if(ix==0x7fff) return x+x; /* x is inf or NaN */ + if(ix< 0x3fde) { /* |x|<2**-34 */ + if(huge+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x4020) { /* |x| > 2**34 */ + w = logl(fabsl(x))+ln2; + } else if (ix>0x4000) { /* 2**34 > |x| > 2.0 */ + t = fabsl(x); + w = logl(2.0*t+one/(sqrtl(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =log1pl(fabsl(x)+t/(one+sqrtl(one+t))); + } + if(hx&0x8000) return -w; else return w; +} diff --git a/openlibm/ld80/s_ceill.c b/openlibm/ld80/s_ceill.c new file mode 100644 index 0000000000..9a3e80526b --- /dev/null +++ b/openlibm/ld80/s_ceill.c @@ -0,0 +1,78 @@ +/* @(#)s_ceil.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * ceill(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to ceil(x). + */ + +#include + +#include "math_private.h" + +static const long double huge = 1.0e4930L; + +long double +ceill(long double x) +{ + int32_t i1,jj0; + u_int32_t i,j,se,i0,sx; + GET_LDOUBLE_WORDS(se,i0,i1,x); + sx = (se>>15)&1; + jj0 = (se&0x7fff)-0x3fff; + if(jj0<31) { + if(jj0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(sx) {se=0x8000;i0=0;i1=0;} + else if((i0|i1)!=0) { se=0x3fff;i0=0;i1=0;} + } + } else { + i = (0x7fffffff)>>jj0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(sx==0) { + if (jj0>0 && (i0+(0x80000000>>jj0))>i0) + i0+=0x80000000>>jj0; + else + { + i = 0x7fffffff; + ++se; + } + } + i0 &= (~i); i1=0; + } + } + } else if (jj0>62) { + if(jj0==0x4000) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(jj0-31); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(sx==0) { + if(jj0==31) i0+=1; + else { + j = i1 + (1<<(63-jj0)); + if(j + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* double erf(double x) + * double erfc(double x) + * x + * 2 |\ + * erf(x) = --------- | exp(-t*t)dt + * sqrt(pi) \| + * 0 + * + * erfc(x) = 1-erf(x) + * Note that + * erf(-x) = -erf(x) + * erfc(-x) = 2 - erfc(x) + * + * Method: + * 1. For |x| in [0, 0.84375] + * erf(x) = x + x*R(x^2) + * erfc(x) = 1 - erf(x) if x in [-.84375,0.25] + * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] + * Remark. The formula is derived by noting + * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) + * and that + * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 + * is close to one. The interval is chosen because the fix + * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is + * near 0.6174), and by some experiment, 0.84375 is chosen to + * guarantee the error is less than one ulp for erf. + * + * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and + * c = 0.84506291151 rounded to single (24 bits) + * erf(x) = sign(x) * (c + P1(s)/Q1(s)) + * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 + * 1+(c+P1(s)/Q1(s)) if x < 0 + * Remark: here we use the taylor series expansion at x=1. + * erf(1+s) = erf(1) + s*Poly(s) + * = 0.845.. + P1(s)/Q1(s) + * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] + * + * 3. For x in [1.25,1/0.35(~2.857143)], + * erfc(x) = (1/x)*exp(-x*x-0.5625+R1(z)/S1(z)) + * z=1/x^2 + * erf(x) = 1 - erfc(x) + * + * 4. For x in [1/0.35,107] + * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 + * = 2.0 - (1/x)*exp(-x*x-0.5625+R2(z)/S2(z)) + * if -6.666 x >= 107 + * erf(x) = sign(x) *(1 - tiny) (raise inexact) + * erfc(x) = tiny*tiny (raise underflow) if x > 0 + * = 2 - tiny if x<0 + * + * 7. Special case: + * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, + * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, + * erfc/erf(NaN) is NaN + */ + + +#include + +#include "math_private.h" + +static const long double +tiny = 1e-4931L, + half = 0.5L, + one = 1.0L, + two = 2.0L, + /* c = (float)0.84506291151 */ + erx = 0.845062911510467529296875L, +/* + * Coefficients for approximation to erf on [0,0.84375] + */ + /* 2/sqrt(pi) - 1 */ + efx = 1.2837916709551257389615890312154517168810E-1L, + /* 8 * (2/sqrt(pi) - 1) */ + efx8 = 1.0270333367641005911692712249723613735048E0L, + + pp[6] = { + 1.122751350964552113068262337278335028553E6L, + -2.808533301997696164408397079650699163276E6L, + -3.314325479115357458197119660818768924100E5L, + -6.848684465326256109712135497895525446398E4L, + -2.657817695110739185591505062971929859314E3L, + -1.655310302737837556654146291646499062882E2L, + }, + + qq[6] = { + 8.745588372054466262548908189000448124232E6L, + 3.746038264792471129367533128637019611485E6L, + 7.066358783162407559861156173539693900031E5L, + 7.448928604824620999413120955705448117056E4L, + 4.511583986730994111992253980546131408924E3L, + 1.368902937933296323345610240009071254014E2L, + /* 1.000000000000000000000000000000000000000E0 */ + }, + +/* + * Coefficients for approximation to erf in [0.84375,1.25] + */ +/* erf(x+1) = 0.845062911510467529296875 + pa(x)/qa(x) + -0.15625 <= x <= +.25 + Peak relative error 8.5e-22 */ + + pa[8] = { + -1.076952146179812072156734957705102256059E0L, + 1.884814957770385593365179835059971587220E2L, + -5.339153975012804282890066622962070115606E1L, + 4.435910679869176625928504532109635632618E1L, + 1.683219516032328828278557309642929135179E1L, + -2.360236618396952560064259585299045804293E0L, + 1.852230047861891953244413872297940938041E0L, + 9.394994446747752308256773044667843200719E-2L, + }, + + qa[7] = { + 4.559263722294508998149925774781887811255E2L, + 3.289248982200800575749795055149780689738E2L, + 2.846070965875643009598627918383314457912E2L, + 1.398715859064535039433275722017479994465E2L, + 6.060190733759793706299079050985358190726E1L, + 2.078695677795422351040502569964299664233E1L, + 4.641271134150895940966798357442234498546E0L, + /* 1.000000000000000000000000000000000000000E0 */ + }, + +/* + * Coefficients for approximation to erfc in [1.25,1/0.35] + */ +/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + ra(x^2)/sa(x^2)) + 1/2.85711669921875 < 1/x < 1/1.25 + Peak relative error 3.1e-21 */ + + ra[] = { + 1.363566591833846324191000679620738857234E-1L, + 1.018203167219873573808450274314658434507E1L, + 1.862359362334248675526472871224778045594E2L, + 1.411622588180721285284945138667933330348E3L, + 5.088538459741511988784440103218342840478E3L, + 8.928251553922176506858267311750789273656E3L, + 7.264436000148052545243018622742770549982E3L, + 2.387492459664548651671894725748959751119E3L, + 2.220916652813908085449221282808458466556E2L, + }, + + sa[] = { + -1.382234625202480685182526402169222331847E1L, + -3.315638835627950255832519203687435946482E2L, + -2.949124863912936259747237164260785326692E3L, + -1.246622099070875940506391433635999693661E4L, + -2.673079795851665428695842853070996219632E4L, + -2.880269786660559337358397106518918220991E4L, + -1.450600228493968044773354186390390823713E4L, + -2.874539731125893533960680525192064277816E3L, + -1.402241261419067750237395034116942296027E2L, + /* 1.000000000000000000000000000000000000000E0 */ + }, +/* + * Coefficients for approximation to erfc in [1/.35,107] + */ +/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + rb(x^2)/sb(x^2)) + 1/6.6666259765625 < 1/x < 1/2.85711669921875 + Peak relative error 4.2e-22 */ + rb[] = { + -4.869587348270494309550558460786501252369E-5L, + -4.030199390527997378549161722412466959403E-3L, + -9.434425866377037610206443566288917589122E-2L, + -9.319032754357658601200655161585539404155E-1L, + -4.273788174307459947350256581445442062291E0L, + -8.842289940696150508373541814064198259278E0L, + -7.069215249419887403187988144752613025255E0L, + -1.401228723639514787920274427443330704764E0L, + }, + + sb[] = { + 4.936254964107175160157544545879293019085E-3L, + 1.583457624037795744377163924895349412015E-1L, + 1.850647991850328356622940552450636420484E0L, + 9.927611557279019463768050710008450625415E0L, + 2.531667257649436709617165336779212114570E1L, + 2.869752886406743386458304052862814690045E1L, + 1.182059497870819562441683560749192539345E1L, + /* 1.000000000000000000000000000000000000000E0 */ + }, +/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + rc(x^2)/sc(x^2)) + 1/107 <= 1/x <= 1/6.6666259765625 + Peak relative error 1.1e-21 */ + rc[] = { + -8.299617545269701963973537248996670806850E-5L, + -6.243845685115818513578933902532056244108E-3L, + -1.141667210620380223113693474478394397230E-1L, + -7.521343797212024245375240432734425789409E-1L, + -1.765321928311155824664963633786967602934E0L, + -1.029403473103215800456761180695263439188E0L, + }, + + sc[] = { + 8.413244363014929493035952542677768808601E-3L, + 2.065114333816877479753334599639158060979E-1L, + 1.639064941530797583766364412782135680148E0L, + 4.936788463787115555582319302981666347450E0L, + 5.005177727208955487404729933261347679090E0L, + /* 1.000000000000000000000000000000000000000E0 */ + }; + +long double +erfl(long double x) +{ + long double R, S, P, Q, s, y, z, r; + int32_t ix, i; + u_int32_t se, i0, i1; + + GET_LDOUBLE_WORDS (se, i0, i1, x); + ix = se & 0x7fff; + + if (ix >= 0x7fff) + { /* erf(nan)=nan */ + i = ((se & 0xffff) >> 15) << 1; + return (long double) (1 - i) + one / x; /* erf(+-inf)=+-1 */ + } + + ix = (ix << 16) | (i0 >> 16); + if (ix < 0x3ffed800) /* |x|<0.84375 */ + { + if (ix < 0x3fde8000) /* |x|<2**-33 */ + { + if (ix < 0x00080000) + return 0.125 * (8.0 * x + efx8 * x); /*avoid underflow */ + return x + efx * x; + } + z = x * x; + r = pp[0] + z * (pp[1] + + z * (pp[2] + z * (pp[3] + z * (pp[4] + z * pp[5])))); + s = qq[0] + z * (qq[1] + + z * (qq[2] + z * (qq[3] + z * (qq[4] + z * (qq[5] + z))))); + y = r / s; + return x + x * y; + } + if (ix < 0x3fffa000) /* 1.25 */ + { /* 0.84375 <= |x| < 1.25 */ + s = fabsl (x) - one; + P = pa[0] + s * (pa[1] + s * (pa[2] + + s * (pa[3] + s * (pa[4] + s * (pa[5] + s * (pa[6] + s * pa[7])))))); + Q = qa[0] + s * (qa[1] + s * (qa[2] + + s * (qa[3] + s * (qa[4] + s * (qa[5] + s * (qa[6] + s)))))); + if ((se & 0x8000) == 0) + return erx + P / Q; + else + return -erx - P / Q; + } + if (ix >= 0x4001d555) /* 6.6666259765625 */ + { /* inf>|x|>=6.666 */ + if ((se & 0x8000) == 0) + return one - tiny; + else + return tiny - one; + } + x = fabsl (x); + s = one / (x * x); + if (ix < 0x4000b6db) /* 2.85711669921875 */ + { + R = ra[0] + s * (ra[1] + s * (ra[2] + s * (ra[3] + s * (ra[4] + + s * (ra[5] + s * (ra[6] + s * (ra[7] + s * ra[8]))))))); + S = sa[0] + s * (sa[1] + s * (sa[2] + s * (sa[3] + s * (sa[4] + + s * (sa[5] + s * (sa[6] + s * (sa[7] + s * (sa[8] + s)))))))); + } + else + { /* |x| >= 1/0.35 */ + R = rb[0] + s * (rb[1] + s * (rb[2] + s * (rb[3] + s * (rb[4] + + s * (rb[5] + s * (rb[6] + s * rb[7])))))); + S = sb[0] + s * (sb[1] + s * (sb[2] + s * (sb[3] + s * (sb[4] + + s * (sb[5] + s * (sb[6] + s)))))); + } + z = x; + GET_LDOUBLE_WORDS (i, i0, i1, z); + i1 = 0; + SET_LDOUBLE_WORDS (z, i, i0, i1); + r = + expl (-z * z - 0.5625) * expl ((z - x) * (z + x) + R / S); + if ((se & 0x8000) == 0) + return one - r / x; + else + return r / x - one; +} + +long double +erfcl(long double x) +{ + int32_t hx, ix; + long double R, S, P, Q, s, y, z, r; + u_int32_t se, i0, i1; + + GET_LDOUBLE_WORDS (se, i0, i1, x); + ix = se & 0x7fff; + if (ix >= 0x7fff) + { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (long double) (((se & 0xffff) >> 15) << 1) + one / x; + } + + ix = (ix << 16) | (i0 >> 16); + if (ix < 0x3ffed800) /* |x|<0.84375 */ + { + if (ix < 0x3fbe0000) /* |x|<2**-65 */ + return one - x; + z = x * x; + r = pp[0] + z * (pp[1] + + z * (pp[2] + z * (pp[3] + z * (pp[4] + z * pp[5])))); + s = qq[0] + z * (qq[1] + + z * (qq[2] + z * (qq[3] + z * (qq[4] + z * (qq[5] + z))))); + y = r / s; + if (ix < 0x3ffd8000) /* x<1/4 */ + { + return one - (x + x * y); + } + else + { + r = x * y; + r += (x - half); + return half - r; + } + } + if (ix < 0x3fffa000) /* 1.25 */ + { /* 0.84375 <= |x| < 1.25 */ + s = fabsl (x) - one; + P = pa[0] + s * (pa[1] + s * (pa[2] + + s * (pa[3] + s * (pa[4] + s * (pa[5] + s * (pa[6] + s * pa[7])))))); + Q = qa[0] + s * (qa[1] + s * (qa[2] + + s * (qa[3] + s * (qa[4] + s * (qa[5] + s * (qa[6] + s)))))); + if ((se & 0x8000) == 0) + { + z = one - erx; + return z - P / Q; + } + else + { + z = erx + P / Q; + return one + z; + } + } + if (ix < 0x4005d600) /* 107 */ + { /* |x|<107 */ + x = fabsl (x); + s = one / (x * x); + if (ix < 0x4000b6db) /* 2.85711669921875 */ + { /* |x| < 1/.35 ~ 2.857143 */ + R = ra[0] + s * (ra[1] + s * (ra[2] + s * (ra[3] + s * (ra[4] + + s * (ra[5] + s * (ra[6] + s * (ra[7] + s * ra[8]))))))); + S = sa[0] + s * (sa[1] + s * (sa[2] + s * (sa[3] + s * (sa[4] + + s * (sa[5] + s * (sa[6] + s * (sa[7] + s * (sa[8] + s)))))))); + } + else if (ix < 0x4001d555) /* 6.6666259765625 */ + { /* 6.666 > |x| >= 1/.35 ~ 2.857143 */ + R = rb[0] + s * (rb[1] + s * (rb[2] + s * (rb[3] + s * (rb[4] + + s * (rb[5] + s * (rb[6] + s * rb[7])))))); + S = sb[0] + s * (sb[1] + s * (sb[2] + s * (sb[3] + s * (sb[4] + + s * (sb[5] + s * (sb[6] + s)))))); + } + else + { /* |x| >= 6.666 */ + if (se & 0x8000) + return two - tiny; /* x < -6.666 */ + + R = rc[0] + s * (rc[1] + s * (rc[2] + s * (rc[3] + + s * (rc[4] + s * rc[5])))); + S = sc[0] + s * (sc[1] + s * (sc[2] + s * (sc[3] + + s * (sc[4] + s)))); + } + z = x; + GET_LDOUBLE_WORDS (hx, i0, i1, z); + i1 = 0; + i0 &= 0xffffff00; + SET_LDOUBLE_WORDS (z, hx, i0, i1); + r = expl (-z * z - 0.5625) * + expl ((z - x) * (z + x) + R / S); + if ((se & 0x8000) == 0) + return r / x; + else + return two - r / x; + } + else + { + if ((se & 0x8000) == 0) + return tiny * tiny; + else + return two - tiny; + } +} diff --git a/openlibm/ld80/s_exp2l.c b/openlibm/ld80/s_exp2l.c new file mode 100644 index 0000000000..0cf5259178 --- /dev/null +++ b/openlibm/ld80/s_exp2l.c @@ -0,0 +1,295 @@ +/*- + * Copyright (c) 2005-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/ld80/s_exp2l.c,v 1.3 2008/02/13 10:44:44 bde Exp $"); + +#include +#include + +#include "bsd_cdefs.h" +#include "amd64/bsd_ieeefp.h" + +#include + +#include "math_private.h" + +#define TBLBITS 7 +#define TBLSIZE (1 << TBLBITS) + +#define BIAS (LDBL_MAX_EXP - 1) +#define EXPMASK (BIAS + LDBL_MAX_EXP) + +static const long double huge = 0x1p10000L; +#if 0 /* XXX Prevent gcc from erroneously constant folding this. */ +static const long double twom10000 = 0x1p-10000L; +#else +static volatile long double twom10000 = 0x1p-10000L; +#endif + +static const double + redux = 0x1.8p63 / TBLSIZE, + P1 = 0x1.62e42fefa39efp-1, + P2 = 0x1.ebfbdff82c58fp-3, + P3 = 0x1.c6b08d7049fap-5, + P4 = 0x1.3b2ab6fba4da5p-7, + P5 = 0x1.5d8804780a736p-10, + P6 = 0x1.430918835e33dp-13; + +static const double tbl[TBLSIZE * 2] = { + 0x1.6a09e667f3bcdp-1, -0x1.bdd3413b2648p-55, + 0x1.6c012750bdabfp-1, -0x1.2895667ff0cp-57, + 0x1.6dfb23c651a2fp-1, -0x1.bbe3a683c88p-58, + 0x1.6ff7df9519484p-1, -0x1.83c0f25860fp-56, + 0x1.71f75e8ec5f74p-1, -0x1.16e4786887bp-56, + 0x1.73f9a48a58174p-1, -0x1.0a8d96c65d5p-55, + 0x1.75feb564267c9p-1, -0x1.0245957316ep-55, + 0x1.780694fde5d3fp-1, 0x1.866b80a0216p-55, + 0x1.7a11473eb0187p-1, -0x1.41577ee0499p-56, + 0x1.7c1ed0130c132p-1, 0x1.f124cd1164ep-55, + 0x1.7e2f336cf4e62p-1, 0x1.05d02ba157ap-57, + 0x1.80427543e1a12p-1, -0x1.27c86626d97p-55, + 0x1.82589994cce13p-1, -0x1.d4c1dd41533p-55, + 0x1.8471a4623c7adp-1, -0x1.8d684a341cep-56, + 0x1.868d99b4492edp-1, -0x1.fc6f89bd4f68p-55, + 0x1.88ac7d98a6699p-1, 0x1.994c2f37cb5p-55, + 0x1.8ace5422aa0dbp-1, 0x1.6e9f156864bp-55, + 0x1.8cf3216b5448cp-1, -0x1.0d55e32e9e4p-57, + 0x1.8f1ae99157736p-1, 0x1.5cc13a2e397p-56, + 0x1.9145b0b91ffc6p-1, -0x1.dd6792e5825p-55, + 0x1.93737b0cdc5e5p-1, -0x1.75fc781b58p-58, + 0x1.95a44cbc8520fp-1, -0x1.64b7c96a5fp-57, + 0x1.97d829fde4e5p-1, -0x1.d185b7c1b86p-55, + 0x1.9a0f170ca07bap-1, -0x1.173bd91cee6p-55, + 0x1.9c49182a3f09p-1, 0x1.c7c46b071f2p-57, + 0x1.9e86319e32323p-1, 0x1.824ca78e64cp-57, + 0x1.a0c667b5de565p-1, -0x1.359495d1cd5p-55, + 0x1.a309bec4a2d33p-1, 0x1.6305c7ddc368p-55, + 0x1.a5503b23e255dp-1, -0x1.d2f6edb8d42p-55, + 0x1.a799e1330b358p-1, 0x1.bcb7ecac564p-55, + 0x1.a9e6b5579fdbfp-1, 0x1.0fac90ef7fdp-55, + 0x1.ac36bbfd3f37ap-1, -0x1.f9234cae76dp-56, + 0x1.ae89f995ad3adp-1, 0x1.7a1cd345dcc8p-55, + 0x1.b0e07298db666p-1, -0x1.bdef54c80e4p-55, + 0x1.b33a2b84f15fbp-1, -0x1.2805e3084d8p-58, + 0x1.b59728de5593ap-1, -0x1.c71dfbbba6ep-55, + 0x1.b7f76f2fb5e47p-1, -0x1.5584f7e54acp-57, + 0x1.ba5b030a1064ap-1, -0x1.efcd30e5429p-55, + 0x1.bcc1e904bc1d2p-1, 0x1.23dd07a2d9fp-56, + 0x1.bf2c25bd71e09p-1, -0x1.efdca3f6b9c8p-55, + 0x1.c199bdd85529cp-1, 0x1.11065895049p-56, + 0x1.c40ab5fffd07ap-1, 0x1.b4537e083c6p-55, + 0x1.c67f12e57d14bp-1, 0x1.2884dff483c8p-55, + 0x1.c8f6d9406e7b5p-1, 0x1.1acbc48805cp-57, + 0x1.cb720dcef9069p-1, 0x1.503cbd1e94ap-57, + 0x1.cdf0b555dc3fap-1, -0x1.dd83b53829dp-56, + 0x1.d072d4a07897cp-1, -0x1.cbc3743797a8p-55, + 0x1.d2f87080d89f2p-1, -0x1.d487b719d858p-55, + 0x1.d5818dcfba487p-1, 0x1.2ed02d75b37p-56, + 0x1.d80e316c98398p-1, -0x1.11ec18bedep-55, + 0x1.da9e603db3285p-1, 0x1.c2300696db5p-55, + 0x1.dd321f301b46p-1, 0x1.2da5778f019p-55, + 0x1.dfc97337b9b5fp-1, -0x1.1a5cd4f184b8p-55, + 0x1.e264614f5a129p-1, -0x1.7b627817a148p-55, + 0x1.e502ee78b3ff6p-1, 0x1.39e8980a9cdp-56, + 0x1.e7a51fbc74c83p-1, 0x1.2d522ca0c8ep-55, + 0x1.ea4afa2a490dap-1, -0x1.e9c23179c288p-55, + 0x1.ecf482d8e67f1p-1, -0x1.c93f3b411ad8p-55, + 0x1.efa1bee615a27p-1, 0x1.dc7f486a4b68p-55, + 0x1.f252b376bba97p-1, 0x1.3a1a5bf0d8e8p-55, + 0x1.f50765b6e454p-1, 0x1.9d3e12dd8a18p-55, + 0x1.f7bfdad9cbe14p-1, -0x1.dbb12d00635p-55, + 0x1.fa7c1819e90d8p-1, 0x1.74853f3a593p-56, + 0x1.fd3c22b8f71f1p-1, 0x1.2eb74966578p-58, + 0x1p+0, 0x0p+0, + 0x1.0163da9fb3335p+0, 0x1.b61299ab8cd8p-54, + 0x1.02c9a3e778061p+0, -0x1.19083535b08p-56, + 0x1.04315e86e7f85p+0, -0x1.0a31c1977c98p-54, + 0x1.059b0d3158574p+0, 0x1.d73e2a475b4p-55, + 0x1.0706b29ddf6dep+0, -0x1.c91dfe2b13cp-55, + 0x1.0874518759bc8p+0, 0x1.186be4bb284p-57, + 0x1.09e3ecac6f383p+0, 0x1.14878183161p-54, + 0x1.0b5586cf9890fp+0, 0x1.8a62e4adc61p-54, + 0x1.0cc922b7247f7p+0, 0x1.01edc16e24f8p-54, + 0x1.0e3ec32d3d1a2p+0, 0x1.03a1727c58p-59, + 0x1.0fb66affed31bp+0, -0x1.b9bedc44ebcp-57, + 0x1.11301d0125b51p+0, -0x1.6c51039449bp-54, + 0x1.12abdc06c31ccp+0, -0x1.1b514b36ca8p-58, + 0x1.1429aaea92dep+0, -0x1.32fbf9af1368p-54, + 0x1.15a98c8a58e51p+0, 0x1.2406ab9eeabp-55, + 0x1.172b83c7d517bp+0, -0x1.19041b9d78ap-55, + 0x1.18af9388c8deap+0, -0x1.11023d1970f8p-54, + 0x1.1a35beb6fcb75p+0, 0x1.e5b4c7b4969p-55, + 0x1.1bbe084045cd4p+0, -0x1.95386352ef6p-54, + 0x1.1d4873168b9aap+0, 0x1.e016e00a264p-54, + 0x1.1ed5022fcd91dp+0, -0x1.1df98027bb78p-54, + 0x1.2063b88628cd6p+0, 0x1.dc775814a85p-55, + 0x1.21f49917ddc96p+0, 0x1.2a97e9494a6p-55, + 0x1.2387a6e756238p+0, 0x1.9b07eb6c7058p-54, + 0x1.251ce4fb2a63fp+0, 0x1.ac155bef4f5p-55, + 0x1.26b4565e27cddp+0, 0x1.2bd339940eap-55, + 0x1.284dfe1f56381p+0, -0x1.a4c3a8c3f0d8p-54, + 0x1.29e9df51fdee1p+0, 0x1.612e8afad12p-55, + 0x1.2b87fd0dad99p+0, -0x1.10adcd6382p-59, + 0x1.2d285a6e4030bp+0, 0x1.0024754db42p-54, + 0x1.2ecafa93e2f56p+0, 0x1.1ca0f45d524p-56, + 0x1.306fe0a31b715p+0, 0x1.6f46ad23183p-55, + 0x1.32170fc4cd831p+0, 0x1.a9ce78e1804p-55, + 0x1.33c08b26416ffp+0, 0x1.327218436598p-54, + 0x1.356c55f929ff1p+0, -0x1.b5cee5c4e46p-55, + 0x1.371a7373aa9cbp+0, -0x1.63aeabf42ebp-54, + 0x1.38cae6d05d866p+0, -0x1.e958d3c99048p-54, + 0x1.3a7db34e59ff7p+0, -0x1.5e436d661f6p-56, + 0x1.3c32dc313a8e5p+0, -0x1.efff8375d2ap-54, + 0x1.3dea64c123422p+0, 0x1.ada0911f09fp-55, + 0x1.3fa4504ac801cp+0, -0x1.7d023f956fap-54, + 0x1.4160a21f72e2ap+0, -0x1.ef3691c309p-58, + 0x1.431f5d950a897p+0, -0x1.1c7dde35f7ap-55, + 0x1.44e086061892dp+0, 0x1.89b7a04ef8p-59, + 0x1.46a41ed1d0057p+0, 0x1.c944bd1648a8p-54, + 0x1.486a2b5c13cdp+0, 0x1.3c1a3b69062p-56, + 0x1.4a32af0d7d3dep+0, 0x1.9cb62f3d1be8p-54, + 0x1.4bfdad5362a27p+0, 0x1.d4397afec42p-56, + 0x1.4dcb299fddd0dp+0, 0x1.8ecdbbc6a78p-54, + 0x1.4f9b2769d2ca7p+0, -0x1.4b309d25958p-54, + 0x1.516daa2cf6642p+0, -0x1.f768569bd94p-55, + 0x1.5342b569d4f82p+0, -0x1.07abe1db13dp-55, + 0x1.551a4ca5d920fp+0, -0x1.d689cefede6p-55, + 0x1.56f4736b527dap+0, 0x1.9bb2c011d938p-54, + 0x1.58d12d497c7fdp+0, 0x1.295e15b9a1ep-55, + 0x1.5ab07dd485429p+0, 0x1.6324c0546478p-54, + 0x1.5c9268a5946b7p+0, 0x1.c4b1b81698p-60, + 0x1.5e76f15ad2148p+0, 0x1.ba6f93080e68p-54, + 0x1.605e1b976dc09p+0, -0x1.3e2429b56de8p-54, + 0x1.6247eb03a5585p+0, -0x1.383c17e40b48p-54, + 0x1.6434634ccc32p+0, -0x1.c483c759d89p-55, + 0x1.6623882552225p+0, -0x1.bb60987591cp-54, + 0x1.68155d44ca973p+0, 0x1.038ae44f74p-57, +}; + +/* + * exp2l(x): compute the base 2 exponential of x + * + * Accuracy: Peak error < 0.511 ulp. + * + * Method: (equally-spaced tables) + * + * Reduce x: + * x = 2**k + y, for integer k and |y| <= 1/2. + * Thus we have exp2l(x) = 2**k * exp2(y). + * + * Reduce y: + * y = i/TBLSIZE + z for integer i near y * TBLSIZE. + * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z), + * with |z| <= 2**-(TBLBITS+1). + * + * We compute exp2(i/TBLSIZE) via table lookup and exp2(z) via a + * degree-6 minimax polynomial with maximum error under 2**-69. + * The table entries each have 104 bits of accuracy, encoded as + * a pair of double precision values. + */ +OLM_DLLEXPORT long double +exp2l(long double x) +{ + union IEEEl2bits u, v; + long double r, twopk, twopkp10000, z; + uint32_t hx, ix, i0; + int k; + + /* Filter out exceptional cases. */ + u.e = x; + hx = u.xbits.expsign; + ix = hx & EXPMASK; + if (ix >= BIAS + 14) { /* |x| >= 16384 or x is NaN */ + if (ix == BIAS + LDBL_MAX_EXP) { + if (u.xbits.man != 1ULL << 63 || (hx & 0x8000) == 0) + return (x + x); /* x is +Inf or NaN */ + else + return (0.0); /* x is -Inf */ + } + if (x >= 16384) + return (huge * huge); /* overflow */ + if (x <= -16446) + return (twom10000 * twom10000); /* underflow */ + } else if (ix <= BIAS - 66) { /* |x| < 0x1p-66 */ + return (1.0 + x); + } + +#ifdef __i386__ + /* + * The default precision on i386 is 53 bits, so long doubles are + * broken. Call exp2() to get an accurate (double precision) result. + */ + if (__fpgetprec() != FP_PE) + return (exp2(x)); +#endif + + + /* + * Reduce x, computing z, i0, and k. The low bits of x + redux + * contain the 16-bit integer part of the exponent (k) followed by + * TBLBITS fractional bits (i0). We use bit tricks to extract these + * as integers, then set z to the remainder. + * + * Example: Suppose x is 0xabc.123456p0 and TBLBITS is 8. + * Then the low-order word of x + redux is 0x000abc12, + * We split this into k = 0xabc and i0 = 0x12 (adjusted to + * index into the table), then we compute z = 0x0.003456p0. + * + * XXX If the exponent is negative, the computation of k depends on + * '>>' doing sign extension. + */ + u.e = x + redux; + i0 = u.bits.manl + TBLSIZE / 2; + k = (int)i0 >> TBLBITS; + i0 = (i0 & (TBLSIZE - 1)) << 1; + u.e -= redux; + z = x - u.e; + v.xbits.man = 1ULL << 63; + if (k >= LDBL_MIN_EXP) { + v.xbits.expsign = LDBL_MAX_EXP - 1 + k; + twopk = v.e; + } else { + v.xbits.expsign = LDBL_MAX_EXP - 1 + k + 10000; + twopkp10000 = v.e; + } + + /* Compute r = exp2l(y) = exp2lt[i0] * p(z). */ + long double t_hi = tbl[i0]; + long double t_lo = tbl[i0 + 1]; + /* XXX This gives > 1 ulp errors outside of FE_TONEAREST mode */ + r = t_lo + (t_hi + t_lo) * z * (P1 + z * (P2 + z * (P3 + z * (P4 + + z * (P5 + z * P6))))) + t_hi; + + /* Scale by 2**k. */ + if (k >= LDBL_MIN_EXP) { + if (k == LDBL_MAX_EXP) + return (r * 2.0 * 0x1p16383L); + return (r * twopk); + } else { + return (r * twopkp10000 * twom10000); + } +} diff --git a/openlibm/ld80/s_expm1l.c b/openlibm/ld80/s_expm1l.c new file mode 100644 index 0000000000..6ad23fad75 --- /dev/null +++ b/openlibm/ld80/s_expm1l.c @@ -0,0 +1,138 @@ +/* $OpenBSD: s_expm1l.c,v 1.2 2011/07/20 21:02:51 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* expm1l.c + * + * Exponential function, minus 1 + * Long double precision + * + * + * SYNOPSIS: + * + * long double x, y, expm1l(); + * + * y = expm1l( x ); + * + * + * + * DESCRIPTION: + * + * Returns e (2.71828...) raised to the x power, minus 1. + * + * Range reduction is accomplished by separating the argument + * into an integer k and fraction f such that + * + * x k f + * e = 2 e. + * + * An expansion x + .5 x^2 + x^3 R(x) approximates exp(f) - 1 + * in the basic range [-0.5 ln 2, 0.5 ln 2]. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -45,+MAXLOG 200,000 1.2e-19 2.5e-20 + * + * ERROR MESSAGES: + * + * message condition value returned + * expm1l overflow x > MAXLOG MAXNUM + * + */ + +#include + +static const long double MAXLOGL = 1.1356523406294143949492E4L; + +/* exp(x) - 1 = x + 0.5 x^2 + x^3 P(x)/Q(x) + -.5 ln 2 < x < .5 ln 2 + Theoretical peak relative error = 3.4e-22 */ + +static const long double + P0 = -1.586135578666346600772998894928250240826E4L, + P1 = 2.642771505685952966904660652518429479531E3L, + P2 = -3.423199068835684263987132888286791620673E2L, + P3 = 1.800826371455042224581246202420972737840E1L, + P4 = -5.238523121205561042771939008061958820811E-1L, + + Q0 = -9.516813471998079611319047060563358064497E4L, + Q1 = 3.964866271411091674556850458227710004570E4L, + Q2 = -7.207678383830091850230366618190187434796E3L, + Q3 = 7.206038318724600171970199625081491823079E2L, + Q4 = -4.002027679107076077238836622982900945173E1L, + /* Q5 = 1.000000000000000000000000000000000000000E0 */ + +/* C1 + C2 = ln 2 */ +C1 = 6.93145751953125E-1L, +C2 = 1.428606820309417232121458176568075500134E-6L, +/* ln 2^-65 */ +minarg = -4.5054566736396445112120088E1L; +static const long double huge = 0x1p10000L; + +long double +expm1l(long double x) +{ +long double px, qx, xx; +int k; + +/* Overflow. */ +if (x > MAXLOGL) + return (huge*huge); /* overflow */ + +if (x == 0.0) + return x; + +/* Minimum value. */ +if (x < minarg) + return -1.0L; + +xx = C1 + C2; + +/* Express x = ln 2 (k + remainder), remainder not exceeding 1/2. */ +px = floorl (0.5 + x / xx); +k = px; +/* remainder times ln 2 */ +x -= px * C1; +x -= px * C2; + +/* Approximate exp(remainder ln 2). */ +px = (((( P4 * x + + P3) * x + + P2) * x + + P1) * x + + P0) * x; + +qx = (((( x + + Q4) * x + + Q3) * x + + Q2) * x + + Q1) * x + + Q0; + +xx = x * x; +qx = x + (0.5 * xx + xx * px / qx); + +/* exp(x) = exp(k ln 2) exp(remainder ln 2) = 2^k exp(remainder ln 2). + We have qx = exp(remainder ln 2) - 1, so + exp(x) - 1 = 2^k (qx + 1) - 1 = 2^k qx + 2^k - 1. */ +px = ldexpl(1.0L, k); +x = px * qx + (px - 1.0); +return x; +} diff --git a/openlibm/ld80/s_floorl.c b/openlibm/ld80/s_floorl.c new file mode 100644 index 0000000000..7d42bd66d2 --- /dev/null +++ b/openlibm/ld80/s_floorl.c @@ -0,0 +1,80 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * floorl(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floor(x). + */ + +#include + +#include "math_private.h" + +static const long double huge = 1.0e4930L; + +long double +floorl(long double x) +{ + int32_t i1,jj0; + u_int32_t i,j,se,i0,sx; + GET_LDOUBLE_WORDS(se,i0,i1,x); + sx = (se>>15)&1; + jj0 = (se&0x7fff)-0x3fff; + if(jj0<31) { + if(jj0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) { + if(sx==0) + return 0.0L; + else if(((se&0x7fff)|i0|i1)!=0) + return -1.0L; + } + } else { + i = (0x7fffffff)>>jj0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(sx) { + if (jj0>0 && (i0+(0x80000000>>jj0))>i0) + i0 += (0x80000000)>>jj0; + else + { + i = 0x7fffffff; + ++se; + } + } + i0 &= (~i); i1=0; + } + } + } else if (jj0>62) { + if(jj0==0x4000) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(jj0-31); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(sx) { + if(jj0==31) i0+=1; + else { + j = i1+(1<<(63-jj0)); + if(j + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* log1pl.c + * + * Relative error logarithm + * Natural logarithm of 1+x, long double precision + * + * + * + * SYNOPSIS: + * + * long double x, y, log1pl(); + * + * y = log1pl( x ); + * + * + * + * DESCRIPTION: + * + * Returns the base e (2.718...) logarithm of 1+x. + * + * The argument 1+x is separated into its exponent and fractional + * parts. If the exponent is between -1 and +1, the logarithm + * of the fraction is approximated by + * + * log(1+x) = x - 0.5 x^2 + x^3 P(x)/Q(x). + * + * Otherwise, setting z = 2(x-1)/x+1), + * + * log(x) = z + z^3 P(z)/Q(z). + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -1.0, 9.0 100000 8.2e-20 2.5e-20 + * + * ERROR MESSAGES: + * + * log singularity: x-1 = 0; returns -INFINITY + * log domain: x-1 < 0; returns NAN + */ + +#include + +#include "math_private.h" + +/* Coefficients for log(1+x) = x - x^2 / 2 + x^3 P(x)/Q(x) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 2.32e-20 + */ + +static long double P[] = { + 4.5270000862445199635215E-5L, + 4.9854102823193375972212E-1L, + 6.5787325942061044846969E0L, + 2.9911919328553073277375E1L, + 6.0949667980987787057556E1L, + 5.7112963590585538103336E1L, + 2.0039553499201281259648E1L, +}; +static long double Q[] = { +/* 1.0000000000000000000000E0,*/ + 1.5062909083469192043167E1L, + 8.3047565967967209469434E1L, + 2.2176239823732856465394E2L, + 3.0909872225312059774938E2L, + 2.1642788614495947685003E2L, + 6.0118660497603843919306E1L, +}; + +/* Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2), + * where z = 2(x-1)/(x+1) + * 1/sqrt(2) <= x < sqrt(2) + * Theoretical peak relative error = 6.16e-22 + */ + +static long double R[4] = { + 1.9757429581415468984296E-3L, +-7.1990767473014147232598E-1L, + 1.0777257190312272158094E1L, +-3.5717684488096787370998E1L, +}; +static long double S[4] = { +/* 1.00000000000000000000E0L,*/ +-2.6201045551331104417768E1L, + 1.9361891836232102174846E2L, +-4.2861221385716144629696E2L, +}; +static const long double C1 = 6.9314575195312500000000E-1L; +static const long double C2 = 1.4286068203094172321215E-6L; + +#define SQRTH 0.70710678118654752440L + +long double +log1pl(long double xm1) +{ +long double x, y, z; +int e; + +if( isnan(xm1) ) + return(xm1); +if( xm1 == INFINITY ) + return(xm1); +if(xm1 == 0.0) + return(xm1); + +x = xm1 + 1.0L; + +/* Test for domain errors. */ +if( x <= 0.0L ) + { + if( x == 0.0L ) + return( -INFINITY ); + else + return( NAN ); + } + +/* Separate mantissa from exponent. + Use frexp so that denormal numbers will be handled properly. */ +x = frexpl( x, &e ); + +/* logarithm using log(x) = z + z^3 P(z)/Q(z), + where z = 2(x-1)/x+1) */ +if( (e > 2) || (e < -2) ) +{ +if( x < SQRTH ) + { /* 2( 2x-1 )/( 2x+1 ) */ + e -= 1; + z = x - 0.5L; + y = 0.5L * z + 0.5L; + } +else + { /* 2 (x-1)/(x+1) */ + z = x - 0.5L; + z -= 0.5L; + y = 0.5L * x + 0.5L; + } +x = z / y; +z = x*x; +z = x * ( z * __polevll( z, R, 3 ) / __p1evll( z, S, 3 ) ); +z = z + e * C2; +z = z + x; +z = z + e * C1; +return( z ); +} + + +/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */ + +if( x < SQRTH ) + { + e -= 1; + if (e != 0) + x = 2.0 * x - 1.0L; + else + x = xm1; + } +else + { + if (e != 0) + x = x - 1.0L; + else + x = xm1; + } +z = x*x; +y = x * ( z * __polevll( x, P, 6 ) / __p1evll( x, Q, 6 ) ); +y = y + e * C2; +z = y - 0.5 * z; +z = z + x; +z = z + e * C1; +return( z ); +} diff --git a/openlibm/ld80/s_modfl.c b/openlibm/ld80/s_modfl.c new file mode 100644 index 0000000000..f9884afef8 --- /dev/null +++ b/openlibm/ld80/s_modfl.c @@ -0,0 +1,69 @@ +/* @(#)s_modf.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * modfl(long double x, long double *iptr) + * return fraction part of x, and return x's integral part in *iptr. + * Method: + * Bit twiddling. + * + * Exception: + * No exception. + */ + +#include + +#include "math_private.h" + +static const long double one = 1.0; + +long double +modfl(long double x, long double *iptr) +{ + int32_t i0,i1,jj0; + u_int32_t i,se; + GET_LDOUBLE_WORDS(se,i0,i1,x); + jj0 = (se&0x7fff)-0x3fff; /* exponent of x */ + if(jj0<32) { /* integer part in high x */ + if(jj0<0) { /* |x|<1 */ + SET_LDOUBLE_WORDS(*iptr,se&0x8000,0,0); /* *iptr = +-0 */ + return x; + } else { + i = (0x7fffffff)>>jj0; + if(((i0&i)|i1)==0) { /* x is integral */ + *iptr = x; + SET_LDOUBLE_WORDS(x,se&0x8000,0,0); /* return +-0 */ + return x; + } else { + SET_LDOUBLE_WORDS(*iptr,se,i0&(~i),0); + return x - *iptr; + } + } + } else if (jj0>63) { /* no fraction part */ + *iptr = x*one; + /* We must handle NaNs separately. */ + if (jj0 == 0x4000 && ((i0 & 0x7fffffff) | i1)) + return x*one; + SET_LDOUBLE_WORDS(x,se&0x8000,0,0); /* return +-0 */ + return x; + } else { /* fraction part in low x */ + i = ((u_int32_t)(0x7fffffff))>>(jj0-32); + if((i1&i)==0) { /* x is integral */ + *iptr = x; + SET_LDOUBLE_WORDS(x,se&0x8000,0,0); /* return +-0 */ + return x; + } else { + SET_LDOUBLE_WORDS(*iptr,se,i0,i1&(~i)); + return x - *iptr; + } + } +} diff --git a/openlibm/ld80/s_nanl.c b/openlibm/ld80/s_nanl.c new file mode 100644 index 0000000000..ca770480d7 --- /dev/null +++ b/openlibm/ld80/s_nanl.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/ld80/s_nanl.c,v 1.2 2007/12/18 23:46:31 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +nanl(const char *s) +{ + union { + union IEEEl2bits ieee; + uint32_t bits[3]; + } u; + + __scan_nan(u.bits, 3, s); + u.ieee.bits.exp = 0x7fff; + u.ieee.bits.manh |= 0xc0000000; /* make it a quiet NaN */ + return (u.ieee.e); +} diff --git a/openlibm/ld80/s_nextafterl.c b/openlibm/ld80/s_nextafterl.c new file mode 100644 index 0000000000..1e0764b608 --- /dev/null +++ b/openlibm/ld80/s_nextafterl.c @@ -0,0 +1,90 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* IEEE functions + * nextafterl(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include + +#include "math_private.h" + +long double +nextafterl(long double x, long double y) +{ + int32_t hx,hy,ix,iy; + u_int32_t lx,ly,esx,esy; + + GET_LDOUBLE_WORDS(esx,hx,lx,x); + GET_LDOUBLE_WORDS(esy,hy,ly,y); + ix = esx&0x7fff; /* |x| */ + iy = esy&0x7fff; /* |y| */ + + if (((ix==0x7fff)&&((hx&0x7fffffff|lx)!=0)) || /* x is nan */ + ((iy==0x7fff)&&((hy&0x7fffffff|ly)!=0))) /* y is nan */ + return x+y; + if(x==y) return y; /* x=y, return y */ + if((ix|hx|lx)==0) { /* x == 0 */ + volatile long double u; + SET_LDOUBLE_WORDS(x,esy&0x8000,0,1);/* return +-minsubnormal */ + u = x; + u = u * u; /* raise underflow flag */ + return x; + } + if(esx<0x8000) { /* x > 0 */ + if(ix>iy||((ix==iy) && (hx>hy||((hx==hy)&&(lx>ly))))) { + /* x > y, x -= ulp */ + if(lx==0) { + if ((hx&0x7fffffff)==0) esx -= 1; + hx = (hx - 1) | (hx & 0x80000000); + } + lx -= 1; + } else { /* x < y, x += ulp */ + lx += 1; + if(lx==0) { + hx = (hx + 1) | (hx & 0x80000000); + if ((hx&0x7fffffff)==0) esx += 1; + } + } + } else { /* x < 0 */ + if(esy>=0||(ix>iy||((ix==iy)&&(hx>hy||((hx==hy)&&(lx>ly)))))){ + /* x < y, x -= ulp */ + if(lx==0) { + if ((hx&0x7fffffff)==0) esx -= 1; + hx = (hx - 1) | (hx & 0x80000000); + } + lx -= 1; + } else { /* x > y, x += ulp */ + lx += 1; + if(lx==0) { + hx = (hx + 1) | (hx & 0x80000000); + if ((hx&0x7fffffff)==0) esx += 1; + } + } + } + esy = esx&0x7fff; + if(esy==0x7fff) return x+x; /* overflow */ + if(esy==0) { + volatile long double u = x*x; /* underflow */ + if(u==x) { + SET_LDOUBLE_WORDS(x,esx,hx,lx); + return x; + } + } + SET_LDOUBLE_WORDS(x,esx,hx,lx); + return x; +} + +//__strong_alias(nexttowardl, nextafterl); diff --git a/openlibm/ld80/s_nexttoward.c b/openlibm/ld80/s_nexttoward.c new file mode 100644 index 0000000000..b01766886d --- /dev/null +++ b/openlibm/ld80/s_nexttoward.c @@ -0,0 +1,86 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* IEEE functions + * nexttoward(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include +#include + +#include "math_private.h" + +double +nexttoward(double x, long double y) +{ + int32_t hx,ix,iy; + u_int32_t lx,hy,ly,esy; + + EXTRACT_WORDS(hx,lx,x); + GET_LDOUBLE_WORDS(esy,hy,ly,y); + ix = hx&0x7fffffff; /* |x| */ + iy = esy&0x7fff; /* |y| */ + + if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) || /* x is nan */ + ((iy>=0x7fff)&&(hy|ly)!=0)) /* y is nan */ + return x+y; + if((long double) x==y) return y; /* x=y, return y */ + if((ix|lx)==0) { /* x == 0 */ + volatile double u; + INSERT_WORDS(x,(esy&0x8000)<<16,1); /* return +-minsub */ + u = x; + u = u * u; /* raise underflow flag */ + return x; + } + if(hx>=0) { /* x > 0 */ + if (esy>=0x8000||((ix>>20)&0x7ff)>iy-0x3c00 + || (((ix>>20)&0x7ff)==iy-0x3c00 + && (((hx<<11)|(lx>>21))>(hy&0x7fffffff) + || (((hx<<11)|(lx>>21))==(hy&0x7fffffff) + && (lx<<11)>ly)))) { /* x > y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x < y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } else { /* x < 0 */ + if (esy<0x8000||((ix>>20)&0x7ff)>iy-0x3c00 + || (((ix>>20)&0x7ff)==iy-0x3c00 + && (((hx<<11)|(lx>>21))>(hy&0x7fffffff) + || (((hx<<11)|(lx>>21))==(hy&0x7fffffff) + && (lx<<11)>ly)))) {/* x < y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x > y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } + hy = hx&0x7ff00000; + if(hy>=0x7ff00000) { + x = x+x; /* overflow */ + return x; + } + if(hy<0x00100000) { + volatile double u = x*x; /* underflow */ + if(u==x) { + INSERT_WORDS(x,hx,lx); + return x; + } + } + INSERT_WORDS(x,hx,lx); + return x; +} diff --git a/openlibm/ld80/s_nexttowardf.c b/openlibm/ld80/s_nexttowardf.c new file mode 100644 index 0000000000..f2ac10df0c --- /dev/null +++ b/openlibm/ld80/s_nexttowardf.c @@ -0,0 +1,67 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +#include + +#include "math_private.h" + +float +nexttowardf(float x, long double y) +{ + int32_t hx,ix,iy; + u_int32_t hy,ly,esy; + + GET_FLOAT_WORD(hx,x); + GET_LDOUBLE_WORDS(esy,hy,ly,y); + ix = hx&0x7fffffff; /* |x| */ + iy = esy&0x7fff; /* |y| */ + + if((ix>0x7f800000) || /* x is nan */ + (iy>=0x7fff&&((hy|ly)!=0))) /* y is nan */ + return x+y; + if((long double) x==y) return y; /* x=y, return y */ + if(ix==0) { /* x == 0 */ + volatile float u; + SET_FLOAT_WORD(x,((esy&0x8000)<<16)|1);/* return +-minsub*/ + u = x; + u = u * u; /* raise underflow flag */ + return x; + } + if(hx>=0) { /* x > 0 */ + if(esy>=0x8000||((ix>>23)&0xff)>iy-0x3f80 + || (((ix>>23)&0xff)==iy-0x3f80 + && ((ix&0x7fffff)<<8)>(hy&0x7fffffff))) {/* x > y, x -= ulp */ + hx -= 1; + } else { /* x < y, x += ulp */ + hx += 1; + } + } else { /* x < 0 */ + if(esy<0x8000||((ix>>23)&0xff)>iy-0x3f80 + || (((ix>>23)&0xff)==iy-0x3f80 + && ((ix&0x7fffff)<<8)>(hy&0x7fffffff))) {/* x < y, x -= ulp */ + hx -= 1; + } else { /* x > y, x += ulp */ + hx += 1; + } + } + hy = hx&0x7f800000; + if(hy>=0x7f800000) { + x = x+x; /* overflow */ + return x; + } + if(hy<0x00800000) { + volatile float u = x*x; /* underflow */ + } + SET_FLOAT_WORD(x,hx); + return x; +} diff --git a/openlibm/ld80/s_remquol.c b/openlibm/ld80/s_remquol.c new file mode 100644 index 0000000000..f649dca588 --- /dev/null +++ b/openlibm/ld80/s_remquol.c @@ -0,0 +1,166 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +#include + +#include +#include +#include + +#include "math_private.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +/* + * These macros add and remove an explicit integer bit in front of the + * fractional mantissa, if the architecture doesn't have such a bit by + * default already. + */ +#ifdef LDBL_IMPLICIT_NBIT +#define LDBL_NBIT 0 +#define SET_NBIT(hx) ((hx) | (1ULL << LDBL_MANH_SIZE)) +#define HFRAC_BITS EXT_FRACHBITS +#else +#define LDBL_NBIT 0x80000000 +#define SET_NBIT(hx) (hx) +#define HFRAC_BITS (EXT_FRACHBITS - 1) +#endif + +#define MANL_SHIFT (EXT_FRACLBITS - 1) + +static const long double Zero[] = {0.0L, -0.0L}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + * + * Assumptions: + * - The low part of the mantissa fits in a manl_t exactly. + * - The high part of the mantissa fits in an int64_t with enough room + * for an explicit integer bit in front of the fractional bits. + */ +long double +remquol(long double x, long double y, int *quo) +{ + int64_t hx,hz; /* We need a carry bit even if LDBL_MANH_SIZE is 32. */ + uint32_t hy; + uint32_t lx,ly,lz; + uint32_t esx, esy; + int ix,iy,n,q,sx,sxy; + + GET_LDOUBLE_WORDS(esx,hx,lx,x); + GET_LDOUBLE_WORDS(esy,hy,ly,y); + sx = esx & 0x8000; + sxy = sx ^ (esy & 0x8000); + esx &= 0x7fff; /* |x| */ + esy &= 0x7fff; /* |y| */ + SET_LDOUBLE_EXP(x,esx); + SET_LDOUBLE_EXP(y,esy); + + /* purge off exception values */ + if((esy|hy|ly)==0 || /* y=0 */ + (esx == BIAS + LDBL_MAX_EXP) || /* or x not finite */ + (esy == BIAS + LDBL_MAX_EXP && + ((hy&~LDBL_NBIT)|ly)!=0)) /* or y is NaN */ + return (x*y)/(x*y); + if(esx<=esy) { + if((esx>MANL_SHIFT); lx = lx+lx;} + else {hx = hz+hz+(lz>>MANL_SHIFT); lx = lz+lz; q++;} + q <<= 1; + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;q++;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) { /* return sign(x)*0 */ + *quo = (sxy ? -q : q); + return Zero[sx!=0]; + } + while(hx<(1ULL<>MANL_SHIFT); lx = lx+lx; + iy -= 1; + } + if (iy < LDBL_MIN_EXP) { + esx = (iy + BIAS + 512) & 0x7fff; + SET_LDOUBLE_WORDS(x,esx,hx,lx); + x *= 0x1p-512; + GET_LDOUBLE_WORDS(esx,hx,lx,x); + } else { + esx = (iy + BIAS) & 0x7fff; + } + SET_LDOUBLE_WORDS(x,esx,hx,lx); +fixup: + y = fabsl(y); + if (y < LDBL_MIN * 2) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5*y || (x==0.5*y && (q & 1))) { + q++; + x-=y; + } + + GET_LDOUBLE_EXP(esx,x); + esx ^= sx; + SET_LDOUBLE_EXP(x,esx); + + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} diff --git a/openlibm/ld80/s_tanhl.c b/openlibm/ld80/s_tanhl.c new file mode 100644 index 0000000000..1a46583272 --- /dev/null +++ b/openlibm/ld80/s_tanhl.c @@ -0,0 +1,79 @@ +/* @(#)s_tanh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* tanhl(x) + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanhl(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanhl(-x) = -tanhl(x). + * 2. 0 <= x <= 2**-55 : tanhl(x) := x*(one+x) + * -t + * 2**-55 < x <= 1 : tanhl(x) := -----; t = expm1l(-2x) + * t + 2 + * 2 + * 1 <= x <= 23.0 : tanhl(x) := 1- ----- ; t=expm1l(2x) + * t + 2 + * 23.0 < x <= INF : tanhl(x) := 1. + * + * Special cases: + * tanhl(NaN) is NaN; + * only tanhl(0)=0 is exact for finite argument. + */ + +#include + +#include "math_private.h" + +static const long double one=1.0, two=2.0, tiny = 1.0e-4900L; + +long double +tanhl(long double x) +{ + long double t,z; + int32_t se; + u_int32_t jj0,jj1,ix; + + /* High word of |x|. */ + GET_LDOUBLE_WORDS(se,jj0,jj1,x); + ix = se&0x7fff; + + /* x is INF or NaN */ + if(ix==0x7fff) { + /* for NaN it's not important which branch: tanhl(NaN) = NaN */ + if (se&0x8000) return one/x-one; /* tanhl(-inf)= -1; */ + else return one/x+one; /* tanhl(+inf)=+1 */ + } + + /* |x| < 23 */ + if (ix < 0x4003 || (ix == 0x4003 && jj0 < 0xb8000000u)) {/* |x|<23 */ + if ((ix|jj0|jj1) == 0) + return x; /* x == +- 0 */ + if (ix<0x3fc8) /* |x|<2**-55 */ + return x*(one+tiny); /* tanh(small) = small */ + if (ix>=0x3fff) { /* |x|>=1 */ + t = expm1l(two*fabsl(x)); + z = one - two/(t+two); + } else { + t = expm1l(-two*fabsl(x)); + z= -t/(t+two); + } + /* |x| > 23, return +-1 */ + } else { + z = one - tiny; /* raised inexact flag */ + } + return (se&0x8000)? -z: z; +} diff --git a/openlibm/ld80/s_truncl.c b/openlibm/ld80/s_truncl.c new file mode 100644 index 0000000000..8a261d8ce6 --- /dev/null +++ b/openlibm/ld80/s_truncl.c @@ -0,0 +1,72 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * From: @(#)s_floor.c 5.1 93/09/24 + */ + +/* + * truncl(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to truncl(x). + */ + +#include +//#include + +#include +#include +#include + +#include "math_private.h" + +#ifdef LDBL_IMPLICIT_NBIT +#define MANH_SIZE (EXT_FRACHBITS + 1) +#else +#define MANH_SIZE EXT_FRACHBITS +#endif + +static const long double huge = 1.0e300; +static const float zero[] = { 0.0, -0.0 }; + +long double +truncl(long double x) +{ + int e, es; + uint32_t ix0, ix1; + + GET_LDOUBLE_WORDS(es,ix0,ix1,x); + e = (es&0x7fff) - LDBL_MAX_EXP + 1; + + if (e < MANH_SIZE - 1) { + if (e < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) + return (zero[(es&0x8000)!=0]); + } else { + uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1); + if (((ix0 & m) | ix1) == 0) + return (x); /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + ix0 &= ~m; + ix1 = 0; + } + } + } else if (e < LDBL_MANT_DIG - 1) { + uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1); + if ((ix1 & m) == 0) + return (x); /* x is integral */ + if (huge + x > 0.0) /* raise inexact flag */ + ix1 &= ~m; + } + SET_LDOUBLE_WORDS(x,es,ix0,ix1); + return (x); +} diff --git a/openlibm/loongarch64/Make.files b/openlibm/loongarch64/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/loongarch64/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/loongarch64/fenv.c b/openlibm/loongarch64/fenv.c new file mode 100644 index 0000000000..9c95f2aa6e --- /dev/null +++ b/openlibm/loongarch64/fenv.c @@ -0,0 +1,27 @@ +#define __fenv_static +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +/* + * Hopefully the system ID byte is immutable, so it's valid to use + * this as a default environment. + */ +const fenv_t __fe_dfl_env = 0; + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); +extern inline int feenableexcept(int __mask); +extern inline int fedisableexcept(int __mask); +extern inline int fegetexcept(void); \ No newline at end of file diff --git a/openlibm/mips/Make.files b/openlibm/mips/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/mips/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/mips/fenv-softfloat.h b/openlibm/mips/fenv-softfloat.h new file mode 100644 index 0000000000..0511335971 --- /dev/null +++ b/openlibm/mips/fenv-softfloat.h @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 2004-2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FENV_H_ +#error "This file is meant to be included only by ." +#endif + +/* + * This file implements the functionality of on platforms that + * lack an FPU and use softfloat in libc for floating point. To use it, + * you must write an that provides the following: + * + * - a typedef for fenv_t, which may be an integer or struct type + * - a typedef for fexcept_t (XXX This file assumes fexcept_t is a + * simple integer type containing the exception mask.) + * - definitions of FE_* constants for the five exceptions and four + * rounding modes in IEEE 754, as described in fenv(3) + * - a definition, and the corresponding external symbol, for FE_DFL_ENV + * - a macro __set_env(env, flags, mask, rnd), which sets the given fenv_t + * from the exception flags, mask, and rounding mode + * - macros __env_flags(env), __env_mask(env), and __env_round(env), which + * extract fields from an fenv_t + * - a definition of __fenv_static + * + * If the architecture supports an optional FPU, it's recommended that you + * define fenv_t and fexcept_t to match the hardware ABI. Otherwise, it + * doesn't matter how you define them. + */ + +extern int __softfloat_float_exception_flags; +extern int __softfloat_float_exception_mask; +extern int __softfloat_float_rounding_mode; +void __softfloat_float_raise(int); + +__fenv_static inline int +feclearexcept(int __excepts) +{ + + __softfloat_float_exception_flags &= ~__excepts; + return (0); +} + +__fenv_static inline int +fegetexceptflag(fexcept_t *__flagp, int __excepts) +{ + + *__flagp = __softfloat_float_exception_flags & __excepts; + return (0); +} + +__fenv_static inline int +fesetexceptflag(const fexcept_t *__flagp, int __excepts) +{ + + __softfloat_float_exception_flags &= ~__excepts; + __softfloat_float_exception_flags |= *__flagp & __excepts; + return (0); +} + +__fenv_static inline int +feraiseexcept(int __excepts) +{ + + __softfloat_float_raise(__excepts); + return (0); +} + +__fenv_static inline int +fetestexcept(int __excepts) +{ + + return (__softfloat_float_exception_flags & __excepts); +} + +__fenv_static inline int +fegetround(void) +{ + + return (__softfloat_float_rounding_mode); +} + +__fenv_static inline int +fesetround(int __round) +{ + + __softfloat_float_rounding_mode = __round; + return (0); +} + +__fenv_static inline int +fegetenv(fenv_t *__envp) +{ + + __set_env(*__envp, __softfloat_float_exception_flags, + __softfloat_float_exception_mask, __softfloat_float_rounding_mode); + return (0); +} + +__fenv_static inline int +feholdexcept(fenv_t *__envp) +{ + fenv_t __env; + + fegetenv(__envp); + __softfloat_float_exception_flags = 0; + __softfloat_float_exception_mask = 0; + return (0); +} + +__fenv_static inline int +fesetenv(const fenv_t *__envp) +{ + + __softfloat_float_exception_flags = __env_flags(*__envp); + __softfloat_float_exception_mask = __env_mask(*__envp); + __softfloat_float_rounding_mode = __env_round(*__envp); + return (0); +} + +__fenv_static inline int +feupdateenv(const fenv_t *__envp) +{ + int __oflags = __softfloat_float_exception_flags; + + fesetenv(__envp); + feraiseexcept(__oflags); + return (0); +} + +#if __BSD_VISIBLE + +/* We currently provide no external definitions of the functions below. */ + +__fenv_static inline int +feenableexcept(int __mask) +{ + int __omask = __softfloat_float_exception_mask; + + __softfloat_float_exception_mask |= __mask; + return (__omask); +} + +__fenv_static inline int +fedisableexcept(int __mask) +{ + int __omask = __softfloat_float_exception_mask; + + __softfloat_float_exception_mask &= ~__mask; + return (__omask); +} + +__fenv_static inline int +fegetexcept(void) +{ + + return (__softfloat_float_exception_mask); +} + +#endif /* __BSD_VISIBLE */ \ No newline at end of file diff --git a/openlibm/mips/fenv.c b/openlibm/mips/fenv.c new file mode 100644 index 0000000000..4a7ed80ca0 --- /dev/null +++ b/openlibm/mips/fenv.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define __fenv_static +#include "openlibm_fenv.h" + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +/* + * Hopefully the system ID byte is immutable, so it's valid to use + * this as a default environment. + */ +const fenv_t __fe_dfl_env = 0; + +#ifdef __mips_soft_float +#define __set_env(env, flags, mask, rnd) env = ((flags) \ + | (mask)<<_FPUSW_SHIFT \ + | (rnd) << 24) +#define __env_flags(env) ((env) & FE_ALL_EXCEPT) +#define __env_mask(env) (((env) >> _FPUSW_SHIFT) \ + & FE_ALL_EXCEPT) +#define __env_round(env) (((env) >> 24) & _ROUND_MASK) +#include "fenv-softfloat.h" +#endif + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); +extern inline int feenableexcept(int __mask); +extern inline int fedisableexcept(int __mask); +extern inline int fegetexcept(void); + diff --git a/openlibm/openlibm.pc.in b/openlibm/openlibm.pc.in new file mode 100644 index 0000000000..bfad87621e --- /dev/null +++ b/openlibm/openlibm.pc.in @@ -0,0 +1,6 @@ +Name: openlibm +Version: ${version} +URL: https://github.com/JuliaMath/openlibm +Description: High quality system independent, open source libm. +Cflags: -I${includedir} +Libs: -L${libdir} -lopenlibm diff --git a/openlibm/powerpc/Make.files b/openlibm/powerpc/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/powerpc/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/powerpc/fenv.c b/openlibm/powerpc/fenv.c new file mode 100644 index 0000000000..bf7d0cbe12 --- /dev/null +++ b/openlibm/powerpc/fenv.c @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +const fenv_t __fe_dfl_env = 0x00000000; + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); diff --git a/openlibm/riscv64/Make.files b/openlibm/riscv64/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/riscv64/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/riscv64/fenv.c b/openlibm/riscv64/fenv.c new file mode 100644 index 0000000000..0905092242 --- /dev/null +++ b/openlibm/riscv64/fenv.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/lib/msun/riscv/fenv.c 332792 2018-04-19 20:36:15Z brooks $ + */ + +#define __fenv_static +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +/* + * Hopefully the system ID byte is immutable, so it's valid to use + * this as a default environment. + */ +const fenv_t __fe_dfl_env = {0}; + +#ifdef __riscv_float_abi_soft +#define __set_env(env, flags, mask, rnd) env = ((flags) | (rnd) << 5) +#define __env_flags(env) ((env) & FE_ALL_EXCEPT) +#define __env_mask(env) (0) /* No exception traps. */ +#define __env_round(env) (((env) >> 5) & _ROUND_MASK) +#include "fenv-softfloat.h" +#endif + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); +extern inline int feenableexcept(int __mask); +extern inline int fedisableexcept(int __mask); +extern inline int fegetexcept(void); diff --git a/openlibm/s390/Make.files b/openlibm/s390/Make.files new file mode 100644 index 0000000000..483a7ccc75 --- /dev/null +++ b/openlibm/s390/Make.files @@ -0,0 +1 @@ +$(CUR_SRCS) = fenv.c diff --git a/openlibm/s390/fenv.c b/openlibm/s390/fenv.c new file mode 100644 index 0000000000..2bcce76048 --- /dev/null +++ b/openlibm/s390/fenv.c @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2016 Dan Horák + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * cloned from ppc/fenv.c + */ + +#define __fenv_static +#include + +#ifdef __GNUC_GNU_INLINE__ +#error "This file must be compiled with C99 'inline' semantics" +#endif + +const fenv_t __fe_dfl_env = 0x00000000; + +extern inline int feclearexcept(int __excepts); +extern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); +extern inline int fesetexceptflag(const fexcept_t *__flagp, int __excepts); +extern inline int feraiseexcept(int __excepts); +extern inline int fetestexcept(int __excepts); +extern inline int fegetround(void); +extern inline int fesetround(int __round); +extern inline int fegetenv(fenv_t *__envp); +extern inline int feholdexcept(fenv_t *__envp); +extern inline int fesetenv(const fenv_t *__envp); +extern inline int feupdateenv(const fenv_t *__envp); diff --git a/openlibm/src/Make.files b/openlibm/src/Make.files new file mode 100644 index 0000000000..23797c5a11 --- /dev/null +++ b/openlibm/src/Make.files @@ -0,0 +1,78 @@ +$(CUR_SRCS) = common.c \ + e_acos.c e_acosf.c e_acosh.c e_acoshf.c e_asin.c e_asinf.c \ + e_atan2.c e_atan2f.c e_atanh.c e_atanhf.c e_cosh.c e_coshf.c e_exp.c \ + e_expf.c e_fmod.c e_fmodf.c \ + e_hypot.c e_hypotf.c e_j0.c e_j0f.c e_j1.c e_j1f.c \ + e_jn.c e_jnf.c e_lgamma.c e_lgamma_r.c e_lgammaf.c e_lgammaf_r.c \ + e_log.c e_log10.c e_log10f.c e_log2.c e_log2f.c e_logf.c \ + e_pow.c e_powf.c e_remainder.c e_remainderf.c \ + e_rem_pio2.c e_rem_pio2f.c \ + e_sinh.c e_sinhf.c e_sqrt.c e_sqrtf.c \ + k_cos.c k_exp.c k_expf.c k_rem_pio2.c k_sin.c k_tan.c \ + k_cosf.c k_sinf.c k_tanf.c \ + s_asinh.c s_asinhf.c s_atan.c s_atanf.c s_carg.c s_cargf.c \ + s_cbrt.c s_cbrtf.c s_ceil.c s_ceilf.c \ + s_copysign.c s_copysignf.c s_copysignl.c s_cos.c s_cosf.c \ + s_csqrt.c s_csqrtf.c s_erf.c s_erff.c \ + s_exp2.c s_exp2f.c s_expm1.c s_expm1f.c s_fabs.c s_fabsf.c s_fdim.c \ + s_floor.c s_floorf.c \ + s_fmax.c s_fmaxf.c s_fmin.c \ + s_fminf.c s_fpclassify.c \ + s_frexp.c s_frexpf.c s_ilogb.c s_ilogbf.c \ + s_isinf.c s_isfinite.c s_isnormal.c s_isnan.c \ + s_log1p.c s_log1pf.c s_logb.c s_logbf.c \ + s_modf.c s_modff.c \ + s_nextafter.c s_nextafterf.c \ + s_nexttowardf.c s_remquo.c s_remquof.c \ + s_rint.c s_rintf.c s_round.c s_roundf.c \ + s_scalbln.c s_scalbn.c s_scalbnf.c s_scalbnl.c s_signbit.c \ + s_signgam.c s_sin.c s_sincos.c \ + s_sinf.c s_sincosf.c s_tan.c s_tanf.c s_tanh.c s_tanhf.c s_tgammaf.c \ + s_trunc.c s_truncf.c s_cpow.c s_cpowf.c \ + w_cabs.c w_cabsf.c + +ifneq ($(ARCH), wasm32) + +$(CUR_SRCS) += \ + s_fma.c s_fmaf.c s_lrint.c s_lrintf.c s_lround.c s_lroundf.c \ + s_llrint.c s_llrintf.c s_llround.c s_llroundf.c s_nearbyint.c + +ifneq ($(OS), WINNT) +$(CUR_SRCS) += s_nan.c +endif + +endif + +# Add in long double functions for x86, x64 and aarch64 +ifeq ($(LONG_DOUBLE_NOT_DOUBLE),1) +# C99 long double functions +$(CUR_SRCS) += s_fabsl.c s_llrintl.c s_lrintl.c s_modfl.c + +# If long double != double use these; otherwise, we alias the double versions. +$(CUR_SRCS) += e_acosl.c e_asinl.c e_atan2l.c e_fmodl.c \ + s_fmaxl.c s_fminl.c s_ilogbl.c \ + e_hypotl.c e_lgammal.c e_remainderl.c e_sqrtl.c \ + s_atanl.c s_ceill.c s_cosl.c s_cprojl.c \ + s_csqrtl.c s_floorl.c s_fmal.c \ + s_frexpl.c s_logbl.c s_nexttoward.c \ + s_remquol.c s_roundl.c s_lroundl.c s_llroundl.c \ + s_cpowl.c s_cargl.c \ + s_sinl.c s_sincosl.c s_tanl.c s_truncl.c w_cabsl.c \ + s_nextafterl.c s_rintl.c polevll.c \ + s_casinl.c s_ctanl.c \ + s_cimagl.c s_conjl.c s_creall.c s_cacoshl.c s_catanhl.c s_casinhl.c \ + s_catanl.c s_csinl.c s_cacosl.c s_cexpl.c s_csinhl.c s_ccoshl.c \ + s_clogl.c s_ctanhl.c s_ccosl.c s_cbrtl.c +endif + +# C99 complex functions +$(CUR_SRCS) += s_ccosh.c s_ccoshf.c s_cexp.c s_cexpf.c \ + s_cimag.c s_cimagf.c \ + s_conj.c s_conjf.c \ + s_cproj.c s_cprojf.c s_creal.c s_crealf.c \ + s_csinh.c s_csinhf.c s_ctanh.c s_ctanhf.c \ + s_cacos.c s_cacosf.c \ + s_cacosh.c s_cacoshf.c \ + s_casin.c s_casinf.c s_casinh.c s_casinhf.c \ + s_catan.c s_catanf.c s_catanh.c s_catanhf.c \ + s_clog.c s_clogf.c diff --git a/openlibm/src/aarch64_fpmath.h b/openlibm/src/aarch64_fpmath.h new file mode 100644 index 0000000000..e1fdc63e91 --- /dev/null +++ b/openlibm/src/aarch64_fpmath.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2002, 2003 David Schultz + * Copyright (2) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/lib/libc/aarch64/_fpmath.h 281197 2015-04-07 09:52:14Z andrew $ + */ + +#include + +union IEEEl2bits { + long double e; + struct { + uint64_t manl :64; + uint64_t manh :48; + unsigned int exp :15; + unsigned int sign :1; + } bits; + /* TODO andrew: Check the packing here */ + struct { + uint64_t manl :64; + uint64_t manh :48; + unsigned int expsign :16; + } xbits; +}; + +#define LDBL_NBIT 0 +#define LDBL_IMPLICIT_NBIT +#define mask_nbit_l(u) ((void)0) + +#define LDBL_MANH_SIZE 48 +#define LDBL_MANL_SIZE 64 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)((u).bits.manl >> 32); \ + (a)[2] = (uint32_t)(u).bits.manh; \ + (a)[3] = (uint32_t)((u).bits.manh >> 32); \ +} while(0) diff --git a/openlibm/src/amd64_fpmath.h b/openlibm/src/amd64_fpmath.h new file mode 100644 index 0000000000..41b01c3d39 --- /dev/null +++ b/openlibm/src/amd64_fpmath.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2002, 2003 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libc/amd64/_fpmath.h,v 1.7 2008/01/17 16:39:06 bde Exp $ + */ + +union IEEEl2bits { + long double e; + struct { + unsigned int manl :32; + unsigned int manh :32; + unsigned int exp :15; + unsigned int sign :1; + unsigned int junkl :16; + unsigned int junkh :32; + } bits; + struct { + unsigned long man :64; + unsigned int expsign :16; + unsigned long junk :48; + } xbits; +}; + +#define LDBL_NBIT 0x80000000 +#define mask_nbit_l(u) ((u).bits.manh &= ~LDBL_NBIT) + +#define LDBL_MANH_SIZE 32 +#define LDBL_MANL_SIZE 32 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)(u).bits.manh; \ +} while (0) diff --git a/openlibm/src/bsd_cdefs.h b/openlibm/src/bsd_cdefs.h new file mode 100644 index 0000000000..579919304e --- /dev/null +++ b/openlibm/src/bsd_cdefs.h @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.8 (Berkeley) 1/9/95 + * $FreeBSD: src/sys/sys/cdefs.h,v 1.114 2011/02/18 21:44:53 nwhitehorn Exp $ + */ + +/* Do not redefine macros if the system provides them in sys/cdefs.h. + * The two macros correspond to different platforms. */ +#ifndef _BSD_CDEFS_H_ +#define _BSD_CDEFS_H_ + +/* + * This code has been put in place to help reduce the addition of + * compiler specific defines in FreeBSD code. It helps to aid in + * having a compiler-agnostic source tree. + */ + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) + +#if __GNUC__ >= 3 || defined(__INTEL_COMPILER) +#define __GNUCLIKE_ASM 3 +#else +#define __GNUCLIKE_ASM 2 +#endif + +#define __CC_SUPPORTS___INLINE__ 1 + +#endif /* __GNUC__ || __INTEL_COMPILER */ + +#if defined(__STDC__) || defined(__cplusplus) + +#define __volatile volatile +#if defined(__cplusplus) +#define __inline inline /* convert to C++ keyword */ +#else +#if !defined(__CC_SUPPORTS___INLINE) +#define __inline /* delete GCC keyword */ +#endif /* ! __CC_SUPPORTS___INLINE */ +#endif /* !__cplusplus */ + +#else /* !(__STDC__ || __cplusplus) */ + +#if !defined(__CC_SUPPORTS___INLINE) +#define __inline +#define __volatile +#endif /* !__CC_SUPPORTS___INLINE */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * Macro to test if we're using a specific version of gcc or later. + */ +#ifndef __GNUC_PREREQ__ +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +#define __GNUC_PREREQ__(ma, mi) \ + (__GNUC__ > (ma) || __GNUC__ == (ma) && __GNUC_MINOR__ >= (mi)) +#else +#define __GNUC_PREREQ__(ma, mi) 0 +#endif +#endif /* __GNUC_PREREQ__ */ + +/* + * Compiler-dependent macro to help declare pure (no side effects) functions. + * It is null except for versions of gcc that are known to support the features + * properly (old versions of gcc-2 supported the dead and pure features + * in a different (wrong) way), and for icc. If we do not provide an implementation + * for a given compiler, let the compile fail if it is told to use + * a feature that we cannot live without. + */ +#if !defined(__pure2) && (__GNUC_PREREQ__(2, 7) || defined(__INTEL_COMPILER)) +#define __pure2 __attribute__((__const__)) +#endif + +#endif /* !_BSD_CDEFS_H_ */ diff --git a/openlibm/src/cdefs-compat.h b/openlibm/src/cdefs-compat.h new file mode 100644 index 0000000000..834c5478a3 --- /dev/null +++ b/openlibm/src/cdefs-compat.h @@ -0,0 +1,105 @@ +#ifndef _CDEFS_COMPAT_H_ +#define _CDEFS_COMPAT_H_ + +#if !defined(__BEGIN_DECLS) +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif +#endif /* !defined(__BEGIN_DECLS) */ + +#ifdef __GNUC__ +#if defined(__strong_alias) && defined(__NetBSD__) +#define openlibm_strong_reference(sym,alias) __strong_alias(alias,sym) +#elif defined(__strong_reference) +#define openlibm_strong_reference(sym,alias) __strong_reference(sym,alias) +#else +#ifdef __APPLE__ +#define openlibm_strong_reference(sym,aliassym) openlibm_weak_reference(sym,aliassym) +#else +#define openlibm_strong_reference(sym,aliassym) \ + OLM_DLLEXPORT extern __typeof (aliassym) aliassym __attribute__ ((__alias__ (#sym))); +#endif /* __APPLE__ */ +#endif /* __strong_reference */ + +#ifdef __wasm__ +#define openlibm_weak_reference(sym,alias) openlibm_strong_reference(sym,alias) +#elif defined(__weak_alias) && defined(__NetBSD__) +#define openlibm_weak_reference(sym,alias) __weak_alias(alias,sym) +#elif defined(__weak_reference) +#define openlibm_weak_reference(sym,alias) __weak_reference(sym,alias) +#else +#ifdef __ELF__ +#ifdef __STDC__ +#define openlibm_weak_reference(sym,alias) \ + __asm__(".weak " #alias); \ + __asm__(".equ " #alias ", " #sym) +#ifdef __warn_references +#define openlibm_warn_references(sym,msg) __warn_references(sym,msg) +#else +#define openlibm_warn_references(sym,msg) \ + __asm__(".section .gnu.warning." #sym); \ + __asm__(".asciz \"" msg "\""); \ + __asm__(".previous") +#endif /* __warn_references */ +#else +#define openlibm_weak_reference(sym,alias) \ + __asm__(".weak alias"); \ + __asm__(".equ alias, sym") +#ifdef __warn_references +#define openlibm_warn_references(sym,msg) __warn_references(sym,msg) +#else +#define openlibm_warn_references(sym,msg) \ + __asm__(".section .gnu.warning.sym"); \ + __asm__(".asciz \"msg\""); \ + __asm__(".previous") +#endif /* __warn_references */ +#endif /* __STDC__ */ +#elif defined(__clang__) /* CLANG */ +#if defined(_WIN32) && defined(_X86_) +#define openlibm_asm_symbol_prefix "_" +#else +#define openlibm_asm_symbol_prefix "" +#endif +#ifdef __STDC__ +#define openlibm_weak_reference(sym,alias) \ + __asm__(".weak_reference " openlibm_asm_symbol_prefix #alias); \ + __asm__(".set " openlibm_asm_symbol_prefix #alias ", " openlibm_asm_symbol_prefix #sym) +#else +#define openlibm_weak_reference(sym,alias) \ + __asm__(".weak_reference openlibm_asm_symbol_prefix/**/alias");\ + __asm__(".set openlibm_asm_symbol_prefix/**/alias, openlibm_asm_symbol_prefix/**/sym") +#endif +#else /* !__ELF__ */ +#ifdef __STDC__ +#define openlibm_weak_reference(sym,alias) \ + __asm__(".stabs \"_" #alias "\",11,0,0,0"); \ + __asm__(".stabs \"_" #sym "\",1,0,0,0") +#ifdef __warn_references +#define openlibm_warn_references(sym,msg) __warn_references(sym,msg) +#else +#define openlibm_warn_references(sym,msg) \ + __asm__(".stabs \"" msg "\",30,0,0,0"); \ + __asm__(".stabs \"_" #sym "\",1,0,0,0") +#endif /* __warn_references */ +#else +#define openlibm_weak_reference(sym,alias) \ + __asm__(".stabs \"_/**/alias\",11,0,0,0"); \ + __asm__(".stabs \"_/**/sym\",1,0,0,0") +#ifdef __warn_references +#define openlibm_warn_references(sym,msg) __warn_references(sym,msg) +#else +#define openlibm_warn_references(sym,msg) \ + __asm__(".stabs msg,30,0,0,0"); \ + __asm__(".stabs \"_/**/sym\",1,0,0,0") +#endif /* __warn_references */ +#endif /* __STDC__ */ +#endif /* __ELF__ */ +#endif /* __weak_reference */ +#endif /* __GNUC__ */ + + +#endif /* _CDEFS_COMPAT_H_ */ diff --git a/openlibm/src/common.c b/openlibm/src/common.c new file mode 100644 index 0000000000..b02d697d80 --- /dev/null +++ b/openlibm/src/common.c @@ -0,0 +1,7 @@ +#include + +#include "math_private.h" + +OLM_DLLEXPORT int isopenlibm(void) { + return 1; +} diff --git a/openlibm/src/e_acos.c b/openlibm/src/e_acos.c new file mode 100644 index 0000000000..7295ad649b --- /dev/null +++ b/openlibm/src/e_acos.c @@ -0,0 +1,111 @@ + +/* @(#)e_acos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_acos.c,v 1.13 2008/07/31 22:41:26 das Exp $"); + +/* __ieee754_acos(x) + * Method : + * acos(x) = pi/2 - asin(x) + * acos(-x) = pi/2 + asin(x) + * For |x|<=0.5 + * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c) + * For x>0.5 + * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) + * = 2asin(sqrt((1-x)/2)) + * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) + * = 2f + (2c + 2s*z*R(z)) + * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term + * for f so that f+c ~ sqrt(z). + * For x<-0.5 + * acos(x) = pi - 2asin(sqrt((1-|x|)/2)) + * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + * Function needed: sqrt + */ + +#include +#include + +#include "math_private.h" + +static const double +one= 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */ +pio2_hi = 1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */ +static volatile double +pio2_lo = 6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */ +static const double +pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ +pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ +pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ +pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ +pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ +pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ +qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ +qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ +qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ + +OLM_DLLEXPORT double +__ieee754_acos(double x) +{ + double z,p,q,r,w,s,c,df; + int32_t hx,ix; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x3ff00000) { /* |x| >= 1 */ + u_int32_t lx; + GET_LOW_WORD(lx,x); + if(((ix-0x3ff00000)|lx)==0) { /* |x|==1 */ + if(hx>0) return 0.0; /* acos(1) = 0 */ + else return pi+2.0*pio2_lo; /* acos(-1)= pi */ + } + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(ix<0x3fe00000) { /* |x| < 0.5 */ + if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/ + z = x*x; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + return pio2_hi - (x - (pio2_lo-x*r)); + } else if (hx<0) { /* x < -0.5 */ + z = (one+x)*0.5; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + s = sqrt(z); + r = p/q; + w = r*s-pio2_lo; + return pi - 2.0*(s+w); + } else { /* x > 0.5 */ + z = (one-x)*0.5; + s = sqrt(z); + df = s; + SET_LOW_WORD(df,0); + c = (z-df*df)/(s+df); + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + w = r*s+c; + return 2.0*(df+w); + } +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(acos, acosl); +#endif diff --git a/openlibm/src/e_acosf.c b/openlibm/src/e_acosf.c new file mode 100644 index 0000000000..2beab5c2da --- /dev/null +++ b/openlibm/src/e_acosf.c @@ -0,0 +1,78 @@ +/* e_acosf.c -- float version of e_acos.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_acosf.c,v 1.11 2008/08/03 17:39:54 das Exp $"); + +#include + +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3F800000 */ +pi = 3.1415925026e+00, /* 0x40490fda */ +pio2_hi = 1.5707962513e+00; /* 0x3fc90fda */ +static volatile float +pio2_lo = 7.5497894159e-08; /* 0x33a22168 */ +static const float +pS0 = 1.6666586697e-01, +pS1 = -4.2743422091e-02, +pS2 = -8.6563630030e-03, +qS1 = -7.0662963390e-01; + +OLM_DLLEXPORT float +__ieee754_acosf(float x) +{ + float z,p,q,r,w,s,c,df; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x3f800000) { /* |x| >= 1 */ + if(ix==0x3f800000) { /* |x| == 1 */ + if(hx>0) return 0.0; /* acos(1) = 0 */ + else return pi+(float)2.0*pio2_lo; /* acos(-1)= pi */ + } + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(ix<0x3f000000) { /* |x| < 0.5 */ + if(ix<=0x32800000) return pio2_hi+pio2_lo;/*if|x|<2**-26*/ + z = x*x; + p = z*(pS0+z*(pS1+z*pS2)); + q = one+z*qS1; + r = p/q; + return pio2_hi - (x - (pio2_lo-x*r)); + } else if (hx<0) { /* x < -0.5 */ + z = (one+x)*(float)0.5; + p = z*(pS0+z*(pS1+z*pS2)); + q = one+z*qS1; + s = sqrtf(z); + r = p/q; + w = r*s-pio2_lo; + return pi - (float)2.0*(s+w); + } else { /* x > 0.5 */ + int32_t idf; + z = (one-x)*(float)0.5; + s = sqrtf(z); + df = s; + GET_FLOAT_WORD(idf,df); + SET_FLOAT_WORD(df,idf&0xfffff000); + c = (z-df*df)/(s+df); + p = z*(pS0+z*(pS1+z*pS2)); + q = one+z*qS1; + r = p/q; + w = r*s+c; + return (float)2.0*(df+w); + } +} diff --git a/openlibm/src/e_acosh.c b/openlibm/src/e_acosh.c new file mode 100644 index 0000000000..1e783a6879 --- /dev/null +++ b/openlibm/src/e_acosh.c @@ -0,0 +1,68 @@ + +/* @(#)e_acosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_acosh.c,v 1.9 2008/02/22 02:30:34 das Exp $"); + +/* __ieee754_acosh(x) + * Method : + * Based on + * acosh(x) = log [ x + sqrt(x*x-1) ] + * we have + * acosh(x) := log(x)+ln2, if x is large; else + * acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else + * acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1. + * + * Special cases: + * acosh(x) is NaN with signal if x<1. + * acosh(NaN) is NaN without signal. + */ + +#include +#include + +#include "math_private.h" + +static const double +one = 1.0, +ln2 = 6.93147180559945286227e-01; /* 0x3FE62E42, 0xFEFA39EF */ + +OLM_DLLEXPORT double +__ieee754_acosh(double x) +{ + double t; + int32_t hx; + u_int32_t lx; + EXTRACT_WORDS(hx,lx,x); + if(hx<0x3ff00000) { /* x < 1 */ + return (x-x)/(x-x); + } else if(hx >=0x41b00000) { /* x > 2**28 */ + if(hx >=0x7ff00000) { /* x is inf of NaN */ + return x+x; + } else + return __ieee754_log(x)+ln2; /* acosh(huge)=log(2x) */ + } else if(((hx-0x3ff00000)|lx)==0) { + return 0.0; /* acosh(1) = 0 */ + } else if (hx > 0x40000000) { /* 2**28 > x > 2 */ + t=x*x; + return __ieee754_log(2.0*x-one/(x+sqrt(t-one))); + } else { /* 1 + +#include "math_private.h" + +static const float +one = 1.0, +ln2 = 6.9314718246e-01; /* 0x3f317218 */ + +OLM_DLLEXPORT float +__ieee754_acoshf(float x) +{ + float t; + int32_t hx; + GET_FLOAT_WORD(hx,x); + if(hx<0x3f800000) { /* x < 1 */ + return (x-x)/(x-x); + } else if(hx >=0x4d800000) { /* x > 2**28 */ + if(hx >=0x7f800000) { /* x is inf of NaN */ + return x+x; + } else + return __ieee754_logf(x)+ln2; /* acosh(huge)=log(2x) */ + } else if (hx==0x3f800000) { + return 0.0; /* acosh(1) = 0 */ + } else if (hx > 0x40000000) { /* 2**28 > x > 2 */ + t=x*x; + return __ieee754_logf((float)2.0*x-one/(x+__ieee754_sqrtf(t-one))); + } else { /* 1. + */ + +#include +#include + +#include "invtrig.h" +#include "math_private.h" + +static const long double +one= 1.00000000000000000000e+00; + +#ifdef __i386__ +/* XXX Work around the fact that gcc truncates long double constants on i386 */ +static volatile double +pi1 = 3.14159265358979311600e+00, /* 0x1.921fb54442d18p+1 */ +pi2 = 1.22514845490862001043e-16; /* 0x1.1a80000000000p-53 */ +#define pi ((long double)pi1 + pi2) +#else +static const long double +pi = 3.14159265358979323846264338327950280e+00L; +#endif + +OLM_DLLEXPORT long double +acosl(long double x) +{ + union IEEEl2bits u; + long double z,p,q,r,w,s,c,df; + int16_t expsign, expt; + u.e = x; + expsign = u.xbits.expsign; + expt = expsign & 0x7fff; + if(expt >= BIAS) { /* |x| >= 1 */ + if(expt==BIAS && ((u.bits.manh&~LDBL_NBIT)|u.bits.manl)==0) { + if (expsign>0) return 0.0; /* acos(1) = 0 */ + else return pi+2.0*pio2_lo; /* acos(-1)= pi */ + } + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(expt 0.5 */ + z = (one-x)*0.5; + s = sqrtl(z); + u.e = s; + u.bits.manl = 0; + df = u.e; + c = (z-df*df)/(s+df); + p = P(z); + q = Q(z); + r = p/q; + w = r*s+c; + return 2.0*(df+w); + } +} diff --git a/openlibm/src/e_asin.c b/openlibm/src/e_asin.c new file mode 100644 index 0000000000..e287bf1cf2 --- /dev/null +++ b/openlibm/src/e_asin.c @@ -0,0 +1,117 @@ + +/* @(#)e_asin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_asin.c,v 1.15 2011/02/10 07:37:50 das Exp $"); + +/* __ieee754_asin(x) + * Method : + * Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... + * we approximate asin(x) on [0,0.5] by + * asin(x) = x + x*x^2*R(x^2) + * where + * R(x^2) is a rational approximation of (asin(x)-x)/x^3 + * and its remez error is bounded by + * |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) + * + * For x in [0.5,1] + * asin(x) = pi/2-2*asin(sqrt((1-x)/2)) + * Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; + * then for x>0.98 + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) + * For x<=0.98, let pio4_hi = pio2_hi/2, then + * f = hi part of s; + * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) + * and + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) + * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + */ + +#include +#include + +#include "math_private.h" + +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +huge = 1.000e+300, +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ +pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ +pio4_hi = 7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */ + /* coefficient for R(x^2) */ +pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ +pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ +pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ +pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ +pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ +pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ +qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ +qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ +qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ + +OLM_DLLEXPORT double +__ieee754_asin(double x) +{ + double t=0.0,w,p,q,c,r,s; + int32_t hx,ix; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>= 0x3ff00000) { /* |x|>= 1 */ + u_int32_t lx; + GET_LOW_WORD(lx,x); + if(((ix-0x3ff00000)|lx)==0) + /* asin(1)=+-pi/2 with inexact */ + return x*pio2_hi+x*pio2_lo; + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3fe00000) { /* |x|<0.5 */ + if(ix<0x3e500000) { /* if |x| < 2**-26 */ + if(huge+x>one) return x;/* return x with inexact if x!=0*/ + } + t = x*x; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + w = p/q; + return x+x*w; + } + /* 1> |x|>= 0.5 */ + w = one-fabs(x); + t = w*0.5; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + s = sqrt(t); + if(ix>=0x3FEF3333) { /* if |x| > 0.975 */ + w = p/q; + t = pio2_hi-(2.0*(s+s*w)-pio2_lo); + } else { + w = s; + SET_LOW_WORD(w,0); + c = (t-w*w)/(s+w); + r = p/q; + p = 2.0*s*r-(pio2_lo-2.0*c); + q = pio4_hi-2.0*w; + t = pio4_hi-(p-q); + } + if(hx>0) return t; else return -t; +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(asin, asinl); +#endif diff --git a/openlibm/src/e_asinf.c b/openlibm/src/e_asinf.c new file mode 100644 index 0000000000..1c1ab2c160 --- /dev/null +++ b/openlibm/src/e_asinf.c @@ -0,0 +1,66 @@ +/* e_asinf.c -- float version of e_asin.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_asinf.c,v 1.13 2008/08/08 00:21:27 das Exp $"); + +#include + +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3F800000 */ +huge = 1.000e+30, + /* coefficient for R(x^2) */ +pS0 = 1.6666586697e-01, +pS1 = -4.2743422091e-02, +pS2 = -8.6563630030e-03, +qS1 = -7.0662963390e-01; + +static const double +pio2 = 1.570796326794896558e+00; + +OLM_DLLEXPORT float +__ieee754_asinf(float x) +{ + double s; + float t,w,p,q; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x3f800000) { /* |x| >= 1 */ + if(ix==0x3f800000) /* |x| == 1 */ + return x*pio2; /* asin(+-1) = +-pi/2 with inexact */ + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3f000000) { /* |x|<0.5 */ + if(ix<0x39800000) { /* |x| < 2**-12 */ + if(huge+x>one) return x;/* return x with inexact if x!=0*/ + } + t = x*x; + p = t*(pS0+t*(pS1+t*pS2)); + q = one+t*qS1; + w = p/q; + return x+x*w; + } + /* 1> |x|>= 0.5 */ + w = one-fabsf(x); + t = w*(float)0.5; + p = t*(pS0+t*(pS1+t*pS2)); + q = one+t*qS1; + s = sqrt(t); + w = p/q; + t = pio2-2.0*(s+s*w); + if(hx>0) return t; else return -t; +} diff --git a/openlibm/src/e_asinl.c b/openlibm/src/e_asinl.c new file mode 100644 index 0000000000..521913a3e4 --- /dev/null +++ b/openlibm/src/e_asinl.c @@ -0,0 +1,77 @@ + +/* @(#)e_asin.c 1.3 95/01/18 */ +/* FreeBSD: head/lib/msun/src/e_asin.c 176451 2008-02-22 02:30:36Z das */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_asinl.c,v 1.2 2008/08/03 17:49:05 das Exp $"); + +/* + * See comments in e_asin.c. + * Converted to long double by David Schultz . + */ + +#include +#include + +#include "invtrig.h" +#include "math_private.h" + +static const long double +one = 1.00000000000000000000e+00, +huge = 1.000e+300; + +OLM_DLLEXPORT long double +asinl(long double x) +{ + union IEEEl2bits u; + long double t=0.0,w,p,q,c,r,s; + int16_t expsign, expt; + u.e = x; + expsign = u.xbits.expsign; + expt = expsign & 0x7fff; + if(expt >= BIAS) { /* |x|>= 1 */ + if(expt==BIAS && ((u.bits.manh&~LDBL_NBIT)|u.bits.manl)==0) + /* asin(1)=+-pi/2 with inexact */ + return x*pio2_hi+x*pio2_lo; + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (exptone) return x;/* return x with inexact if x!=0*/ + } + t = x*x; + p = P(t); + q = Q(t); + w = p/q; + return x+x*w; + } + /* 1> |x|>= 0.5 */ + w = one-fabsl(x); + t = w*0.5; + p = P(t); + q = Q(t); + s = sqrtl(t); + if(u.bits.manh>=THRESH) { /* if |x| is close to 1 */ + w = p/q; + t = pio2_hi-(2.0*(s+s*w)-pio2_lo); + } else { + u.e = s; + u.bits.manl = 0; + w = u.e; + c = (t-w*w)/(s+w); + r = p/q; + p = 2.0*s*r-(pio2_lo-2.0*c); + q = pio4_hi-2.0*w; + t = pio4_hi-(p-q); + } + if(expsign>0) return t; else return -t; +} diff --git a/openlibm/src/e_atan2.c b/openlibm/src/e_atan2.c new file mode 100644 index 0000000000..9b021b92d8 --- /dev/null +++ b/openlibm/src/e_atan2.c @@ -0,0 +1,129 @@ + +/* @(#)e_atan2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_atan2.c,v 1.14 2008/08/02 19:17:00 das Exp $"); + +/* __ieee754_atan2(y,x) + * Method : + * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). + * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0, + * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + * + * Special cases: + * + * ATAN2((anything), NaN ) is NaN; + * ATAN2(NAN , (anything) ) is NaN; + * ATAN2(+-0, +(anything but NaN)) is +-0 ; + * ATAN2(+-0, -(anything but NaN)) is +-pi ; + * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + * ATAN2(+-INF,+INF ) is +-pi/4 ; + * ATAN2(+-INF,-INF ) is +-3pi/4; + * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static volatile double +tiny = 1.0e-300; +static const double +zero = 0.0, +pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */ +pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */ +pi = 3.1415926535897931160E+00; /* 0x400921FB, 0x54442D18 */ +static volatile double +pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ + +OLM_DLLEXPORT double +__ieee754_atan2(double y, double x) +{ + double z; + int32_t k,m,hx,hy,ix,iy; + u_int32_t lx,ly; + + EXTRACT_WORDS(hx,lx,x); + ix = hx&0x7fffffff; + EXTRACT_WORDS(hy,ly,y); + iy = hy&0x7fffffff; + if(((ix|((lx|-lx)>>31))>0x7ff00000)|| + ((iy|((ly|-ly)>>31))>0x7ff00000)) /* x or y is NaN */ + return x+y; + if(hx==0x3ff00000&&lx==0) return atan(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if((iy|ly)==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7ff00000) { + if(iy==0x7ff00000) { + switch(m) { + case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ + case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ + case 2: return 3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ + case 3: return -3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>20; + if(k > 60) { /* |y/x| > 2**60 */ + z=pi_o_2+0.5*pi_lo; + m&=1; + } + else if(hx<0&&k<-60) z=0.0; /* 0 > |y|/x > -2**-60 */ + else z=atan(fabs(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: return -z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(atan2, atan2l); +#endif diff --git a/openlibm/src/e_atan2f.c b/openlibm/src/e_atan2f.c new file mode 100644 index 0000000000..a52a581592 --- /dev/null +++ b/openlibm/src/e_atan2f.c @@ -0,0 +1,97 @@ +/* e_atan2f.c -- float version of e_atan2.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_atan2f.c,v 1.12 2008/08/03 17:39:54 das Exp $"); + +#include + +#include "math_private.h" + +static volatile float +tiny = 1.0e-30; +static const float +zero = 0.0, +pi_o_4 = 7.8539818525e-01, /* 0x3f490fdb */ +pi_o_2 = 1.5707963705e+00, /* 0x3fc90fdb */ +pi = 3.1415927410e+00; /* 0x40490fdb */ +static volatile float +pi_lo = -8.7422776573e-08; /* 0xb3bbbd2e */ + +OLM_DLLEXPORT float +__ieee754_atan2f(float y, float x) +{ + float z; + int32_t k,m,hx,hy,ix,iy; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + GET_FLOAT_WORD(hy,y); + iy = hy&0x7fffffff; + if((ix>0x7f800000)|| + (iy>0x7f800000)) /* x or y is NaN */ + return x+y; + if(hx==0x3f800000) return atanf(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if(iy==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if(ix==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7f800000) { + if(iy==0x7f800000) { + switch(m) { + case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ + case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ + case 2: return (float)3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ + case 3: return (float)-3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(iy==0x7f800000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>23; + if(k > 26) { /* |y/x| > 2**26 */ + z=pi_o_2+(float)0.5*pi_lo; + m&=1; + } + else if(k<-26&&hx<0) z=0.0; /* 0 > |y|/x > -2**-26 */ + else z=atanf(fabsf(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: return -z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} diff --git a/openlibm/src/e_atan2l.c b/openlibm/src/e_atan2l.c new file mode 100644 index 0000000000..1c0a79688e --- /dev/null +++ b/openlibm/src/e_atan2l.c @@ -0,0 +1,120 @@ + +/* @(#)e_atan2.c 1.3 95/01/18 */ +/* FreeBSD: head/lib/msun/src/e_atan2.c 176451 2008-02-22 02:30:36Z das */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_atan2l.c,v 1.3 2008/08/02 19:17:00 das Exp $"); + +/* + * See comments in e_atan2.c. + * Converted to long double by David Schultz . + */ + +#include +#include + +#include "invtrig.h" +#include "math_private.h" + +static volatile long double +tiny = 1.0e-300; +static const long double +zero = 0.0; + +#ifdef __i386__ +/* XXX Work around the fact that gcc truncates long double constants on i386 */ +static volatile double +pi1 = 3.14159265358979311600e+00, /* 0x1.921fb54442d18p+1 */ +pi2 = 1.22514845490862001043e-16; /* 0x1.1a80000000000p-53 */ +#define pi ((long double)pi1 + pi2) +#else +static const long double +pi = 3.14159265358979323846264338327950280e+00L; +#endif + +OLM_DLLEXPORT long double +atan2l(long double y, long double x) +{ + union IEEEl2bits ux, uy; + long double z; + int32_t k,m; + int16_t exptx, expsignx, expty, expsigny; + + uy.e = y; + expsigny = uy.xbits.expsign; + expty = expsigny & 0x7fff; + ux.e = x; + expsignx = ux.xbits.expsign; + exptx = expsignx & 0x7fff; + + if ((exptx==BIAS+LDBL_MAX_EXP && + ((ux.bits.manh&~LDBL_NBIT)|ux.bits.manl)!=0) || /* x is NaN */ + (expty==BIAS+LDBL_MAX_EXP && + ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl)!=0)) /* y is NaN */ + return x+y; + if (expsignx==BIAS && ((ux.bits.manh&~LDBL_NBIT)|ux.bits.manl)==0) + return atanl(y); /* x=1.0 */ + m = ((expsigny>>15)&1)|((expsignx>>14)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if(expty==0 && ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl)==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if(exptx==0 && ((ux.bits.manh&~LDBL_NBIT)|ux.bits.manl)==0) + return (expsigny<0)? -pio2_hi-tiny: pio2_hi+tiny; + + /* when x is INF */ + if(exptx==BIAS+LDBL_MAX_EXP) { + if(expty==BIAS+LDBL_MAX_EXP) { + switch(m) { + case 0: return pio2_hi*0.5+tiny;/* atan(+INF,+INF) */ + case 1: return -pio2_hi*0.5-tiny;/* atan(-INF,+INF) */ + case 2: return 1.5*pio2_hi+tiny;/*atan(+INF,-INF)*/ + case 3: return -1.5*pio2_hi-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(expty==BIAS+LDBL_MAX_EXP) + return (expsigny<0)? -pio2_hi-tiny: pio2_hi+tiny; + + /* compute y/x */ + k = expty-exptx; + if(k > LDBL_MANT_DIG+2) { /* |y/x| huge */ + z=pio2_hi+pio2_lo; + m&=1; + } + else if(expsignx<0&&k<-LDBL_MANT_DIG-2) z=0.0; /* |y/x| tiny, x<0 */ + else z=atanl(fabsl(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: return -z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} diff --git a/openlibm/src/e_atanh.c b/openlibm/src/e_atanh.c new file mode 100644 index 0000000000..5e1db914ca --- /dev/null +++ b/openlibm/src/e_atanh.c @@ -0,0 +1,68 @@ + +/* @(#)e_atanh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_atanh.c,v 1.8 2008/02/22 02:30:34 das Exp $"); + +/* __ieee754_atanh(x) + * Method : + * 1.Reduced x to positive by atanh(-x) = -atanh(x) + * 2.For x>=0.5 + * 1 2x x + * atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) + * 2 1 - x 1 - x + * + * For x<0.5 + * atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) + * + * Special cases: + * atanh(x) is NaN if |x| > 1 with signal; + * atanh(NaN) is that NaN with no signal; + * atanh(+-1) is +-INF with signal. + * + */ + +#include +#include + +#include "math_private.h" + +static const double one = 1.0, huge = 1e300; +static const double zero = 0.0; + +OLM_DLLEXPORT double +__ieee754_atanh(double x) +{ + double t; + int32_t hx,ix; + u_int32_t lx; + EXTRACT_WORDS(hx,lx,x); + ix = hx&0x7fffffff; + if ((ix|((lx|(-lx))>>31))>0x3ff00000) /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3ff00000) + return x/zero; + if(ix<0x3e300000&&(huge+x)>zero) return x; /* x<2**-28 */ + SET_HIGH_WORD(x,ix); + if(ix<0x3fe00000) { /* x < 0.5 */ + t = x+x; + t = 0.5*log1p(t+t*x/(one-x)); + } else + t = 0.5*log1p((x+x)/(one-x)); + if(hx>=0) return t; else return -t; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(atanh, atanhl); +#endif diff --git a/openlibm/src/e_atanhf.c b/openlibm/src/e_atanhf.c new file mode 100644 index 0000000000..1ce329c6bd --- /dev/null +++ b/openlibm/src/e_atanhf.c @@ -0,0 +1,46 @@ +/* e_atanhf.c -- float version of e_atanh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_atanhf.c,v 1.7 2008/02/22 02:30:34 das Exp $"); + +#include + +#include "math_private.h" + +static const float one = 1.0, huge = 1e30; + +static const float zero = 0.0; + +OLM_DLLEXPORT float +__ieee754_atanhf(float x) +{ + float t; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if (ix>0x3f800000) /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3f800000) + return x/zero; + if(ix<0x31800000&&(huge+x)>zero) return x; /* x<2**-28 */ + SET_FLOAT_WORD(x,ix); + if(ix<0x3f000000) { /* x < 0.5 */ + t = x+x; + t = (float)0.5*log1pf(t+t*x/(one-x)); + } else + t = (float)0.5*log1pf((x+x)/(one-x)); + if(hx>=0) return t; else return -t; +} diff --git a/openlibm/src/e_cosh.c b/openlibm/src/e_cosh.c new file mode 100644 index 0000000000..2e76570611 --- /dev/null +++ b/openlibm/src/e_cosh.c @@ -0,0 +1,85 @@ + +/* @(#)e_cosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_cosh.c,v 1.10 2011/10/21 06:28:47 das Exp $"); + +/* __ieee754_cosh(x) + * Method : + * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (cosh(x) = cosh(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : cosh(x) := ------------------- + * 2 + * 22 <= x <= lnovft : cosh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : cosh(x) := huge*huge (overflow) + * + * Special cases: + * cosh(x) is |x| if x is +INF, -INF, or NaN. + * only cosh(0)=1 is exact for finite x. + */ + +#include +#include + +#include "math_private.h" + +static const double one = 1.0, half=0.5, huge = 1.0e300; + +OLM_DLLEXPORT double +__ieee754_cosh(double x) +{ + double t,w; + int32_t ix; + + /* High word of |x|. */ + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */ + if(ix<0x3fd62e43) { + t = expm1(fabs(x)); + w = one+t; + if (ix<0x3c800000) return w; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */ + if (ix < 0x40360000) { + t = __ieee754_exp(fabs(x)); + return half*t+half/t; + } + + /* |x| in [22, log(maxdouble)] return half*exp(|x|) */ + if (ix < 0x40862E42) return half*__ieee754_exp(fabs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + if (ix<=0x408633CE) + return __ldexp_exp(fabs(x), -1); + + /* |x| > overflowthresold, cosh(x) overflow */ + return huge*huge; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(cosh, coshl); +#endif diff --git a/openlibm/src/e_coshf.c b/openlibm/src/e_coshf.c new file mode 100644 index 0000000000..e129659335 --- /dev/null +++ b/openlibm/src/e_coshf.c @@ -0,0 +1,60 @@ +/* e_coshf.c -- float version of e_cosh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_coshf.c,v 1.9 2011/10/21 06:28:47 das Exp $"); + +#include + +#include "math_private.h" + +static const float one = 1.0, half=0.5, huge = 1.0e30; + +OLM_DLLEXPORT float +__ieee754_coshf(float x) +{ + float t,w; + int32_t ix; + + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7f800000) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */ + if(ix<0x3eb17218) { + t = expm1f(fabsf(x)); + w = one+t; + if (ix<0x39800000) return one; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,9], return (exp(|x|)+1/exp(|x|))/2; */ + if (ix < 0x41100000) { + t = __ieee754_expf(fabsf(x)); + return half*t+half/t; + } + + /* |x| in [9, log(maxfloat)] return half*exp(|x|) */ + if (ix < 0x42b17217) return half*__ieee754_expf(fabsf(x)); + + /* |x| in [log(maxfloat), overflowthresold] */ + if (ix<=0x42b2d4fc) + return __ldexp_expf(fabsf(x), -1); + + /* |x| > overflowthresold, cosh(x) overflow */ + return huge*huge; +} diff --git a/openlibm/src/e_exp.c b/openlibm/src/e_exp.c new file mode 100644 index 0000000000..639b9fe18d --- /dev/null +++ b/openlibm/src/e_exp.c @@ -0,0 +1,171 @@ + +/* @(#)e_exp.c 1.6 04/04/22 */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_exp.c,v 1.14 2011/10/21 06:26:38 das Exp $"); + +/* __ieee754_exp(x) + * Returns the exponential of x. + * + * Method + * 1. Argument reduction: + * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2. + * + * Here r will be represented as r = hi-lo for better + * accuracy. + * + * 2. Approximation of exp(r) by a special rational function on + * the interval [0,0.34658]: + * Write + * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... + * We use a special Remes algorithm on [0,0.34658] to generate + * a polynomial of degree 5 to approximate R. The maximum error + * of this polynomial approximation is bounded by 2**-59. In + * other words, + * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 + * (where z=r*r, and the values of P1 to P5 are listed below) + * and + * | 5 | -59 + * | 2.0+P1*z+...+P5*z - R(z) | <= 2 + * | | + * The computation of exp(r) thus becomes + * 2*r + * exp(r) = 1 + ------- + * R - r + * r*R1(r) + * = 1 + r + ----------- (for better accuracy) + * 2 - R1(r) + * where + * 2 4 10 + * R1(r) = r - (P1*r + P2*r + ... + P5*r ). + * + * 3. Scale back to obtain exp(x): + * From step 1, we have + * exp(x) = 2^k * exp(r) + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF) is 0, and + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then exp(x) overflow + * if x < -7.45133219101941108420e+02 then exp(x) underflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static const double +one = 1.0, +halF[2] = {0.5,-0.5,}, +huge = 1.0e+300, +o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ +u_threshold= -7.45133219101941108420e+02, /* 0xc0874910, 0xD52D3051 */ +ln2HI[2] ={ 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ + -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */ +ln2LO[2] ={ 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ + -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */ +invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ + +static volatile double +twom1000= 9.33263618503218878990e-302; /* 2**-1000=0x01700000,0*/ + +OLM_DLLEXPORT double +__ieee754_exp(double x) /* default IEEE double exp */ +{ + double y,hi=0.0,lo=0.0,c,t,twopk; + int32_t k=0,xsb; + u_int32_t hx; + + GET_HIGH_WORD(hx,x); + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + u_int32_t lx; + GET_LOW_WORD(lx,x); + if(((hx&0xfffff)|lx)!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + } + if(x > o_threshold) return huge*huge; /* overflow */ + if(x < u_threshold) return twom1000*twom1000; /* underflow */ + } + + /* this implementation gives 2.7182818284590455 for exp(1.0), + which is well within the allowable error. however, + 2.718281828459045 is closer to the true value so we prefer that + answer, given that 1.0 is such an important argument value. */ + if (x == 1.0) + return 2.718281828459045235360; + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = (int)(invln2*x+halF[xsb]); + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + STRICT_ASSIGN(double, x, hi - lo); + } + else if(hx < 0x3e300000) { /* when |x|<2**-28 */ + if(huge+x>one) return one+x;/* trigger inexact */ + } + else k = 0; + + /* x is now in primary range */ + t = x*x; + if(k >= -1021) + INSERT_WORDS(twopk,0x3ff00000+(k<<20), 0); + else + INSERT_WORDS(twopk,0x3ff00000+((k+1000)<<20), 0); + c = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + if(k==0) return one-((x*c)/(c-2.0)-x); + else y = one-((lo-(x*c)/(2.0-c))-hi); + if(k >= -1021) { + if (k==1024) return y*2.0*0x1p1023; + return y*twopk; + } else { + return y*twopk*twom1000; + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(exp, expl); +#endif diff --git a/openlibm/src/e_expf.c b/openlibm/src/e_expf.c new file mode 100644 index 0000000000..a239413cd0 --- /dev/null +++ b/openlibm/src/e_expf.c @@ -0,0 +1,97 @@ +/* e_expf.c -- float version of e_exp.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_expf.c,v 1.16 2011/10/21 06:26:38 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const float +one = 1.0, +halF[2] = {0.5,-0.5,}, +huge = 1.0e+30, +o_threshold= 8.8721679688e+01, /* 0x42b17180 */ +u_threshold= -1.0397208405e+02, /* 0xc2cff1b5 */ +ln2HI[2] ={ 6.9314575195e-01, /* 0x3f317200 */ + -6.9314575195e-01,}, /* 0xbf317200 */ +ln2LO[2] ={ 1.4286067653e-06, /* 0x35bfbe8e */ + -1.4286067653e-06,}, /* 0xb5bfbe8e */ +invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */ +/* + * Domain [-0.34568, 0.34568], range ~[-4.278e-9, 4.447e-9]: + * |x*(exp(x)+1)/(exp(x)-1) - p(x)| < 2**-27.74 + */ +P1 = 1.6666625440e-1, /* 0xaaaa8f.0p-26 */ +P2 = -2.7667332906e-3; /* -0xb55215.0p-32 */ + +static volatile float twom100 = 7.8886090522e-31; /* 2**-100=0x0d800000 */ + +OLM_DLLEXPORT float +__ieee754_expf(float x) +{ + float y,hi=0.0,lo=0.0,c,t,twopk; + int32_t k=0,xsb; + u_int32_t hx; + + GET_FLOAT_WORD(hx,x); + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x42b17218) { /* if |x|>=88.721... */ + if(hx>0x7f800000) + return x+x; /* NaN */ + if(hx==0x7f800000) + return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + if(x > o_threshold) return huge*huge; /* overflow */ + if(x < u_threshold) return twom100*twom100; /* underflow */ + } + + /* argument reduction */ + if(hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3F851592) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = invln2*x+halF[xsb]; + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + STRICT_ASSIGN(float, x, hi - lo); + } + else if(hx < 0x39000000) { /* when |x|<2**-14 */ + if(huge+x>one) return one+x;/* trigger inexact */ + } + else k = 0; + + /* x is now in primary range */ + t = x*x; + if(k >= -125) + SET_FLOAT_WORD(twopk,0x3f800000+(k<<23)); + else + SET_FLOAT_WORD(twopk,0x3f800000+((k+100)<<23)); + c = x - t*(P1+t*P2); + if(k==0) return one-((x*c)/(c-(float)2.0)-x); + else y = one-((lo-(x*c)/((float)2.0-c))-hi); + if(k >= -125) { + if(k==128) return y*2.0F*0x1p127F; + return y*twopk; + } else { + return y*twopk*twom100; + } +} diff --git a/openlibm/src/e_fmod.c b/openlibm/src/e_fmod.c new file mode 100644 index 0000000000..285da8da23 --- /dev/null +++ b/openlibm/src/e_fmod.c @@ -0,0 +1,133 @@ + +/* @(#)e_fmod.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_fmod.c,v 1.10 2008/02/22 02:30:34 das Exp $"); + +/* + * __ieee754_fmod(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + */ + +#include + +#include "math_private.h" + +static const double one = 1.0, Zero[] = {0.0, -0.0,}; + +OLM_DLLEXPORT double +__ieee754_fmod(double x, double y) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + u_int32_t lx,ly,lz; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if((hy|ly)==0||(hx>=0x7ff00000)|| /* y=0,or x not finite */ + ((hy|((ly|-ly)>>31))>0x7ff00000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<=hy) { + if((hx>31]; /* |x|=|y| return x*0*/ + } + + /* determine ix = ilogb(x) */ + if(hx<0x00100000) { /* subnormal x */ + if(hx==0) { + for (ix = -1043, i=lx; i>0; i<<=1) ix -=1; + } else { + for (ix = -1022,i=(hx<<11); i>0; i<<=1) ix -=1; + } + } else ix = (hx>>20)-1023; + + /* determine iy = ilogb(y) */ + if(hy<0x00100000) { /* subnormal y */ + if(hy==0) { + for (iy = -1043, i=ly; i>0; i<<=1) iy -=1; + } else { + for (iy = -1022,i=(hy<<11); i>0; i<<=1) iy -=1; + } + } else iy = (hy>>20)-1023; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -1022) + hx = 0x00100000|(0x000fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -1022-ix; + if(n<=31) { + hx = (hx<>(32-n)); + lx <<= n; + } else { + hx = lx<<(n-32); + lx = 0; + } + } + if(iy >= -1022) + hy = 0x00100000|(0x000fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -1022-iy; + if(n<=31) { + hy = (hy<>(32-n)); + ly <<= n; + } else { + hy = ly<<(n-32); + ly = 0; + } + } + + /* fix point fmod */ + n = ix - iy; + while(n--) { + hz=hx-hy;lz=lx-ly; if(lx>31); lx = lx+lx;} + else { + if((hz|lz)==0) /* return sign(x)*0 */ + return Zero[(u_int32_t)sx>>31]; + hx = hz+hz+(lz>>31); lx = lz+lz; + } + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) /* return sign(x)*0 */ + return Zero[(u_int32_t)sx>>31]; + while(hx<0x00100000) { /* normalize x */ + hx = hx+hx+(lx>>31); lx = lx+lx; + iy -= 1; + } + if(iy>= -1022) { /* normalize output */ + hx = ((hx-0x00100000)|((iy+1023)<<20)); + INSERT_WORDS(x,hx|sx,lx); + } else { /* subnormal output */ + n = -1022 - iy; + if(n<=20) { + lx = (lx>>n)|((u_int32_t)hx<<(32-n)); + hx >>= n; + } else if (n<=31) { + lx = (hx<<(32-n))|(lx>>n); hx = sx; + } else { + lx = hx>>(n-32); hx = sx; + } + INSERT_WORDS(x,hx|sx,lx); + x *= one; /* create necessary signal */ + } + return x; /* exact output */ +} diff --git a/openlibm/src/e_fmodf.c b/openlibm/src/e_fmodf.c new file mode 100644 index 0000000000..88fd8aed3e --- /dev/null +++ b/openlibm/src/e_fmodf.c @@ -0,0 +1,105 @@ +/* e_fmodf.c -- float version of e_fmod.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_fmodf.c,v 1.7 2008/02/22 02:30:34 das Exp $"); + +/* + * __ieee754_fmodf(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + */ + +#include + +#include "math_private.h" + +static const float one = 1.0, Zero[] = {0.0, -0.0,}; + +OLM_DLLEXPORT float +__ieee754_fmodf(float x, float y) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if(hy==0||(hx>=0x7f800000)|| /* y=0,or x not finite */ + (hy>0x7f800000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx>31]; /* |x|=|y| return x*0*/ + + /* determine ix = ilogb(x) */ + if(hx<0x00800000) { /* subnormal x */ + for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; + } else ix = (hx>>23)-127; + + /* determine iy = ilogb(y) */ + if(hy<0x00800000) { /* subnormal y */ + for (iy = -126,i=(hy<<8); i>=0; i<<=1) iy -=1; + } else iy = (hy>>23)-127; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -126) + hx = 0x00800000|(0x007fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -126-ix; + hx = hx<= -126) + hy = 0x00800000|(0x007fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -126-iy; + hy = hy<>31]; + hx = hz+hz; + } + } + hz=hx-hy; + if(hz>=0) {hx=hz;} + + /* convert back to floating value and restore the sign */ + if(hx==0) /* return sign(x)*0 */ + return Zero[(u_int32_t)sx>>31]; + while(hx<0x00800000) { /* normalize x */ + hx = hx+hx; + iy -= 1; + } + if(iy>= -126) { /* normalize output */ + hx = ((hx-0x00800000)|((iy+127)<<23)); + SET_FLOAT_WORD(x,hx|sx); + } else { /* subnormal output */ + n = -126 - iy; + hx >>= n; + SET_FLOAT_WORD(x,hx|sx); + x *= one; /* create necessary signal */ + } + return x; /* exact output */ +} diff --git a/openlibm/src/e_fmodl.c b/openlibm/src/e_fmodl.c new file mode 100644 index 0000000000..47d961b328 --- /dev/null +++ b/openlibm/src/e_fmodl.c @@ -0,0 +1,150 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_fmodl.c,v 1.2 2008/07/31 20:09:47 das Exp $"); + +#include +#include +#include + +#include "fpmath.h" + +#include "math_private.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +#if LDBL_MANL_SIZE > 32 +typedef u_int64_t manl_t; +#else +typedef u_int32_t manl_t; +#endif + +#if LDBL_MANH_SIZE > 32 +typedef u_int64_t manh_t; +#else +typedef u_int32_t manh_t; +#endif + +/* + * These macros add and remove an explicit integer bit in front of the + * fractional mantissa, if the architecture doesn't have such a bit by + * default already. + */ +#ifdef LDBL_IMPLICIT_NBIT +#define SET_NBIT(hx) ((hx) | (1ULL << LDBL_MANH_SIZE)) +#define HFRAC_BITS LDBL_MANH_SIZE +#else +#define SET_NBIT(hx) (hx) +#define HFRAC_BITS (LDBL_MANH_SIZE - 1) +#endif + +#define MANL_SHIFT (LDBL_MANL_SIZE - 1) + +static const long double one = 1.0, Zero[] = {0.0, -0.0,}; + +/* + * fmodl(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + * + * Assumptions: + * - The low part of the mantissa fits in a manl_t exactly. + * - The high part of the mantissa fits in an int64_t with enough room + * for an explicit integer bit in front of the fractional bits. + */ +OLM_DLLEXPORT long double +fmodl(long double x, long double y) +{ + union IEEEl2bits ux, uy; + int64_t hx,hz; /* We need a carry bit even if LDBL_MANH_SIZE is 32. */ + manh_t hy; + manl_t lx,ly,lz; + int ix,iy,n,sx; + + ux.e = x; + uy.e = y; + sx = ux.bits.sign; + + /* purge off exception values */ + if((uy.bits.exp|uy.bits.manh|uy.bits.manl)==0 || /* y=0 */ + (ux.bits.exp == BIAS + LDBL_MAX_EXP) || /* or x not finite */ + (uy.bits.exp == BIAS + LDBL_MAX_EXP && + ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl)!=0)) /* or y is NaN */ + return (x*y)/(x*y); + if(ux.bits.exp<=uy.bits.exp) { + if((ux.bits.exp>MANL_SHIFT); lx = lx+lx;} + else { + if ((hz|lz)==0) /* return sign(x)*0 */ + return Zero[sx]; + hx = hz+hz+(lz>>MANL_SHIFT); lx = lz+lz; + } + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) /* return sign(x)*0 */ + return Zero[sx]; + while(hx<(1ULL<>MANL_SHIFT); lx = lx+lx; + iy -= 1; + } + ux.bits.manh = hx; /* The mantissa is truncated here if needed. */ + ux.bits.manl = lx; + if (iy < LDBL_MIN_EXP) { + ux.bits.exp = iy + (BIAS + 512); + ux.e *= 0x1p-512; + } else { + ux.bits.exp = iy + BIAS; + } + x = ux.e * one; /* create necessary signal */ + return x; /* exact output */ +} diff --git a/openlibm/src/e_hypot.c b/openlibm/src/e_hypot.c new file mode 100644 index 0000000000..3f4c1d140f --- /dev/null +++ b/openlibm/src/e_hypot.c @@ -0,0 +1,131 @@ + +/* @(#)e_hypot.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_hypot.c,v 1.14 2011/10/15 07:00:28 das Exp $"); + +/* __ieee754_hypot(x,y) + * + * Method : + * If (assume round-to-nearest) z=x*x+y*y + * has error less than sqrt(2)/2 ulp, than + * sqrt(z) has error less than 1 ulp (exercise). + * + * So, compute sqrt(x*x+y*y) with some care as + * follows to get the error below 1 ulp: + * + * Assume x>y>0; + * (if possible, set rounding to round-to-nearest) + * 1. if x > 2y use + * x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y + * where x1 = x with lower 32 bits cleared, x2 = x-x1; else + * 2. if x <= 2y use + * t1*y1+((x-y)*(x-y)+(t1*y2+t2*y)) + * where t1 = 2x with lower 32 bits cleared, t2 = 2x-t1, + * y1= y with lower 32 bits chopped, y2 = y-y1. + * + * NOTE: scaling may be necessary if some argument is too + * large or too tiny + * + * Special cases: + * hypot(x,y) is INF if x or y is +INF or -INF; else + * hypot(x,y) is NAN if x or y is NAN. + * + * Accuracy: + * hypot(x,y) returns sqrt(x^2+y^2) with error less + * than 1 ulps (units in the last place) + */ + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +__ieee754_hypot(double x, double y) +{ + double a,b,t1,t2,y1,y2,w; + int32_t j,k,ha,hb; + + GET_HIGH_WORD(ha,x); + ha &= 0x7fffffff; + GET_HIGH_WORD(hb,y); + hb &= 0x7fffffff; + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + a = fabs(a); + b = fabs(b); + if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */ + k=0; + if(ha > 0x5f300000) { /* a>2**500 */ + if(ha >= 0x7ff00000) { /* Inf or NaN */ + u_int32_t low; + /* Use original arg order iff result is NaN; quieten sNaNs. */ + w = fabs(x+0.0)-fabs(y+0.0); + GET_LOW_WORD(low,a); + if(((ha&0xfffff)|low)==0) w = a; + GET_LOW_WORD(low,b); + if(((hb^0x7ff00000)|low)==0) w = b; + return w; + } + /* scale a and b by 2**-600 */ + ha -= 0x25800000; hb -= 0x25800000; k += 600; + SET_HIGH_WORD(a,ha); + SET_HIGH_WORD(b,hb); + } + if(hb < 0x20b00000) { /* b < 2**-500 */ + if(hb <= 0x000fffff) { /* subnormal b or 0 */ + u_int32_t low; + GET_LOW_WORD(low,b); + if((hb|low)==0) return a; + t1=0; + SET_HIGH_WORD(t1,0x7fd00000); /* t1=2^1022 */ + b *= t1; + a *= t1; + k -= 1022; + } else { /* scale a and b by 2^600 */ + ha += 0x25800000; /* a *= 2^600 */ + hb += 0x25800000; /* b *= 2^600 */ + k -= 600; + SET_HIGH_WORD(a,ha); + SET_HIGH_WORD(b,hb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + t1 = 0; + SET_HIGH_WORD(t1,ha); + t2 = a-t1; + w = sqrt(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + y1 = 0; + SET_HIGH_WORD(y1,hb); + y2 = b - y1; + t1 = 0; + SET_HIGH_WORD(t1,ha+0x00100000); + t2 = a - t1; + w = sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + u_int32_t high; + t1 = 1.0; + GET_HIGH_WORD(high,t1); + SET_HIGH_WORD(t1,high+(k<<20)); + return t1*w; + } else return w; +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(hypot, hypotl); +#endif diff --git a/openlibm/src/e_hypotf.c b/openlibm/src/e_hypotf.c new file mode 100644 index 0000000000..e90fb5dc33 --- /dev/null +++ b/openlibm/src/e_hypotf.c @@ -0,0 +1,84 @@ +/* e_hypotf.c -- float version of e_hypot.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_hypotf.c,v 1.14 2011/10/15 07:00:28 das Exp $"); + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +__ieee754_hypotf(float x, float y) +{ + float a,b,t1,t2,y1,y2,w; + int32_t j,k,ha,hb; + + GET_FLOAT_WORD(ha,x); + ha &= 0x7fffffff; + GET_FLOAT_WORD(hb,y); + hb &= 0x7fffffff; + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + a = fabsf(a); + b = fabsf(b); + if((ha-hb)>0xf000000) {return a+b;} /* x/y > 2**30 */ + k=0; + if(ha > 0x58800000) { /* a>2**50 */ + if(ha >= 0x7f800000) { /* Inf or NaN */ + /* Use original arg order iff result is NaN; quieten sNaNs. */ + w = fabsf(x+0.0F)-fabsf(y+0.0F); + if(ha == 0x7f800000) w = a; + if(hb == 0x7f800000) w = b; + return w; + } + /* scale a and b by 2**-68 */ + ha -= 0x22000000; hb -= 0x22000000; k += 68; + SET_FLOAT_WORD(a,ha); + SET_FLOAT_WORD(b,hb); + } + if(hb < 0x26800000) { /* b < 2**-50 */ + if(hb <= 0x007fffff) { /* subnormal b or 0 */ + if(hb==0) return a; + SET_FLOAT_WORD(t1,0x7e800000); /* t1=2^126 */ + b *= t1; + a *= t1; + k -= 126; + } else { /* scale a and b by 2^68 */ + ha += 0x22000000; /* a *= 2^68 */ + hb += 0x22000000; /* b *= 2^68 */ + k -= 68; + SET_FLOAT_WORD(a,ha); + SET_FLOAT_WORD(b,hb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + SET_FLOAT_WORD(t1,ha&0xfffff000); + t2 = a-t1; + w = __ieee754_sqrtf(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + SET_FLOAT_WORD(y1,hb&0xfffff000); + y2 = b - y1; + SET_FLOAT_WORD(t1,(ha+0x00800000)&0xfffff000); + t2 = a - t1; + w = __ieee754_sqrtf(t1*y1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + SET_FLOAT_WORD(t1,0x3f800000+(k<<23)); + return t1*w; + } else return w; +} diff --git a/openlibm/src/e_hypotl.c b/openlibm/src/e_hypotl.c new file mode 100644 index 0000000000..2632774520 --- /dev/null +++ b/openlibm/src/e_hypotl.c @@ -0,0 +1,124 @@ +/* From: @(#)e_hypot.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_hypotl.c,v 1.3 2011/10/16 05:36:39 das Exp $"); + +/* long double version of hypot(). See e_hypot.c for most comments. */ + +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#define GET_LDBL_MAN(h, l, v) do { \ + union IEEEl2bits uv; \ + \ + uv.e = v; \ + h = uv.bits.manh; \ + l = uv.bits.manl; \ +} while (0) + +#undef GET_HIGH_WORD +#define GET_HIGH_WORD(i, v) GET_LDBL_EXPSIGN(i, v) +#undef SET_HIGH_WORD +#define SET_HIGH_WORD(v, i) SET_LDBL_EXPSIGN(v, i) + +#define DESW(exp) (exp) /* delta expsign word */ +#define ESW(exp) (MAX_EXP - 1 + (exp)) /* expsign word */ +#define MANT_DIG LDBL_MANT_DIG +#define MAX_EXP LDBL_MAX_EXP + +#if LDBL_MANL_SIZE > 32 +typedef u_int64_t man_t; +#else +typedef u_int32_t man_t; +#endif + +OLM_DLLEXPORT long double +hypotl(long double x, long double y) +{ + long double a=x,b=y,t1,t2,y1,y2,w; + int32_t j,k,ha,hb; + + GET_HIGH_WORD(ha,x); + ha &= 0x7fff; + GET_HIGH_WORD(hb,y); + hb &= 0x7fff; + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + a = fabsl(a); + b = fabsl(b); + if((ha-hb)>DESW(MANT_DIG+7)) {return a+b;} /* x/y > 2**(MANT_DIG+7) */ + k=0; + if(ha > ESW(MAX_EXP/2-12)) { /* a>2**(MAX_EXP/2-12) */ + if(ha >= ESW(MAX_EXP)) { /* Inf or NaN */ + man_t manh, manl; + /* Use original arg order iff result is NaN; quieten sNaNs. */ + w = fabsl(x+0.0)-fabsl(y+0.0); + GET_LDBL_MAN(manh,manl,a); + if (manh == LDBL_NBIT && manl == 0) w = a; + GET_LDBL_MAN(manh,manl,b); + if (hb >= ESW(MAX_EXP) && manh == LDBL_NBIT && manl == 0) w = b; + return w; + } + /* scale a and b by 2**-(MAX_EXP/2+88) */ + ha -= DESW(MAX_EXP/2+88); hb -= DESW(MAX_EXP/2+88); + k += MAX_EXP/2+88; + SET_HIGH_WORD(a,ha); + SET_HIGH_WORD(b,hb); + } + if(hb < ESW(-(MAX_EXP/2-12))) { /* b < 2**-(MAX_EXP/2-12) */ + if(hb <= 0) { /* subnormal b or 0 */ + man_t manh, manl; + GET_LDBL_MAN(manh,manl,b); + if((manh|manl)==0) return a; + t1=1; + SET_HIGH_WORD(t1,ESW(MAX_EXP-2)); /* t1=2^(MAX_EXP-2) */ + b *= t1; + a *= t1; + k -= MAX_EXP-2; + } else { /* scale a and b by 2^(MAX_EXP/2+88) */ + ha += DESW(MAX_EXP/2+88); + hb += DESW(MAX_EXP/2+88); + k -= MAX_EXP/2+88; + SET_HIGH_WORD(a,ha); + SET_HIGH_WORD(b,hb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + t1 = a; + union IEEEl2bits uv; + uv.e = t1; uv.bits.manl = 0; t1 = uv.e; + t2 = a-t1; + w = sqrtl(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + y1 = b; + union IEEEl2bits uv; + uv.e = y1; uv.bits.manl = 0; y1 = uv.e; + y2 = b - y1; + t1 = a; + uv.e = t1; uv.bits.manl = 0; t1 = uv.e; + t2 = a - t1; + w = sqrtl(t1*y1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + u_int32_t high; + t1 = 1.0; + GET_HIGH_WORD(high,t1); + SET_HIGH_WORD(t1,high+DESW(k)); + return t1*w; + } else return w; +} diff --git a/openlibm/src/e_j0.c b/openlibm/src/e_j0.c new file mode 100644 index 0000000000..a2419d3c1c --- /dev/null +++ b/openlibm/src/e_j0.c @@ -0,0 +1,388 @@ + +/* @(#)e_j0.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_j0.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +/* __ieee754_j0(x), __ieee754_y0(x) + * Bessel function of the first and second kinds of order zero. + * Method -- j0(x): + * 1. For tiny x, we use j0(x) = 1 - x^2/4 + x^4/64 - ... + * 2. Reduce x to |x| since j0(x)=j0(-x), and + * for x in (0,2) + * j0(x) = 1-z/4+ z^2*R0/S0, where z = x*x; + * (precision: |j0-1+z/4-z^2R0/S0 |<2**-63.67 ) + * for x in (2,inf) + * j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0)) + * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) + * as follow: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (cos(x) + sin(x)) + * sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * (To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one.) + * + * 3 Special cases + * j0(nan)= nan + * j0(0) = 1 + * j0(inf) = 0 + * + * Method -- y0(x): + * 1. For x<2. + * Since + * y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...) + * therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. + * We use the following function to approximate y0, + * y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2 + * where + * U(z) = u00 + u01*z + ... + u06*z^6 + * V(z) = 1 + v01*z + ... + v04*z^4 + * with absolute approximation error bounded by 2**-72. + * Note: For tiny x, U/V = u0 and j0(x)~1, hence + * y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27) + * 2. For x>=2. + * y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0)) + * where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) + * by the method mentioned above. + * 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. + */ + +#include + +#include "math_private.h" + +static double pzero(double), qzero(double); + +static const double +huge = 1e300, +one = 1.0, +invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ +tpi = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ + /* R0/S0 on [0, 2.00] */ +R02 = 1.56249999999999947958e-02, /* 0x3F8FFFFF, 0xFFFFFFFD */ +R03 = -1.89979294238854721751e-04, /* 0xBF28E6A5, 0xB61AC6E9 */ +R04 = 1.82954049532700665670e-06, /* 0x3EBEB1D1, 0x0C503919 */ +R05 = -4.61832688532103189199e-09, /* 0xBE33D5E7, 0x73D63FCE */ +S01 = 1.56191029464890010492e-02, /* 0x3F8FFCE8, 0x82C8C2A4 */ +S02 = 1.16926784663337450260e-04, /* 0x3F1EA6D2, 0xDD57DBF4 */ +S03 = 5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */ +S04 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */ + +static const double zero = 0.0; + +OLM_DLLEXPORT double +__ieee754_j0(double x) +{ + double z, s,c,ss,cc,r,u,v; + int32_t hx,ix; + + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return one/(x*x); + x = fabs(x); + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = sin(x); + c = cos(x); + ss = s-c; + cc = s+c; + if(ix<0x7fe00000) { /* make sure x+x not overflow */ + z = -cos(x+x); + if ((s*c)0x48000000) z = (invsqrtpi*cc)/sqrt(x); + else { + u = pzero(x); v = qzero(x); + z = invsqrtpi*(u*cc-v*ss)/sqrt(x); + } + return z; + } + if(ix<0x3f200000) { /* |x| < 2**-13 */ + if(huge+x>one) { /* raise inexact if x != 0 */ + if(ix<0x3e400000) return one; /* |x|<2**-27 */ + else return one - 0.25*x*x; + } + } + z = x*x; + r = z*(R02+z*(R03+z*(R04+z*R05))); + s = one+z*(S01+z*(S02+z*(S03+z*S04))); + if(ix < 0x3FF00000) { /* |x| < 1.00 */ + return one + z*(-0.25+(r/s)); + } else { + u = 0.5*x; + return((one+u)*(one-u)+z*(r/s)); + } +} + +static const double +u00 = -7.38042951086872317523e-02, /* 0xBFB2E4D6, 0x99CBD01F */ +u01 = 1.76666452509181115538e-01, /* 0x3FC69D01, 0x9DE9E3FC */ +u02 = -1.38185671945596898896e-02, /* 0xBF8C4CE8, 0xB16CFA97 */ +u03 = 3.47453432093683650238e-04, /* 0x3F36C54D, 0x20B29B6B */ +u04 = -3.81407053724364161125e-06, /* 0xBECFFEA7, 0x73D25CAD */ +u05 = 1.95590137035022920206e-08, /* 0x3E550057, 0x3B4EABD4 */ +u06 = -3.98205194132103398453e-11, /* 0xBDC5E43D, 0x693FB3C8 */ +v01 = 1.27304834834123699328e-02, /* 0x3F8A1270, 0x91C9C71A */ +v02 = 7.60068627350353253702e-05, /* 0x3F13ECBB, 0xF578C6C1 */ +v03 = 2.59150851840457805467e-07, /* 0x3E91642D, 0x7FF202FD */ +v04 = 4.41110311332675467403e-10; /* 0x3DFE5018, 0x3BD6D9EF */ + +OLM_DLLEXPORT double +__ieee754_y0(double x) +{ + double z, s,c,ss,cc,u,v; + int32_t hx,ix,lx; + + EXTRACT_WORDS(hx,lx,x); + ix = 0x7fffffff&hx; + /* Y0(NaN) is NaN, y0(-inf) is Nan, y0(inf) is 0 */ + if(ix>=0x7ff00000) return one/(x+x*x); + if((ix|lx)==0) return -one/zero; + if(hx<0) return zero/zero; + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + /* y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0)) + * where x0 = x-pi/4 + * Better formula: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) + cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + s = sin(x); + c = cos(x); + ss = s-c; + cc = s+c; + /* + * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + */ + if(ix<0x7fe00000) { /* make sure x+x not overflow */ + z = -cos(x+x); + if ((s*c)0x48000000) z = (invsqrtpi*ss)/sqrt(x); + else { + u = pzero(x); v = qzero(x); + z = invsqrtpi*(u*ss+v*cc)/sqrt(x); + } + return z; + } + if(ix<=0x3e400000) { /* x < 2**-27 */ + return(u00 + tpi*__ieee754_log(x)); + } + z = x*x; + u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06))))); + v = one+z*(v01+z*(v02+z*(v03+z*v04))); + return(u/v + tpi*(__ieee754_j0(x)*__ieee754_log(x))); +} + +/* The asymptotic expansions of pzero is + * 1 - 9/128 s^2 + 11025/98304 s^4 - ..., where s = 1/x. + * For x >= 2, We approximate pzero by + * pzero(x) = 1 + (R/S) + * where R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10 + * S = 1 + pS0*s^2 + ... + pS4*s^10 + * and + * | pzero(x)-1-R/S | <= 2 ** ( -60.26) + */ +static const double pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + -7.03124999999900357484e-02, /* 0xBFB1FFFF, 0xFFFFFD32 */ + -8.08167041275349795626e+00, /* 0xC02029D0, 0xB44FA779 */ + -2.57063105679704847262e+02, /* 0xC0701102, 0x7B19E863 */ + -2.48521641009428822144e+03, /* 0xC0A36A6E, 0xCD4DCAFC */ + -5.25304380490729545272e+03, /* 0xC0B4850B, 0x36CC643D */ +}; +static const double pS8[5] = { + 1.16534364619668181717e+02, /* 0x405D2233, 0x07A96751 */ + 3.83374475364121826715e+03, /* 0x40ADF37D, 0x50596938 */ + 4.05978572648472545552e+04, /* 0x40E3D2BB, 0x6EB6B05F */ + 1.16752972564375915681e+05, /* 0x40FC810F, 0x8F9FA9BD */ + 4.76277284146730962675e+04, /* 0x40E74177, 0x4F2C49DC */ +}; + +static const double pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + -1.14125464691894502584e-11, /* 0xBDA918B1, 0x47E495CC */ + -7.03124940873599280078e-02, /* 0xBFB1FFFF, 0xE69AFBC6 */ + -4.15961064470587782438e+00, /* 0xC010A370, 0xF90C6BBF */ + -6.76747652265167261021e+01, /* 0xC050EB2F, 0x5A7D1783 */ + -3.31231299649172967747e+02, /* 0xC074B3B3, 0x6742CC63 */ + -3.46433388365604912451e+02, /* 0xC075A6EF, 0x28A38BD7 */ +}; +static const double pS5[5] = { + 6.07539382692300335975e+01, /* 0x404E6081, 0x0C98C5DE */ + 1.05125230595704579173e+03, /* 0x40906D02, 0x5C7E2864 */ + 5.97897094333855784498e+03, /* 0x40B75AF8, 0x8FBE1D60 */ + 9.62544514357774460223e+03, /* 0x40C2CCB8, 0xFA76FA38 */ + 2.40605815922939109441e+03, /* 0x40A2CC1D, 0xC70BE864 */ +}; + +static const double pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + -2.54704601771951915620e-09, /* 0xBE25E103, 0x6FE1AA86 */ + -7.03119616381481654654e-02, /* 0xBFB1FFF6, 0xF7C0E24B */ + -2.40903221549529611423e+00, /* 0xC00345B2, 0xAEA48074 */ + -2.19659774734883086467e+01, /* 0xC035F74A, 0x4CB94E14 */ + -5.80791704701737572236e+01, /* 0xC04D0A22, 0x420A1A45 */ + -3.14479470594888503854e+01, /* 0xC03F72AC, 0xA892D80F */ +}; +static const double pS3[5] = { + 3.58560338055209726349e+01, /* 0x4041ED92, 0x84077DD3 */ + 3.61513983050303863820e+02, /* 0x40769839, 0x464A7C0E */ + 1.19360783792111533330e+03, /* 0x4092A66E, 0x6D1061D6 */ + 1.12799679856907414432e+03, /* 0x40919FFC, 0xB8C39B7E */ + 1.73580930813335754692e+02, /* 0x4065B296, 0xFC379081 */ +}; + +static const double pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + -8.87534333032526411254e-08, /* 0xBE77D316, 0xE927026D */ + -7.03030995483624743247e-02, /* 0xBFB1FF62, 0x495E1E42 */ + -1.45073846780952986357e+00, /* 0xBFF73639, 0x8A24A843 */ + -7.63569613823527770791e+00, /* 0xC01E8AF3, 0xEDAFA7F3 */ + -1.11931668860356747786e+01, /* 0xC02662E6, 0xC5246303 */ + -3.23364579351335335033e+00, /* 0xC009DE81, 0xAF8FE70F */ +}; +static const double pS2[5] = { + 2.22202997532088808441e+01, /* 0x40363865, 0x908B5959 */ + 1.36206794218215208048e+02, /* 0x4061069E, 0x0EE8878F */ + 2.70470278658083486789e+02, /* 0x4070E786, 0x42EA079B */ + 1.53875394208320329881e+02, /* 0x40633C03, 0x3AB6FAFF */ + 1.46576176948256193810e+01, /* 0x402D50B3, 0x44391809 */ +}; + + /* Note: This function is only called for ix>=0x40000000 (see above) */ + static double pzero(double x) +{ + const double *p,*q; + double z,r,s; + int32_t ix; + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + assert(ix>=0x40000000 && ix<=0x48000000); + if(ix>=0x40200000) {p = pR8; q= pS8;} + else if(ix>=0x40122E8B){p = pR5; q= pS5;} + else if(ix>=0x4006DB6D){p = pR3; q= pS3;} + else {p = pR2; q= pS2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qzero is + * -1/8 s + 75/1024 s^3 - ..., where s = 1/x. + * We approximate pzero by + * qzero(x) = s*(-1.25 + (R/S)) + * where R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10 + * S = 1 + qS0*s^2 + ... + qS5*s^12 + * and + * | qzero(x)/s +1.25-R/S | <= 2 ** ( -61.22) + */ +static const double qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + 7.32421874999935051953e-02, /* 0x3FB2BFFF, 0xFFFFFE2C */ + 1.17682064682252693899e+01, /* 0x40278952, 0x5BB334D6 */ + 5.57673380256401856059e+02, /* 0x40816D63, 0x15301825 */ + 8.85919720756468632317e+03, /* 0x40C14D99, 0x3E18F46D */ + 3.70146267776887834771e+04, /* 0x40E212D4, 0x0E901566 */ +}; +static const double qS8[6] = { + 1.63776026895689824414e+02, /* 0x406478D5, 0x365B39BC */ + 8.09834494656449805916e+03, /* 0x40BFA258, 0x4E6B0563 */ + 1.42538291419120476348e+05, /* 0x41016652, 0x54D38C3F */ + 8.03309257119514397345e+05, /* 0x412883DA, 0x83A52B43 */ + 8.40501579819060512818e+05, /* 0x4129A66B, 0x28DE0B3D */ + -3.43899293537866615225e+05, /* 0xC114FD6D, 0x2C9530C5 */ +}; + +static const double qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + 1.84085963594515531381e-11, /* 0x3DB43D8F, 0x29CC8CD9 */ + 7.32421766612684765896e-02, /* 0x3FB2BFFF, 0xD172B04C */ + 5.83563508962056953777e+00, /* 0x401757B0, 0xB9953DD3 */ + 1.35111577286449829671e+02, /* 0x4060E392, 0x0A8788E9 */ + 1.02724376596164097464e+03, /* 0x40900CF9, 0x9DC8C481 */ + 1.98997785864605384631e+03, /* 0x409F17E9, 0x53C6E3A6 */ +}; +static const double qS5[6] = { + 8.27766102236537761883e+01, /* 0x4054B1B3, 0xFB5E1543 */ + 2.07781416421392987104e+03, /* 0x40A03BA0, 0xDA21C0CE */ + 1.88472887785718085070e+04, /* 0x40D267D2, 0x7B591E6D */ + 5.67511122894947329769e+04, /* 0x40EBB5E3, 0x97E02372 */ + 3.59767538425114471465e+04, /* 0x40E19118, 0x1F7A54A0 */ + -5.35434275601944773371e+03, /* 0xC0B4EA57, 0xBEDBC609 */ +}; + +static const double qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + 4.37741014089738620906e-09, /* 0x3E32CD03, 0x6ADECB82 */ + 7.32411180042911447163e-02, /* 0x3FB2BFEE, 0x0E8D0842 */ + 3.34423137516170720929e+00, /* 0x400AC0FC, 0x61149CF5 */ + 4.26218440745412650017e+01, /* 0x40454F98, 0x962DAEDD */ + 1.70808091340565596283e+02, /* 0x406559DB, 0xE25EFD1F */ + 1.66733948696651168575e+02, /* 0x4064D77C, 0x81FA21E0 */ +}; +static const double qS3[6] = { + 4.87588729724587182091e+01, /* 0x40486122, 0xBFE343A6 */ + 7.09689221056606015736e+02, /* 0x40862D83, 0x86544EB3 */ + 3.70414822620111362994e+03, /* 0x40ACF04B, 0xE44DFC63 */ + 6.46042516752568917582e+03, /* 0x40B93C6C, 0xD7C76A28 */ + 2.51633368920368957333e+03, /* 0x40A3A8AA, 0xD94FB1C0 */ + -1.49247451836156386662e+02, /* 0xC062A7EB, 0x201CF40F */ +}; + +static const double qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + 1.50444444886983272379e-07, /* 0x3E84313B, 0x54F76BDB */ + 7.32234265963079278272e-02, /* 0x3FB2BEC5, 0x3E883E34 */ + 1.99819174093815998816e+00, /* 0x3FFFF897, 0xE727779C */ + 1.44956029347885735348e+01, /* 0x402CFDBF, 0xAAF96FE5 */ + 3.16662317504781540833e+01, /* 0x403FAA8E, 0x29FBDC4A */ + 1.62527075710929267416e+01, /* 0x403040B1, 0x71814BB4 */ +}; +static const double qS2[6] = { + 3.03655848355219184498e+01, /* 0x403E5D96, 0xF7C07AED */ + 2.69348118608049844624e+02, /* 0x4070D591, 0xE4D14B40 */ + 8.44783757595320139444e+02, /* 0x408A6645, 0x22B3BF22 */ + 8.82935845112488550512e+02, /* 0x408B977C, 0x9C5CC214 */ + 2.12666388511798828631e+02, /* 0x406A9553, 0x0E001365 */ + -5.31095493882666946917e+00, /* 0xC0153E6A, 0xF8B32931 */ +}; + + /* Note: This function is only called for ix>=0x40000000 (see above) */ + static double qzero(double x) +{ + const double *p,*q; + double s,r,z; + int32_t ix; + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + assert(ix>=0x40000000 && ix<=0x48000000); + if(ix>=0x40200000) {p = qR8; q= qS8;} + else if(ix>=0x40122E8B){p = qR5; q= qS5;} + else if(ix>=0x4006DB6D){p = qR3; q= qS3;} + else {p = qR2; q= qS2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (-.125 + r/s)/x; +} diff --git a/openlibm/src/e_j0f.c b/openlibm/src/e_j0f.c new file mode 100644 index 0000000000..62258b1a88 --- /dev/null +++ b/openlibm/src/e_j0f.c @@ -0,0 +1,337 @@ +/* e_j0f.c -- float version of e_j0.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +#include "cdefs-compat.h" + +#include +#include "math_private.h" + +static float pzerof(float), qzerof(float); + +static const float +huge = 1e30, +one = 1.0, +invsqrtpi= 5.6418961287e-01, /* 0x3f106ebb */ +tpi = 6.3661974669e-01, /* 0x3f22f983 */ + /* R0/S0 on [0, 2.00] */ +R02 = 1.5625000000e-02, /* 0x3c800000 */ +R03 = -1.8997929874e-04, /* 0xb947352e */ +R04 = 1.8295404516e-06, /* 0x35f58e88 */ +R05 = -4.6183270541e-09, /* 0xb19eaf3c */ +S01 = 1.5619102865e-02, /* 0x3c7fe744 */ +S02 = 1.1692678527e-04, /* 0x38f53697 */ +S03 = 5.1354652442e-07, /* 0x3509daa6 */ +S04 = 1.1661400734e-09; /* 0x30a045e8 */ + +static const float zero = 0.0; + +OLM_DLLEXPORT float +__ieee754_j0f(float x) +{ + float z, s,c,ss,cc,r,u,v; + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) return one/(x*x); + x = fabsf(x); + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = sinf(x); + c = cosf(x); + ss = s-c; + cc = s+c; + if(ix<0x7f000000) { /* make sure x+x not overflow */ + z = -cosf(x+x); + if ((s*c)0x58000000) z = (invsqrtpi*cc)/sqrtf(x); /* |x|>2**49 */ + else { + u = pzerof(x); v = qzerof(x); + z = invsqrtpi*(u*cc-v*ss)/sqrtf(x); + } + return z; + } + if(ix<0x3b000000) { /* |x| < 2**-9 */ + if(huge+x>one) { /* raise inexact if x != 0 */ + if(ix<0x39800000) return one; /* |x|<2**-12 */ + else return one - x*x/4; + } + } + z = x*x; + r = z*(R02+z*(R03+z*(R04+z*R05))); + s = one+z*(S01+z*(S02+z*(S03+z*S04))); + if(ix < 0x3F800000) { /* |x| < 1.00 */ + return one + z*((float)-0.25+(r/s)); + } else { + u = (float)0.5*x; + return((one+u)*(one-u)+z*(r/s)); + } +} + +static const float +u00 = -7.3804296553e-02, /* 0xbd9726b5 */ +u01 = 1.7666645348e-01, /* 0x3e34e80d */ +u02 = -1.3818567619e-02, /* 0xbc626746 */ +u03 = 3.4745343146e-04, /* 0x39b62a69 */ +u04 = -3.8140706238e-06, /* 0xb67ff53c */ +u05 = 1.9559013964e-08, /* 0x32a802ba */ +u06 = -3.9820518410e-11, /* 0xae2f21eb */ +v01 = 1.2730483897e-02, /* 0x3c509385 */ +v02 = 7.6006865129e-05, /* 0x389f65e0 */ +v03 = 2.5915085189e-07, /* 0x348b216c */ +v04 = 4.4111031494e-10; /* 0x2ff280c2 */ + +OLM_DLLEXPORT float +__ieee754_y0f(float x) +{ + float z, s,c,ss,cc,u,v; + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + ix = 0x7fffffff&hx; + /* Y0(NaN) is NaN, y0(-inf) is Nan, y0(inf) is 0 */ + if(ix>=0x7f800000) return one/(x+x*x); + if(ix==0) return -one/zero; + if(hx<0) return zero/zero; + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + /* y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0)) + * where x0 = x-pi/4 + * Better formula: + * cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + * = 1/sqrt(2) * (sin(x) + cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + s = sinf(x); + c = cosf(x); + ss = s-c; + cc = s+c; + /* + * j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + * y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + */ + if(ix<0x7f000000) { /* make sure x+x not overflow */ + z = -cosf(x+x); + if ((s*c)0x58000000) z = (invsqrtpi*ss)/sqrtf(x); /* |x|>2**49 */ + else { + u = pzerof(x); v = qzerof(x); + z = invsqrtpi*(u*ss+v*cc)/sqrtf(x); + } + return z; + } + if(ix<=0x39000000) { /* x < 2**-13 */ + return(u00 + tpi*__ieee754_logf(x)); + } + z = x*x; + u = u00+z*(u01+z*(u02+z*(u03+z*(u04+z*(u05+z*u06))))); + v = one+z*(v01+z*(v02+z*(v03+z*v04))); + return(u/v + tpi*(__ieee754_j0f(x)*__ieee754_logf(x))); +} + +/* The asymptotic expansions of pzero is + * 1 - 9/128 s^2 + 11025/98304 s^4 - ..., where s = 1/x. + * For x >= 2, We approximate pzero by + * pzero(x) = 1 + (R/S) + * where R = pR0 + pR1*s^2 + pR2*s^4 + ... + pR5*s^10 + * S = 1 + pS0*s^2 + ... + pS4*s^10 + * and + * | pzero(x)-1-R/S | <= 2 ** ( -60.26) + */ +static const float pR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0000000000e+00, /* 0x00000000 */ + -7.0312500000e-02, /* 0xbd900000 */ + -8.0816707611e+00, /* 0xc1014e86 */ + -2.5706311035e+02, /* 0xc3808814 */ + -2.4852163086e+03, /* 0xc51b5376 */ + -5.2530439453e+03, /* 0xc5a4285a */ +}; +static const float pS8[5] = { + 1.1653436279e+02, /* 0x42e91198 */ + 3.8337448730e+03, /* 0x456f9beb */ + 4.0597855469e+04, /* 0x471e95db */ + 1.1675296875e+05, /* 0x47e4087c */ + 4.7627726562e+04, /* 0x473a0bba */ +}; +static const float pR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + -1.1412546255e-11, /* 0xad48c58a */ + -7.0312492549e-02, /* 0xbd8fffff */ + -4.1596107483e+00, /* 0xc0851b88 */ + -6.7674766541e+01, /* 0xc287597b */ + -3.3123129272e+02, /* 0xc3a59d9b */ + -3.4643338013e+02, /* 0xc3ad3779 */ +}; +static const float pS5[5] = { + 6.0753936768e+01, /* 0x42730408 */ + 1.0512523193e+03, /* 0x44836813 */ + 5.9789707031e+03, /* 0x45bad7c4 */ + 9.6254453125e+03, /* 0x461665c8 */ + 2.4060581055e+03, /* 0x451660ee */ +}; + +static const float pR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + -2.5470459075e-09, /* 0xb12f081b */ + -7.0311963558e-02, /* 0xbd8fffb8 */ + -2.4090321064e+00, /* 0xc01a2d95 */ + -2.1965976715e+01, /* 0xc1afba52 */ + -5.8079170227e+01, /* 0xc2685112 */ + -3.1447946548e+01, /* 0xc1fb9565 */ +}; +static const float pS3[5] = { + 3.5856033325e+01, /* 0x420f6c94 */ + 3.6151397705e+02, /* 0x43b4c1ca */ + 1.1936077881e+03, /* 0x44953373 */ + 1.1279968262e+03, /* 0x448cffe6 */ + 1.7358093262e+02, /* 0x432d94b8 */ +}; + +static const float pR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + -8.8753431271e-08, /* 0xb3be98b7 */ + -7.0303097367e-02, /* 0xbd8ffb12 */ + -1.4507384300e+00, /* 0xbfb9b1cc */ + -7.6356959343e+00, /* 0xc0f4579f */ + -1.1193166733e+01, /* 0xc1331736 */ + -3.2336456776e+00, /* 0xc04ef40d */ +}; +static const float pS2[5] = { + 2.2220300674e+01, /* 0x41b1c32d */ + 1.3620678711e+02, /* 0x430834f0 */ + 2.7047027588e+02, /* 0x43873c32 */ + 1.5387539673e+02, /* 0x4319e01a */ + 1.4657617569e+01, /* 0x416a859a */ +}; + + static float pzerof(float x) +{ + const float *p,*q; + float z,r,s; + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + if(ix>=0x41000000) {p = pR8; q= pS8;} + else if(ix>=0x409173eb){p = pR5; q= pS5;} + else if(ix>=0x4036d917){p = pR3; q= pS3;} + else {p = pR2; q= pS2;} /* ix>=0x40000000 */ + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qzero is + * -1/8 s + 75/1024 s^3 - ..., where s = 1/x. + * We approximate pzero by + * qzero(x) = s*(-1.25 + (R/S)) + * where R = qR0 + qR1*s^2 + qR2*s^4 + ... + qR5*s^10 + * S = 1 + qS0*s^2 + ... + qS5*s^12 + * and + * | qzero(x)/s +1.25-R/S | <= 2 ** ( -61.22) + */ +static const float qR8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0000000000e+00, /* 0x00000000 */ + 7.3242187500e-02, /* 0x3d960000 */ + 1.1768206596e+01, /* 0x413c4a93 */ + 5.5767340088e+02, /* 0x440b6b19 */ + 8.8591972656e+03, /* 0x460a6cca */ + 3.7014625000e+04, /* 0x471096a0 */ +}; +static const float qS8[6] = { + 1.6377603149e+02, /* 0x4323c6aa */ + 8.0983447266e+03, /* 0x45fd12c2 */ + 1.4253829688e+05, /* 0x480b3293 */ + 8.0330925000e+05, /* 0x49441ed4 */ + 8.4050156250e+05, /* 0x494d3359 */ + -3.4389928125e+05, /* 0xc8a7eb69 */ +}; + +static const float qR5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + 1.8408595828e-11, /* 0x2da1ec79 */ + 7.3242180049e-02, /* 0x3d95ffff */ + 5.8356351852e+00, /* 0x40babd86 */ + 1.3511157227e+02, /* 0x43071c90 */ + 1.0272437744e+03, /* 0x448067cd */ + 1.9899779053e+03, /* 0x44f8bf4b */ +}; +static const float qS5[6] = { + 8.2776611328e+01, /* 0x42a58da0 */ + 2.0778142090e+03, /* 0x4501dd07 */ + 1.8847289062e+04, /* 0x46933e94 */ + 5.6751113281e+04, /* 0x475daf1d */ + 3.5976753906e+04, /* 0x470c88c1 */ + -5.3543427734e+03, /* 0xc5a752be */ +}; + +static const float qR3[6] = {/* for x in [4.547,2.8571]=1/[0.2199,0.35001] */ + 4.3774099900e-09, /* 0x3196681b */ + 7.3241114616e-02, /* 0x3d95ff70 */ + 3.3442313671e+00, /* 0x405607e3 */ + 4.2621845245e+01, /* 0x422a7cc5 */ + 1.7080809021e+02, /* 0x432acedf */ + 1.6673394775e+02, /* 0x4326bbe4 */ +}; +static const float qS3[6] = { + 4.8758872986e+01, /* 0x42430916 */ + 7.0968920898e+02, /* 0x44316c1c */ + 3.7041481934e+03, /* 0x4567825f */ + 6.4604252930e+03, /* 0x45c9e367 */ + 2.5163337402e+03, /* 0x451d4557 */ + -1.4924745178e+02, /* 0xc3153f59 */ +}; + +static const float qR2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + 1.5044444979e-07, /* 0x342189db */ + 7.3223426938e-02, /* 0x3d95f62a */ + 1.9981917143e+00, /* 0x3fffc4bf */ + 1.4495602608e+01, /* 0x4167edfd */ + 3.1666231155e+01, /* 0x41fd5471 */ + 1.6252708435e+01, /* 0x4182058c */ +}; +static const float qS2[6] = { + 3.0365585327e+01, /* 0x41f2ecb8 */ + 2.6934811401e+02, /* 0x4386ac8f */ + 8.4478375244e+02, /* 0x44533229 */ + 8.8293585205e+02, /* 0x445cbbe5 */ + 2.1266638184e+02, /* 0x4354aa98 */ + -5.3109550476e+00, /* 0xc0a9f358 */ +}; + + static float qzerof(float x) +{ + const float *p,*q; + float s,r,z; + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + if(ix>=0x41000000) {p = qR8; q= qS8;} + else if(ix>=0x409173eb){p = qR5; q= qS5;} + else if(ix>=0x4036d917){p = qR3; q= qS3;} + else {p = qR2; q= qS2;} /* ix>=0x40000000 */ + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (-(float).125 + r/s)/x; +} diff --git a/openlibm/src/e_j1.c b/openlibm/src/e_j1.c new file mode 100644 index 0000000000..4be57cd247 --- /dev/null +++ b/openlibm/src/e_j1.c @@ -0,0 +1,383 @@ + +/* @(#)e_j1.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_j1.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +/* __ieee754_j1(x), __ieee754_y1(x) + * Bessel function of the first and second kinds of order zero. + * Method -- j1(x): + * 1. For tiny x, we use j1(x) = x/2 - x^3/16 + x^5/384 - ... + * 2. Reduce x to |x| since j1(x)=-j1(-x), and + * for x in (0,2) + * j1(x) = x/2 + x*z*R0/S0, where z = x*x; + * (precision: |j1/x - 1/2 - R0/S0 |<2**-61.51 ) + * for x in (2,inf) + * j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1)) + * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) + * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) + * as follow: + * cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (sin(x) + cos(x)) + * (To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one.) + * + * 3 Special cases + * j1(nan)= nan + * j1(0) = 0 + * j1(inf) = 0 + * + * Method -- y1(x): + * 1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN + * 2. For x<2. + * Since + * y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x^3-...) + * therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function. + * We use the following function to approximate y1, + * y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x^2 + * where for x in [0,2] (abs err less than 2**-65.89) + * U(z) = U0[0] + U0[1]*z + ... + U0[4]*z^4 + * V(z) = 1 + v0[0]*z + ... + v0[4]*z^5 + * Note: For tiny x, 1/x dominate y1 and hence + * y1(tiny) = -2/pi/tiny, (choose tiny<2**-54) + * 3. For x>=2. + * y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) + * where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) + * by method mentioned above. + */ + +#include + +#include "math_private.h" + +static double pone(double), qone(double); + +static const double +huge = 1e300, +one = 1.0, +invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ +tpi = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ + /* R0/S0 on [0,2] */ +r00 = -6.25000000000000000000e-02, /* 0xBFB00000, 0x00000000 */ +r01 = 1.40705666955189706048e-03, /* 0x3F570D9F, 0x98472C61 */ +r02 = -1.59955631084035597520e-05, /* 0xBEF0C5C6, 0xBA169668 */ +r03 = 4.96727999609584448412e-08, /* 0x3E6AAAFA, 0x46CA0BD9 */ +s01 = 1.91537599538363460805e-02, /* 0x3F939D0B, 0x12637E53 */ +s02 = 1.85946785588630915560e-04, /* 0x3F285F56, 0xB9CDF664 */ +s03 = 1.17718464042623683263e-06, /* 0x3EB3BFF8, 0x333F8498 */ +s04 = 5.04636257076217042715e-09, /* 0x3E35AC88, 0xC97DFF2C */ +s05 = 1.23542274426137913908e-11; /* 0x3DAB2ACF, 0xCFB97ED8 */ + +static const double zero = 0.0; + +OLM_DLLEXPORT double +__ieee754_j1(double x) +{ + double z, s,c,ss,cc,r,u,v,y; + int32_t hx,ix; + + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return one/x; + y = fabs(x); + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = sin(y); + c = cos(y); + ss = -s-c; + cc = s-c; + if(ix<0x7fe00000) { /* make sure y+y not overflow */ + z = cos(y+y); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* + * j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x) + * y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x) + */ + if(ix>0x48000000) z = (invsqrtpi*cc)/sqrt(y); + else { + u = pone(y); v = qone(y); + z = invsqrtpi*(u*cc-v*ss)/sqrt(y); + } + if(hx<0) return -z; + else return z; + } + if(ix<0x3e400000) { /* |x|<2**-27 */ + if(huge+x>one) return 0.5*x;/* inexact if x!=0 necessary */ + } + z = x*x; + r = z*(r00+z*(r01+z*(r02+z*r03))); + s = one+z*(s01+z*(s02+z*(s03+z*(s04+z*s05)))); + r *= x; + return(x*0.5+r/s); +} + +static const double U0[5] = { + -1.96057090646238940668e-01, /* 0xBFC91866, 0x143CBC8A */ + 5.04438716639811282616e-02, /* 0x3FA9D3C7, 0x76292CD1 */ + -1.91256895875763547298e-03, /* 0xBF5F55E5, 0x4844F50F */ + 2.35252600561610495928e-05, /* 0x3EF8AB03, 0x8FA6B88E */ + -9.19099158039878874504e-08, /* 0xBE78AC00, 0x569105B8 */ +}; +static const double V0[5] = { + 1.99167318236649903973e-02, /* 0x3F94650D, 0x3F4DA9F0 */ + 2.02552581025135171496e-04, /* 0x3F2A8C89, 0x6C257764 */ + 1.35608801097516229404e-06, /* 0x3EB6C05A, 0x894E8CA6 */ + 6.22741452364621501295e-09, /* 0x3E3ABF1D, 0x5BA69A86 */ + 1.66559246207992079114e-11, /* 0x3DB25039, 0xDACA772A */ +}; + +OLM_DLLEXPORT double +__ieee754_y1(double x) +{ + double z, s,c,ss,cc,u,v; + int32_t hx,ix,lx; + + EXTRACT_WORDS(hx,lx,x); + ix = 0x7fffffff&hx; + /* if Y1(NaN) is NaN, Y1(-inf) is NaN, Y1(inf) is 0 */ + if(ix>=0x7ff00000) return one/(x+x*x); + if((ix|lx)==0) return -one/zero; + if(hx<0) return zero/zero; + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = sin(x); + c = cos(x); + ss = -s-c; + cc = s-c; + if(ix<0x7fe00000) { /* make sure x+x not overflow */ + z = cos(x+x); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0)) + * where x0 = x-3pi/4 + * Better formula: + * cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (cos(x) + sin(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + if(ix>0x48000000) z = (invsqrtpi*ss)/sqrt(x); + else { + u = pone(x); v = qone(x); + z = invsqrtpi*(u*ss+v*cc)/sqrt(x); + } + return z; + } + if(ix<=0x3c900000) { /* x < 2**-54 */ + return(-tpi/x); + } + z = x*x; + u = U0[0]+z*(U0[1]+z*(U0[2]+z*(U0[3]+z*U0[4]))); + v = one+z*(V0[0]+z*(V0[1]+z*(V0[2]+z*(V0[3]+z*V0[4])))); + return(x*(u/v) + tpi*(__ieee754_j1(x)*__ieee754_log(x)-one/x)); +} + +/* For x >= 8, the asymptotic expansions of pone is + * 1 + 15/128 s^2 - 4725/2^15 s^4 - ..., where s = 1/x. + * We approximate pone by + * pone(x) = 1 + (R/S) + * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10 + * S = 1 + ps0*s^2 + ... + ps4*s^10 + * and + * | pone(x)-1-R/S | <= 2 ** ( -60.06) + */ + +static const double pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + 1.17187499999988647970e-01, /* 0x3FBDFFFF, 0xFFFFFCCE */ + 1.32394806593073575129e+01, /* 0x402A7A9D, 0x357F7FCE */ + 4.12051854307378562225e+02, /* 0x4079C0D4, 0x652EA590 */ + 3.87474538913960532227e+03, /* 0x40AE457D, 0xA3A532CC */ + 7.91447954031891731574e+03, /* 0x40BEEA7A, 0xC32782DD */ +}; +static const double ps8[5] = { + 1.14207370375678408436e+02, /* 0x405C8D45, 0x8E656CAC */ + 3.65093083420853463394e+03, /* 0x40AC85DC, 0x964D274F */ + 3.69562060269033463555e+04, /* 0x40E20B86, 0x97C5BB7F */ + 9.76027935934950801311e+04, /* 0x40F7D42C, 0xB28F17BB */ + 3.08042720627888811578e+04, /* 0x40DE1511, 0x697A0B2D */ +}; + +static const double pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + 1.31990519556243522749e-11, /* 0x3DAD0667, 0xDAE1CA7D */ + 1.17187493190614097638e-01, /* 0x3FBDFFFF, 0xE2C10043 */ + 6.80275127868432871736e+00, /* 0x401B3604, 0x6E6315E3 */ + 1.08308182990189109773e+02, /* 0x405B13B9, 0x452602ED */ + 5.17636139533199752805e+02, /* 0x40802D16, 0xD052D649 */ + 5.28715201363337541807e+02, /* 0x408085B8, 0xBB7E0CB7 */ +}; +static const double ps5[5] = { + 5.92805987221131331921e+01, /* 0x404DA3EA, 0xA8AF633D */ + 9.91401418733614377743e+02, /* 0x408EFB36, 0x1B066701 */ + 5.35326695291487976647e+03, /* 0x40B4E944, 0x5706B6FB */ + 7.84469031749551231769e+03, /* 0x40BEA4B0, 0xB8A5BB15 */ + 1.50404688810361062679e+03, /* 0x40978030, 0x036F5E51 */ +}; + +static const double pr3[6] = { + 3.02503916137373618024e-09, /* 0x3E29FC21, 0xA7AD9EDD */ + 1.17186865567253592491e-01, /* 0x3FBDFFF5, 0x5B21D17B */ + 3.93297750033315640650e+00, /* 0x400F76BC, 0xE85EAD8A */ + 3.51194035591636932736e+01, /* 0x40418F48, 0x9DA6D129 */ + 9.10550110750781271918e+01, /* 0x4056C385, 0x4D2C1837 */ + 4.85590685197364919645e+01, /* 0x4048478F, 0x8EA83EE5 */ +}; +static const double ps3[5] = { + 3.47913095001251519989e+01, /* 0x40416549, 0xA134069C */ + 3.36762458747825746741e+02, /* 0x40750C33, 0x07F1A75F */ + 1.04687139975775130551e+03, /* 0x40905B7C, 0x5037D523 */ + 8.90811346398256432622e+02, /* 0x408BD67D, 0xA32E31E9 */ + 1.03787932439639277504e+02, /* 0x4059F26D, 0x7C2EED53 */ +}; + +static const double pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + 1.07710830106873743082e-07, /* 0x3E7CE9D4, 0xF65544F4 */ + 1.17176219462683348094e-01, /* 0x3FBDFF42, 0xBE760D83 */ + 2.36851496667608785174e+00, /* 0x4002F2B7, 0xF98FAEC0 */ + 1.22426109148261232917e+01, /* 0x40287C37, 0x7F71A964 */ + 1.76939711271687727390e+01, /* 0x4031B1A8, 0x177F8EE2 */ + 5.07352312588818499250e+00, /* 0x40144B49, 0xA574C1FE */ +}; +static const double ps2[5] = { + 2.14364859363821409488e+01, /* 0x40356FBD, 0x8AD5ECDC */ + 1.25290227168402751090e+02, /* 0x405F5293, 0x14F92CD5 */ + 2.32276469057162813669e+02, /* 0x406D08D8, 0xD5A2DBD9 */ + 1.17679373287147100768e+02, /* 0x405D6B7A, 0xDA1884A9 */ + 8.36463893371618283368e+00, /* 0x4020BAB1, 0xF44E5192 */ +}; + + /* Note: This function is only called for ix>=0x40000000 (see above) */ + static double pone(double x) +{ + const double *p,*q; + double z,r,s; + int32_t ix; + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + assert(ix>=0x40000000 && ix<=0x48000000); + if(ix>=0x40200000) {p = pr8; q= ps8;} + else if(ix>=0x40122E8B){p = pr5; q= ps5;} + else if(ix>=0x4006DB6D){p = pr3; q= ps3;} + else {p = pr2; q= ps2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qone is + * 3/8 s - 105/1024 s^3 - ..., where s = 1/x. + * We approximate pone by + * qone(x) = s*(0.375 + (R/S)) + * where R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10 + * S = 1 + qs1*s^2 + ... + qs6*s^12 + * and + * | qone(x)/s -0.375-R/S | <= 2 ** ( -61.13) + */ + +static const double qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ + -1.02539062499992714161e-01, /* 0xBFBA3FFF, 0xFFFFFDF3 */ + -1.62717534544589987888e+01, /* 0xC0304591, 0xA26779F7 */ + -7.59601722513950107896e+02, /* 0xC087BCD0, 0x53E4B576 */ + -1.18498066702429587167e+04, /* 0xC0C724E7, 0x40F87415 */ + -4.84385124285750353010e+04, /* 0xC0E7A6D0, 0x65D09C6A */ +}; +static const double qs8[6] = { + 1.61395369700722909556e+02, /* 0x40642CA6, 0xDE5BCDE5 */ + 7.82538599923348465381e+03, /* 0x40BE9162, 0xD0D88419 */ + 1.33875336287249578163e+05, /* 0x4100579A, 0xB0B75E98 */ + 7.19657723683240939863e+05, /* 0x4125F653, 0x72869C19 */ + 6.66601232617776375264e+05, /* 0x412457D2, 0x7719AD5C */ + -2.94490264303834643215e+05, /* 0xC111F969, 0x0EA5AA18 */ +}; + +static const double qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + -2.08979931141764104297e-11, /* 0xBDB6FA43, 0x1AA1A098 */ + -1.02539050241375426231e-01, /* 0xBFBA3FFF, 0xCB597FEF */ + -8.05644828123936029840e+00, /* 0xC0201CE6, 0xCA03AD4B */ + -1.83669607474888380239e+02, /* 0xC066F56D, 0x6CA7B9B0 */ + -1.37319376065508163265e+03, /* 0xC09574C6, 0x6931734F */ + -2.61244440453215656817e+03, /* 0xC0A468E3, 0x88FDA79D */ +}; +static const double qs5[6] = { + 8.12765501384335777857e+01, /* 0x405451B2, 0xFF5A11B2 */ + 1.99179873460485964642e+03, /* 0x409F1F31, 0xE77BF839 */ + 1.74684851924908907677e+04, /* 0x40D10F1F, 0x0D64CE29 */ + 4.98514270910352279316e+04, /* 0x40E8576D, 0xAABAD197 */ + 2.79480751638918118260e+04, /* 0x40DB4B04, 0xCF7C364B */ + -4.71918354795128470869e+03, /* 0xC0B26F2E, 0xFCFFA004 */ +}; + +static const double qr3[6] = { + -5.07831226461766561369e-09, /* 0xBE35CFA9, 0xD38FC84F */ + -1.02537829820837089745e-01, /* 0xBFBA3FEB, 0x51AEED54 */ + -4.61011581139473403113e+00, /* 0xC01270C2, 0x3302D9FF */ + -5.78472216562783643212e+01, /* 0xC04CEC71, 0xC25D16DA */ + -2.28244540737631695038e+02, /* 0xC06C87D3, 0x4718D55F */ + -2.19210128478909325622e+02, /* 0xC06B66B9, 0x5F5C1BF6 */ +}; +static const double qs3[6] = { + 4.76651550323729509273e+01, /* 0x4047D523, 0xCCD367E4 */ + 6.73865112676699709482e+02, /* 0x40850EEB, 0xC031EE3E */ + 3.38015286679526343505e+03, /* 0x40AA684E, 0x448E7C9A */ + 5.54772909720722782367e+03, /* 0x40B5ABBA, 0xA61D54A6 */ + 1.90311919338810798763e+03, /* 0x409DBC7A, 0x0DD4DF4B */ + -1.35201191444307340817e+02, /* 0xC060E670, 0x290A311F */ +}; + +static const double qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + -1.78381727510958865572e-07, /* 0xBE87F126, 0x44C626D2 */ + -1.02517042607985553460e-01, /* 0xBFBA3E8E, 0x9148B010 */ + -2.75220568278187460720e+00, /* 0xC0060484, 0x69BB4EDA */ + -1.96636162643703720221e+01, /* 0xC033A9E2, 0xC168907F */ + -4.23253133372830490089e+01, /* 0xC04529A3, 0xDE104AAA */ + -2.13719211703704061733e+01, /* 0xC0355F36, 0x39CF6E52 */ +}; +static const double qs2[6] = { + 2.95333629060523854548e+01, /* 0x403D888A, 0x78AE64FF */ + 2.52981549982190529136e+02, /* 0x406F9F68, 0xDB821CBA */ + 7.57502834868645436472e+02, /* 0x4087AC05, 0xCE49A0F7 */ + 7.39393205320467245656e+02, /* 0x40871B25, 0x48D4C029 */ + 1.55949003336666123687e+02, /* 0x40637E5E, 0x3C3ED8D4 */ + -4.95949898822628210127e+00, /* 0xC013D686, 0xE71BE86B */ +}; + + /* Note: This function is only called for ix>=0x40000000 (see above) */ + static double qone(double x) +{ + const double *p,*q; + double s,r,z; + int32_t ix; + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + assert(ix>=0x40000000 && ix<=0x48000000); + if(ix>=0x40200000) {p = qr8; q= qs8;} + else if(ix>=0x40122E8B){p = qr5; q= qs5;} + else if(ix>=0x4006DB6D){p = qr3; q= qs3;} + else {p = qr2; q= qs2;} + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return (.375 + r/s)/x; +} diff --git a/openlibm/src/e_j1f.c b/openlibm/src/e_j1f.c new file mode 100644 index 0000000000..ac32c6fd68 --- /dev/null +++ b/openlibm/src/e_j1f.c @@ -0,0 +1,332 @@ +/* e_j1f.c -- float version of e_j1.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +#include "cdefs-compat.h" +#include +#include "math_private.h" + +static float ponef(float), qonef(float); + +static const float +huge = 1e30, +one = 1.0, +invsqrtpi= 5.6418961287e-01, /* 0x3f106ebb */ +tpi = 6.3661974669e-01, /* 0x3f22f983 */ + /* R0/S0 on [0,2] */ +r00 = -6.2500000000e-02, /* 0xbd800000 */ +r01 = 1.4070566976e-03, /* 0x3ab86cfd */ +r02 = -1.5995563444e-05, /* 0xb7862e36 */ +r03 = 4.9672799207e-08, /* 0x335557d2 */ +s01 = 1.9153760746e-02, /* 0x3c9ce859 */ +s02 = 1.8594678841e-04, /* 0x3942fab6 */ +s03 = 1.1771846857e-06, /* 0x359dffc2 */ +s04 = 5.0463624390e-09, /* 0x31ad6446 */ +s05 = 1.2354227016e-11; /* 0x2d59567e */ + +static const float zero = 0.0; + +OLM_DLLEXPORT float +__ieee754_j1f(float x) +{ + float z, s,c,ss,cc,r,u,v,y; + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) return one/x; + y = fabsf(x); + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = sinf(y); + c = cosf(y); + ss = -s-c; + cc = s-c; + if(ix<0x7f000000) { /* make sure y+y not overflow */ + z = cosf(y+y); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* + * j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x) + * y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x) + */ + if(ix>0x58000000) z = (invsqrtpi*cc)/sqrtf(y); /* |x|>2**49 */ + else { + u = ponef(y); v = qonef(y); + z = invsqrtpi*(u*cc-v*ss)/sqrtf(y); + } + if(hx<0) return -z; + else return z; + } + if(ix<0x39000000) { /* |x|<2**-13 */ + if(huge+x>one) return (float)0.5*x;/* inexact if x!=0 necessary */ + } + z = x*x; + r = z*(r00+z*(r01+z*(r02+z*r03))); + s = one+z*(s01+z*(s02+z*(s03+z*(s04+z*s05)))); + r *= x; + return(x*(float)0.5+r/s); +} + +static const float U0[5] = { + -1.9605709612e-01, /* 0xbe48c331 */ + 5.0443872809e-02, /* 0x3d4e9e3c */ + -1.9125689287e-03, /* 0xbafaaf2a */ + 2.3525259166e-05, /* 0x37c5581c */ + -9.1909917899e-08, /* 0xb3c56003 */ +}; +static const float V0[5] = { + 1.9916731864e-02, /* 0x3ca3286a */ + 2.0255257550e-04, /* 0x3954644b */ + 1.3560879779e-06, /* 0x35b602d4 */ + 6.2274145840e-09, /* 0x31d5f8eb */ + 1.6655924903e-11, /* 0x2d9281cf */ +}; + +OLM_DLLEXPORT float +__ieee754_y1f(float x) +{ + float z, s,c,ss,cc,u,v; + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + ix = 0x7fffffff&hx; + /* if Y1(NaN) is NaN, Y1(-inf) is NaN, Y1(inf) is 0 */ + if(ix>=0x7f800000) return one/(x+x*x); + if(ix==0) return -one/zero; + if(hx<0) return zero/zero; + if(ix >= 0x40000000) { /* |x| >= 2.0 */ + s = sinf(x); + c = cosf(x); + ss = -s-c; + cc = s-c; + if(ix<0x7f000000) { /* make sure x+x not overflow */ + z = cosf(x+x); + if ((s*c)>zero) cc = z/ss; + else ss = z/cc; + } + /* y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0)) + * where x0 = x-3pi/4 + * Better formula: + * cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + * = 1/sqrt(2) * (sin(x) - cos(x)) + * sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + * = -1/sqrt(2) * (cos(x) + sin(x)) + * To avoid cancellation, use + * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + * to compute the worse one. + */ + if(ix>0x58000000) z = (invsqrtpi*ss)/sqrtf(x); /* |x|>2**49 */ + else { + u = ponef(x); v = qonef(x); + z = invsqrtpi*(u*ss+v*cc)/sqrtf(x); + } + return z; + } + if(ix<=0x33000000) { /* x < 2**-25 */ + return(-tpi/x); + } + z = x*x; + u = U0[0]+z*(U0[1]+z*(U0[2]+z*(U0[3]+z*U0[4]))); + v = one+z*(V0[0]+z*(V0[1]+z*(V0[2]+z*(V0[3]+z*V0[4])))); + return(x*(u/v) + tpi*(__ieee754_j1f(x)*__ieee754_logf(x)-one/x)); +} + +/* For x >= 8, the asymptotic expansions of pone is + * 1 + 15/128 s^2 - 4725/2^15 s^4 - ..., where s = 1/x. + * We approximate pone by + * pone(x) = 1 + (R/S) + * where R = pr0 + pr1*s^2 + pr2*s^4 + ... + pr5*s^10 + * S = 1 + ps0*s^2 + ... + ps4*s^10 + * and + * | pone(x)-1-R/S | <= 2 ** ( -60.06) + */ + +static const float pr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0000000000e+00, /* 0x00000000 */ + 1.1718750000e-01, /* 0x3df00000 */ + 1.3239480972e+01, /* 0x4153d4ea */ + 4.1205184937e+02, /* 0x43ce06a3 */ + 3.8747453613e+03, /* 0x45722bed */ + 7.9144794922e+03, /* 0x45f753d6 */ +}; +static const float ps8[5] = { + 1.1420736694e+02, /* 0x42e46a2c */ + 3.6509309082e+03, /* 0x45642ee5 */ + 3.6956207031e+04, /* 0x47105c35 */ + 9.7602796875e+04, /* 0x47bea166 */ + 3.0804271484e+04, /* 0x46f0a88b */ +}; + +static const float pr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + 1.3199052094e-11, /* 0x2d68333f */ + 1.1718749255e-01, /* 0x3defffff */ + 6.8027510643e+00, /* 0x40d9b023 */ + 1.0830818176e+02, /* 0x42d89dca */ + 5.1763616943e+02, /* 0x440168b7 */ + 5.2871520996e+02, /* 0x44042dc6 */ +}; +static const float ps5[5] = { + 5.9280597687e+01, /* 0x426d1f55 */ + 9.9140142822e+02, /* 0x4477d9b1 */ + 5.3532670898e+03, /* 0x45a74a23 */ + 7.8446904297e+03, /* 0x45f52586 */ + 1.5040468750e+03, /* 0x44bc0180 */ +}; + +static const float pr3[6] = { + 3.0250391081e-09, /* 0x314fe10d */ + 1.1718686670e-01, /* 0x3defffab */ + 3.9329774380e+00, /* 0x407bb5e7 */ + 3.5119403839e+01, /* 0x420c7a45 */ + 9.1055007935e+01, /* 0x42b61c2a */ + 4.8559066772e+01, /* 0x42423c7c */ +}; +static const float ps3[5] = { + 3.4791309357e+01, /* 0x420b2a4d */ + 3.3676245117e+02, /* 0x43a86198 */ + 1.0468714600e+03, /* 0x4482dbe3 */ + 8.9081134033e+02, /* 0x445eb3ed */ + 1.0378793335e+02, /* 0x42cf936c */ +}; + +static const float pr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + 1.0771083225e-07, /* 0x33e74ea8 */ + 1.1717621982e-01, /* 0x3deffa16 */ + 2.3685150146e+00, /* 0x401795c0 */ + 1.2242610931e+01, /* 0x4143e1bc */ + 1.7693971634e+01, /* 0x418d8d41 */ + 5.0735230446e+00, /* 0x40a25a4d */ +}; +static const float ps2[5] = { + 2.1436485291e+01, /* 0x41ab7dec */ + 1.2529022980e+02, /* 0x42fa9499 */ + 2.3227647400e+02, /* 0x436846c7 */ + 1.1767937469e+02, /* 0x42eb5bd7 */ + 8.3646392822e+00, /* 0x4105d590 */ +}; + + static float ponef(float x) +{ + const float *p,*q; + float z,r,s; + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + if(ix>=0x41000000) {p = pr8; q= ps8;} + else if(ix>=0x409173eb){p = pr5; q= ps5;} + else if(ix>=0x4036d917){p = pr3; q= ps3;} + else {p = pr2; q= ps2;} /* ix>=0x40000000 */ + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))); + return one+ r/s; +} + + +/* For x >= 8, the asymptotic expansions of qone is + * 3/8 s - 105/1024 s^3 - ..., where s = 1/x. + * We approximate pone by + * qone(x) = s*(0.375 + (R/S)) + * where R = qr1*s^2 + qr2*s^4 + ... + qr5*s^10 + * S = 1 + qs1*s^2 + ... + qs6*s^12 + * and + * | qone(x)/s -0.375-R/S | <= 2 ** ( -61.13) + */ + +static const float qr8[6] = { /* for x in [inf, 8]=1/[0,0.125] */ + 0.0000000000e+00, /* 0x00000000 */ + -1.0253906250e-01, /* 0xbdd20000 */ + -1.6271753311e+01, /* 0xc1822c8d */ + -7.5960174561e+02, /* 0xc43de683 */ + -1.1849806641e+04, /* 0xc639273a */ + -4.8438511719e+04, /* 0xc73d3683 */ +}; +static const float qs8[6] = { + 1.6139537048e+02, /* 0x43216537 */ + 7.8253862305e+03, /* 0x45f48b17 */ + 1.3387534375e+05, /* 0x4802bcd6 */ + 7.1965775000e+05, /* 0x492fb29c */ + 6.6660125000e+05, /* 0x4922be94 */ + -2.9449025000e+05, /* 0xc88fcb48 */ +}; + +static const float qr5[6] = { /* for x in [8,4.5454]=1/[0.125,0.22001] */ + -2.0897993405e-11, /* 0xadb7d219 */ + -1.0253904760e-01, /* 0xbdd1fffe */ + -8.0564479828e+00, /* 0xc100e736 */ + -1.8366960144e+02, /* 0xc337ab6b */ + -1.3731937256e+03, /* 0xc4aba633 */ + -2.6124443359e+03, /* 0xc523471c */ +}; +static const float qs5[6] = { + 8.1276550293e+01, /* 0x42a28d98 */ + 1.9917987061e+03, /* 0x44f8f98f */ + 1.7468484375e+04, /* 0x468878f8 */ + 4.9851425781e+04, /* 0x4742bb6d */ + 2.7948074219e+04, /* 0x46da5826 */ + -4.7191835938e+03, /* 0xc5937978 */ +}; + +static const float qr3[6] = { + -5.0783124372e-09, /* 0xb1ae7d4f */ + -1.0253783315e-01, /* 0xbdd1ff5b */ + -4.6101160049e+00, /* 0xc0938612 */ + -5.7847221375e+01, /* 0xc267638e */ + -2.2824453735e+02, /* 0xc3643e9a */ + -2.1921012878e+02, /* 0xc35b35cb */ +}; +static const float qs3[6] = { + 4.7665153503e+01, /* 0x423ea91e */ + 6.7386511230e+02, /* 0x4428775e */ + 3.3801528320e+03, /* 0x45534272 */ + 5.5477290039e+03, /* 0x45ad5dd5 */ + 1.9031191406e+03, /* 0x44ede3d0 */ + -1.3520118713e+02, /* 0xc3073381 */ +}; + +static const float qr2[6] = {/* for x in [2.8570,2]=1/[0.3499,0.5] */ + -1.7838172539e-07, /* 0xb43f8932 */ + -1.0251704603e-01, /* 0xbdd1f475 */ + -2.7522056103e+00, /* 0xc0302423 */ + -1.9663616180e+01, /* 0xc19d4f16 */ + -4.2325313568e+01, /* 0xc2294d1f */ + -2.1371921539e+01, /* 0xc1aaf9b2 */ +}; +static const float qs2[6] = { + 2.9533363342e+01, /* 0x41ec4454 */ + 2.5298155212e+02, /* 0x437cfb47 */ + 7.5750280762e+02, /* 0x443d602e */ + 7.3939318848e+02, /* 0x4438d92a */ + 1.5594900513e+02, /* 0x431bf2f2 */ + -4.9594988823e+00, /* 0xc09eb437 */ +}; + + static float qonef(float x) +{ + const float *p,*q; + float s,r,z; + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + if(ix>=0x41000000) {p = qr8; q= qs8;} + else if(ix>=0x409173eb){p = qr5; q= qs5;} + else if(ix>=0x4036d917){p = qr3; q= qs3;} + else {p = qr2; q= qs2;} /* ix>=0x40000000 */ + z = one/(x*x); + r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); + s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); + return ((float).375 + r/s)/x; +} diff --git a/openlibm/src/e_jn.c b/openlibm/src/e_jn.c new file mode 100644 index 0000000000..1a5396572d --- /dev/null +++ b/openlibm/src/e_jn.c @@ -0,0 +1,271 @@ + +/* @(#)e_jn.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_jn.c,v 1.11 2010/11/13 10:54:10 uqs Exp $"); + +/* + * __ieee754_jn(n, x), __ieee754_yn(n, x) + * floating point Bessel's function of the 1st and 2nd kind + * of order n + * + * Special cases: + * y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal; + * y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal. + * Note 2. About jn(n,x), yn(n,x) + * For n=0, j0(x) is called, + * for n=1, j1(x) is called, + * for nx, a continued fraction approximation to + * j(n,x)/j(n-1,x) is evaluated and then backward + * recursion is used starting from a supposed value + * for j(n,x). The resulting value of j(0,x) is + * compared with the actual value to correct the + * supposed value of j(n,x). + * + * yn(n,x) is similar in all respects, except + * that forward recursion is used for all + * values of n>1. + * + */ + +#include + +#include "math_private.h" + +static const double +invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ +two = 2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */ +one = 1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */ + +static const double zero = 0.00000000000000000000e+00; + +OLM_DLLEXPORT double +__ieee754_jn(int n, double x) +{ + int32_t i,hx,ix,lx, sgn; + double a, b, temp, di; + double z, w; + + /* J(-n,x) = (-1)^n * J(n, x), J(n, -x) = (-1)^n * J(n, x) + * Thus, J(-n,x) = J(n,-x) + */ + EXTRACT_WORDS(hx,lx,x); + ix = 0x7fffffff&hx; + /* if J(n,NaN) is NaN */ + if((ix|((u_int32_t)(lx|-lx))>>31)>0x7ff00000) return x+x; + if(n<0){ + n = -n; + x = -x; + hx ^= 0x80000000; + } + if(n==0) return(__ieee754_j0(x)); + if(n==1) return(__ieee754_j1(x)); + sgn = (n&1)&(hx>>31); /* even n -- 0, odd n -- sign(x) */ + x = fabs(x); + if((ix|lx)==0||ix>=0x7ff00000) /* if x is 0 or inf */ + b = zero; + else if((double)n<=x) { + /* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */ + if(ix>=0x52D00000) { /* x > 2**302 */ + /* (x >> n**2) + * Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Let s=sin(x), c=cos(x), + * xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + * + * n sin(xn)*sqt2 cos(xn)*sqt2 + * ---------------------------------- + * 0 s-c c+s + * 1 -s-c -c+s + * 2 -s+c -c-s + * 3 s+c c-s + */ + switch(n&3) { + case 0: temp = cos(x)+sin(x); break; + case 1: temp = -cos(x)+sin(x); break; + case 2: temp = -cos(x)-sin(x); break; + case 3: temp = cos(x)-sin(x); break; + } + b = invsqrtpi*temp/sqrt(x); + } else { + a = __ieee754_j0(x); + b = __ieee754_j1(x); + for(i=1;i33) /* underflow */ + b = zero; + else { + temp = x*0.5; b = temp; + for (a=one,i=2;i<=n;i++) { + a *= (double)i; /* a = n! */ + b *= temp; /* b = (x/2)^n */ + } + b = b/a; + } + } else { + /* use backward recurrence */ + /* x x^2 x^2 + * J(n,x)/J(n-1,x) = ---- ------ ------ ..... + * 2n - 2(n+1) - 2(n+2) + * + * 1 1 1 + * (for large x) = ---- ------ ------ ..... + * 2n 2(n+1) 2(n+2) + * -- - ------ - ------ - + * x x x + * + * Let w = 2n/x and h=2/x, then the above quotient + * is equal to the continued fraction: + * 1 + * = ----------------------- + * 1 + * w - ----------------- + * 1 + * w+h - --------- + * w+2h - ... + * + * To determine how many terms needed, let + * Q(0) = w, Q(1) = w(w+h) - 1, + * Q(k) = (w+k*h)*Q(k-1) - Q(k-2), + * When Q(k) > 1e4 good for single + * When Q(k) > 1e9 good for double + * When Q(k) > 1e17 good for quadruple + */ + /* determine k */ + double t,v; + double q0,q1,h,tmp; int32_t k,m; + w = (n+n)/(double)x; h = 2.0/(double)x; + q0 = w; z = w+h; q1 = w*z - 1.0; k=1; + while(q1<1.0e9) { + k += 1; z += h; + tmp = z*q1 - q0; + q0 = q1; + q1 = tmp; + } + m = n+n; + for(t=zero, i = 2*(n+k); i>=m; i -= 2) t = one/(i/x-t); + a = t; + b = one; + /* estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n) + * Hence, if n*(log(2n/x)) > ... + * single 8.8722839355e+01 + * double 7.09782712893383973096e+02 + * long double 1.1356523406294143949491931077970765006170e+04 + * then recurrent value may overflow and the result is + * likely underflow to zero + */ + tmp = n; + v = two/x; + tmp = tmp*__ieee754_log(fabs(v*tmp)); + if(tmp<7.09782712893383973096e+02) { + for(i=n-1,di=(double)(i+i);i>0;i--){ + temp = b; + b *= di; + b = b/x - a; + a = temp; + di -= two; + } + } else { + for(i=n-1,di=(double)(i+i);i>0;i--){ + temp = b; + b *= di; + b = b/x - a; + a = temp; + di -= two; + /* scale b to avoid spurious overflow */ + if(b>1e100) { + a /= b; + t /= b; + b = one; + } + } + } + z = __ieee754_j0(x); + w = __ieee754_j1(x); + if (fabs(z) >= fabs(w)) + b = (t*z/b); + else + b = (t*w/a); + } + } + if(sgn==1) return -b; else return b; +} + +OLM_DLLEXPORT double +__ieee754_yn(int n, double x) +{ + int32_t i,hx,ix,lx; + int32_t sign; + double a, b, temp; + + EXTRACT_WORDS(hx,lx,x); + ix = 0x7fffffff&hx; + /* if Y(n,NaN) is NaN */ + if((ix|((u_int32_t)(lx|-lx))>>31)>0x7ff00000) return x+x; + if((ix|lx)==0) return -one/zero; + if(hx<0) return zero/zero; + sign = 1; + if(n<0){ + n = -n; + sign = 1 - ((n&1)<<1); + } + if(n==0) return(__ieee754_y0(x)); + if(n==1) return(sign*__ieee754_y1(x)); + if(ix==0x7ff00000) return zero; + if(ix>=0x52D00000) { /* x > 2**302 */ + /* (x >> n**2) + * Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + * Let s=sin(x), c=cos(x), + * xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + * + * n sin(xn)*sqt2 cos(xn)*sqt2 + * ---------------------------------- + * 0 s-c c+s + * 1 -s-c -c+s + * 2 -s+c -c-s + * 3 s+c c-s + */ + switch(n&3) { + case 0: temp = sin(x)-cos(x); break; + case 1: temp = -sin(x)-cos(x); break; + case 2: temp = -sin(x)+cos(x); break; + case 3: temp = sin(x)+cos(x); break; + } + b = invsqrtpi*temp/sqrt(x); + } else { + u_int32_t high; + a = __ieee754_y0(x); + b = __ieee754_y1(x); + /* quit if b is -inf */ + GET_HIGH_WORD(high,b); + for(i=1;i0) return b; else return -b; +} diff --git a/openlibm/src/e_jnf.c b/openlibm/src/e_jnf.c new file mode 100644 index 0000000000..cb73e8640c --- /dev/null +++ b/openlibm/src/e_jnf.c @@ -0,0 +1,200 @@ +/* e_jnf.c -- float version of e_jn.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_jnf.c,v 1.11 2010/11/13 10:54:10 uqs Exp $"); + +#include + +#include "math_private.h" + +static const float +two = 2.0000000000e+00, /* 0x40000000 */ +one = 1.0000000000e+00; /* 0x3F800000 */ + +static const float zero = 0.0000000000e+00; + +OLM_DLLEXPORT float +__ieee754_jnf(int n, float x) +{ + int32_t i,hx,ix, sgn; + float a, b, temp, di; + float z, w; + + /* J(-n,x) = (-1)^n * J(n, x), J(n, -x) = (-1)^n * J(n, x) + * Thus, J(-n,x) = J(n,-x) + */ + GET_FLOAT_WORD(hx,x); + ix = 0x7fffffff&hx; + /* if J(n,NaN) is NaN */ + if(ix>0x7f800000) return x+x; + if(n<0){ + n = -n; + x = -x; + hx ^= 0x80000000; + } + if(n==0) return(__ieee754_j0f(x)); + if(n==1) return(__ieee754_j1f(x)); + sgn = (n&1)&(hx>>31); /* even n -- 0, odd n -- sign(x) */ + x = fabsf(x); + if(ix==0||ix>=0x7f800000) /* if x is 0 or inf */ + b = zero; + else if((float)n<=x) { + /* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */ + a = __ieee754_j0f(x); + b = __ieee754_j1f(x); + for(i=1;i33) /* underflow */ + b = zero; + else { + temp = x*(float)0.5; b = temp; + for (a=one,i=2;i<=n;i++) { + a *= (float)i; /* a = n! */ + b *= temp; /* b = (x/2)^n */ + } + b = b/a; + } + } else { + /* use backward recurrence */ + /* x x^2 x^2 + * J(n,x)/J(n-1,x) = ---- ------ ------ ..... + * 2n - 2(n+1) - 2(n+2) + * + * 1 1 1 + * (for large x) = ---- ------ ------ ..... + * 2n 2(n+1) 2(n+2) + * -- - ------ - ------ - + * x x x + * + * Let w = 2n/x and h=2/x, then the above quotient + * is equal to the continued fraction: + * 1 + * = ----------------------- + * 1 + * w - ----------------- + * 1 + * w+h - --------- + * w+2h - ... + * + * To determine how many terms needed, let + * Q(0) = w, Q(1) = w(w+h) - 1, + * Q(k) = (w+k*h)*Q(k-1) - Q(k-2), + * When Q(k) > 1e4 good for single + * When Q(k) > 1e9 good for double + * When Q(k) > 1e17 good for quadruple + */ + /* determine k */ + float t,v; + float q0,q1,h,tmp; int32_t k,m; + w = (n+n)/(float)x; h = (float)2.0/(float)x; + q0 = w; z = w+h; q1 = w*z - (float)1.0; k=1; + while(q1<(float)1.0e9) { + k += 1; z += h; + tmp = z*q1 - q0; + q0 = q1; + q1 = tmp; + } + m = n+n; + for(t=zero, i = 2*(n+k); i>=m; i -= 2) t = one/(i/x-t); + a = t; + b = one; + /* estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n) + * Hence, if n*(log(2n/x)) > ... + * single 8.8722839355e+01 + * double 7.09782712893383973096e+02 + * long double 1.1356523406294143949491931077970765006170e+04 + * then recurrent value may overflow and the result is + * likely underflow to zero + */ + tmp = n; + v = two/x; + tmp = tmp*__ieee754_logf(fabsf(v*tmp)); + if(tmp<(float)8.8721679688e+01) { + for(i=n-1,di=(float)(i+i);i>0;i--){ + temp = b; + b *= di; + b = b/x - a; + a = temp; + di -= two; + } + } else { + for(i=n-1,di=(float)(i+i);i>0;i--){ + temp = b; + b *= di; + b = b/x - a; + a = temp; + di -= two; + /* scale b to avoid spurious overflow */ + if(b>(float)1e10) { + a /= b; + t /= b; + b = one; + } + } + } + z = __ieee754_j0f(x); + w = __ieee754_j1f(x); + if (fabsf(z) >= fabsf(w)) + b = (t*z/b); + else + b = (t*w/a); + } + } + if(sgn==1) return -b; else return b; +} + +OLM_DLLEXPORT float +__ieee754_ynf(int n, float x) +{ + int32_t i,hx,ix,ib; + int32_t sign; + float a, b, temp; + + GET_FLOAT_WORD(hx,x); + ix = 0x7fffffff&hx; + /* if Y(n,NaN) is NaN */ + if(ix>0x7f800000) return x+x; + if(ix==0) return -one/zero; + if(hx<0) return zero/zero; + sign = 1; + if(n<0){ + n = -n; + sign = 1 - ((n&1)<<1); + } + if(n==0) return(__ieee754_y0f(x)); + if(n==1) return(sign*__ieee754_y1f(x)); + if(ix==0x7f800000) return zero; + + a = __ieee754_y0f(x); + b = __ieee754_y1f(x); + /* quit if b is -inf */ + GET_FLOAT_WORD(ib,b); + for(i=1;i0) return b; else return -b; +} diff --git a/openlibm/src/e_lgamma.c b/openlibm/src/e_lgamma.c new file mode 100644 index 0000000000..d3167964be --- /dev/null +++ b/openlibm/src/e_lgamma.c @@ -0,0 +1,36 @@ + +/* @(#)e_lgamma.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_lgamma.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +/* __ieee754_lgamma(x) + * Return the logarithm of the Gamma function of x. + * + * Method: call __ieee754_lgamma_r + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +__ieee754_lgamma(double x) +{ +#ifdef OPENLIBM_ONLY_THREAD_SAFE + int signgam; +#endif + + return __ieee754_lgamma_r(x,&signgam); +} diff --git a/openlibm/src/e_lgamma_r.c b/openlibm/src/e_lgamma_r.c new file mode 100644 index 0000000000..df3ae23703 --- /dev/null +++ b/openlibm/src/e_lgamma_r.c @@ -0,0 +1,298 @@ + +/* @(#)e_lgamma_r.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_lgamma_r.c,v 1.11 2011/10/15 07:00:28 das Exp $"); + +/* __ieee754_lgamma_r(x, signgamp) + * Reentrant version of the logarithm of the Gamma function + * with user provide pointer for the sign of Gamma(x). + * + * Method: + * 1. Argument Reduction for 0 < x <= 8 + * Since gamma(1+s)=s*gamma(s), for x in [0,8], we may + * reduce x to a number in [1.5,2.5] by + * lgamma(1+s) = log(s) + lgamma(s) + * for example, + * lgamma(7.3) = log(6.3) + lgamma(6.3) + * = log(6.3*5.3) + lgamma(5.3) + * = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3) + * 2. Polynomial approximation of lgamma around its + * minimun ymin=1.461632144968362245 to maintain monotonicity. + * On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use + * Let z = x-ymin; + * lgamma(x) = -1.214862905358496078218 + z^2*poly(z) + * where + * poly(z) is a 14 degree polynomial. + * 2. Rational approximation in the primary interval [2,3] + * We use the following approximation: + * s = x-2.0; + * lgamma(x) = 0.5*s + s*P(s)/Q(s) + * with accuracy + * |P/Q - (lgamma(x)-0.5s)| < 2**-61.71 + * Our algorithms are based on the following observation + * + * zeta(2)-1 2 zeta(3)-1 3 + * lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ... + * 2 3 + * + * where Euler = 0.5771... is the Euler constant, which is very + * close to 0.5. + * + * 3. For x>=8, we have + * lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+.... + * (better formula: + * lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...) + * Let z = 1/x, then we approximation + * f(z) = lgamma(x) - (x-0.5)(log(x)-1) + * by + * 3 5 11 + * w = w0 + w1*z + w2*z + w3*z + ... + w6*z + * where + * |w - f(z)| < 2**-58.74 + * + * 4. For negative x, since (G is gamma function) + * -x*G(-x)*G(x) = pi/sin(pi*x), + * we have + * G(x) = pi/(sin(pi*x)*(-x)*G(-x)) + * since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0 + * Hence, for x<0, signgam = sign(sin(pi*x)) and + * lgamma(x) = log(|Gamma(x)|) + * = log(pi/(|x*sin(pi*x)|)) - lgamma(-x); + * Note: one should avoid compute pi*(-x) directly in the + * computation of sin(pi*(-x)). + * + * 5. Special Cases + * lgamma(2+s) ~ s*(1-Euler) for tiny s + * lgamma(1) = lgamma(2) = 0 + * lgamma(x) ~ -log(|x|) for tiny x + * lgamma(0) = lgamma(neg.integer) = inf and raise divide-by-zero + * lgamma(inf) = inf + * lgamma(-inf) = inf (bug for bug compatible with C99!?) + * + */ + +#include + +#include "math_private.h" + +static const double +two52= 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ +half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */ +a0 = 7.72156649015328655494e-02, /* 0x3FB3C467, 0xE37DB0C8 */ +a1 = 3.22467033424113591611e-01, /* 0x3FD4A34C, 0xC4A60FAD */ +a2 = 6.73523010531292681824e-02, /* 0x3FB13E00, 0x1A5562A7 */ +a3 = 2.05808084325167332806e-02, /* 0x3F951322, 0xAC92547B */ +a4 = 7.38555086081402883957e-03, /* 0x3F7E404F, 0xB68FEFE8 */ +a5 = 2.89051383673415629091e-03, /* 0x3F67ADD8, 0xCCB7926B */ +a6 = 1.19270763183362067845e-03, /* 0x3F538A94, 0x116F3F5D */ +a7 = 5.10069792153511336608e-04, /* 0x3F40B6C6, 0x89B99C00 */ +a8 = 2.20862790713908385557e-04, /* 0x3F2CF2EC, 0xED10E54D */ +a9 = 1.08011567247583939954e-04, /* 0x3F1C5088, 0x987DFB07 */ +a10 = 2.52144565451257326939e-05, /* 0x3EFA7074, 0x428CFA52 */ +a11 = 4.48640949618915160150e-05, /* 0x3F07858E, 0x90A45837 */ +tc = 1.46163214496836224576e+00, /* 0x3FF762D8, 0x6356BE3F */ +tf = -1.21486290535849611461e-01, /* 0xBFBF19B9, 0xBCC38A42 */ +/* tt = -(tail of tf) */ +tt = -3.63867699703950536541e-18, /* 0xBC50C7CA, 0xA48A971F */ +t0 = 4.83836122723810047042e-01, /* 0x3FDEF72B, 0xC8EE38A2 */ +t1 = -1.47587722994593911752e-01, /* 0xBFC2E427, 0x8DC6C509 */ +t2 = 6.46249402391333854778e-02, /* 0x3FB08B42, 0x94D5419B */ +t3 = -3.27885410759859649565e-02, /* 0xBFA0C9A8, 0xDF35B713 */ +t4 = 1.79706750811820387126e-02, /* 0x3F9266E7, 0x970AF9EC */ +t5 = -1.03142241298341437450e-02, /* 0xBF851F9F, 0xBA91EC6A */ +t6 = 6.10053870246291332635e-03, /* 0x3F78FCE0, 0xE370E344 */ +t7 = -3.68452016781138256760e-03, /* 0xBF6E2EFF, 0xB3E914D7 */ +t8 = 2.25964780900612472250e-03, /* 0x3F6282D3, 0x2E15C915 */ +t9 = -1.40346469989232843813e-03, /* 0xBF56FE8E, 0xBF2D1AF1 */ +t10 = 8.81081882437654011382e-04, /* 0x3F4CDF0C, 0xEF61A8E9 */ +t11 = -5.38595305356740546715e-04, /* 0xBF41A610, 0x9C73E0EC */ +t12 = 3.15632070903625950361e-04, /* 0x3F34AF6D, 0x6C0EBBF7 */ +t13 = -3.12754168375120860518e-04, /* 0xBF347F24, 0xECC38C38 */ +t14 = 3.35529192635519073543e-04, /* 0x3F35FD3E, 0xE8C2D3F4 */ +u0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */ +u1 = 6.32827064025093366517e-01, /* 0x3FE4401E, 0x8B005DFF */ +u2 = 1.45492250137234768737e+00, /* 0x3FF7475C, 0xD119BD6F */ +u3 = 9.77717527963372745603e-01, /* 0x3FEF4976, 0x44EA8450 */ +u4 = 2.28963728064692451092e-01, /* 0x3FCD4EAE, 0xF6010924 */ +u5 = 1.33810918536787660377e-02, /* 0x3F8B678B, 0xBF2BAB09 */ +v1 = 2.45597793713041134822e+00, /* 0x4003A5D7, 0xC2BD619C */ +v2 = 2.12848976379893395361e+00, /* 0x40010725, 0xA42B18F5 */ +v3 = 7.69285150456672783825e-01, /* 0x3FE89DFB, 0xE45050AF */ +v4 = 1.04222645593369134254e-01, /* 0x3FBAAE55, 0xD6537C88 */ +v5 = 3.21709242282423911810e-03, /* 0x3F6A5ABB, 0x57D0CF61 */ +s0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */ +s1 = 2.14982415960608852501e-01, /* 0x3FCB848B, 0x36E20878 */ +s2 = 3.25778796408930981787e-01, /* 0x3FD4D98F, 0x4F139F59 */ +s3 = 1.46350472652464452805e-01, /* 0x3FC2BB9C, 0xBEE5F2F7 */ +s4 = 2.66422703033638609560e-02, /* 0x3F9B481C, 0x7E939961 */ +s5 = 1.84028451407337715652e-03, /* 0x3F5E26B6, 0x7368F239 */ +s6 = 3.19475326584100867617e-05, /* 0x3F00BFEC, 0xDD17E945 */ +r1 = 1.39200533467621045958e+00, /* 0x3FF645A7, 0x62C4AB74 */ +r2 = 7.21935547567138069525e-01, /* 0x3FE71A18, 0x93D3DCDC */ +r3 = 1.71933865632803078993e-01, /* 0x3FC601ED, 0xCCFBDF27 */ +r4 = 1.86459191715652901344e-02, /* 0x3F9317EA, 0x742ED475 */ +r5 = 7.77942496381893596434e-04, /* 0x3F497DDA, 0xCA41A95B */ +r6 = 7.32668430744625636189e-06, /* 0x3EDEBAF7, 0xA5B38140 */ +w0 = 4.18938533204672725052e-01, /* 0x3FDACFE3, 0x90C97D69 */ +w1 = 8.33333333333329678849e-02, /* 0x3FB55555, 0x5555553B */ +w2 = -2.77777777728775536470e-03, /* 0xBF66C16C, 0x16B02E5C */ +w3 = 7.93650558643019558500e-04, /* 0x3F4A019F, 0x98CF38B6 */ +w4 = -5.95187557450339963135e-04, /* 0xBF4380CB, 0x8C0FE741 */ +w5 = 8.36339918996282139126e-04, /* 0x3F4B67BA, 0x4CDAD5D1 */ +w6 = -1.63092934096575273989e-03; /* 0xBF5AB89D, 0x0B9E43E4 */ + +static const double zero= 0.00000000000000000000e+00; + + static double sin_pi(double x) +{ + double y,z; + int n,ix; + + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + + if(ix<0x3fd00000) return __kernel_sin(pi*x,zero,0); + y = -x; /* x is assume negative */ + + /* + * argument reduction, make sure inexact flag not raised if input + * is an integer + */ + z = floor(y); + if(z!=y) { /* inexact anyway */ + y *= 0.5; + y = 2.0*(y - floor(y)); /* y = |x| mod 2.0 */ + n = (int) (y*4.0); + } else { + if(ix>=0x43400000) { + y = zero; n = 0; /* y must be even */ + } else { + if(ix<0x43300000) z = y+two52; /* exact */ + GET_LOW_WORD(n,z); + n &= 1; + y = n; + n<<= 2; + } + } + switch (n) { + case 0: y = __kernel_sin(pi*y,zero,0); break; + case 1: + case 2: y = __kernel_cos(pi*(0.5-y),zero); break; + case 3: + case 4: y = __kernel_sin(pi*(one-y),zero,0); break; + case 5: + case 6: y = -__kernel_cos(pi*(y-1.5),zero); break; + default: y = __kernel_sin(pi*(y-2.0),zero,0); break; + } + return -y; +} + + +OLM_DLLEXPORT double +__ieee754_lgamma_r(double x, int *signgamp) +{ + double t,y,z,nadj,p,p1,p2,p3,q,r,w; + int32_t hx; + int i,lx,ix; + + EXTRACT_WORDS(hx,lx,x); + + /* purge off +-inf, NaN, +-0, tiny and negative arguments */ + *signgamp = 1; + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return x*x; + if((ix|lx)==0) return one/zero; + if(ix<0x3b900000) { /* |x|<2**-70, return -log(|x|) */ + if(hx<0) { + *signgamp = -1; + return -__ieee754_log(-x); + } else return -__ieee754_log(x); + } + if(hx<0) { + if(ix>=0x43300000) /* |x|>=2**52, must be -integer */ + return one/zero; + t = sin_pi(x); + if(t==zero) return one/zero; /* -integer */ + nadj = __ieee754_log(pi/fabs(t*x)); + if(t=0x3FE76944) {y = one-x; i= 0;} + else if(ix>=0x3FCDA661) {y= x-(tc-one); i=1;} + else {y = x; i=2;} + } else { + r = zero; + if(ix>=0x3FFBB4C3) {y=2.0-x;i=0;} /* [1.7316,2] */ + else if(ix>=0x3FF3B4C4) {y=x-tc;i=1;} /* [1.23,1.73] */ + else {y=x-one;i=2;} + } + switch(i) { + case 0: + z = y*y; + p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10)))); + p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11))))); + p = y*p1+p2; + r += (p-0.5*y); break; + case 1: + z = y*y; + w = z*y; + p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */ + p2 = t1+w*(t4+w*(t7+w*(t10+w*t13))); + p3 = t2+w*(t5+w*(t8+w*(t11+w*t14))); + p = z*p1-(tt-w*(p2+y*p3)); + r += (tf + p); break; + case 2: + p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5))))); + p2 = one+y*(v1+y*(v2+y*(v3+y*(v4+y*v5)))); + r += (-0.5*y + p1/p2); + } + } + else if(ix<0x40200000) { /* x < 8.0 */ + i = (int)x; + y = x-(double)i; + p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6)))))); + q = one+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6))))); + r = half*y+p/q; + z = one; /* lgamma(1+s) = log(s) + lgamma(s) */ + switch(i) { + case 7: z *= (y+6.0); /* FALLTHRU */ + case 6: z *= (y+5.0); /* FALLTHRU */ + case 5: z *= (y+4.0); /* FALLTHRU */ + case 4: z *= (y+3.0); /* FALLTHRU */ + case 3: z *= (y+2.0); /* FALLTHRU */ + r += __ieee754_log(z); break; + } + /* 8.0 <= x < 2**58 */ + } else if (ix < 0x43900000) { + t = __ieee754_log(x); + z = one/x; + y = z*z; + w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6))))); + r = (x-half)*(t-one)+w; + } else + /* 2**58 <= x <= inf */ + r = x*(__ieee754_log(x)-one); + if(hx<0) r = nadj - r; + return r; +} diff --git a/openlibm/src/e_lgammaf.c b/openlibm/src/e_lgammaf.c new file mode 100644 index 0000000000..5b95f02a93 --- /dev/null +++ b/openlibm/src/e_lgammaf.c @@ -0,0 +1,37 @@ +/* e_lgammaf.c -- float version of e_lgamma.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_lgammaf.c,v 1.8 2008/02/22 02:30:35 das Exp $"); + +/* __ieee754_lgammaf(x) + * Return the logarithm of the Gamma function of x. + * + * Method: call __ieee754_lgammaf_r + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +__ieee754_lgammaf(float x) +{ +#ifdef OPENLIBM_ONLY_THREAD_SAFE + int signgam; +#endif + + return __ieee754_lgammaf_r(x,&signgam); +} diff --git a/openlibm/src/e_lgammaf_r.c b/openlibm/src/e_lgammaf_r.c new file mode 100644 index 0000000000..7446dfc172 --- /dev/null +++ b/openlibm/src/e_lgammaf_r.c @@ -0,0 +1,231 @@ +/* e_lgammaf_r.c -- float version of e_lgamma_r.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_lgammaf_r.c,v 1.12 2011/10/15 07:00:28 das Exp $"); + +#include + +#include "math_private.h" + +static const float +two23= 8.3886080000e+06, /* 0x4b000000 */ +half= 5.0000000000e-01, /* 0x3f000000 */ +one = 1.0000000000e+00, /* 0x3f800000 */ +pi = 3.1415927410e+00, /* 0x40490fdb */ +a0 = 7.7215664089e-02, /* 0x3d9e233f */ +a1 = 3.2246702909e-01, /* 0x3ea51a66 */ +a2 = 6.7352302372e-02, /* 0x3d89f001 */ +a3 = 2.0580807701e-02, /* 0x3ca89915 */ +a4 = 7.3855509982e-03, /* 0x3bf2027e */ +a5 = 2.8905137442e-03, /* 0x3b3d6ec6 */ +a6 = 1.1927076848e-03, /* 0x3a9c54a1 */ +a7 = 5.1006977446e-04, /* 0x3a05b634 */ +a8 = 2.2086278477e-04, /* 0x39679767 */ +a9 = 1.0801156895e-04, /* 0x38e28445 */ +a10 = 2.5214456400e-05, /* 0x37d383a2 */ +a11 = 4.4864096708e-05, /* 0x383c2c75 */ +tc = 1.4616321325e+00, /* 0x3fbb16c3 */ +tf = -1.2148628384e-01, /* 0xbdf8cdcd */ +/* tt = -(tail of tf) */ +tt = 6.6971006518e-09, /* 0x31e61c52 */ +t0 = 4.8383611441e-01, /* 0x3ef7b95e */ +t1 = -1.4758771658e-01, /* 0xbe17213c */ +t2 = 6.4624942839e-02, /* 0x3d845a15 */ +t3 = -3.2788541168e-02, /* 0xbd064d47 */ +t4 = 1.7970675603e-02, /* 0x3c93373d */ +t5 = -1.0314224288e-02, /* 0xbc28fcfe */ +t6 = 6.1005386524e-03, /* 0x3bc7e707 */ +t7 = -3.6845202558e-03, /* 0xbb7177fe */ +t8 = 2.2596477065e-03, /* 0x3b141699 */ +t9 = -1.4034647029e-03, /* 0xbab7f476 */ +t10 = 8.8108185446e-04, /* 0x3a66f867 */ +t11 = -5.3859531181e-04, /* 0xba0d3085 */ +t12 = 3.1563205994e-04, /* 0x39a57b6b */ +t13 = -3.1275415677e-04, /* 0xb9a3f927 */ +t14 = 3.3552918467e-04, /* 0x39afe9f7 */ +u0 = -7.7215664089e-02, /* 0xbd9e233f */ +u1 = 6.3282704353e-01, /* 0x3f2200f4 */ +u2 = 1.4549225569e+00, /* 0x3fba3ae7 */ +u3 = 9.7771751881e-01, /* 0x3f7a4bb2 */ +u4 = 2.2896373272e-01, /* 0x3e6a7578 */ +u5 = 1.3381091878e-02, /* 0x3c5b3c5e */ +v1 = 2.4559779167e+00, /* 0x401d2ebe */ +v2 = 2.1284897327e+00, /* 0x4008392d */ +v3 = 7.6928514242e-01, /* 0x3f44efdf */ +v4 = 1.0422264785e-01, /* 0x3dd572af */ +v5 = 3.2170924824e-03, /* 0x3b52d5db */ +s0 = -7.7215664089e-02, /* 0xbd9e233f */ +s1 = 2.1498242021e-01, /* 0x3e5c245a */ +s2 = 3.2577878237e-01, /* 0x3ea6cc7a */ +s3 = 1.4635047317e-01, /* 0x3e15dce6 */ +s4 = 2.6642270386e-02, /* 0x3cda40e4 */ +s5 = 1.8402845599e-03, /* 0x3af135b4 */ +s6 = 3.1947532989e-05, /* 0x3805ff67 */ +r1 = 1.3920053244e+00, /* 0x3fb22d3b */ +r2 = 7.2193557024e-01, /* 0x3f38d0c5 */ +r3 = 1.7193385959e-01, /* 0x3e300f6e */ +r4 = 1.8645919859e-02, /* 0x3c98bf54 */ +r5 = 7.7794247773e-04, /* 0x3a4beed6 */ +r6 = 7.3266842264e-06, /* 0x36f5d7bd */ +w0 = 4.1893854737e-01, /* 0x3ed67f1d */ +w1 = 8.3333335817e-02, /* 0x3daaaaab */ +w2 = -2.7777778450e-03, /* 0xbb360b61 */ +w3 = 7.9365057172e-04, /* 0x3a500cfd */ +w4 = -5.9518753551e-04, /* 0xba1c065c */ +w5 = 8.3633989561e-04, /* 0x3a5b3dd2 */ +w6 = -1.6309292987e-03; /* 0xbad5c4e8 */ + +static const float zero= 0.0000000000e+00; + + static float sin_pif(float x) +{ + float y,z; + int n,ix; + + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + + if(ix<0x3e800000) return __kernel_sindf(pi*x); + y = -x; /* x is assume negative */ + + /* + * argument reduction, make sure inexact flag not raised if input + * is an integer + */ + z = floorf(y); + if(z!=y) { /* inexact anyway */ + y *= (float)0.5; + y = (float)2.0*(y - floorf(y)); /* y = |x| mod 2.0 */ + n = (int) (y*(float)4.0); + } else { + if(ix>=0x4b800000) { + y = zero; n = 0; /* y must be even */ + } else { + if(ix<0x4b000000) z = y+two23; /* exact */ + GET_FLOAT_WORD(n,z); + n &= 1; + y = n; + n<<= 2; + } + } + switch (n) { + case 0: y = __kernel_sindf(pi*y); break; + case 1: + case 2: y = __kernel_cosdf(pi*((float)0.5-y)); break; + case 3: + case 4: y = __kernel_sindf(pi*(one-y)); break; + case 5: + case 6: y = -__kernel_cosdf(pi*(y-(float)1.5)); break; + default: y = __kernel_sindf(pi*(y-(float)2.0)); break; + } + return -y; +} + + +OLM_DLLEXPORT float +__ieee754_lgammaf_r(float x, int *signgamp) +{ + float t,y,z,nadj,p,p1,p2,p3,q,r,w; + int32_t hx; + int i,ix; + + GET_FLOAT_WORD(hx,x); + + /* purge off +-inf, NaN, +-0, tiny and negative arguments */ + *signgamp = 1; + ix = hx&0x7fffffff; + if(ix>=0x7f800000) return x*x; + if(ix==0) return one/zero; + if(ix<0x35000000) { /* |x|<2**-21, return -log(|x|) */ + if(hx<0) { + *signgamp = -1; + return -__ieee754_logf(-x); + } else return -__ieee754_logf(x); + } + if(hx<0) { + if(ix>=0x4b000000) /* |x|>=2**23, must be -integer */ + return one/zero; + t = sin_pif(x); + if(t==zero) return one/zero; /* -integer */ + nadj = __ieee754_logf(pi/fabsf(t*x)); + if(t=0x3f3b4a20) {y = one-x; i= 0;} + else if(ix>=0x3e6d3308) {y= x-(tc-one); i=1;} + else {y = x; i=2;} + } else { + r = zero; + if(ix>=0x3fdda618) {y=(float)2.0-x;i=0;} /* [1.7316,2] */ + else if(ix>=0x3F9da620) {y=x-tc;i=1;} /* [1.23,1.73] */ + else {y=x-one;i=2;} + } + switch(i) { + case 0: + z = y*y; + p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10)))); + p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11))))); + p = y*p1+p2; + r += (p-(float)0.5*y); break; + case 1: + z = y*y; + w = z*y; + p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */ + p2 = t1+w*(t4+w*(t7+w*(t10+w*t13))); + p3 = t2+w*(t5+w*(t8+w*(t11+w*t14))); + p = z*p1-(tt-w*(p2+y*p3)); + r += (tf + p); break; + case 2: + p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5))))); + p2 = one+y*(v1+y*(v2+y*(v3+y*(v4+y*v5)))); + r += (-(float)0.5*y + p1/p2); + } + } + else if(ix<0x41000000) { /* x < 8.0 */ + i = (int)x; + y = x-(float)i; + p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6)))))); + q = one+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6))))); + r = half*y+p/q; + z = one; /* lgamma(1+s) = log(s) + lgamma(s) */ + switch(i) { + case 7: z *= (y+(float)6.0); /* FALLTHRU */ + case 6: z *= (y+(float)5.0); /* FALLTHRU */ + case 5: z *= (y+(float)4.0); /* FALLTHRU */ + case 4: z *= (y+(float)3.0); /* FALLTHRU */ + case 3: z *= (y+(float)2.0); /* FALLTHRU */ + r += __ieee754_logf(z); break; + } + /* 8.0 <= x < 2**58 */ + } else if (ix < 0x5c800000) { + t = __ieee754_logf(x); + z = one/x; + y = z*z; + w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6))))); + r = (x-half)*(t-one)+w; + } else + /* 2**58 <= x <= inf */ + r = x*(__ieee754_logf(x)-one); + if(hx<0) r = nadj - r; + return r; +} diff --git a/openlibm/src/e_lgammal.c b/openlibm/src/e_lgammal.c new file mode 100644 index 0000000000..c1bfb25a40 --- /dev/null +++ b/openlibm/src/e_lgammal.c @@ -0,0 +1,15 @@ +#include "cdefs-compat.h" + +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +lgammal(long double x) +{ +#ifdef OPENLIBM_ONLY_THREAD_SAFE + int signgam; +#endif + + return (lgammal_r(x, &signgam)); +} diff --git a/openlibm/src/e_log.c b/openlibm/src/e_log.c new file mode 100644 index 0000000000..29584bb9bf --- /dev/null +++ b/openlibm/src/e_log.c @@ -0,0 +1,146 @@ + +/* @(#)e_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_log.c,v 1.15 2008/03/29 16:37:59 das Exp $"); + +/* __ieee754_log(x) + * Return the logrithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static const double +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ +ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ +two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ +Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static const double zero = 0.0; + +OLM_DLLEXPORT double +__ieee754_log(double x) +{ + double hfsq,f,s,z,R,w,t1,t2,dk; + int32_t k,hx,i,j; + u_int32_t lx; + + EXTRACT_WORDS(hx,lx,x); + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + GET_HIGH_WORD(hx,x); + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + SET_HIGH_WORD(x,hx|(i^0x3ff00000)); /* normalize x or x/2 */ + k += (i>>20); + f = x-1.0; + if((0x000fffff&(2+hx))<3) { /* -2**-20 <= f < 2**-20 */ + if(f==zero) { + if(k==0) { + return zero; + } else { + dk=(double)k; + return dk*ln2_hi+dk*ln2_lo; + } + } + R = f*f*(0.5-0.33333333333333333*f); + if(k==0) return f-R; else {dk=(double)k; + return dk*ln2_hi-((R-dk*ln2_lo)-f);} + } + s = f/(2.0+f); + dk = (double)k; + z = s*s; + i = hx-0x6147a; + w = z*z; + j = 0x6b851-hx; + t1= w*(Lg2+w*(Lg4+w*Lg6)); + t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); + i |= j; + R = t2+t1; + if(i>0) { + hfsq=0.5*f*f; + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f); + } else { + if(k==0) return f-s*(f-R); else + return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f); + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(log, logl); +#endif diff --git a/openlibm/src/e_log10.c b/openlibm/src/e_log10.c new file mode 100644 index 0000000000..8e3c4e0864 --- /dev/null +++ b/openlibm/src/e_log10.c @@ -0,0 +1,93 @@ + +/* @(#)e_log10.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_log10.c,v 1.15 2011/10/15 05:23:28 das Exp $"); + +/* + * Return the base 10 logarithm of x. See e_log.c and k_log.h for most + * comments. + * + * log10(x) = (f - 0.5*f*f + k_log1p(f)) / ln10 + k * log10(2) + * in not-quite-routine extra precision. + */ + +#include +#include + +#include "math_private.h" +#include "k_log.h" + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +ivln10hi = 4.34294481878168880939e-01, /* 0x3fdbcb7b, 0x15200000 */ +ivln10lo = 2.50829467116452752298e-11, /* 0x3dbb9438, 0xca9aadd5 */ +log10_2hi = 3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */ +log10_2lo = 3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */ + +static const double zero = 0.0; + +OLM_DLLEXPORT double +__ieee754_log10(double x) +{ + double f,hfsq,hi,lo,r,val_hi,val_lo,w,y,y2; + int32_t i,k,hx; + u_int32_t lx; + + EXTRACT_WORDS(hx,lx,x); + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + GET_HIGH_WORD(hx,x); + } + if (hx >= 0x7ff00000) return x+x; + if (hx == 0x3ff00000 && lx == 0) + return zero; /* log(1) = +0 */ + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + SET_HIGH_WORD(x,hx|(i^0x3ff00000)); /* normalize x or x/2 */ + k += (i>>20); + y = (double)k; + f = x - 1.0; + hfsq = 0.5*f*f; + r = k_log1p(f); + + /* See e_log2.c for most details. */ + hi = f - hfsq; + SET_LOW_WORD(hi,0); + lo = (f - hi) - hfsq + r; + val_hi = hi*ivln10hi; + y2 = y*log10_2hi; + val_lo = y*log10_2lo + (lo+hi)*ivln10lo + lo*ivln10hi; + + /* + * Extra precision in for adding y*log10_2hi is not strictly needed + * since there is no very large cancellation near x = sqrt(2) or + * x = 1/sqrt(2), but we do it anyway since it costs little on CPUs + * with some parallelism and it reduces the error for many args. + */ + w = y2 + val_hi; + val_lo += (y2 - w) + val_hi; + val_hi = w; + + return val_lo + val_hi; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(log10, log10l); +#endif diff --git a/openlibm/src/e_log10f.c b/openlibm/src/e_log10f.c new file mode 100644 index 0000000000..1f6e31a2f0 --- /dev/null +++ b/openlibm/src/e_log10f.c @@ -0,0 +1,75 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_log10f.c,v 1.13 2011/10/16 05:36:23 das Exp $"); + +/* + * Float version of e_log10.c. See the latter for most comments. + */ + +#include + +#include "math_private.h" +#include "k_logf.h" + +// VBS +#define float_t float + +static const float +two25 = 3.3554432000e+07, /* 0x4c000000 */ +ivln10hi = 4.3432617188e-01, /* 0x3ede6000 */ +ivln10lo = -3.1689971365e-05, /* 0xb804ead9 */ +log10_2hi = 3.0102920532e-01, /* 0x3e9a2080 */ +log10_2lo = 7.9034151668e-07; /* 0x355427db */ + +static const float zero = 0.0; + +OLM_DLLEXPORT float +__ieee754_log10f(float x) +{ + float f,hfsq,hi,lo,r,y; + int32_t i,k,hx; + + GET_FLOAT_WORD(hx,x); + + k=0; + if (hx < 0x00800000) { /* x < 2**-126 */ + if ((hx&0x7fffffff)==0) + return -two25/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 25; x *= two25; /* subnormal number, scale up x */ + GET_FLOAT_WORD(hx,x); + } + if (hx >= 0x7f800000) return x+x; + if (hx == 0x3f800000) + return zero; /* log(1) = +0 */ + k += (hx>>23)-127; + hx &= 0x007fffff; + i = (hx+(0x4afb0d))&0x800000; + SET_FLOAT_WORD(x,hx|(i^0x3f800000)); /* normalize x or x/2 */ + k += (i>>23); + y = (float)k; + f = x - (float)1.0; + hfsq = (float)0.5*f*f; + r = k_log1pf(f); + + /* See e_log2f.c and e_log2.c for details. */ + if (sizeof(float_t) > sizeof(float)) + return (r - hfsq + f) * ((float_t)ivln10lo + ivln10hi) + + y * ((float_t)log10_2lo + log10_2hi); + hi = f - hfsq; + GET_FLOAT_WORD(hx,hi); + SET_FLOAT_WORD(hi,hx&0xfffff000); + lo = (f - hi) - hfsq + r; + return y*log10_2lo + (lo+hi)*ivln10lo + lo*ivln10hi + hi*ivln10hi + + y*log10_2hi; +} diff --git a/openlibm/src/e_log2.c b/openlibm/src/e_log2.c new file mode 100644 index 0000000000..1570233247 --- /dev/null +++ b/openlibm/src/e_log2.c @@ -0,0 +1,116 @@ + +/* @(#)e_log10.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_log2.c,v 1.4 2011/10/15 05:23:28 das Exp $"); + +/* + * Return the base 2 logarithm of x. See e_log.c and k_log.h for most + * comments. + * + * This reduces x to {k, 1+f} exactly as in e_log.c, then calls the kernel, + * then does the combining and scaling steps + * log2(x) = (f - 0.5*f*f + k_log1p(f)) / ln2 + k + * in not-quite-routine extra precision. + */ + +#include +#include + +#include "math_private.h" +#include "k_log.h" + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +ivln2hi = 1.44269504072144627571e+00, /* 0x3ff71547, 0x65200000 */ +ivln2lo = 1.67517131648865118353e-10; /* 0x3de705fc, 0x2eefa200 */ + +static const double zero = 0.0; + +OLM_DLLEXPORT double +__ieee754_log2(double x) +{ + double f,hfsq,hi,lo,r,val_hi,val_lo,w,y; + int32_t i,k,hx; + u_int32_t lx; + + EXTRACT_WORDS(hx,lx,x); + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + GET_HIGH_WORD(hx,x); + } + if (hx >= 0x7ff00000) return x+x; + if (hx == 0x3ff00000 && lx == 0) + return zero; /* log(1) = +0 */ + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + SET_HIGH_WORD(x,hx|(i^0x3ff00000)); /* normalize x or x/2 */ + k += (i>>20); + y = (double)k; + f = x - 1.0; + hfsq = 0.5*f*f; + r = k_log1p(f); + + /* + * f-hfsq must (for args near 1) be evaluated in extra precision + * to avoid a large cancellation when x is near sqrt(2) or 1/sqrt(2). + * This is fairly efficient since f-hfsq only depends on f, so can + * be evaluated in parallel with R. Not combining hfsq with R also + * keeps R small (though not as small as a true `lo' term would be), + * so that extra precision is not needed for terms involving R. + * + * Compiler bugs involving extra precision used to break Dekker's + * theorem for spitting f-hfsq as hi+lo, unless double_t was used + * or the multi-precision calculations were avoided when double_t + * has extra precision. These problems are now automatically + * avoided as a side effect of the optimization of combining the + * Dekker splitting step with the clear-low-bits step. + * + * y must (for args near sqrt(2) and 1/sqrt(2)) be added in extra + * precision to avoid a very large cancellation when x is very near + * these values. Unlike the above cancellations, this problem is + * specific to base 2. It is strange that adding +-1 is so much + * harder than adding +-ln2 or +-log10_2. + * + * This uses Dekker's theorem to normalize y+val_hi, so the + * compiler bugs are back in some configurations, sigh. And I + * don't want to used double_t to avoid them, since that gives a + * pessimization and the support for avoiding the pessimization + * is not yet available. + * + * The multi-precision calculations for the multiplications are + * routine. + */ + hi = f - hfsq; + SET_LOW_WORD(hi,0); + lo = (f - hi) - hfsq + r; + val_hi = hi*ivln2hi; + val_lo = (lo+hi)*ivln2lo + lo*ivln2hi; + + /* spadd(val_hi, val_lo, y), except for not using double_t: */ + w = y + val_hi; + val_lo += (y - w) + val_hi; + val_hi = w; + + return val_lo + val_hi; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(log2, log2l); +#endif diff --git a/openlibm/src/e_log2f.c b/openlibm/src/e_log2f.c new file mode 100644 index 0000000000..58977ac3b7 --- /dev/null +++ b/openlibm/src/e_log2f.c @@ -0,0 +1,85 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_log2f.c,v 1.5 2011/10/15 05:23:28 das Exp $"); + +/* + * Float version of e_log2.c. See the latter for most comments. + */ + +#include + +#include "math_private.h" +#include "k_logf.h" + +// VBS +#define float_t float + +static const float +two25 = 3.3554432000e+07, /* 0x4c000000 */ +ivln2hi = 1.4428710938e+00, /* 0x3fb8b000 */ +ivln2lo = -1.7605285393e-04; /* 0xb9389ad4 */ + +static const float zero = 0.0; + +OLM_DLLEXPORT float +__ieee754_log2f(float x) +{ + float f,hfsq,hi,lo,r,y; + int32_t i,k,hx; + + GET_FLOAT_WORD(hx,x); + + k=0; + if (hx < 0x00800000) { /* x < 2**-126 */ + if ((hx&0x7fffffff)==0) + return -two25/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 25; x *= two25; /* subnormal number, scale up x */ + GET_FLOAT_WORD(hx,x); + } + if (hx >= 0x7f800000) return x+x; + if (hx == 0x3f800000) + return zero; /* log(1) = +0 */ + k += (hx>>23)-127; + hx &= 0x007fffff; + i = (hx+(0x4afb0d))&0x800000; + SET_FLOAT_WORD(x,hx|(i^0x3f800000)); /* normalize x or x/2 */ + k += (i>>23); + y = (float)k; + f = x - (float)1.0; + hfsq = (float)0.5*f*f; + r = k_log1pf(f); + + /* + * We no longer need to avoid falling into the multi-precision + * calculations due to compiler bugs breaking Dekker's theorem. + * Keep avoiding this as an optimization. See e_log2.c for more + * details (some details are here only because the optimization + * is not yet available in double precision). + * + * Another compiler bug turned up. With gcc on i386, + * (ivln2lo + ivln2hi) would be evaluated in float precision + * despite runtime evaluations using double precision. So we + * must cast one of its terms to float_t. This makes the whole + * expression have type float_t, so return is forced to waste + * time clobbering its extra precision. + */ + if (sizeof(float_t) > sizeof(float)) + return (r - hfsq + f) * ((float_t)ivln2lo + ivln2hi) + y; + + hi = f - hfsq; + GET_FLOAT_WORD(hx,hi); + SET_FLOAT_WORD(hi,hx&0xfffff000); + lo = (f - hi) - hfsq + r; + return (lo+hi)*ivln2lo + lo*ivln2hi + hi*ivln2hi + y; +} diff --git a/openlibm/src/e_logf.c b/openlibm/src/e_logf.c new file mode 100644 index 0000000000..dc5e1516ab --- /dev/null +++ b/openlibm/src/e_logf.c @@ -0,0 +1,89 @@ +/* e_logf.c -- float version of e_log.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_logf.c,v 1.11 2008/03/29 16:37:59 das Exp $"); + +#include + +#include "math_private.h" + +static const float +ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ +ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ +two25 = 3.355443200e+07, /* 0x4c000000 */ +/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ +Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */ +Lg2 = 0xccce13.0p-25, /* 0.40000972152 */ +Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */ +Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */ + +static const float zero = 0.0; + +OLM_DLLEXPORT float +__ieee754_logf(float x) +{ + float hfsq,f,s,z,R,w,t1,t2,dk; + int32_t k,ix,i,j; + + GET_FLOAT_WORD(ix,x); + + k=0; + if (ix < 0x00800000) { /* x < 2**-126 */ + if ((ix&0x7fffffff)==0) + return -two25/zero; /* log(+-0)=-inf */ + if (ix<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 25; x *= two25; /* subnormal number, scale up x */ + GET_FLOAT_WORD(ix,x); + } + if (ix >= 0x7f800000) return x+x; + k += (ix>>23)-127; + ix &= 0x007fffff; + i = (ix+(0x95f64<<3))&0x800000; + SET_FLOAT_WORD(x,ix|(i^0x3f800000)); /* normalize x or x/2 */ + k += (i>>23); + f = x-(float)1.0; + if((0x007fffff&(0x8000+ix))<0xc000) { /* -2**-9 <= f < 2**-9 */ + if(f==zero) { + if(k==0) { + return zero; + } else { + dk=(float)k; + return dk*ln2_hi+dk*ln2_lo; + } + } + R = f*f*((float)0.5-(float)0.33333333333333333*f); + if(k==0) return f-R; else {dk=(float)k; + return dk*ln2_hi-((R-dk*ln2_lo)-f);} + } + s = f/((float)2.0+f); + dk = (float)k; + z = s*s; + i = ix-(0x6147a<<3); + w = z*z; + j = (0x6b851<<3)-ix; + t1= w*(Lg2+w*Lg4); + t2= z*(Lg1+w*Lg3); + i |= j; + R = t2+t1; + if(i>0) { + hfsq=(float)0.5*f*f; + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f); + } else { + if(k==0) return f-s*(f-R); else + return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f); + } +} diff --git a/openlibm/src/e_pow.c b/openlibm/src/e_pow.c new file mode 100644 index 0000000000..a891552364 --- /dev/null +++ b/openlibm/src/e_pow.c @@ -0,0 +1,317 @@ +/* @(#)e_pow.c 1.5 04/04/22 SMI */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_pow.c,v 1.14 2011/10/21 06:26:07 das Exp $"); + +/* __ieee754_pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static const double +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ +dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ +zero = 0.0, +one = 1.0, +two = 2.0, +two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ +huge = 1.0e300, +tiny = 1.0e-300, + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ +L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ +L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ +L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ +L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ +L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ +lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ +lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ +ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ +cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ +cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ +cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ +ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ +ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ +ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +OLM_DLLEXPORT double +__ieee754_pow(double x, double y) +{ + double z,ax,z_h,z_l,p_h,p_l; + double y1,t1,t2,r,s,t,u,v,w; + int32_t i,j,k,yisint,n; + int32_t hx,hy,ix,iy; + u_int32_t lx,ly; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if((iy|ly)==0) return one; + + /* x==1: 1**y = 1, even if y is NaN */ + if (hx==0x3ff00000 && lx == 0) return one; + + /* y!=zero: result is NaN if either arg is NaN */ + if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) || + iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return (x+0.0)+(y+0.0); + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */ + else if(iy>=0x3ff00000) { + k = (iy>>20)-0x3ff; /* exponent */ + if(k>20) { + j = ly>>(52-k); + if((j<<(52-k))==ly) yisint = 2-(j&1); + } else if(ly==0) { + j = iy>>(20-k); + if((j<<(20-k))==iy) yisint = 2-(j&1); + } + } + } + + /* special value of y */ + if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */ + if(((ix-0x3ff00000)|lx)==0) + return one; /* (-1)**+-inf is NaN */ + else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3ff00000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x40080000) return x*x*x; /* y is 3 */ + if(hy==0x40100000) { /* y is 4 */ + u = x*x; + return u*u; + } + if(hy==0x3fe00000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return sqrt(x); + } + } + + ax = fabs(x); + /* special value of x */ + if(lx==0) { + if(ix==0x7ff00000||ix==0||ix==0x3ff00000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3ff00000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + /* CYGNUS LOCAL + fdlibm-5.3 fix: This used to be + n = (hx>>31)+1; + but ANSI C says a right shift of a signed negative quantity is + implementation defined. */ + n = ((u_int32_t)hx>>31)-1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x41e00000) { /* if |y| > 2**31 */ + if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */ + if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny; + if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny; + } + /* over/underflow if x is not close to one */ + if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny; + if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax-one; /* t has 20 trailing zeros */ + w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25)); + u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + SET_LOW_WORD(t1,0); + t2 = v-(t1-u); + } else { + double ss,s2,s_h,s_l,t_h,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00100000) + {ax *= two53; n -= 53; GET_HIGH_WORD(ix,ax); } + n += ((ix)>>20)-0x3ff; + j = ix&0x000fffff; + /* determine interval */ + ix = j|0x3ff00000; /* normalize ix */ + if(j<=0x3988E) k=0; /* |x|>1)|0x20000000)+0x00080000+(k<<18)); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = ss*ss; + r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); + r += s_l*(s_h+ss); + s2 = s_h*s_h; + t_h = 3.0+s2+r; + SET_LOW_WORD(t_h,0); + t_l = r-((t_h-3.0)-s2); + /* u+v = ss*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*ss; + /* 2/(3log2)*(ss+...) */ + p_h = u+v; + SET_LOW_WORD(p_h,0); + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h+z_l)+dp_h[k])+t); + SET_LOW_WORD(t1,0); + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + SET_LOW_WORD(y1,0); + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + EXTRACT_WORDS(j,i,z); + if (j>=0x40900000) { /* z >= 1024 */ + if(((j-0x40900000)|i)!=0) /* if z > 1024 */ + return s*huge*huge; /* overflow */ + else { + if(p_l+ovt>z-p_h) return s*huge*huge; /* overflow */ + } + } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */ + if(((j-0xc090cc00)|i)!=0) /* z < -1075 */ + return s*tiny*tiny; /* underflow */ + else { + if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>20)-0x3ff; + n = 0; + if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00100000>>(k+1)); + k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ + t = zero; + SET_HIGH_WORD(t,n&~(0x000fffff>>k)); + n = ((n&0x000fffff)|0x00100000)>>(20-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + SET_LOW_WORD(t,0); + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + GET_HIGH_WORD(j,z); + j += (n<<20); + if((j>>20)<=0) z = scalbn(z,n); /* subnormal output */ + else SET_HIGH_WORD(z,j); + return s*z; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(pow, powl); +#endif diff --git a/openlibm/src/e_powf.c b/openlibm/src/e_powf.c new file mode 100644 index 0000000000..9527ef0b57 --- /dev/null +++ b/openlibm/src/e_powf.c @@ -0,0 +1,253 @@ +/* e_powf.c -- float version of e_pow.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_powf.c,v 1.16 2011/10/21 06:26:07 das Exp $"); + +#include + +#include "math_private.h" + +static const float +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84960938e-01,}, /* 0x3f15c000 */ +dp_l[] = { 0.0, 1.56322085e-06,}, /* 0x35d1cfdc */ +zero = 0.0, +half = 0.5, +qrtr = 0.25, +thrd = 3.33333343e-01, /* 0x3eaaaaab */ +one = 1.0, +two = 2.0, +two24 = 16777216.0, /* 0x4b800000 */ +huge = 1.0e30, +tiny = 1.0e-30, + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 6.0000002384e-01, /* 0x3f19999a */ +L2 = 4.2857143283e-01, /* 0x3edb6db7 */ +L3 = 3.3333334327e-01, /* 0x3eaaaaab */ +L4 = 2.7272811532e-01, /* 0x3e8ba305 */ +L5 = 2.3066075146e-01, /* 0x3e6c3255 */ +L6 = 2.0697501302e-01, /* 0x3e53f142 */ +P1 = 1.6666667163e-01, /* 0x3e2aaaab */ +P2 = -2.7777778450e-03, /* 0xbb360b61 */ +P3 = 6.6137559770e-05, /* 0x388ab355 */ +P4 = -1.6533901999e-06, /* 0xb5ddea0e */ +P5 = 4.1381369442e-08, /* 0x3331bb4c */ +lg2 = 6.9314718246e-01, /* 0x3f317218 */ +lg2_h = 6.93145752e-01, /* 0x3f317200 */ +lg2_l = 1.42860654e-06, /* 0x35bfbe8c */ +ovt = 4.2995665694e-08, /* -(128-log2(ovfl+.5ulp)) */ +cp = 9.6179670095e-01, /* 0x3f76384f =2/(3ln2) */ +cp_h = 9.6191406250e-01, /* 0x3f764000 =12b cp */ +cp_l = -1.1736857402e-04, /* 0xb8f623c6 =tail of cp_h */ +ivln2 = 1.4426950216e+00, /* 0x3fb8aa3b =1/ln2 */ +ivln2_h = 1.4426879883e+00, /* 0x3fb8aa00 =16b 1/ln2*/ +ivln2_l = 7.0526075433e-06; /* 0x36eca570 =1/ln2 tail*/ + +OLM_DLLEXPORT float +__ieee754_powf(float x, float y) +{ + float z,ax,z_h,z_l,p_h,p_l; + float y1,t1,t2,r,s,sn,t,u,v,w; + int32_t i,j,k,yisint,n; + int32_t hx,hy,ix,iy,is; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if(iy==0) return one; + + /* x==1: 1**y = 1, even if y is NaN */ + if (hx==0x3f800000) return one; + + /* y!=zero: result is NaN if either arg is NaN */ + if(ix > 0x7f800000 || + iy > 0x7f800000) + return nan_mix(x, y); + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x4b800000) yisint = 2; /* even integer y */ + else if(iy>=0x3f800000) { + k = (iy>>23)-0x7f; /* exponent */ + j = iy>>(23-k); + if((j<<(23-k))==iy) yisint = 2-(j&1); + } + } + + /* special value of y */ + if (iy==0x7f800000) { /* y is +-inf */ + if (ix==0x3f800000) + return one; /* (-1)**+-inf is NaN */ + else if (ix > 0x3f800000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3f800000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3f000000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return __ieee754_sqrtf(x); + } + + ax = fabsf(x); + /* special value of x */ + if(ix==0x7f800000||ix==0||ix==0x3f800000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3f800000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + + n = ((u_int32_t)hx>>31)-1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + sn = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) sn = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x4d000000) { /* if |y| > 2**27 */ + /* over/underflow if x is not close to one */ + if(ix<0x3f7ffff6) return (hy<0)? sn*huge*huge:sn*tiny*tiny; + if(ix>0x3f800007) return (hy>0)? sn*huge*huge:sn*tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax-1; /* t has 20 trailing zeros */ + w = (t*t)*(half-t*(thrd-t*qrtr)); + u = ivln2_h*t; /* ivln2_h has 16 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + GET_FLOAT_WORD(is,t1); + SET_FLOAT_WORD(t1,is&0xfffff000); + t2 = v-(t1-u); + } else { + float s2,s_h,s_l,t_h,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00800000) + {ax *= two24; n -= 24; GET_FLOAT_WORD(ix,ax); } + n += ((ix)>>23)-0x7f; + j = ix&0x007fffff; + /* determine interval */ + ix = j|0x3f800000; /* normalize ix */ + if(j<=0x1cc471) k=0; /* |x|>1)&0xfffff000)|0x20000000; + SET_FLOAT_WORD(t_h,is+0x00400000+(k<<21)); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = s*s; + r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); + r += s_l*(s_h+s); + s2 = s_h*s_h; + t_h = 3+s2+r; + GET_FLOAT_WORD(is,t_h); + SET_FLOAT_WORD(t_h,is&0xfffff000); + t_l = r-((t_h-3)-s2); + /* u+v = s*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*s; + /* 2/(3log2)*(s+...) */ + p_h = u+v; + GET_FLOAT_WORD(is,p_h); + SET_FLOAT_WORD(p_h,is&0xfffff000); + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = n; + t1 = (((z_h+z_l)+dp_h[k])+t); + GET_FLOAT_WORD(is,t1); + SET_FLOAT_WORD(t1,is&0xfffff000); + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + GET_FLOAT_WORD(is,y); + SET_FLOAT_WORD(y1,is&0xfffff000); + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + GET_FLOAT_WORD(j,z); + if (j>0x43000000) /* if z > 128 */ + return sn*huge*huge; /* overflow */ + else if (j==0x43000000) { /* if z == 128 */ + if(p_l+ovt>z-p_h) return sn*huge*huge; /* overflow */ + } + else if ((j&0x7fffffff)>0x43160000) /* z <= -150 */ + return sn*tiny*tiny; /* underflow */ + else if (j==0xc3160000){ /* z == -150 */ + if(p_l<=z-p_h) return sn*tiny*tiny; /* underflow */ + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>23)-0x7f; + n = 0; + if(i>0x3f000000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00800000>>(k+1)); + k = ((n&0x7fffffff)>>23)-0x7f; /* new k for n */ + SET_FLOAT_WORD(t,n&~(0x007fffff>>k)); + n = ((n&0x007fffff)|0x00800000)>>(23-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + GET_FLOAT_WORD(is,t); + SET_FLOAT_WORD(t,is&0xffff8000); + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + GET_FLOAT_WORD(j,z); + j += (n<<23); + if((j>>23)<=0) z = scalbnf(z,n); /* subnormal output */ + else SET_FLOAT_WORD(z,j); + return sn*z; +} diff --git a/openlibm/src/e_rem_pio2.c b/openlibm/src/e_rem_pio2.c new file mode 100644 index 0000000000..c09e38a364 --- /dev/null +++ b/openlibm/src/e_rem_pio2.c @@ -0,0 +1,183 @@ + +/* @(#)e_rem_pio2.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * Optimized by Bruce D. Evans. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_rem_pio2.c,v 1.22 2011/06/19 17:07:58 kargl Exp $"); + +/* __ieee754_rem_pio2(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2() + */ + +#include +#include + +#include "math_private.h" + +/* + * invpio2: 53 bits of 2/pi + * pio2_1: first 33 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 33 bit of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 33 bit of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const double +zero = 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ +pio2_1 = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */ +pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */ +pio2_2 = 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */ +pio2_2t = 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */ +pio2_3 = 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */ +pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ + +__inline int +__ieee754_rem_pio2(double x, double *y) +{ + double z,w,t,r,fn; + double tx[3],ty[2]; + int32_t e0,i,j,nx,n,ix,hx; + u_int32_t low; + + GET_HIGH_WORD(hx,x); /* high word of x */ + ix = hx&0x7fffffff; +#if 0 /* Must be handled in caller. */ + if(ix<=0x3fe921fb) /* |x| ~<= pi/4 , no need for reduction */ + {y[0] = x; y[1] = 0; return 0;} +#endif + if (ix <= 0x400f6a7a) { /* |x| ~<= 5pi/4 */ + if ((ix & 0xfffff) == 0x921fb) /* |x| ~= pi/2 or 2pi/2 */ + goto medium; /* cancellation -- use medium case */ + if (ix <= 0x4002d97c) { /* |x| ~<= 3pi/4 */ + if (hx > 0) { + z = x - pio2_1; /* one round good to 85 bits */ + y[0] = z - pio2_1t; + y[1] = (z-y[0])-pio2_1t; + return 1; + } else { + z = x + pio2_1; + y[0] = z + pio2_1t; + y[1] = (z-y[0])+pio2_1t; + return -1; + } + } else { + if (hx > 0) { + z = x - 2*pio2_1; + y[0] = z - 2*pio2_1t; + y[1] = (z-y[0])-2*pio2_1t; + return 2; + } else { + z = x + 2*pio2_1; + y[0] = z + 2*pio2_1t; + y[1] = (z-y[0])+2*pio2_1t; + return -2; + } + } + } + if (ix <= 0x401c463b) { /* |x| ~<= 9pi/4 */ + if (ix <= 0x4015fdbc) { /* |x| ~<= 7pi/4 */ + if (ix == 0x4012d97c) /* |x| ~= 3pi/2 */ + goto medium; + if (hx > 0) { + z = x - 3*pio2_1; + y[0] = z - 3*pio2_1t; + y[1] = (z-y[0])-3*pio2_1t; + return 3; + } else { + z = x + 3*pio2_1; + y[0] = z + 3*pio2_1t; + y[1] = (z-y[0])+3*pio2_1t; + return -3; + } + } else { + if (ix == 0x401921fb) /* |x| ~= 4pi/2 */ + goto medium; + if (hx > 0) { + z = x - 4*pio2_1; + y[0] = z - 4*pio2_1t; + y[1] = (z-y[0])-4*pio2_1t; + return 4; + } else { + z = x + 4*pio2_1; + y[0] = z + 4*pio2_1t; + y[1] = (z-y[0])+4*pio2_1t; + return -4; + } + } + } + if(ix<0x413921fb) { /* |x| ~< 2^20*(pi/2), medium size */ +medium: + /* Use a specialized rint() to get fn. Assume round-to-nearest. */ + STRICT_ASSIGN(double,fn,x*invpio2+0x1.8p52); + fn = fn-0x1.8p52; +#ifdef HAVE_EFFICIENT_IRINT + n = irint(fn); +#else + n = (int32_t)fn; +#endif + r = x-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 85 bit */ + { + u_int32_t high; + j = ix>>20; + y[0] = r-w; + GET_HIGH_WORD(high,y[0]); + i = j-((high>>20)&0x7ff); + if(i>16) { /* 2nd iteration needed, good to 118 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + GET_HIGH_WORD(high,y[0]); + i = j-((high>>20)&0x7ff); + if(i>49) { /* 3rd iteration need, 151 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + return n; + } + /* + * all other (large) arguments + */ + if(ix>=0x7ff00000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-23) */ + GET_LOW_WORD(low,x); + e0 = (ix>>20)-1046; /* e0 = ilogb(z)-23; */ + INSERT_WORDS(z, ix - ((int32_t)(e0<<20)), low); + for(i=0;i<2;i++) { + tx[i] = (double)((int32_t)(z)); + z = (z-tx[i])*two24; + } + tx[2] = z; + nx = 3; + while(tx[nx-1]==zero) nx--; /* skip zero term */ + n = __kernel_rem_pio2(tx,ty,e0,nx,1); + if(hx<0) {y[0] = -ty[0]; y[1] = -ty[1]; return -n;} + y[0] = ty[0]; y[1] = ty[1]; return n; +} diff --git a/openlibm/src/e_rem_pio2f.c b/openlibm/src/e_rem_pio2f.c new file mode 100644 index 0000000000..9861bb9dc6 --- /dev/null +++ b/openlibm/src/e_rem_pio2f.c @@ -0,0 +1,81 @@ +/* e_rem_pio2f.c -- float version of e_rem_pio2.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Debugged and optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_rem_pio2f.c,v 1.32 2009/06/03 08:16:34 ed Exp $"); + +/* __ieee754_rem_pio2f(x,y) + * + * return the remainder of x rem pi/2 in *y + * use double precision for everything except passing x + * use __kernel_rem_pio2() for large x + */ + +#include +#include + +#include "math_private.h" + +/* + * invpio2: 53 bits of 2/pi + * pio2_1: first 33 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + */ + +static const double +invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ +pio2_1 = 1.57079631090164184570e+00, /* 0x3FF921FB, 0x50000000 */ +pio2_1t = 1.58932547735281966916e-08; /* 0x3E5110b4, 0x611A6263 */ + +__inline int +__ieee754_rem_pio2f(float x, double *y) +{ + double w,r,fn; + double tx[1],ty[1]; + float z; + int32_t e0,n,ix,hx; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + /* 33+53 bit pi is good enough for medium size */ + if(ix<0x4dc90fdb) { /* |x| ~< 2^28*(pi/2), medium size */ + /* Use a specialized rint() to get fn. Assume round-to-nearest. */ + STRICT_ASSIGN(double,fn,x*invpio2+0x1.8p52); + fn = fn-0x1.8p52; +#ifdef HAVE_EFFICIENT_IRINT + n = irint(fn); +#else + n = (int32_t)fn; +#endif + r = x-fn*pio2_1; + w = fn*pio2_1t; + *y = r-w; + return n; + } + /* + * all other (large) arguments + */ + if(ix>=0x7f800000) { /* x is inf or NaN */ + *y=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(|x|)-23) */ + e0 = (ix>>23)-150; /* e0 = ilogb(|x|)-23; */ + SET_FLOAT_WORD(z, ix - ((int32_t)(e0<<23))); + tx[0] = z; + n = __kernel_rem_pio2(tx,ty,e0,1,0); + if(hx<0) {*y = -ty[0]; return -n;} + *y = ty[0]; return n; +} diff --git a/openlibm/src/e_remainder.c b/openlibm/src/e_remainder.c new file mode 100644 index 0000000000..a72631c4ed --- /dev/null +++ b/openlibm/src/e_remainder.c @@ -0,0 +1,79 @@ + +/* @(#)e_remainder.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_remainder.c,v 1.12 2008/03/30 20:47:42 das Exp $"); + +/* __ieee754_remainder(x,p) + * Return : + * returns x REM p = x - [x/p]*p as if in infinite + * precise arithmetic, where [x/p] is the (infinite bit) + * integer nearest x/p (in half way case choose the even one). + * Method : + * Based on fmod() return x-[x/p]chopped*p exactlp. + */ + +#include +#include + +#include "math_private.h" + +static const double zero = 0.0; + + +OLM_DLLEXPORT double +__ieee754_remainder(double x, double p) +{ + int32_t hx,hp; + u_int32_t sx,lx,lp; + double p_half; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hp,lp,p); + sx = hx&0x80000000; + hp &= 0x7fffffff; + hx &= 0x7fffffff; + + /* purge off exception values */ + if((hp|lp)==0) return (x*p)/(x*p); /* p = 0 */ + if((hx>=0x7ff00000)|| /* x not finite */ + ((hp>=0x7ff00000)&& /* p is NaN */ + (((hp-0x7ff00000)|lp)!=0))) + return ((long double)x*p)/((long double)x*p); + + + if (hp<=0x7fdfffff) x = __ieee754_fmod(x,p+p); /* now x < 2p */ + if (((hx-hp)|(lx-lp))==0) return zero*x; + x = fabs(x); + p = fabs(p); + if (hp<0x00200000) { + if(x+x>p) { + x-=p; + if(x+x>=p) x -= p; + } + } else { + p_half = 0.5*p; + if(x>p_half) { + x-=p; + if(x>=p_half) x -= p; + } + } + GET_HIGH_WORD(hx,x); + if ((hx&0x7fffffff)==0) hx = 0; + SET_HIGH_WORD(x,hx^sx); + return x; +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(remainder, remainderl); +#endif diff --git a/openlibm/src/e_remainderf.c b/openlibm/src/e_remainderf.c new file mode 100644 index 0000000000..ac0d153807 --- /dev/null +++ b/openlibm/src/e_remainderf.c @@ -0,0 +1,66 @@ +/* e_remainderf.c -- float version of e_remainder.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_remainderf.c,v 1.8 2008/02/12 17:11:36 bde Exp $"); + +#include + +#include "math_private.h" + +static const float zero = 0.0; + + +OLM_DLLEXPORT float +__ieee754_remainderf(float x, float p) +{ + int32_t hx,hp; + u_int32_t sx; + float p_half; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hp,p); + sx = hx&0x80000000; + hp &= 0x7fffffff; + hx &= 0x7fffffff; + + /* purge off exception values */ + if(hp==0) return (x*p)/(x*p); /* p = 0 */ + if((hx>=0x7f800000)|| /* x not finite */ + ((hp>0x7f800000))) /* p is NaN */ + return ((long double)x*p)/((long double)x*p); + + + if (hp<=0x7effffff) x = __ieee754_fmodf(x,p+p); /* now x < 2p */ + if ((hx-hp)==0) return zero*x; + x = fabsf(x); + p = fabsf(p); + if (hp<0x01000000) { + if(x+x>p) { + x-=p; + if(x+x>=p) x -= p; + } + } else { + p_half = (float)0.5*p; + if(x>p_half) { + x-=p; + if(x>=p_half) x -= p; + } + } + GET_FLOAT_WORD(hx,x); + if ((hx&0x7fffffff)==0) hx = 0; + SET_FLOAT_WORD(x,hx^sx); + return x; +} diff --git a/openlibm/src/e_remainderl.c b/openlibm/src/e_remainderl.c new file mode 100644 index 0000000000..5f1ee614e2 --- /dev/null +++ b/openlibm/src/e_remainderl.c @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_remainderl.c,v 1.1 2008/03/30 20:47:42 das Exp $"); + +#include +#include "math_private.h" + +OLM_DLLEXPORT long double +remainderl(long double x, long double y) +{ + int quo; + + return (remquol(x, y, &quo)); +} diff --git a/openlibm/src/e_sinh.c b/openlibm/src/e_sinh.c new file mode 100644 index 0000000000..7f3a2bff3d --- /dev/null +++ b/openlibm/src/e_sinh.c @@ -0,0 +1,79 @@ + +/* @(#)e_sinh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_sinh.c,v 1.11 2011/10/21 06:28:47 das Exp $"); + +/* __ieee754_sinh(x) + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinh(-x) = -sinh(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 22 : sinh(x) := --------------, E=expm1(x) + * 2 + * + * 22 <= x <= lnovft : sinh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: sinh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : sinh(x) := x*shuge (overflow) + * + * Special cases: + * sinh(x) is |x| if x is +INF, -INF, or NaN. + * only sinh(0)=0 is exact for finite x. + */ + +#include +#include + +#include "math_private.h" + +static const double one = 1.0, shuge = 1.0e307; + +OLM_DLLEXPORT double +__ieee754_sinh(double x) +{ + double t,h; + int32_t ix,jx; + + /* High word of |x|. */ + GET_HIGH_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x+x; + + h = 0.5; + if (jx<0) h = -h; + /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3e300000) /* |x|<2**-28 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = expm1(fabs(x)); + if(ix<0x3ff00000) return h*(2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix < 0x40862E42) return h*__ieee754_exp(fabs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + if (ix<=0x408633CE) + return h*2.0*__ldexp_exp(fabs(x), -1); + + /* |x| > overflowthresold, sinh(x) overflow */ + return x*shuge; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(sinh, sinhl); +#endif diff --git a/openlibm/src/e_sinhf.c b/openlibm/src/e_sinhf.c new file mode 100644 index 0000000000..6e5e06d22a --- /dev/null +++ b/openlibm/src/e_sinhf.c @@ -0,0 +1,57 @@ +/* e_sinhf.c -- float version of e_sinh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_sinhf.c,v 1.10 2011/10/21 06:28:47 das Exp $"); + +#include + +#include "math_private.h" + +static const float one = 1.0, shuge = 1.0e37; + +OLM_DLLEXPORT float +__ieee754_sinhf(float x) +{ + float t,h; + int32_t ix,jx; + + GET_FLOAT_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7f800000) return x+x; + + h = 0.5; + if (jx<0) h = -h; + /* |x| in [0,9], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x41100000) { /* |x|<9 */ + if (ix<0x39800000) /* |x|<2**-12 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = expm1f(fabsf(x)); + if(ix<0x3f800000) return h*((float)2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [9, logf(maxfloat)] return 0.5*exp(|x|) */ + if (ix < 0x42b17217) return h*__ieee754_expf(fabsf(x)); + + /* |x| in [logf(maxfloat), overflowthresold] */ + if (ix<=0x42b2d4fc) + return h*2.0F*__ldexp_expf(fabsf(x), -1); + + /* |x| > overflowthresold, sinh(x) overflow */ + return x*shuge; +} diff --git a/openlibm/src/e_sqrt.c b/openlibm/src/e_sqrt.c new file mode 100644 index 0000000000..2eb4a1a66d --- /dev/null +++ b/openlibm/src/e_sqrt.c @@ -0,0 +1,451 @@ + +/* @(#)e_sqrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_sqrt.c,v 1.11 2008/03/02 01:47:58 das Exp $"); + +/* __ieee754_sqrt(x) + * Return correctly rounded sqrt. + * ------------------------------------------ + * | Use the hardware sqrt if you have one | + * ------------------------------------------ + * Method: + * Bit by bit method using integer arithmetic. (Slow, but portable) + * 1. Normalization + * Scale x to y in [1,4) with even powers of 2: + * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then + * sqrt(x) = 2^k * sqrt(y) + * 2. Bit by bit computation + * Let q = sqrt(y) truncated to i bit after binary point (q = 1), + * i 0 + * i+1 2 + * s = 2*q , and y = 2 * ( y - q ). (1) + * i i i i + * + * To compute q from q , one checks whether + * i+1 i + * + * -(i+1) 2 + * (q + 2 ) <= y. (2) + * i + * -(i+1) + * If (2) is false, then q = q ; otherwise q = q + 2 . + * i+1 i i+1 i + * + * With some algebric manipulation, it is not difficult to see + * that (2) is equivalent to + * -(i+1) + * s + 2 <= y (3) + * i i + * + * The advantage of (3) is that s and y can be computed by + * i i + * the following recurrence formula: + * if (3) is false + * + * s = s , y = y ; (4) + * i+1 i i+1 i + * + * otherwise, + * -i -(i+1) + * s = s + 2 , y = y - s - 2 (5) + * i+1 i i+1 i i + * + * One may easily use induction to prove (4) and (5). + * Note. Since the left hand side of (3) contain only i+2 bits, + * it does not necessary to do a full (53-bit) comparison + * in (3). + * 3. Final rounding + * After generating the 53 bits result, we compute one more bit. + * Together with the remainder, we can decide whether the + * result is exact, bigger than 1/2ulp, or less than 1/2ulp + * (it will never equal to 1/2ulp). + * The rounding mode can be detected by checking whether + * huge + tiny is equal to huge, and whether huge - tiny is + * equal to huge for some floating point number "huge" and "tiny". + * + * Special cases: + * sqrt(+-0) = +-0 ... exact + * sqrt(inf) = inf + * sqrt(-ve) = NaN ... with invalid signal + * sqrt(NaN) = NaN ... with invalid signal for signaling NaN + * + * Other methods : see the appended file at the end of the program below. + *--------------- + */ + +#include +#include + +#include "math_private.h" + +static const double one = 1.0, tiny=1.0e-300; + +OLM_DLLEXPORT double +__ieee754_sqrt(double x) +{ + double z; + int32_t sign = (int)0x80000000; + int32_t ix0,s0,q,m,t,i; + u_int32_t r,t1,s1,ix1,q1; + + EXTRACT_WORDS(ix0,ix1,x); + + /* take care of Inf and NaN */ + if((ix0&0x7ff00000)==0x7ff00000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix0<=0) { + if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix0<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix0>>20); + if(m==0) { /* subnormal x */ + while(ix0==0) { + m -= 21; + ix0 |= (ix1>>11); ix1 <<= 21; + } + for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; + m -= i-1; + ix0 |= (ix1>>(32-i)); + ix1 <<= i; + } + m -= 1023; /* unbias exponent */ + ix0 = (ix0&0x000fffff)|0x00100000; + if(m&1){ /* odd m, double x to make it even */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + } + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ + r = 0x00200000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s0+r; + if(t<=ix0) { + s0 = t+r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + r>>=1; + } + + r = sign; + while(r!=0) { + t1 = s1+r; + t = s0; + if((t>31); + ix1 += ix1; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if((ix0|ix1)!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (q1==(u_int32_t)0xffffffff) { q1=0; q += 1;} + else if (z>one) { + if (q1==(u_int32_t)0xfffffffe) q+=1; + q1+=2; + } else + q1 += (q1&1); + } + } + ix0 = (q>>1)+0x3fe00000; + ix1 = q1>>1; + if ((q&1)==1) ix1 |= sign; + ix0 += (m <<20); + INSERT_WORDS(z,ix0,ix1); + return z; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(sqrt, sqrtl); +#endif + +/* +Other methods (use floating-point arithmetic) +------------- +(This is a copy of a drafted paper by Prof W. Kahan +and K.C. Ng, written in May, 1986) + + Two algorithms are given here to implement sqrt(x) + (IEEE double precision arithmetic) in software. + Both supply sqrt(x) correctly rounded. The first algorithm (in + Section A) uses newton iterations and involves four divisions. + The second one uses reciproot iterations to avoid division, but + requires more multiplications. Both algorithms need the ability + to chop results of arithmetic operations instead of round them, + and the INEXACT flag to indicate when an arithmetic operation + is executed exactly with no roundoff error, all part of the + standard (IEEE 754-1985). The ability to perform shift, add, + subtract and logical AND operations upon 32-bit words is needed + too, though not part of the standard. + +A. sqrt(x) by Newton Iteration + + (1) Initial approximation + + Let x0 and x1 be the leading and the trailing 32-bit words of + a floating point number x (in IEEE double format) respectively + + 1 11 52 ...widths + ------------------------------------------------------ + x: |s| e | f | + ------------------------------------------------------ + msb lsb msb lsb ...order + + + ------------------------ ------------------------ + x0: |s| e | f1 | x1: | f2 | + ------------------------ ------------------------ + + By performing shifts and subtracts on x0 and x1 (both regarded + as integers), we obtain an 8-bit approximation of sqrt(x) as + follows. + + k := (x0>>1) + 0x1ff80000; + y0 := k - T1[31&(k>>15)]. ... y ~ sqrt(x) to 8 bits + Here k is a 32-bit integer and T1[] is an integer array containing + correction terms. Now magically the floating value of y (y's + leading 32-bit word is y0, the value of its trailing word is 0) + approximates sqrt(x) to almost 8-bit. + + Value of T1: + static int T1[32]= { + 0, 1024, 3062, 5746, 9193, 13348, 18162, 23592, + 29598, 36145, 43202, 50740, 58733, 67158, 75992, 85215, + 83599, 71378, 60428, 50647, 41945, 34246, 27478, 21581, + 16499, 12183, 8588, 5674, 3403, 1742, 661, 130,}; + + (2) Iterative refinement + + Apply Heron's rule three times to y, we have y approximates + sqrt(x) to within 1 ulp (Unit in the Last Place): + + y := (y+x/y)/2 ... almost 17 sig. bits + y := (y+x/y)/2 ... almost 35 sig. bits + y := y-(y-x/y)/2 ... within 1 ulp + + + Remark 1. + Another way to improve y to within 1 ulp is: + + y := (y+x/y) ... almost 17 sig. bits to 2*sqrt(x) + y := y - 0x00100006 ... almost 18 sig. bits to sqrt(x) + + 2 + (x-y )*y + y := y + 2* ---------- ...within 1 ulp + 2 + 3y + x + + + This formula has one division fewer than the one above; however, + it requires more multiplications and additions. Also x must be + scaled in advance to avoid spurious overflow in evaluating the + expression 3y*y+x. Hence it is not recommended uless division + is slow. If division is very slow, then one should use the + reciproot algorithm given in section B. + + (3) Final adjustment + + By twiddling y's last bit it is possible to force y to be + correctly rounded according to the prevailing rounding mode + as follows. Let r and i be copies of the rounding mode and + inexact flag before entering the square root program. Also we + use the expression y+-ulp for the next representable floating + numbers (up and down) of y. Note that y+-ulp = either fixed + point y+-1, or multiply y by nextafter(1,+-inf) in chopped + mode. + + I := FALSE; ... reset INEXACT flag I + R := RZ; ... set rounding mode to round-toward-zero + z := x/y; ... chopped quotient, possibly inexact + If(not I) then { ... if the quotient is exact + if(z=y) { + I := i; ... restore inexact flag + R := r; ... restore rounded mode + return sqrt(x):=y. + } else { + z := z - ulp; ... special rounding + } + } + i := TRUE; ... sqrt(x) is inexact + If (r=RN) then z=z+ulp ... rounded-to-nearest + If (r=RP) then { ... round-toward-+inf + y = y+ulp; z=z+ulp; + } + y := y+z; ... chopped sum + y0:=y0-0x00100000; ... y := y/2 is correctly rounded. + I := i; ... restore inexact flag + R := r; ... restore rounded mode + return sqrt(x):=y. + + (4) Special cases + + Square root of +inf, +-0, or NaN is itself; + Square root of a negative number is NaN with invalid signal. + + +B. sqrt(x) by Reciproot Iteration + + (1) Initial approximation + + Let x0 and x1 be the leading and the trailing 32-bit words of + a floating point number x (in IEEE double format) respectively + (see section A). By performing shifs and subtracts on x0 and y0, + we obtain a 7.8-bit approximation of 1/sqrt(x) as follows. + + k := 0x5fe80000 - (x0>>1); + y0:= k - T2[63&(k>>14)]. ... y ~ 1/sqrt(x) to 7.8 bits + + Here k is a 32-bit integer and T2[] is an integer array + containing correction terms. Now magically the floating + value of y (y's leading 32-bit word is y0, the value of + its trailing word y1 is set to zero) approximates 1/sqrt(x) + to almost 7.8-bit. + + Value of T2: + static int T2[64]= { + 0x1500, 0x2ef8, 0x4d67, 0x6b02, 0x87be, 0xa395, 0xbe7a, 0xd866, + 0xf14a, 0x1091b,0x11fcd,0x13552,0x14999,0x15c98,0x16e34,0x17e5f, + 0x18d03,0x19a01,0x1a545,0x1ae8a,0x1b5c4,0x1bb01,0x1bfde,0x1c28d, + 0x1c2de,0x1c0db,0x1ba73,0x1b11c,0x1a4b5,0x1953d,0x18266,0x16be0, + 0x1683e,0x179d8,0x18a4d,0x19992,0x1a789,0x1b445,0x1bf61,0x1c989, + 0x1d16d,0x1d77b,0x1dddf,0x1e2ad,0x1e5bf,0x1e6e8,0x1e654,0x1e3cd, + 0x1df2a,0x1d635,0x1cb16,0x1be2c,0x1ae4e,0x19bde,0x1868e,0x16e2e, + 0x1527f,0x1334a,0x11051,0xe951, 0xbe01, 0x8e0d, 0x5924, 0x1edd,}; + + (2) Iterative refinement + + Apply Reciproot iteration three times to y and multiply the + result by x to get an approximation z that matches sqrt(x) + to about 1 ulp. To be exact, we will have + -1ulp < sqrt(x)-z<1.0625ulp. + + ... set rounding mode to Round-to-nearest + y := y*(1.5-0.5*x*y*y) ... almost 15 sig. bits to 1/sqrt(x) + y := y*((1.5-2^-30)+0.5*x*y*y)... about 29 sig. bits to 1/sqrt(x) + ... special arrangement for better accuracy + z := x*y ... 29 bits to sqrt(x), with z*y<1 + z := z + 0.5*z*(1-z*y) ... about 1 ulp to sqrt(x) + + Remark 2. The constant 1.5-2^-30 is chosen to bias the error so that + (a) the term z*y in the final iteration is always less than 1; + (b) the error in the final result is biased upward so that + -1 ulp < sqrt(x) - z < 1.0625 ulp + instead of |sqrt(x)-z|<1.03125ulp. + + (3) Final adjustment + + By twiddling y's last bit it is possible to force y to be + correctly rounded according to the prevailing rounding mode + as follows. Let r and i be copies of the rounding mode and + inexact flag before entering the square root program. Also we + use the expression y+-ulp for the next representable floating + numbers (up and down) of y. Note that y+-ulp = either fixed + point y+-1, or multiply y by nextafter(1,+-inf) in chopped + mode. + + R := RZ; ... set rounding mode to round-toward-zero + switch(r) { + case RN: ... round-to-nearest + if(x<= z*(z-ulp)...chopped) z = z - ulp; else + if(x<= z*(z+ulp)...chopped) z = z; else z = z+ulp; + break; + case RZ:case RM: ... round-to-zero or round-to--inf + R:=RP; ... reset rounding mod to round-to-+inf + if(x=(z+ulp)*(z+ulp) ...rounded up) z = z+ulp; + break; + case RP: ... round-to-+inf + if(x>(z+ulp)*(z+ulp)...chopped) z = z+2*ulp; else + if(x>z*z ...chopped) z = z+ulp; + break; + } + + Remark 3. The above comparisons can be done in fixed point. For + example, to compare x and w=z*z chopped, it suffices to compare + x1 and w1 (the trailing parts of x and w), regarding them as + two's complement integers. + + ...Is z an exact square root? + To determine whether z is an exact square root of x, let z1 be the + trailing part of z, and also let x0 and x1 be the leading and + trailing parts of x. + + If ((z1&0x03ffffff)!=0) ... not exact if trailing 26 bits of z!=0 + I := 1; ... Raise Inexact flag: z is not exact + else { + j := 1 - [(x0>>20)&1] ... j = logb(x) mod 2 + k := z1 >> 26; ... get z's 25-th and 26-th + fraction bits + I := i or (k&j) or ((k&(j+j+1))!=(x1&3)); + } + R:= r ... restore rounded mode + return sqrt(x):=z. + + If multiplication is cheaper then the foregoing red tape, the + Inexact flag can be evaluated by + + I := i; + I := (z*z!=x) or I. + + Note that z*z can overwrite I; this value must be sensed if it is + True. + + Remark 4. If z*z = x exactly, then bit 25 to bit 0 of z1 must be + zero. + + -------------------- + z1: | f2 | + -------------------- + bit 31 bit 0 + + Further more, bit 27 and 26 of z1, bit 0 and 1 of x1, and the odd + or even of logb(x) have the following relations: + + ------------------------------------------------- + bit 27,26 of z1 bit 1,0 of x1 logb(x) + ------------------------------------------------- + 00 00 odd and even + 01 01 even + 10 10 odd + 10 00 even + 11 01 even + ------------------------------------------------- + + (4) Special cases (see (4) of Section A). + + */ + diff --git a/openlibm/src/e_sqrtf.c b/openlibm/src/e_sqrtf.c new file mode 100644 index 0000000000..2aeaa9fd5e --- /dev/null +++ b/openlibm/src/e_sqrtf.c @@ -0,0 +1,86 @@ +/* e_sqrtf.c -- float version of e_sqrt.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include + +#include "math_private.h" + +static const float one = 1.0, tiny=1.0e-30; + +OLM_DLLEXPORT float +__ieee754_sqrtf(float x) +{ + float z; + int32_t sign = (int)0x80000000; + int32_t ix,s,q,m,t,i; + u_int32_t r; + + GET_FLOAT_WORD(ix,x); + + /* take care of Inf and NaN */ + if((ix&0x7f800000)==0x7f800000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix<=0) { + if((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix>>23); + if(m==0) { /* subnormal x */ + for(i=0;(ix&0x00800000)==0;i++) ix<<=1; + m -= i-1; + } + m -= 127; /* unbias exponent */ + ix = (ix&0x007fffff)|0x00800000; + if(m&1) /* odd m, double x to make it even */ + ix += ix; + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix += ix; + q = s = 0; /* q = sqrt(x) */ + r = 0x01000000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s+r; + if(t<=ix) { + s = t+r; + ix -= t; + q += r; + } + ix += ix; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if(ix!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (z>one) + q += 2; + else + q += (q&1); + } + } + ix = (q>>1)+0x3f000000; + ix += (m <<23); + SET_FLOAT_WORD(z,ix); + return z; +} diff --git a/openlibm/src/e_sqrtl.c b/openlibm/src/e_sqrtl.c new file mode 100644 index 0000000000..a073bf9f37 --- /dev/null +++ b/openlibm/src/e_sqrtl.c @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2007 Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/e_sqrtl.c,v 1.1 2008/03/02 01:47:58 das Exp $"); + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +/* Return (x + ulp) for normal positive x. Assumes no overflow. */ +static inline long double +inc(long double x) +{ + union IEEEl2bits u; + + u.e = x; + if (++u.bits.manl == 0) { + if (++u.bits.manh == 0) { + u.bits.exp++; + u.bits.manh |= LDBL_NBIT; + } + } + return (u.e); +} + +/* Return (x - ulp) for normal positive x. Assumes no underflow. */ +static inline long double +dec(long double x) +{ + union IEEEl2bits u; + + u.e = x; + if (u.bits.manl-- == 0) { + if (u.bits.manh-- == LDBL_NBIT) { + u.bits.exp--; + u.bits.manh |= LDBL_NBIT; + } + } + return (u.e); +} + +#ifndef __GNUC__ +#pragma STDC FENV_ACCESS ON +#endif + +/* + * This is slow, but simple and portable. You should use hardware sqrt + * if possible. + */ + +OLM_DLLEXPORT long double +sqrtl(long double x) +{ + union IEEEl2bits u; + int k, r; + long double lo, xn; + fenv_t env; + + u.e = x; + + /* If x = NaN, then sqrt(x) = NaN. */ + /* If x = Inf, then sqrt(x) = Inf. */ + /* If x = -Inf, then sqrt(x) = NaN. */ + if (u.bits.exp == LDBL_MAX_EXP * 2 - 1) + return (x * x + x); + + /* If x = +-0, then sqrt(x) = +-0. */ + if ((u.bits.manh | u.bits.manl | u.bits.exp) == 0) + return (x); + + /* If x < 0, then raise invalid and return NaN */ + if (u.bits.sign) + return ((x - x) / (x - x)); + + feholdexcept(&env); + + if (u.bits.exp == 0) { + /* Adjust subnormal numbers. */ + u.e *= 0x1.0p514; + k = -514; + } else { + k = 0; + } + /* + * u.e is a normal number, so break it into u.e = e*2^n where + * u.e = (2*e)*2^2k for odd n and u.e = (4*e)*2^2k for even n. + */ + if ((u.bits.exp - 0x3ffe) & 1) { /* n is odd. */ + k += u.bits.exp - 0x3fff; /* 2k = n - 1. */ + u.bits.exp = 0x3fff; /* u.e in [1,2). */ + } else { + k += u.bits.exp - 0x4000; /* 2k = n - 2. */ + u.bits.exp = 0x4000; /* u.e in [2,4). */ + } + + /* + * Newton's iteration. + * Split u.e into a high and low part to achieve additional precision. + */ + xn = sqrt(u.e); /* 53-bit estimate of sqrtl(x). */ +#if LDBL_MANT_DIG > 100 + xn = (xn + (u.e / xn)) * 0.5; /* 106-bit estimate. */ +#endif + lo = u.e; + u.bits.manl = 0; /* Zero out lower bits. */ + lo = (lo - u.e) / xn; /* Low bits divided by xn. */ + xn = xn + (u.e / xn); /* High portion of estimate. */ + u.e = xn + lo; /* Combine everything. */ + u.bits.exp += (k >> 1) - 1; + + feclearexcept(FE_INEXACT); + r = fegetround(); + fesetround(FE_TOWARDZERO); /* Set to round-toward-zero. */ + xn = x / u.e; /* Chopped quotient (inexact?). */ + + if (!fetestexcept(FE_INEXACT)) { /* Quotient is exact. */ + if (xn == u.e) { + fesetenv(&env); + return (u.e); + } + /* Round correctly for inputs like x = y**2 - ulp. */ + xn = dec(xn); /* xn = xn - ulp. */ + } + + if (r == FE_TONEAREST) { + xn = inc(xn); /* xn = xn + ulp. */ + } else if (r == FE_UPWARD) { + u.e = inc(u.e); /* u.e = u.e + ulp. */ + xn = inc(xn); /* xn = xn + ulp. */ + } + u.e = u.e + xn; /* Chopped sum. */ + feupdateenv(&env); /* Restore env and raise inexact */ + u.bits.exp--; + return (u.e); +} diff --git a/openlibm/src/fpmath.h b/openlibm/src/fpmath.h new file mode 100644 index 0000000000..eb824d7093 --- /dev/null +++ b/openlibm/src/fpmath.h @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2003 Mike Barcroft + * Copyright (c) 2002 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libc/include/fpmath.h,v 1.4 2008/12/23 22:20:59 marcel Exp $ + */ +#ifndef _FPMATH_H_ +#define _FPMATH_H_ + +#if defined(__aarch64__) +#include "aarch64_fpmath.h" +#elif defined(__i386__) || defined(__x86_64__) +#ifdef __LP64__ +#include "amd64_fpmath.h" +#else +#include "i386_fpmath.h" +#endif +#elif defined(__powerpc__) || defined(__POWERPC__) +#include "powerpc_fpmath.h" +#elif defined(__mips__) +#include "mips_fpmath.h" +#elif defined(__s390__) +#include "s390_fpmath.h" +#elif defined(__riscv) +#include "riscv_fpmath.h" +#elif defined(__loongarch64) +#include "loongarch64_fpmath.h" +#endif + +/* Definitions provided directly by GCC and Clang. */ +#if !(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__)) + +#if defined(__GLIBC__) + +#include +#include +#define __ORDER_LITTLE_ENDIAN__ __LITTLE_ENDIAN +#define __ORDER_BIG_ENDIAN__ __BIG_ENDIAN +#define __BYTE_ORDER__ __BYTE_ORDER + +#elif defined(__APPLE__) + +#include +#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN +#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN +#define __BYTE_ORDER__ BYTE_ORDER + +#elif defined(_WIN32) + +#define __ORDER_LITTLE_ENDIAN__ 1234 +#define __ORDER_BIG_ENDIAN__ 4321 +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ + +#endif + +#endif /* __BYTE_ORDER__, __ORDER_LITTLE_ENDIAN__ and __ORDER_BIG_ENDIAN__ */ + +#ifndef __FLOAT_WORD_ORDER__ +#define __FLOAT_WORD_ORDER__ __BYTE_ORDER__ +#endif + +union IEEEf2bits { + float f; + struct { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + unsigned int man :23; + unsigned int exp :8; + unsigned int sign :1; +#else /* _BIG_ENDIAN */ + unsigned int sign :1; + unsigned int exp :8; + unsigned int man :23; +#endif + } bits; +}; + +#define DBL_MANH_SIZE 20 +#define DBL_MANL_SIZE 32 + +union IEEEd2bits { + double d; + struct { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__ + unsigned int manl :32; +#endif + unsigned int manh :20; + unsigned int exp :11; + unsigned int sign :1; +#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__ + unsigned int manl :32; +#endif +#else /* _BIG_ENDIAN */ + unsigned int sign :1; + unsigned int exp :11; + unsigned int manh :20; + unsigned int manl :32; +#endif + } bits; +}; + +#endif diff --git a/openlibm/src/i386_fpmath.h b/openlibm/src/i386_fpmath.h new file mode 100644 index 0000000000..455631ca00 --- /dev/null +++ b/openlibm/src/i386_fpmath.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2002, 2003 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libc/i386/_fpmath.h,v 1.6 2008/01/17 16:39:06 bde Exp $ + */ + +union IEEEl2bits { + long double e; + struct { + unsigned int manl :32; + unsigned int manh :32; + unsigned int exp :15; + unsigned int sign :1; + unsigned int junk :16; + } bits; + struct { + unsigned long long man :64; + unsigned int expsign :16; + unsigned int junk :16; + } xbits; +}; + +#define LDBL_NBIT 0x80000000 +#define mask_nbit_l(u) ((u).bits.manh &= ~LDBL_NBIT) + +#define LDBL_MANH_SIZE 32 +#define LDBL_MANL_SIZE 32 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)(u).bits.manh; \ +} while (0) diff --git a/openlibm/src/k_cos.c b/openlibm/src/k_cos.c new file mode 100644 index 0000000000..f3054ef8a1 --- /dev/null +++ b/openlibm/src/k_cos.c @@ -0,0 +1,80 @@ + +/* @(#)k_cos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_cos.c,v 1.12 2008/02/19 12:54:14 bde Exp $"); + +/* + * __kernel_cos( x, y ) + * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * + * Algorithm + * 1. Since cos(-x) = cos(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. + * 3. cos(x) is approximated by a polynomial of degree 14 on + * [0,pi/4] + * 4 14 + * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x + * where the remez error is + * + * | 2 4 6 8 10 12 14 | -58 + * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 + * | | + * + * 4 6 8 10 12 14 + * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then + * cos(x) ~ 1 - x*x/2 + r + * since cos(x+y) ~ cos(x) - sin(x)*y + * ~ cos(x) - x*y, + * a correction term is necessary in cos(x) and hence + * cos(x+y) = 1 - (x*x/2 - (r - x*y)) + * For better accuracy, rearrange to + * cos(x+y) ~ w + (tmp + (r-x*y)) + * where w = 1 - x*x/2 and tmp is a tiny correction term + * (1 - x*x/2 == w + tmp exactly in infinite precision). + * The exactness of w + tmp in infinite precision depends on w + * and tmp having the same precision as x. If they have extra + * precision due to compiler bugs, then the extra precision is + * only good provided it is retained in all terms of the final + * expression for cos(). Retention happens in all cases tested + * under FreeBSD, so don't pessimize things by forcibly clipping + * any extra precision in w. + */ + +#include + +#include "math_private.h" + +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ +C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ +C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ +C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ +C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ +C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ + +OLM_DLLEXPORT double +__kernel_cos(double x, double y) +{ + double hz,z,r,w; + + z = x*x; + w = z*z; + r = z*(C1+z*(C2+z*C3)) + w*w*(C4+z*(C5+z*C6)); + hz = 0.5*z; + w = one-hz; + return w + (((one-w)-hz) + (z*r-x*y)); +} diff --git a/openlibm/src/k_cosf.c b/openlibm/src/k_cosf.c new file mode 100644 index 0000000000..6774db4600 --- /dev/null +++ b/openlibm/src/k_cosf.c @@ -0,0 +1,48 @@ +/* k_cosf.c -- float version of k_cos.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Debugged and optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#ifndef INLINE_KERNEL_COSDF +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_cosf.c,v 1.18 2009/06/03 08:16:34 ed Exp $"); +#endif + +#include + +#include "math_private.h" + +/* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */ +static const double +one = 1.0, +C0 = -0x1ffffffd0c5e81.0p-54, /* -0.499999997251031003120 */ +C1 = 0x155553e1053a42.0p-57, /* 0.0416666233237390631894 */ +C2 = -0x16c087e80f1e27.0p-62, /* -0.00138867637746099294692 */ +C3 = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */ + +#ifndef INLINE_KERNEL_COSDF +extern +#endif +//__inline float +OLM_DLLEXPORT float +__kernel_cosdf(double x) +{ + double r, w, z; + + /* Try to optimize for parallel evaluation as in k_tanf.c. */ + z = x*x; + w = z*z; + r = C2+z*C3; + return ((one+z*C0) + w*C1) + (w*z)*r; +} diff --git a/openlibm/src/k_exp.c b/openlibm/src/k_exp.c new file mode 100644 index 0000000000..4739e20082 --- /dev/null +++ b/openlibm/src/k_exp.c @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_exp.c,v 1.1 2011/10/21 06:27:56 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const u_int32_t k = 1799; /* constant for reduction */ +static const double kln2 = 1246.97177782734161156; /* k * ln2 */ + +/* + * Compute exp(x), scaled to avoid spurious overflow. An exponent is + * returned separately in 'expt'. + * + * Input: ln(DBL_MAX) <= x < ln(2 * DBL_MAX / DBL_MIN_DENORM) ~= 1454.91 + * Output: 2**1023 <= y < 2**1024 + */ +static double +__frexp_exp(double x, int *expt) +{ + double exp_x; + u_int32_t hx; + + /* + * We use exp(x) = exp(x - kln2) * 2**k, carefully chosen to + * minimize |exp(kln2) - 2**k|. We also scale the exponent of + * exp_x to MAX_EXP so that the result can be multiplied by + * a tiny number without losing accuracy due to denormalization. + */ + exp_x = exp(x - kln2); + GET_HIGH_WORD(hx, exp_x); + *expt = (hx >> 20) - (0x3ff + 1023) + k; + SET_HIGH_WORD(exp_x, (hx & 0xfffff) | ((0x3ff + 1023) << 20)); + return (exp_x); +} + +/* + * __ldexp_exp(x, expt) and __ldexp_cexp(x, expt) compute exp(x) * 2**expt. + * They are intended for large arguments (real part >= ln(DBL_MAX)) + * where care is needed to avoid overflow. + * + * The present implementation is narrowly tailored for our hyperbolic and + * exponential functions. We assume expt is small (0 or -1), and the caller + * has filtered out very large x, for which overflow would be inevitable. + */ + +OLM_DLLEXPORT double +__ldexp_exp(double x, int expt) +{ + double exp_x, scale; + int ex_expt; + + exp_x = __frexp_exp(x, &ex_expt); + expt += ex_expt; + INSERT_WORDS(scale, (0x3ff + expt) << 20, 0); + return (exp_x * scale); +} + +OLM_DLLEXPORT double complex +__ldexp_cexp(double complex z, int expt) +{ + double x, y, exp_x, scale1, scale2; + int ex_expt, half_expt; + + x = creal(z); + y = cimag(z); + exp_x = __frexp_exp(x, &ex_expt); + expt += ex_expt; + + /* + * Arrange so that scale1 * scale2 == 2**expt. We use this to + * compensate for scalbn being horrendously slow. + */ + half_expt = expt / 2; + INSERT_WORDS(scale1, (0x3ff + half_expt) << 20, 0); + half_expt = expt - half_expt; + INSERT_WORDS(scale2, (0x3ff + half_expt) << 20, 0); + + return (CMPLX(cos(y) * exp_x * scale1 * scale2, + sin(y) * exp_x * scale1 * scale2)); +} diff --git a/openlibm/src/k_expf.c b/openlibm/src/k_expf.c new file mode 100644 index 0000000000..bbf094c38f --- /dev/null +++ b/openlibm/src/k_expf.c @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_expf.c,v 1.1 2011/10/21 06:27:56 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const u_int32_t k = 235; /* constant for reduction */ +static const float kln2 = 162.88958740F; /* k * ln2 */ + +/* + * See k_exp.c for details. + * + * Input: ln(FLT_MAX) <= x < ln(2 * FLT_MAX / FLT_MIN_DENORM) ~= 192.7 + * Output: 2**127 <= y < 2**128 + */ +static float +__frexp_expf(float x, int *expt) +{ + double exp_x; + u_int32_t hx; + + exp_x = expf(x - kln2); + GET_FLOAT_WORD(hx, exp_x); + *expt = (hx >> 23) - (0x7f + 127) + k; + SET_FLOAT_WORD(exp_x, (hx & 0x7fffff) | ((0x7f + 127) << 23)); + return (exp_x); +} + +OLM_DLLEXPORT float +__ldexp_expf(float x, int expt) +{ + float exp_x, scale; + int ex_expt; + + exp_x = __frexp_expf(x, &ex_expt); + expt += ex_expt; + SET_FLOAT_WORD(scale, (0x7f + expt) << 23); + return (exp_x * scale); +} + +OLM_DLLEXPORT float complex +__ldexp_cexpf(float complex z, int expt) +{ + float x, y, exp_x, scale1, scale2; + int ex_expt, half_expt; + + x = crealf(z); + y = cimagf(z); + exp_x = __frexp_expf(x, &ex_expt); + expt += ex_expt; + + half_expt = expt / 2; + SET_FLOAT_WORD(scale1, (0x7f + half_expt) << 23); + half_expt = expt - half_expt; + SET_FLOAT_WORD(scale2, (0x7f + half_expt) << 23); + + return (CMPLXF(cosf(y) * exp_x * scale1 * scale2, + sinf(y) * exp_x * scale1 * scale2)); +} diff --git a/openlibm/src/k_log.h b/openlibm/src/k_log.h new file mode 100644 index 0000000000..a0943c400a --- /dev/null +++ b/openlibm/src/k_log.h @@ -0,0 +1,100 @@ + +/* @(#)e_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_log.h,v 1.2 2011/10/15 05:23:28 das Exp $"); + +/* + * k_log1p(f): + * Return log(1+f) - f for 1+f in ~[sqrt(2)/2, sqrt(2)]. + * + * The following describes the overall strategy for computing + * logarithms in base e. The argument reduction and adding the final + * term of the polynomial are done by the caller for increased accuracy + * when different bases are used. + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +static const double +Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +/* + * We always inline k_log1p(), since doing so produces a + * substantial performance improvement (~40% on amd64). + */ +static inline double +k_log1p(double f) +{ + double hfsq,s,z,R,w,t1,t2; + + s = f/(2.0+f); + z = s*s; + w = z*z; + t1= w*(Lg2+w*(Lg4+w*Lg6)); + t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); + R = t2+t1; + hfsq=0.5*f*f; + return s*(hfsq+R); +} diff --git a/openlibm/src/k_logf.h b/openlibm/src/k_logf.h new file mode 100644 index 0000000000..1b665ae238 --- /dev/null +++ b/openlibm/src/k_logf.h @@ -0,0 +1,39 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_logf.h,v 1.3 2011/10/15 05:23:28 das Exp $"); + +/* + * Float version of k_log.h. See the latter for most comments. + */ + +static const float +/* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ +Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */ +Lg2 = 0xccce13.0p-25, /* 0.40000972152 */ +Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */ +Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */ + +static inline float +k_log1pf(float f) +{ + float hfsq,s,z,R,w,t1,t2; + + s = f/((float)2.0+f); + z = s*s; + w = z*z; + t1= w*(Lg2+w*Lg4); + t2= z*(Lg1+w*Lg3); + R = t2+t1; + hfsq=(float)0.5*f*f; + return s*(hfsq+R); +} diff --git a/openlibm/src/k_rem_pio2.c b/openlibm/src/k_rem_pio2.c new file mode 100644 index 0000000000..9555d3ab52 --- /dev/null +++ b/openlibm/src/k_rem_pio2.c @@ -0,0 +1,444 @@ + +/* @(#)k_rem_pio2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_rem_pio2.c,v 1.11 2008/02/25 11:43:20 bde Exp $"); + +/* + * __kernel_rem_pio2(x,y,e0,nx,prec) + * double x[],y[]; int e0,nx,prec; + * + * __kernel_rem_pio2 return the last three digits of N with + * y = x - N*pi/2 + * so that |y| < pi/2. + * + * The method is to compute the integer (mod 8) and fraction parts of + * (2/pi)*x without doing the full multiplication. In general we + * skip the part of the product that are known to be a huge integer ( + * more accurately, = 0 mod 8 ). Thus the number of operations are + * independent of the exponent of the input. + * + * (2/pi) is represented by an array of 24-bit integers in ipio2[]. + * + * Input parameters: + * x[] The input value (must be positive) is broken into nx + * pieces of 24-bit integers in double precision format. + * x[i] will be the i-th 24 bit of x. The scaled exponent + * of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 + * match x's up to 24 bits. + * + * Example of breaking a double positive z into x[0]+x[1]+x[2]: + * e0 = ilogb(z)-23 + * z = scalbn(z,-e0) + * for i = 0,1,2 + * x[i] = floor(z) + * z = (z-x[i])*2**24 + * + * + * y[] ouput result in an array of double precision numbers. + * The dimension of y[] is: + * 24-bit precision 1 + * 53-bit precision 2 + * 64-bit precision 2 + * 113-bit precision 3 + * The actual value is the sum of them. Thus for 113-bit + * precison, one may have to do something like: + * + * long double t,w,r_head, r_tail; + * t = (long double)y[2] + (long double)y[1]; + * w = (long double)y[0]; + * r_head = t+w; + * r_tail = w - (r_head - t); + * + * e0 The exponent of x[0]. Must be <= 16360 or you need to + * expand the ipio2 table. + * + * nx dimension of x[] + * + * prec an integer indicating the precision: + * 0 24 bits (single) + * 1 53 bits (double) + * 2 64 bits (extended) + * 3 113 bits (quad) + * + * External function: + * double scalbn(), floor(); + * + * + * Here is the description of some local variables: + * + * jk jk+1 is the initial number of terms of ipio2[] needed + * in the computation. The minimum and recommended value + * for jk is 3,4,4,6 for single, double, extended, and quad. + * jk+1 must be 2 larger than you might expect so that our + * recomputation test works. (Up to 24 bits in the integer + * part (the 24 bits of it that we compute) and 23 bits in + * the fraction part may be lost to cancelation before we + * recompute.) + * + * jz local integer variable indicating the number of + * terms of ipio2[] used. + * + * jx nx - 1 + * + * jv index for pointing to the suitable ipio2[] for the + * computation. In general, we want + * ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 + * is an integer. Thus + * e0-3-24*jv >= 0 or (e0-3)/24 >= jv + * Hence jv = max(0,(e0-3)/24). + * + * jp jp+1 is the number of terms in PIo2[] needed, jp = jk. + * + * q[] double array with integral value, representing the + * 24-bits chunk of the product of x and 2/pi. + * + * q0 the corresponding exponent of q[0]. Note that the + * exponent for q[i] would be q0-24*i. + * + * PIo2[] double precision array, obtained by cutting pi/2 + * into 24 bits chunks. + * + * f[] ipio2[] in floating point + * + * iq[] integer array by breaking up q[] in 24-bits chunk. + * + * fq[] final product of x*(2/pi) in fq[0],..,fq[jk] + * + * ih integer. If >0 it indicates q[] is >= 0.5, hence + * it also indicates the *sign* of the result. + * + */ + + +/* + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static const int init_jk[] = {3,4,4,6}; /* initial value for jk */ + +/* + * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi + * + * integer array, contains the (24*i)-th to (24*i+23)-th + * bit of 2/pi after binary point. The corresponding + * floating value is + * + * ipio2[i] * 2^(-24(i+1)). + * + * NB: This table must have at least (e0-3)/24 + jk terms. + * For quad precision (e0 <= 16360, jk = 6), this is 686. + */ +static const int32_t ipio2[] = { +0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, +0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, +0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, +0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, +0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, +0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, +0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, +0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, +0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, +0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, +0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, + +#if LDBL_MAX_EXP > 1024 +#if LDBL_MAX_EXP > 16384 +#error "ipio2 table needs to be expanded" +#endif +0x47C419, 0xC367CD, 0xDCE809, 0x2A8359, 0xC4768B, 0x961CA6, +0xDDAF44, 0xD15719, 0x053EA5, 0xFF0705, 0x3F7E33, 0xE832C2, +0xDE4F98, 0x327DBB, 0xC33D26, 0xEF6B1E, 0x5EF89F, 0x3A1F35, +0xCAF27F, 0x1D87F1, 0x21907C, 0x7C246A, 0xFA6ED5, 0x772D30, +0x433B15, 0xC614B5, 0x9D19C3, 0xC2C4AD, 0x414D2C, 0x5D000C, +0x467D86, 0x2D71E3, 0x9AC69B, 0x006233, 0x7CD2B4, 0x97A7B4, +0xD55537, 0xF63ED7, 0x1810A3, 0xFC764D, 0x2A9D64, 0xABD770, +0xF87C63, 0x57B07A, 0xE71517, 0x5649C0, 0xD9D63B, 0x3884A7, +0xCB2324, 0x778AD6, 0x23545A, 0xB91F00, 0x1B0AF1, 0xDFCE19, +0xFF319F, 0x6A1E66, 0x615799, 0x47FBAC, 0xD87F7E, 0xB76522, +0x89E832, 0x60BFE6, 0xCDC4EF, 0x09366C, 0xD43F5D, 0xD7DE16, +0xDE3B58, 0x929BDE, 0x2822D2, 0xE88628, 0x4D58E2, 0x32CAC6, +0x16E308, 0xCB7DE0, 0x50C017, 0xA71DF3, 0x5BE018, 0x34132E, +0x621283, 0x014883, 0x5B8EF5, 0x7FB0AD, 0xF2E91E, 0x434A48, +0xD36710, 0xD8DDAA, 0x425FAE, 0xCE616A, 0xA4280A, 0xB499D3, +0xF2A606, 0x7F775C, 0x83C2A3, 0x883C61, 0x78738A, 0x5A8CAF, +0xBDD76F, 0x63A62D, 0xCBBFF4, 0xEF818D, 0x67C126, 0x45CA55, +0x36D9CA, 0xD2A828, 0x8D61C2, 0x77C912, 0x142604, 0x9B4612, +0xC459C4, 0x44C5C8, 0x91B24D, 0xF31700, 0xAD43D4, 0xE54929, +0x10D5FD, 0xFCBE00, 0xCC941E, 0xEECE70, 0xF53E13, 0x80F1EC, +0xC3E7B3, 0x28F8C7, 0x940593, 0x3E71C1, 0xB3092E, 0xF3450B, +0x9C1288, 0x7B20AB, 0x9FB52E, 0xC29247, 0x2F327B, 0x6D550C, +0x90A772, 0x1FE76B, 0x96CB31, 0x4A1679, 0xE27941, 0x89DFF4, +0x9794E8, 0x84E6E2, 0x973199, 0x6BED88, 0x365F5F, 0x0EFDBB, +0xB49A48, 0x6CA467, 0x427271, 0x325D8D, 0xB8159F, 0x09E5BC, +0x25318D, 0x3974F7, 0x1C0530, 0x010C0D, 0x68084B, 0x58EE2C, +0x90AA47, 0x02E774, 0x24D6BD, 0xA67DF7, 0x72486E, 0xEF169F, +0xA6948E, 0xF691B4, 0x5153D1, 0xF20ACF, 0x339820, 0x7E4BF5, +0x6863B2, 0x5F3EDD, 0x035D40, 0x7F8985, 0x295255, 0xC06437, +0x10D86D, 0x324832, 0x754C5B, 0xD4714E, 0x6E5445, 0xC1090B, +0x69F52A, 0xD56614, 0x9D0727, 0x50045D, 0xDB3BB4, 0xC576EA, +0x17F987, 0x7D6B49, 0xBA271D, 0x296996, 0xACCCC6, 0x5414AD, +0x6AE290, 0x89D988, 0x50722C, 0xBEA404, 0x940777, 0x7030F3, +0x27FC00, 0xA871EA, 0x49C266, 0x3DE064, 0x83DD97, 0x973FA3, +0xFD9443, 0x8C860D, 0xDE4131, 0x9D3992, 0x8C70DD, 0xE7B717, +0x3BDF08, 0x2B3715, 0xA0805C, 0x93805A, 0x921110, 0xD8E80F, +0xAF806C, 0x4BFFDB, 0x0F9038, 0x761859, 0x15A562, 0xBBCB61, +0xB989C7, 0xBD4010, 0x04F2D2, 0x277549, 0xF6B6EB, 0xBB22DB, +0xAA140A, 0x2F2689, 0x768364, 0x333B09, 0x1A940E, 0xAA3A51, +0xC2A31D, 0xAEEDAF, 0x12265C, 0x4DC26D, 0x9C7A2D, 0x9756C0, +0x833F03, 0xF6F009, 0x8C402B, 0x99316D, 0x07B439, 0x15200C, +0x5BC3D8, 0xC492F5, 0x4BADC6, 0xA5CA4E, 0xCD37A7, 0x36A9E6, +0x9492AB, 0x6842DD, 0xDE6319, 0xEF8C76, 0x528B68, 0x37DBFC, +0xABA1AE, 0x3115DF, 0xA1AE00, 0xDAFB0C, 0x664D64, 0xB705ED, +0x306529, 0xBF5657, 0x3AFF47, 0xB9F96A, 0xF3BE75, 0xDF9328, +0x3080AB, 0xF68C66, 0x15CB04, 0x0622FA, 0x1DE4D9, 0xA4B33D, +0x8F1B57, 0x09CD36, 0xE9424E, 0xA4BE13, 0xB52333, 0x1AAAF0, +0xA8654F, 0xA5C1D2, 0x0F3F0B, 0xCD785B, 0x76F923, 0x048B7B, +0x721789, 0x53A6C6, 0xE26E6F, 0x00EBEF, 0x584A9B, 0xB7DAC4, +0xBA66AA, 0xCFCF76, 0x1D02D1, 0x2DF1B1, 0xC1998C, 0x77ADC3, +0xDA4886, 0xA05DF7, 0xF480C6, 0x2FF0AC, 0x9AECDD, 0xBC5C3F, +0x6DDED0, 0x1FC790, 0xB6DB2A, 0x3A25A3, 0x9AAF00, 0x9353AD, +0x0457B6, 0xB42D29, 0x7E804B, 0xA707DA, 0x0EAA76, 0xA1597B, +0x2A1216, 0x2DB7DC, 0xFDE5FA, 0xFEDB89, 0xFDBE89, 0x6C76E4, +0xFCA906, 0x70803E, 0x156E85, 0xFF87FD, 0x073E28, 0x336761, +0x86182A, 0xEABD4D, 0xAFE7B3, 0x6E6D8F, 0x396795, 0x5BBF31, +0x48D784, 0x16DF30, 0x432DC7, 0x356125, 0xCE70C9, 0xB8CB30, +0xFD6CBF, 0xA200A4, 0xE46C05, 0xA0DD5A, 0x476F21, 0xD21262, +0x845CB9, 0x496170, 0xE0566B, 0x015299, 0x375550, 0xB7D51E, +0xC4F133, 0x5F6E13, 0xE4305D, 0xA92E85, 0xC3B21D, 0x3632A1, +0xA4B708, 0xD4B1EA, 0x21F716, 0xE4698F, 0x77FF27, 0x80030C, +0x2D408D, 0xA0CD4F, 0x99A520, 0xD3A2B3, 0x0A5D2F, 0x42F9B4, +0xCBDA11, 0xD0BE7D, 0xC1DB9B, 0xBD17AB, 0x81A2CA, 0x5C6A08, +0x17552E, 0x550027, 0xF0147F, 0x8607E1, 0x640B14, 0x8D4196, +0xDEBE87, 0x2AFDDA, 0xB6256B, 0x34897B, 0xFEF305, 0x9EBFB9, +0x4F6A68, 0xA82A4A, 0x5AC44F, 0xBCF82D, 0x985AD7, 0x95C7F4, +0x8D4D0D, 0xA63A20, 0x5F57A4, 0xB13F14, 0x953880, 0x0120CC, +0x86DD71, 0xB6DEC9, 0xF560BF, 0x11654D, 0x6B0701, 0xACB08C, +0xD0C0B2, 0x485551, 0x0EFB1E, 0xC37295, 0x3B06A3, 0x3540C0, +0x7BDC06, 0xCC45E0, 0xFA294E, 0xC8CAD6, 0x41F3E8, 0xDE647C, +0xD8649B, 0x31BED9, 0xC397A4, 0xD45877, 0xC5E369, 0x13DAF0, +0x3C3ABA, 0x461846, 0x5F7555, 0xF5BDD2, 0xC6926E, 0x5D2EAC, +0xED440E, 0x423E1C, 0x87C461, 0xE9FD29, 0xF3D6E7, 0xCA7C22, +0x35916F, 0xC5E008, 0x8DD7FF, 0xE26A6E, 0xC6FDB0, 0xC10893, +0x745D7C, 0xB2AD6B, 0x9D6ECD, 0x7B723E, 0x6A11C6, 0xA9CFF7, +0xDF7329, 0xBAC9B5, 0x5100B7, 0x0DB2E2, 0x24BA74, 0x607DE5, +0x8AD874, 0x2C150D, 0x0C1881, 0x94667E, 0x162901, 0x767A9F, +0xBEFDFD, 0xEF4556, 0x367ED9, 0x13D9EC, 0xB9BA8B, 0xFC97C4, +0x27A831, 0xC36EF1, 0x36C594, 0x56A8D8, 0xB5A8B4, 0x0ECCCF, +0x2D8912, 0x34576F, 0x89562C, 0xE3CE99, 0xB920D6, 0xAA5E6B, +0x9C2A3E, 0xCC5F11, 0x4A0BFD, 0xFBF4E1, 0x6D3B8E, 0x2C86E2, +0x84D4E9, 0xA9B4FC, 0xD1EEEF, 0xC9352E, 0x61392F, 0x442138, +0xC8D91B, 0x0AFC81, 0x6A4AFB, 0xD81C2F, 0x84B453, 0x8C994E, +0xCC2254, 0xDC552A, 0xD6C6C0, 0x96190B, 0xB8701A, 0x649569, +0x605A26, 0xEE523F, 0x0F117F, 0x11B5F4, 0xF5CBFC, 0x2DBC34, +0xEEBC34, 0xCC5DE8, 0x605EDD, 0x9B8E67, 0xEF3392, 0xB817C9, +0x9B5861, 0xBC57E1, 0xC68351, 0x103ED8, 0x4871DD, 0xDD1C2D, +0xA118AF, 0x462C21, 0xD7F359, 0x987AD9, 0xC0549E, 0xFA864F, +0xFC0656, 0xAE79E5, 0x362289, 0x22AD38, 0xDC9367, 0xAAE855, +0x382682, 0x9BE7CA, 0xA40D51, 0xB13399, 0x0ED7A9, 0x480569, +0xF0B265, 0xA7887F, 0x974C88, 0x36D1F9, 0xB39221, 0x4A827B, +0x21CF98, 0xDC9F40, 0x5547DC, 0x3A74E1, 0x42EB67, 0xDF9DFE, +0x5FD45E, 0xA4677B, 0x7AACBA, 0xA2F655, 0x23882B, 0x55BA41, +0x086E59, 0x862A21, 0x834739, 0xE6E389, 0xD49EE5, 0x40FB49, +0xE956FF, 0xCA0F1C, 0x8A59C5, 0x2BFA94, 0xC5C1D3, 0xCFC50F, +0xAE5ADB, 0x86C547, 0x624385, 0x3B8621, 0x94792C, 0x876110, +0x7B4C2A, 0x1A2C80, 0x12BF43, 0x902688, 0x893C78, 0xE4C4A8, +0x7BDBE5, 0xC23AC4, 0xEAF426, 0x8A67F7, 0xBF920D, 0x2BA365, +0xB1933D, 0x0B7CBD, 0xDC51A4, 0x63DD27, 0xDDE169, 0x19949A, +0x9529A8, 0x28CE68, 0xB4ED09, 0x209F44, 0xCA984E, 0x638270, +0x237C7E, 0x32B90F, 0x8EF5A7, 0xE75614, 0x08F121, 0x2A9DB5, +0x4D7E6F, 0x5119A5, 0xABF9B5, 0xD6DF82, 0x61DD96, 0x023616, +0x9F3AC4, 0xA1A283, 0x6DED72, 0x7A8D39, 0xA9B882, 0x5C326B, +0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901, 0x8071E0, +#endif + +}; + +static const double PIo2[] = { + 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ + 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ + 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ + 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ + 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ + 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ + 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ + 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ +}; + +static const double +zero = 0.0, +one = 1.0, +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +twon24 = 5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */ + +OLM_DLLEXPORT int +__kernel_rem_pio2(double *x, double *y, int e0, int nx, int prec) +{ + int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; + double z,fw,f[20],fq[20],q[20]; + + /* initialize jk*/ + jk = init_jk[prec]; + jp = jk; + + /* determine jx,jv,q0, note that 3>q0 */ + jx = nx-1; + jv = (e0-3)/24; if(jv<0) jv=0; + q0 = e0-24*(jv+1); + + /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ + j = jv-jx; m = jx+jk; + for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (double) ipio2[j]; + + /* compute q[0],q[1],...q[jk] */ + for (i=0;i<=jk;i++) { + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; + q[i] = fw; + } + + jz = jk; +recompute: + /* distill q[] into iq[] reversingly */ + for(i=0,j=jz,z=q[jz];j>0;i++,j--) { + fw = (double)((int32_t)(twon24* z)); + iq[i] = (int32_t)(z-two24*fw); + z = q[j-1]+fw; + } + + /* compute n */ + z = scalbn(z,q0); /* actual value of z */ + z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */ + n = (int32_t) z; + z -= (double)n; + ih = 0; + if(q0>0) { /* need iq[jz-1] to determine n */ + i = (iq[jz-1]>>(24-q0)); n += i; + iq[jz-1] -= i<<(24-q0); + ih = iq[jz-1]>>(23-q0); + } + else if(q0==0) ih = iq[jz-1]>>23; + else if(z>=0.5) ih=2; + + if(ih>0) { /* q > 0.5 */ + n += 1; carry = 0; + for(i=0;i0) { /* rare case: chance is 1 in 12 */ + switch(q0) { + case 1: + iq[jz-1] &= 0x7fffff; break; + case 2: + iq[jz-1] &= 0x3fffff; break; + } + } + if(ih==2) { + z = one - z; + if(carry!=0) z -= scalbn(one,q0); + } + } + + /* check if recomputation is needed */ + if(z==zero) { + j = 0; + for (i=jz-1;i>=jk;i--) j |= iq[i]; + if(j==0) { /* need recomputation */ + for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ + + for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ + f[jx+i] = (double) ipio2[jv+i]; + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; + q[i] = fw; + } + jz += k; + goto recompute; + } + } + + /* chop off zero terms */ + if(z==0.0) { + jz -= 1; q0 -= 24; + while(iq[jz]==0) { jz--; q0-=24;} + } else { /* break z into 24-bit if necessary */ + z = scalbn(z,-q0); + if(z>=two24) { + fw = (double)((int32_t)(twon24*z)); + iq[jz] = (int32_t)(z-two24*fw); + jz += 1; q0 += 24; + iq[jz] = (int32_t) fw; + } else iq[jz] = (int32_t) z ; + } + + /* convert integer "bit" chunk to floating-point value */ + fw = scalbn(one,q0); + for(i=jz;i>=0;i--) { + q[i] = fw*(double)iq[i]; fw*=twon24; + } + + /* compute PIo2[0,...,jp]*q[jz,...,0] */ + for(i=jz;i>=0;i--) { + for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; + fq[jz-i] = fw; + } + + /* compress fq[] into y[] */ + switch(prec) { + case 0: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + break; + case 1: + case 2: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + STRICT_ASSIGN(double,fw,fw); + y[0] = (ih==0)? fw: -fw; + fw = fq[0]-fw; + for (i=1;i<=jz;i++) fw += fq[i]; + y[1] = (ih==0)? fw: -fw; + break; + case 3: /* painful */ + for (i=jz;i>0;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (i=jz;i>1;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; + if(ih==0) { + y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; + } else { + y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; + } + } + return n&7; +} diff --git a/openlibm/src/k_sin.c b/openlibm/src/k_sin.c new file mode 100644 index 0000000000..0b3de39438 --- /dev/null +++ b/openlibm/src/k_sin.c @@ -0,0 +1,71 @@ + +/* @(#)k_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_sin.c,v 1.11 2008/02/19 12:54:14 bde Exp $"); + +/* __kernel_sin( x, y, iy) + * kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). + * + * Algorithm + * 1. Since sin(-x) = -sin(x), we need only to consider positive x. + * 2. Callers must return sin(-0) = -0 without calling here since our + * odd polynomial is not evaluated in a way that preserves -0. + * Callers may do the optimization sin(x) ~ x for tiny x. + * 3. sin(x) is approximated by a polynomial of degree 13 on + * [0,pi/4] + * 3 13 + * sin(x) ~ x + S1*x + ... + S6*x + * where + * + * |sin(x) 2 4 6 8 10 12 | -58 + * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 + * | x | + * + * 4. sin(x+y) = sin(x) + sin'(x')*y + * ~ sin(x) + (1-x*x/2)*y + * For better accuracy, let + * 3 2 2 2 2 + * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) + * then 3 2 + * sin(x) = x + (S1*x + (x *(r-y/2)+y)) + */ + +#include + +#include "math_private.h" + +static const double +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ +S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ +S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ +S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ +S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ +S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ + +OLM_DLLEXPORT double +__kernel_sin(double x, double y, int iy) +{ + double z,r,v,w; + + z = x*x; + w = z*z; + r = S2+z*(S3+z*S4) + z*w*(S5+z*S6); + v = z*x; + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} diff --git a/openlibm/src/k_sinf.c b/openlibm/src/k_sinf.c new file mode 100644 index 0000000000..15c8e03175 --- /dev/null +++ b/openlibm/src/k_sinf.c @@ -0,0 +1,48 @@ +/* k_sinf.c -- float version of k_sin.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#ifndef INLINE_KERNEL_SINDF +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_sinf.c,v 1.16 2009/06/03 08:16:34 ed Exp $"); +#endif + +#include + +#include "math_private.h" + +/* |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]). */ +static const double +S1 = -0x15555554cbac77.0p-55, /* -0.166666666416265235595 */ +S2 = 0x111110896efbb2.0p-59, /* 0.0083333293858894631756 */ +S3 = -0x1a00f9e2cae774.0p-65, /* -0.000198393348360966317347 */ +S4 = 0x16cd878c3b46a7.0p-71; /* 0.0000027183114939898219064 */ + +#ifndef INLINE_KERNEL_SINDF +extern +#endif +//__inline float +OLM_DLLEXPORT float +__kernel_sindf(double x) +{ + double r, s, w, z; + + /* Try to optimize for parallel evaluation as in k_tanf.c. */ + z = x*x; + w = z*z; + r = S3+z*S4; + s = z*x; + return (x + s*(S1+z*S2)) + s*w*r; +} diff --git a/openlibm/src/k_tan.c b/openlibm/src/k_tan.c new file mode 100644 index 0000000000..41f754ba0c --- /dev/null +++ b/openlibm/src/k_tan.c @@ -0,0 +1,134 @@ +/* @(#)k_tan.c 1.5 04/04/22 SMI */ + +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* INDENT OFF */ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_tan.c,v 1.13 2008/02/22 02:30:35 das Exp $"); + +/* __kernel_tan( x, y, k ) + * kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned. + * + * Algorithm + * 1. Since tan(-x) = -tan(x), we need only to consider positive x. + * 2. Callers must return tan(-0) = -0 without calling here since our + * odd polynomial is not evaluated in a way that preserves -0. + * Callers may do the optimization tan(x) ~ x for tiny x. + * 3. tan(x) is approximated by a odd polynomial of degree 27 on + * [0,0.67434] + * 3 27 + * tan(x) ~ x + T1*x + ... + T13*x + * where + * + * |tan(x) 2 4 26 | -59.2 + * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 + * | x | + * + * Note: tan(x+y) = tan(x) + tan'(x)*y + * ~ tan(x) + (1+x*x)*y + * Therefore, for better accuracy in computing tan(x+y), let + * 3 2 2 2 2 + * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) + * then + * 3 2 + * tan(x+y) = x + (T1*x + (x *(r+y)+y)) + * + * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then + * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) + * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + */ + +#include + +#include "math_private.h" + +static const double xxx[] = { + 3.33333333333334091986e-01, /* 3FD55555, 55555563 */ + 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */ + 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */ + 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */ + 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */ + 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */ + 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */ + 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */ + 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */ + 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */ + 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */ + -1.85586374855275456654e-05, /* BEF375CB, DB605373 */ + 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */ +/* one */ 1.00000000000000000000e+00, /* 3FF00000, 00000000 */ +/* pio4 */ 7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */ +/* pio4lo */ 3.06161699786838301793e-17 /* 3C81A626, 33145C07 */ +}; +#define one xxx[13] +#define pio4 xxx[14] +#define pio4lo xxx[15] +#define T xxx +/* INDENT ON */ + +double +__kernel_tan(double x, double y, int iy) { + double z, r, v, w, s; + int32_t ix, hx; + + GET_HIGH_WORD(hx,x); + ix = hx & 0x7fffffff; /* high word of |x| */ + if (ix >= 0x3FE59428) { /* |x| >= 0.6744 */ + if (hx < 0) { + x = -x; + y = -y; + } + z = pio4 - x; + w = pio4lo - y; + x = z + w; + y = 0.0; + } + z = x * x; + w = z * z; + /* + * Break x^5*(T[1]+x^2*T[2]+...) into + * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + + * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) + */ + r = T[1] + w * (T[3] + w * (T[5] + w * (T[7] + w * (T[9] + + w * T[11])))); + v = z * (T[2] + w * (T[4] + w * (T[6] + w * (T[8] + w * (T[10] + + w * T[12]))))); + s = z * x; + r = y + z * (s * (r + v) + y); + r += T[0] * s; + w = x + r; + if (ix >= 0x3FE59428) { + v = (double) iy; + return (double) (1 - ((hx >> 30) & 2)) * + (v - 2.0 * (x - (w * w / (w + v) - r))); + } + if (iy == 1) + return w; + else { + /* + * if allow error up to 2 ulp, simply return + * -1.0 / (x+r) here + */ + /* compute -1.0 / (x+r) accurately */ + double a, t; + z = w; + SET_LOW_WORD(z,0); + v = r - (z - x); /* z+v = r+x */ + t = a = -1.0 / w; /* a = -1.0/w */ + SET_LOW_WORD(t,0); + s = 1.0 + t * z; + return t + a * (s + t * v); + } +} diff --git a/openlibm/src/k_tanf.c b/openlibm/src/k_tanf.c new file mode 100644 index 0000000000..9c17d1b2e2 --- /dev/null +++ b/openlibm/src/k_tanf.c @@ -0,0 +1,68 @@ +/* k_tanf.c -- float version of k_tan.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#ifndef INLINE_KERNEL_TANDF +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/k_tanf.c,v 1.23 2009/06/03 08:16:34 ed Exp $"); +#endif + +#include + +#include "math_private.h" + +/* |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]). */ +static const double +T[] = { + 0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */ + 0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */ + 0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */ + 0x191df3908c33ce.0p-58, /* 0.0245283181166547278873 */ + 0x185dadfcecf44e.0p-61, /* 0.00297435743359967304927 */ + 0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */ +}; + +#ifndef INLINE_KERNEL_TANDF +extern +#endif +//__inline float +OLM_DLLEXPORT float +__kernel_tandf(double x, int iy) +{ + double z,r,w,s,t,u; + + z = x*x; + /* + * Split up the polynomial into small independent terms to give + * opportunities for parallel evaluation. The chosen splitting is + * micro-optimized for Athlons (XP, X64). It costs 2 multiplications + * relative to Horner's method on sequential machines. + * + * We add the small terms from lowest degree up for efficiency on + * non-sequential machines (the lowest degree terms tend to be ready + * earlier). Apart from this, we don't care about order of + * operations, and don't need to to care since we have precision to + * spare. However, the chosen splitting is good for accuracy too, + * and would give results as accurate as Horner's method if the + * small terms were added from highest degree down. + */ + r = T[4]+z*T[5]; + t = T[2]+z*T[3]; + w = z*z; + s = z*x; + u = T[0]+z*T[1]; + r = (x+s*u)+(s*w)*(t+w*r); + if(iy==1) return r; + else return -1.0/r; +} diff --git a/openlibm/src/loongarch64_fpmath.h b/openlibm/src/loongarch64_fpmath.h new file mode 100644 index 0000000000..31ea131004 --- /dev/null +++ b/openlibm/src/loongarch64_fpmath.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2023 Yifan An + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +union IEEEl2bits { + long double e; + struct { + uint64_t manl :64; + uint64_t manh :48; + unsigned int exp :15; + unsigned int sign :1; + } bits; + struct { + uint64_t manl :64; + uint64_t manh :48; + unsigned int expsign :16; + } xbits; +}; + +#define LDBL_NBIT 0 +#define LDBL_IMPLICIT_NBIT +#define mask_nbit_l(u) ((void)0) + +#define LDBL_MANH_SIZE 48 +#define LDBL_MANL_SIZE 64 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)((u).bits.manl >> 32); \ + (a)[2] = (uint32_t)(u).bits.manh; \ + (a)[3] = (uint32_t)((u).bits.manh >> 32); \ +} while(0) diff --git a/openlibm/src/math_private.h b/openlibm/src/math_private.h new file mode 100644 index 0000000000..15a27b2845 --- /dev/null +++ b/openlibm/src/math_private.h @@ -0,0 +1,375 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * from: @(#)fdlibm.h 5.1 93/09/24 + * $FreeBSD: src/lib/msun/src/math_private.h,v 1.34 2011/10/21 06:27:56 das Exp $ + */ + +#ifndef _MATH_PRIVATE_H_ +#define _MATH_PRIVATE_H_ + +#include +#include +#include "cdefs-compat.h" +#include "types-compat.h" +#include "fpmath.h" +#include +#include "math_private_openbsd.h" + +/* + * The original fdlibm code used statements like: + * n0 = ((*(int*)&one)>>29)^1; * index of high word * + * ix0 = *(n0+(int*)&x); * high word of x * + * ix1 = *((1-n0)+(int*)&x); * low word of x * + * to dig two 32 bit words out of the 64 bit IEEE floating point + * value. That is non-ANSI, and, moreover, the gcc instruction + * scheduler gets it wrong. We instead use the following macros. + * Unlike the original code, we determine the endianness at compile + * time, not at run time; I don't see much benefit to selecting + * endianness at run time. + */ + +/* + * A union which permits us to convert between a double and two 32 bit + * ints. + */ + +#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__ + +typedef union +{ + double value; + struct + { + u_int32_t msw; + u_int32_t lsw; + } parts; + struct + { + u_int64_t w; + } xparts; +} ieee_double_shape_type; + +#endif + +#if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__ + +typedef union +{ + double value; + struct + { + u_int32_t lsw; + u_int32_t msw; + } parts; + struct + { + u_int64_t w; + } xparts; +} ieee_double_shape_type; + +#endif + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS(ix0,ix1,d) \ +do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ +} while (0) + +/* Get a 64-bit int from a double. */ +#define EXTRACT_WORD64(ix,d) \ +do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix) = ew_u.xparts.w; \ +} while (0) + +/* Get the more significant 32 bit int from a double. */ + +#define GET_HIGH_WORD(i,d) \ +do { \ + ieee_double_shape_type gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ +} while (0) + +/* Get the less significant 32 bit int from a double. */ + +#define GET_LOW_WORD(i,d) \ +do { \ + ieee_double_shape_type gl_u; \ + gl_u.value = (d); \ + (i) = gl_u.parts.lsw; \ +} while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS(d,ix0,ix1) \ +do { \ + ieee_double_shape_type iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ +} while (0) + +/* Set a double from a 64-bit int. */ +#define INSERT_WORD64(d,ix) \ +do { \ + ieee_double_shape_type iw_u; \ + iw_u.xparts.w = (ix); \ + (d) = iw_u.value; \ +} while (0) + +/* Set the more significant 32 bits of a double from an int. */ + +#define SET_HIGH_WORD(d,v) \ +do { \ + ieee_double_shape_type sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ +} while (0) + +/* Set the less significant 32 bits of a double from an int. */ + +#define SET_LOW_WORD(d,v) \ +do { \ + ieee_double_shape_type sl_u; \ + sl_u.value = (d); \ + sl_u.parts.lsw = (v); \ + (d) = sl_u.value; \ +} while (0) + +/* + * A union which permits us to convert between a float and a 32 bit + * int. + */ + +typedef union +{ + float value; + /* FIXME: Assumes 32 bit int. */ + unsigned int word; +} ieee_float_shape_type; + +/* Get a 32 bit int from a float. */ + +#define GET_FLOAT_WORD(i,d) \ +do { \ + ieee_float_shape_type gf_u; \ + gf_u.value = (d); \ + (i) = gf_u.word; \ +} while (0) + +/* Set a float from a 32 bit int. */ + +#define SET_FLOAT_WORD(d,i) \ +do { \ + ieee_float_shape_type sf_u; \ + sf_u.word = (i); \ + (d) = sf_u.value; \ +} while (0) + +/* Get expsign as a 16 bit int from a long double. */ + +#define GET_LDBL_EXPSIGN(i,d) \ +do { \ + union IEEEl2bits ge_u; \ + ge_u.e = (d); \ + (i) = ge_u.xbits.expsign; \ +} while (0) + +/* Set expsign of a long double from a 16 bit int. */ + +#define SET_LDBL_EXPSIGN(d,v) \ +do { \ + union IEEEl2bits se_u; \ + se_u.e = (d); \ + se_u.xbits.expsign = (v); \ + (d) = se_u.e; \ +} while (0) + + +#ifndef __FreeBSD__ +#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval)) +#else +#ifdef FLT_EVAL_METHOD +// Attempt to get strict C99 semantics for assignment with non-C99 compilers. +#if FLT_EVAL_METHOD == 0 || __GNUC__ == 0 +#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval)) +#else +#define STRICT_ASSIGN(type, lval, rval) do { \ + volatile type __lval; \ + \ + if (sizeof(type) >= sizeof(long double)) \ + (lval) = (rval); \ + else { \ + __lval = (rval); \ + (lval) = __lval; \ + } \ +} while (0) +#endif +#endif +#endif + +/* + * Common routine to process the arguments to nan(), nanf(), and nanl(). + */ +void __scan_nan(u_int32_t *__words, int __num_words, const char *__s); + +/* + * Mix 1 or 2 NaNs. First add 0 to each arg. This normally just turns + * signaling NaNs into quiet NaNs by setting a quiet bit. We do this + * because we want to never return a signaling NaN, and also because we + * don't want the quiet bit to affect the result. Then mix the converted + * args using addition. The result is typically the arg whose mantissa + * bits (considered as in integer) are largest. + * + * Technical complications: the result in bits might depend on the precision + * and/or on compiler optimizations, especially when different register sets + * are used for different precisions. Try to make the result not depend on + * at least the precision by always doing the main mixing step in long double + * precision. Try to reduce dependencies on optimizations by adding the + * the 0's in different precisions (unless everything is in long double + * precision). + */ +#define nan_mix(x, y) (((x) + 0.0L) + ((y) + 0)) + +#ifdef __GNUCLIKE_ASM + +/* Asm versions of some functions. */ + +#ifdef __amd64__ +static __inline int +irint(double x) +{ + int n; + + __asm__("cvtsd2si %1,%0" : "=r" (n) : "x" (x)); + return (n); +} +#define HAVE_EFFICIENT_IRINT +#endif + +#ifdef __i386__ +static __inline int +irint(double x) +{ + int n; + + __asm__("fistl %0" : "=m" (n) : "t" (x)); + return (n); +} +#define HAVE_EFFICIENT_IRINT +#endif + +#endif /* __GNUCLIKE_ASM */ + +/* + * ieee style elementary functions + * + * We rename functions here to improve other sources' diffability + * against fdlibm. + */ +#define __ieee754_sqrt sqrt +#define __ieee754_acos acos +#define __ieee754_acosh acosh +#define __ieee754_log log +#define __ieee754_log2 log2 +#define __ieee754_atanh atanh +#define __ieee754_asin asin +#define __ieee754_atan2 atan2 +#define __ieee754_exp exp +#define __ieee754_cosh cosh +#define __ieee754_fmod fmod +#define __ieee754_pow pow +#define __ieee754_lgamma lgamma +#define __ieee754_lgamma_r lgamma_r +#define __ieee754_log10 log10 +#define __ieee754_sinh sinh +#define __ieee754_hypot hypot +#define __ieee754_j0 j0 +#define __ieee754_j1 j1 +#define __ieee754_y0 y0 +#define __ieee754_y1 y1 +#define __ieee754_jn jn +#define __ieee754_yn yn +#define __ieee754_remainder remainder +#define __ieee754_sqrtf sqrtf +#define __ieee754_acosf acosf +#define __ieee754_acoshf acoshf +#define __ieee754_logf logf +#define __ieee754_atanhf atanhf +#define __ieee754_asinf asinf +#define __ieee754_atan2f atan2f +#define __ieee754_expf expf +#define __ieee754_coshf coshf +#define __ieee754_fmodf fmodf +#define __ieee754_powf powf +#define __ieee754_lgammaf lgammaf +#define __ieee754_lgammaf_r lgammaf_r +#define __ieee754_log10f log10f +#define __ieee754_log2f log2f +#define __ieee754_sinhf sinhf +#define __ieee754_hypotf hypotf +#define __ieee754_j0f j0f +#define __ieee754_j1f j1f +#define __ieee754_y0f y0f +#define __ieee754_y1f y1f +#define __ieee754_jnf jnf +#define __ieee754_ynf ynf +#define __ieee754_remainderf remainderf + +/* fdlibm kernel function */ +int __kernel_rem_pio2(double*,double*,int,int,int); + +/* double precision kernel functions */ +#ifdef INLINE_REM_PIO2 +__inline +#endif +int __ieee754_rem_pio2(double,double*); +double __kernel_sin(double,double,int); +double __kernel_cos(double,double); +double __kernel_tan(double,double,int); +double __ldexp_exp(double,int); +double complex __ldexp_cexp(double complex,int); + +/* float precision kernel functions */ +#ifdef INLINE_REM_PIO2F +__inline +#endif +int __ieee754_rem_pio2f(float,double*); +#ifdef INLINE_KERNEL_SINDF +__inline +#endif +float __kernel_sindf(double); +#ifdef INLINE_KERNEL_COSDF +__inline +#endif +float __kernel_cosdf(double); +#ifdef INLINE_KERNEL_TANDF +__inline +#endif +float __kernel_tandf(double,int); +float __ldexp_expf(float,int); +float complex __ldexp_cexpf(float complex,int); + +/* long double precision kernel functions */ +long double __kernel_sinl(long double, long double, int); +long double __kernel_cosl(long double, long double); +long double __kernel_tanl(long double, long double, int); + +#endif /* !_MATH_PRIVATE_H_ */ diff --git a/openlibm/src/math_private_openbsd.h b/openlibm/src/math_private_openbsd.h new file mode 100644 index 0000000000..021670f27c --- /dev/null +++ b/openlibm/src/math_private_openbsd.h @@ -0,0 +1,218 @@ +/* $OpenBSD: math_private.h,v 1.17 2014/06/02 19:31:17 kettenis Exp $ */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * from: @(#)fdlibm.h 5.1 93/09/24 + */ + +#ifndef _MATH_PRIVATE_OPENBSD_H_ +#define _MATH_PRIVATE_OPENBSD_H_ + +#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__ + +typedef union +{ + long double value; + struct { + u_int32_t mswhi; + u_int32_t mswlo; + u_int32_t lswhi; + u_int32_t lswlo; + } parts32; + struct { + u_int64_t msw; + u_int64_t lsw; + } parts64; +} ieee_quad_shape_type; + +#endif + +#if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__ + +typedef union +{ + long double value; + struct { + u_int32_t lswlo; + u_int32_t lswhi; + u_int32_t mswlo; + u_int32_t mswhi; + } parts32; + struct { + u_int64_t lsw; + u_int64_t msw; + } parts64; +} ieee_quad_shape_type; + +#endif + +/* Get two 64 bit ints from a long double. */ + +#define GET_LDOUBLE_WORDS64(ix0,ix1,d) \ +do { \ + ieee_quad_shape_type qw_u; \ + qw_u.value = (d); \ + (ix0) = qw_u.parts64.msw; \ + (ix1) = qw_u.parts64.lsw; \ +} while (0) + +/* Set a long double from two 64 bit ints. */ + +#define SET_LDOUBLE_WORDS64(d,ix0,ix1) \ +do { \ + ieee_quad_shape_type qw_u; \ + qw_u.parts64.msw = (ix0); \ + qw_u.parts64.lsw = (ix1); \ + (d) = qw_u.value; \ +} while (0) + +/* Get the more significant 64 bits of a long double mantissa. */ + +#define GET_LDOUBLE_MSW64(v,d) \ +do { \ + ieee_quad_shape_type sh_u; \ + sh_u.value = (d); \ + (v) = sh_u.parts64.msw; \ +} while (0) + +/* Set the more significant 64 bits of a long double mantissa from an int. */ + +#define SET_LDOUBLE_MSW64(d,v) \ +do { \ + ieee_quad_shape_type sh_u; \ + sh_u.value = (d); \ + sh_u.parts64.msw = (v); \ + (d) = sh_u.value; \ +} while (0) + +/* Get the least significant 64 bits of a long double mantissa. */ + +#define GET_LDOUBLE_LSW64(v,d) \ +do { \ + ieee_quad_shape_type sh_u; \ + sh_u.value = (d); \ + (v) = sh_u.parts64.lsw; \ +} while (0) + +/* A union which permits us to convert between a long double and + three 32 bit ints. */ + +#if __FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__ + +typedef union +{ + long double value; + struct { +#ifdef __LP64__ + int padh:32; +#endif + int exp:16; + int padl:16; + u_int32_t msw; + u_int32_t lsw; + } parts; +} ieee_extended_shape_type; + +#endif + +#if __FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__ + +typedef union +{ + long double value; + struct { + u_int32_t lsw; + u_int32_t msw; + int exp:16; + int padl:16; +#ifdef __LP64__ + int padh:32; +#endif + } parts; +} ieee_extended_shape_type; + +#endif + +/* Get three 32 bit ints from a double. */ + +#define GET_LDOUBLE_WORDS(se,ix0,ix1,d) \ +do { \ + ieee_extended_shape_type ew_u; \ + ew_u.value = (d); \ + (se) = ew_u.parts.exp; \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ +} while (0) + +/* Set a double from two 32 bit ints. */ + +#define SET_LDOUBLE_WORDS(d,se,ix0,ix1) \ +do { \ + ieee_extended_shape_type iw_u; \ + iw_u.parts.exp = (se); \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ +} while (0) + +/* Get the more significant 32 bits of a long double mantissa. */ + +#define GET_LDOUBLE_MSW(v,d) \ +do { \ + ieee_extended_shape_type sh_u; \ + sh_u.value = (d); \ + (v) = sh_u.parts.msw; \ +} while (0) + +/* Set the more significant 32 bits of a long double mantissa from an int. */ + +#define SET_LDOUBLE_MSW(d,v) \ +do { \ + ieee_extended_shape_type sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ +} while (0) + +/* Get int from the exponent of a long double. */ + +#define GET_LDOUBLE_EXP(se,d) \ +do { \ + ieee_extended_shape_type ge_u; \ + ge_u.value = (d); \ + (se) = ge_u.parts.exp; \ +} while (0) + +/* Set exponent of a long double from an int. */ + +#define SET_LDOUBLE_EXP(d,se) \ +do { \ + ieee_extended_shape_type se_u; \ + se_u.value = (d); \ + se_u.parts.exp = (se); \ + (d) = se_u.value; \ +} while (0) + +/* + * Common routine to process the arguments to nan(), nanf(), and nanl(). + */ +void __scan_nan(uint32_t *__words, int __num_words, const char *__s); + +/* + * Functions internal to the math package, yet not static. + */ +double __exp__D(double, double); +struct Double __log__D(double); +long double __p1evll(long double, void *, int); +long double __polevll(long double, void *, int); + +#endif /* _MATH_PRIVATE_OPENBSD_H_ */ diff --git a/openlibm/src/mips_fpmath.h b/openlibm/src/mips_fpmath.h new file mode 100644 index 0000000000..50f119f39f --- /dev/null +++ b/openlibm/src/mips_fpmath.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2002, 2003 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +union IEEEl2bits { + long double e; + struct { +#ifndef __MIPSEB__ + unsigned int manl :32; + unsigned int manh :20; + unsigned int exp :11; + unsigned int sign :1; +#else + unsigned int sign :1; + unsigned int exp :11; + unsigned int manh :20; + unsigned int manl :32; +#endif + } bits; +}; + +#define LDBL_NBIT 0 +#define mask_nbit_l(u) ((void)0) +#define LDBL_IMPLICIT_NBIT + +#define LDBL_MANH_SIZE 20 +#define LDBL_MANL_SIZE 32 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)(u).bits.manh; \ +} while(0) + diff --git a/openlibm/src/polevll.c b/openlibm/src/polevll.c new file mode 100644 index 0000000000..1c785d8870 --- /dev/null +++ b/openlibm/src/polevll.c @@ -0,0 +1,104 @@ +/* $OpenBSD: polevll.c,v 1.2 2013/11/12 20:35:09 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* polevll.c + * p1evll.c + * + * Evaluate polynomial + * + * + * + * SYNOPSIS: + * + * int N; + * long double x, y, coef[N+1], polevl[]; + * + * y = polevll( x, coef, N ); + * + * + * + * DESCRIPTION: + * + * Evaluates polynomial of degree N: + * + * 2 N + * y = C + C x + C x +...+ C x + * 0 1 2 N + * + * Coefficients are stored in reverse order: + * + * coef[0] = C , ..., coef[N] = C . + * N 0 + * + * The function p1evll() assumes that coef[N] = 1.0 and is + * omitted from the array. Its calling arguments are + * otherwise the same as polevll(). + * + * + * SPEED: + * + * In the interest of speed, there are no checks for out + * of bounds arithmetic. This routine is used by most of + * the functions in the library. Depending on available + * equipment features, the user may wish to rewrite the + * program in microcode or assembly language. + * + */ + +#include + +#include "math_private.h" + +/* + * Polynomial evaluator: + * P[0] x^n + P[1] x^(n-1) + ... + P[n] + */ +long double +__polevll(long double x, void *PP, int n) +{ + long double y; + long double *P; + + P = (long double *)PP; + y = *P++; + do { + y = y * x + *P++; + } while (--n); + + return (y); +} + +/* + * Polynomial evaluator: + * x^n + P[0] x^(n-1) + P[1] x^(n-2) + ... + P[n] + */ +long double +__p1evll(long double x, void *PP, int n) +{ + long double y; + long double *P; + + P = (long double *)PP; + n -= 1; + y = x + *P++; + do { + y = y * x + *P++; + } while (--n); + + return (y); +} diff --git a/openlibm/src/powerpc_fpmath.h b/openlibm/src/powerpc_fpmath.h new file mode 100644 index 0000000000..6d80eb4bdf --- /dev/null +++ b/openlibm/src/powerpc_fpmath.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2003 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +union IEEEl2bits { + long double e; + struct { + unsigned int sign :1; + unsigned int exp :11; + unsigned int manh :20; + unsigned int manl :32; + } bits; +}; + +#define mask_nbit_l(u) ((void)0) +#define LDBL_IMPLICIT_NBIT +#define LDBL_NBIT 0 + +#define LDBL_MANH_SIZE 20 +#define LDBL_MANL_SIZE 32 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)(u).bits.manh; \ +} while(0) diff --git a/openlibm/src/riscv_fpmath.h b/openlibm/src/riscv_fpmath.h new file mode 100644 index 0000000000..cfa85ff244 --- /dev/null +++ b/openlibm/src/riscv_fpmath.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 2002, 2003 David Schultz + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/lib/libc/riscv/_fpmath.h 362788 2020-06-29 19:30:35Z mhorne $ + */ + +union IEEEl2bits { + long double e; + struct { + unsigned long manl :64; + unsigned long manh :48; + unsigned int exp :15; + unsigned int sign :1; + } bits; + struct { + unsigned long manl :64; + unsigned long manh :48; + unsigned int expsign :16; + } xbits; +}; + +#define LDBL_NBIT 0 +#define LDBL_IMPLICIT_NBIT +#define mask_nbit_l(u) ((void)0) + +#define LDBL_MANH_SIZE 48 +#define LDBL_MANL_SIZE 64 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)((u).bits.manl >> 32); \ + (a)[2] = (uint32_t)(u).bits.manh; \ + (a)[3] = (uint32_t)((u).bits.manh >> 32); \ +} while(0) + diff --git a/openlibm/src/s390_fpmath.h b/openlibm/src/s390_fpmath.h new file mode 100644 index 0000000000..1e7787c03e --- /dev/null +++ b/openlibm/src/s390_fpmath.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2016 Dan Horák + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * cloned from powerpc_fpmath.h + */ + +union IEEEl2bits { + long double e; + struct { + unsigned int sign :1; + unsigned int exp :11; + unsigned int manh :20; + unsigned int manl :32; + } bits; +}; + +#define mask_nbit_l(u) ((void)0) +#define LDBL_IMPLICIT_NBIT +#define LDBL_NBIT 0 + +#define LDBL_MANH_SIZE 20 +#define LDBL_MANL_SIZE 32 + +#define LDBL_TO_ARRAY32(u, a) do { \ + (a)[0] = (uint32_t)(u).bits.manl; \ + (a)[1] = (uint32_t)(u).bits.manh; \ +} while(0) diff --git a/openlibm/src/s_asinh.c b/openlibm/src/s_asinh.c new file mode 100644 index 0000000000..ca7937c334 --- /dev/null +++ b/openlibm/src/s_asinh.c @@ -0,0 +1,62 @@ +/* @(#)s_asinh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_asinh.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +/* asinh(x) + * Method : + * Based on + * asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] + * we have + * asinh(x) := x if 1+x*x=1, + * := sign(x)*(log(x)+ln2)) for large |x|, else + * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else + * := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) + */ + +#include +#include + +#include "math_private.h" + +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +ln2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +huge= 1.00000000000000000000e+300; + +OLM_DLLEXPORT double +asinh(double x) +{ + double t,w; + int32_t hx,ix; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return x+x; /* x is inf or NaN */ + if(ix< 0x3e300000) { /* |x|<2**-28 */ + if(huge+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x41b00000) { /* |x| > 2**28 */ + w = __ieee754_log(fabs(x))+ln2; + } else if (ix>0x40000000) { /* 2**28 > |x| > 2.0 */ + t = fabs(x); + w = __ieee754_log(2.0*t+one/(__ieee754_sqrt(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =log1p(fabs(x)+t/(one+__ieee754_sqrt(one+t))); + } + if(hx>0) return w; else return -w; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(asinh, asinhl); +#endif diff --git a/openlibm/src/s_asinhf.c b/openlibm/src/s_asinhf.c new file mode 100644 index 0000000000..c9d369401c --- /dev/null +++ b/openlibm/src/s_asinhf.c @@ -0,0 +1,49 @@ +/* s_asinhf.c -- float version of s_asinh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_asinhf.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3F800000 */ +ln2 = 6.9314718246e-01, /* 0x3f317218 */ +huge= 1.0000000000e+30; + +OLM_DLLEXPORT float +asinhf(float x) +{ + float t,w; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) return x+x; /* x is inf or NaN */ + if(ix< 0x31800000) { /* |x|<2**-28 */ + if(huge+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x4d800000) { /* |x| > 2**28 */ + w = __ieee754_logf(fabsf(x))+ln2; + } else if (ix>0x40000000) { /* 2**28 > |x| > 2.0 */ + t = fabsf(x); + w = __ieee754_logf((float)2.0*t+one/(__ieee754_sqrtf(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =log1pf(fabsf(x)+t/(one+__ieee754_sqrtf(one+t))); + } + if(hx>0) return w; else return -w; +} diff --git a/openlibm/src/s_atan.c b/openlibm/src/s_atan.c new file mode 100644 index 0000000000..23a29490b5 --- /dev/null +++ b/openlibm/src/s_atan.c @@ -0,0 +1,124 @@ +/* @(#)s_atan.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_atan.c,v 1.13 2011/02/10 07:37:50 das Exp $"); + +/* atan(x) + * Method + * 1. Reduce x to positive by atan(x) = -atan(-x). + * 2. According to the integer k=4t+0.25 chopped, t=x, the argument + * is further reduced to one of the following intervals and the + * arctangent of t is evaluated by the corresponding formula: + * + * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) + * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) + * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) + * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) + * [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static const double atanhi[] = { + 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ + 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ + 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ + 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ +}; + +static const double atanlo[] = { + 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ + 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ + 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ + 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ +}; + +static const double aT[] = { + 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ + -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ + 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ + -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ + 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ + -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ + 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ + -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ + 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ + -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ + 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ +}; + + static const double +one = 1.0, +huge = 1.0e300; + +OLM_DLLEXPORT double +atan(double x) +{ + double w,s1,s2,z; + int32_t ix,hx,id; + + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x44100000) { /* if |x| >= 2^66 */ + u_int32_t low; + GET_LOW_WORD(low,x); + if(ix>0x7ff00000|| + (ix==0x7ff00000&&(low!=0))) + return x+x; /* NaN */ + if(hx>0) return atanhi[3]+*(volatile double *)&atanlo[3]; + else return -atanhi[3]-*(volatile double *)&atanlo[3]; + } if (ix < 0x3fdc0000) { /* |x| < 0.4375 */ + if (ix < 0x3e400000) { /* |x| < 2^-27 */ + if(huge+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fabs(x); + if (ix < 0x3ff30000) { /* |x| < 1.1875 */ + if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */ + id = 0; x = (2.0*x-one)/(2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (ix < 0x40038000) { /* |x| < 2.4375 */ + id = 2; x = (x-1.5)/(one+1.5*x); + } else { /* 2.4375 <= |x| < 2^66 */ + id = 3; x = -1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10]))))); + s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9])))); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (hx<0)? -z:z; + } +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(atan, atanl); +#endif diff --git a/openlibm/src/s_atanf.c b/openlibm/src/s_atanf.c new file mode 100644 index 0000000000..cdba6a3b5f --- /dev/null +++ b/openlibm/src/s_atanf.c @@ -0,0 +1,93 @@ +/* s_atanf.c -- float version of s_atan.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_atanf.c,v 1.10 2008/08/01 01:24:25 das Exp $"); + +#include + +#include "math_private.h" + +static const float atanhi[] = { + 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */ + 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */ + 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */ + 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */ +}; + +static const float atanlo[] = { + 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */ + 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */ + 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */ + 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */ +}; + +static const float aT[] = { + 3.3333328366e-01, + -1.9999158382e-01, + 1.4253635705e-01, + -1.0648017377e-01, + 6.1687607318e-02, +}; + +static const float +one = 1.0, +huge = 1.0e30; + +OLM_DLLEXPORT float +atanf(float x) +{ + float w,s1,s2,z; + int32_t ix,hx,id; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x4c800000) { /* if |x| >= 2**26 */ + if(ix>0x7f800000) + return x+x; /* NaN */ + if(hx>0) return atanhi[3]+*(volatile float *)&atanlo[3]; + else return -atanhi[3]-*(volatile float *)&atanlo[3]; + } if (ix < 0x3ee00000) { /* |x| < 0.4375 */ + if (ix < 0x39800000) { /* |x| < 2**-12 */ + if(huge+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fabsf(x); + if (ix < 0x3f980000) { /* |x| < 1.1875 */ + if (ix < 0x3f300000) { /* 7/16 <=|x|<11/16 */ + id = 0; x = ((float)2.0*x-one)/((float)2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (ix < 0x401c0000) { /* |x| < 2.4375 */ + id = 2; x = (x-(float)1.5)/(one+(float)1.5*x); + } else { /* 2.4375 <= |x| < 2**26 */ + id = 3; x = -(float)1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*(aT[0]+w*(aT[2]+w*aT[4])); + s2 = w*(aT[1]+w*aT[3]); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (hx<0)? -z:z; + } +} diff --git a/openlibm/src/s_atanl.c b/openlibm/src/s_atanl.c new file mode 100644 index 0000000000..4edfa5a38a --- /dev/null +++ b/openlibm/src/s_atanl.c @@ -0,0 +1,85 @@ +/* @(#)s_atan.c 5.1 93/09/24 */ +/* FreeBSD: head/lib/msun/src/s_atan.c 176451 2008-02-22 02:30:36Z das */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_atanl.c,v 1.1 2008/07/31 22:41:26 das Exp $"); + +/* + * See comments in s_atan.c. + * Converted to long double by David Schultz . + */ + +#include +#include + +#include "invtrig.h" +#include "math_private.h" + +static const long double +one = 1.0, +huge = 1.0e300; + +OLM_DLLEXPORT long double +atanl(long double x) +{ + union IEEEl2bits u; + long double w,s1,s2,z; + int id; + int16_t expsign, expt; + int32_t expman; + + u.e = x; + expsign = u.xbits.expsign; + expt = expsign & 0x7fff; + if(expt >= ATAN_CONST) { /* if |x| is large, atan(x)~=pi/2 */ + if(expt == BIAS + LDBL_MAX_EXP && + ((u.bits.manh&~LDBL_NBIT)|u.bits.manl)!=0) + return x+x; /* NaN */ + if(expsign>0) return atanhi[3]+atanlo[3]; + else return -atanhi[3]-atanlo[3]; + } + /* Extract the exponent and the first few bits of the mantissa. */ + /* XXX There should be a more convenient way to do this. */ + expman = (expt << 8) | ((u.bits.manh >> (MANH_SIZE - 9)) & 0xff); + if (expman < ((BIAS - 2) << 8) + 0xc0) { /* |x| < 0.4375 */ + if (expt < ATAN_LINEAR) { /* if |x| is small, atanl(x)~=x */ + if(huge+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fabsl(x); + if (expman < (BIAS << 8) + 0x30) { /* |x| < 1.1875 */ + if (expman < ((BIAS - 1) << 8) + 0x60) { /* 7/16 <=|x|<11/16 */ + id = 0; x = (2.0*x-one)/(2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (expman < ((BIAS + 1) << 8) + 0x38) { /* |x| < 2.4375 */ + id = 2; x = (x-1.5)/(one+1.5*x); + } else { /* 2.4375 <= |x| < 2^ATAN_CONST */ + id = 3; x = -1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum aT[i]z**(i+1) into odd and even poly */ + s1 = z*T_even(w); + s2 = w*T_odd(w); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (expsign<0)? -z:z; + } +} diff --git a/openlibm/src/s_cabs.c b/openlibm/src/s_cabs.c new file mode 100644 index 0000000000..481632aee0 --- /dev/null +++ b/openlibm/src/s_cabs.c @@ -0,0 +1,32 @@ +/* $OpenBSD: s_cabs.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Martynas Venckus + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "math_private.h" + +double +cabs(double complex z) +{ + return hypot(__real__ z, __imag__ z); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(cabs, cabsl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_cabsf.c b/openlibm/src/s_cabsf.c new file mode 100644 index 0000000000..8d9bd96909 --- /dev/null +++ b/openlibm/src/s_cabsf.c @@ -0,0 +1,25 @@ +/* $OpenBSD: s_cabsf.c,v 1.1 2008/09/07 20:36:09 martynas Exp $ */ +/* + * Copyright (c) 2008 Martynas Venckus + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +float +cabsf(float complex z) +{ + return hypotf(__real__ z, __imag__ z); +} diff --git a/openlibm/src/s_cabsl.c b/openlibm/src/s_cabsl.c new file mode 100644 index 0000000000..847ded7126 --- /dev/null +++ b/openlibm/src/s_cabsl.c @@ -0,0 +1,26 @@ +/* $OpenBSD: s_cabsl.c,v 1.1 2011/07/08 19:25:31 martynas Exp $ */ + +/* + * Copyright (c) 2011 Martynas Venckus + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +long double +cabsl(long double complex z) +{ + return hypotl(__real__ z, __imag__ z); +} diff --git a/openlibm/src/s_cacos.c b/openlibm/src/s_cacos.c new file mode 100644 index 0000000000..e29717b0f5 --- /dev/null +++ b/openlibm/src/s_cacos.c @@ -0,0 +1,67 @@ +/* $OpenBSD: s_cacos.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cacos() + * + * Complex circular arc cosine + * + * + * + * SYNOPSIS: + * + * double complex cacos(); + * double complex z, w; + * + * w = cacos (z); + * + * + * + * DESCRIPTION: + * + * + * w = arccos z = PI/2 - arcsin z. + * + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5200 1.6e-15 2.8e-16 + * IEEE -10,+10 30000 1.8e-14 2.2e-15 + */ + +#include +#include +#include + +#include "math_private.h" + +double complex +cacos(double complex z) +{ + double complex w; + + w = casin (z); + w = (M_PI_2 - creal (w)) - cimag (w) * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(cacos, cacosl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_cacosf.c b/openlibm/src/s_cacosf.c new file mode 100644 index 0000000000..f3c0eb9c04 --- /dev/null +++ b/openlibm/src/s_cacosf.c @@ -0,0 +1,60 @@ +/* $OpenBSD: s_cacosf.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cacosf() + * + * Complex circular arc cosine + * + * + * + * SYNOPSIS: + * + * void cacosf(); + * cmplxf z, w; + * + * cacosf( &z, &w ); + * + * + * + * DESCRIPTION: + * + * + * w = arccos z = PI/2 - arcsin z. + * + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 9.2e-6 1.2e-6 + * + */ + +#include +#include + +float complex +cacosf(float complex z) +{ + float complex w; + + w = casinf( z ); + w = ((float)M_PI_2 - crealf (w)) - cimagf (w) * I; + return (w); +} diff --git a/openlibm/src/s_cacosh.c b/openlibm/src/s_cacosh.c new file mode 100644 index 0000000000..f16c14ce81 --- /dev/null +++ b/openlibm/src/s_cacosh.c @@ -0,0 +1,62 @@ +/* $OpenBSD: s_cacosh.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cacosh + * + * Complex inverse hyperbolic cosine + * + * + * + * SYNOPSIS: + * + * double complex cacosh(); + * double complex z, w; + * + * w = cacosh (z); + * + * + * + * DESCRIPTION: + * + * acosh z = i acos z . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.6e-14 2.1e-15 + * + */ + +#include +#include +#include + +#include "math_private.h" + +double complex +cacosh(double complex z) +{ + double complex w; + + w = I * cacos (z); + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(cacosh, cacoshl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_cacoshf.c b/openlibm/src/s_cacoshf.c new file mode 100644 index 0000000000..f3c40aedda --- /dev/null +++ b/openlibm/src/s_cacoshf.c @@ -0,0 +1,55 @@ +/* $OpenBSD: s_cacoshf.c,v 1.1 2008/09/07 20:36:09 martynas Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cacoshf + * + * Complex inverse hyperbolic cosine + * + * + * + * SYNOPSIS: + * + * float complex cacoshf(); + * float complex z, w; + * + * w = cacoshf (z); + * + * + * + * DESCRIPTION: + * + * acosh z = i acos z . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.6e-14 2.1e-15 + * + */ + +#include +#include + +float complex +cacoshf(float complex z) +{ + float complex w; + + w = I * cacosf (z); + return (w); +} diff --git a/openlibm/src/s_cacoshl.c b/openlibm/src/s_cacoshl.c new file mode 100644 index 0000000000..5e5ae31d1b --- /dev/null +++ b/openlibm/src/s_cacoshl.c @@ -0,0 +1,56 @@ +/* $OpenBSD: s_cacoshl.c,v 1.1 2011/07/08 19:25:31 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cacoshl + * + * Complex inverse hyperbolic cosine + * + * + * + * SYNOPSIS: + * + * long double complex cacoshl(); + * long double complex z, w; + * + * w = cacoshl (z); + * + * + * + * DESCRIPTION: + * + * acosh z = i acos z . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.6e-14 2.1e-15 + * + */ + +#include +#include + +long double complex +cacoshl(long double complex z) +{ + long double complex w; + + w = I * cacosl(z); + return (w); +} diff --git a/openlibm/src/s_cacosl.c b/openlibm/src/s_cacosl.c new file mode 100644 index 0000000000..8a7b8a1070 --- /dev/null +++ b/openlibm/src/s_cacosl.c @@ -0,0 +1,63 @@ +/* $OpenBSD: s_cacosl.c,v 1.3 2011/07/20 21:02:51 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cacosl() + * + * Complex circular arc cosine + * + * + * + * SYNOPSIS: + * + * long double complex cacosl(); + * long double complex z, w; + * + * w = cacosl( z ); + * + * + * + * DESCRIPTION: + * + * + * w = arccos z = PI/2 - arcsin z. + * + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5200 1.6e-15 2.8e-16 + * IEEE -10,+10 30000 1.8e-14 2.2e-15 + */ + +#include +#include + +static const long double PIO2L = 1.570796326794896619231321691639751442098585L; + +long double complex +cacosl(long double complex z) +{ + long double complex w; + + w = casinl(z); + w = (PIO2L - creall(w)) - cimagl(w) * I; + return (w); +} diff --git a/openlibm/src/s_carg.c b/openlibm/src/s_carg.c new file mode 100644 index 0000000000..2ebfe17792 --- /dev/null +++ b/openlibm/src/s_carg.c @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_carg.c,v 1.1 2007/12/12 23:43:51 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +carg(double complex z) +{ + + return (atan2(cimag(z), creal(z))); +} diff --git a/openlibm/src/s_cargf.c b/openlibm/src/s_cargf.c new file mode 100644 index 0000000000..41e320b340 --- /dev/null +++ b/openlibm/src/s_cargf.c @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cargf.c,v 1.1 2007/12/12 23:43:51 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +cargf(float complex z) +{ + + return (atan2f(cimagf(z), crealf(z))); +} diff --git a/openlibm/src/s_cargl.c b/openlibm/src/s_cargl.c new file mode 100644 index 0000000000..5052133e34 --- /dev/null +++ b/openlibm/src/s_cargl.c @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2005-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cargl.c,v 1.1 2008/07/31 22:41:26 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +cargl(long double complex z) +{ + + return (atan2l(cimagl(z), creall(z))); +} diff --git a/openlibm/src/s_casin.c b/openlibm/src/s_casin.c new file mode 100644 index 0000000000..4e5daaa711 --- /dev/null +++ b/openlibm/src/s_casin.c @@ -0,0 +1,136 @@ +/* $OpenBSD: s_casin.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* casin() + * + * Complex circular arc sine + * + * + * + * SYNOPSIS: + * + * double complex casin(); + * double complex z, w; + * + * w = casin (z); + * + * + * + * DESCRIPTION: + * + * Inverse complex sine: + * + * 2 + * w = -i clog( iz + csqrt( 1 - z ) ). + * + * casin(z) = -i casinh(iz) + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 10100 2.1e-15 3.4e-16 + * IEEE -10,+10 30000 2.2e-14 2.7e-15 + * Larger relative error can be observed for z near zero. + * Also tested by csin(casin(z)) = z. + */ + +#include +#include +#include + +#include "math_private.h" + +double complex +casin(double complex z) +{ + double complex w; + static double complex ca, ct, zz, z2; + double x, y; + + x = creal (z); + y = cimag (z); + + if (y == 0.0) { + if (fabs(x) > 1.0) { + w = M_PI_2 + 0.0 * I; + /*mtherr ("casin", DOMAIN);*/ + } + else { + w = asin (x) + 0.0 * I; + } + return (w); + } + + /* Power series expansion */ + /* + b = cabs(z); + if( b < 0.125 ) { + z2.r = (x - y) * (x + y); + z2.i = 2.0 * x * y; + + cn = 1.0; + n = 1.0; + ca.r = x; + ca.i = y; + sum.r = x; + sum.i = y; + do { + ct.r = z2.r * ca.r - z2.i * ca.i; + ct.i = z2.r * ca.i + z2.i * ca.r; + ca.r = ct.r; + ca.i = ct.i; + + cn *= n; + n += 1.0; + cn /= n; + n += 1.0; + b = cn/n; + + ct.r *= b; + ct.i *= b; + sum.r += ct.r; + sum.i += ct.i; + b = fabs(ct.r) + fabs(ct.i); + } + while( b > MACHEP ); + w->r = sum.r; + w->i = sum.i; + return; + } + */ + + ca = x + y * I; + ct = ca * I; + /* sqrt( 1 - z*z) */ + /* cmul( &ca, &ca, &zz ) */ + /*x * x - y * y */ + zz = (x - y) * (x + y) + (2.0 * x * y) * I; + + zz = 1.0 - creal(zz) - cimag(zz) * I; + z2 = csqrt (zz); + + zz = ct + z2; + zz = clog (zz); + /* multiply by 1/i = -i */ + w = zz * (-1.0 * I); + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(casin, casinl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_casinf.c b/openlibm/src/s_casinf.c new file mode 100644 index 0000000000..1573264deb --- /dev/null +++ b/openlibm/src/s_casinf.c @@ -0,0 +1,132 @@ +/* $OpenBSD: s_casinf.c,v 1.3 2011/07/20 19:28:33 martynas Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* casinf() + * + * Complex circular arc sine + * + * + * + * SYNOPSIS: + * + * void casinf(); + * cmplxf z, w; + * + * casinf( &z, &w ); + * + * + * + * DESCRIPTION: + * + * Inverse complex sine: + * + * 2 + * w = -i clog( iz + csqrt( 1 - z ) ). + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.1e-5 1.5e-6 + * Larger relative error can be observed for z near zero. + * + */ + +#include +#include + +float complex +casinf(float complex z) +{ + float complex w; + float x, y; + static float complex ca, ct, zz, z2; + /* + float cn, n; + static float a, b, s, t, u, v, y2; + static cmplxf sum; + */ + + x = crealf(z); + y = cimagf(z); + + if(y == 0.0f) { + if(fabsf(x) > 1.0f) { + w = (float)M_PI_2 + 0.0f * I; + /*mtherr( "casinf", DOMAIN );*/ + } + else { + w = asinf (x) + 0.0f * I; + } + return (w); + } + + /* Power series expansion */ + /* + b = cabsf(z); + if(b < 0.125) { + z2.r = (x - y) * (x + y); + z2.i = 2.0 * x * y; + + cn = 1.0; + n = 1.0; + ca.r = x; + ca.i = y; + sum.r = x; + sum.i = y; + do { + ct.r = z2.r * ca.r - z2.i * ca.i; + ct.i = z2.r * ca.i + z2.i * ca.r; + ca.r = ct.r; + ca.i = ct.i; + + cn *= n; + n += 1.0; + cn /= n; + n += 1.0; + b = cn/n; + + ct.r *= b; + ct.i *= b; + sum.r += ct.r; + sum.i += ct.i; + b = fabsf(ct.r) + fabsf(ct.i); + } + while(b > MACHEPF); + w->r = sum.r; + w->i = sum.i; + return; + } + */ + + + ca = x + y * I; + ct = ca * I; /* iz */ + /* sqrt( 1 - z*z) */ + /* cmul( &ca, &ca, &zz ) */ + /*x * x - y * y */ + zz = (x - y) * (x + y) + (2.0f * x * y) * I; + zz = 1.0f - crealf(zz) - cimagf(zz) * I; + z2 = csqrtf (zz); + + zz = ct + z2; + zz = clogf (zz); + /* multiply by 1/i = -i */ + w = zz * (-1.0f * I); + return (w); +} diff --git a/openlibm/src/s_casinh.c b/openlibm/src/s_casinh.c new file mode 100644 index 0000000000..4b1154d9a4 --- /dev/null +++ b/openlibm/src/s_casinh.c @@ -0,0 +1,62 @@ +/* $OpenBSD: s_casinh.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* casinh + * + * Complex inverse hyperbolic sine + * + * + * + * SYNOPSIS: + * + * double complex casinh(); + * double complex z, w; + * + * w = casinh (z); + * + * + * + * DESCRIPTION: + * + * casinh z = -i casin iz . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.8e-14 2.6e-15 + * + */ + +#include +#include +#include + +#include "math_private.h" + +double complex +casinh(double complex z) +{ + double complex w; + + w = -1.0 * I * casin (z * I); + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(casinh, casinhl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_casinhf.c b/openlibm/src/s_casinhf.c new file mode 100644 index 0000000000..88946249fe --- /dev/null +++ b/openlibm/src/s_casinhf.c @@ -0,0 +1,55 @@ +/* $OpenBSD: s_casinhf.c,v 1.1 2008/09/07 20:36:09 martynas Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* casinhf + * + * Complex inverse hyperbolic sine + * + * + * + * SYNOPSIS: + * + * float complex casinhf(); + * float complex z, w; + * + * w = casinhf (z); + * + * + * + * DESCRIPTION: + * + * casinh z = -i casin iz . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.8e-14 2.6e-15 + * + */ + +#include +#include + +float complex +casinhf(float complex z) +{ + float complex w; + + w = -1.0f * I * casinf (z * I); + return (w); +} diff --git a/openlibm/src/s_casinhl.c b/openlibm/src/s_casinhl.c new file mode 100644 index 0000000000..33ae2179fa --- /dev/null +++ b/openlibm/src/s_casinhl.c @@ -0,0 +1,56 @@ +/* $OpenBSD: s_casinhl.c,v 1.1 2011/07/08 19:25:31 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* casinhl + * + * Complex inverse hyperbolic sine + * + * + * + * SYNOPSIS: + * + * long double complex casinhf(); + * long double complex z, w; + * + * w = casinhl (z); + * + * + * + * DESCRIPTION: + * + * casinh z = -i casin iz . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.8e-14 2.6e-15 + * + */ + +#include +#include + +long double complex +casinhl(long double complex z) +{ + long double complex w; + + w = -1.0L * I * casinl(z * I); + return (w); +} diff --git a/openlibm/src/s_casinl.c b/openlibm/src/s_casinl.c new file mode 100644 index 0000000000..b346272703 --- /dev/null +++ b/openlibm/src/s_casinl.c @@ -0,0 +1,130 @@ +/* $OpenBSD: s_casinl.c,v 1.3 2011/07/20 21:02:51 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* casinl() + * + * Complex circular arc sine + * + * + * + * SYNOPSIS: + * + * long double complex casinl(); + * long double complex z, w; + * + * w = casinl( z ); + * + * + * + * DESCRIPTION: + * + * Inverse complex sine: + * + * 2 + * w = -i clog( iz + csqrt( 1 - z ) ). + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 10100 2.1e-15 3.4e-16 + * IEEE -10,+10 30000 2.2e-14 2.7e-15 + * Larger relative error can be observed for z near zero. + * Also tested by csin(casin(z)) = z. + */ + +#include +#include +#include + +#if LDBL_MANT_DIG == 64 +static const long double MACHEPL= 5.42101086242752217003726400434970855712890625E-20L; +#elif LDBL_MANT_DIG == 113 +static const long double MACHEPL = 9.629649721936179265279889712924636592690508e-35L; +#endif + +static const long double PIO2L = 1.570796326794896619231321691639751442098585L; + +long double complex +casinl(long double complex z) +{ + long double complex w; + long double x, y, b; + static long double complex ca, ct, zz, z2; + + x = creall(z); + y = cimagl(z); + + if (y == 0.0L) { + if (fabsl(x) > 1.0L) { + w = PIO2L + 0.0L * I; + /*mtherr( "casinl", DOMAIN );*/ + } + else { + w = asinl(x) + 0.0L * I; + } + return (w); + } + + /* Power series expansion */ + b = cabsl(z); + if (b < 0.125L) { + long double complex sum; + long double n, cn; + + z2 = (x - y) * (x + y) + (2.0L * x * y) * I; + cn = 1.0L; + n = 1.0L; + ca = x + y * I; + sum = x + y * I; + do { + ct = z2 * ca; + ca = ct; + + cn *= n; + n += 1.0L; + cn /= n; + n += 1.0L; + b = cn/n; + + ct *= b; + sum += ct; + b = cabsl(ct); + } + + while (b > MACHEPL); + w = sum; + return w; + } + + ca = x + y * I; + ct = ca * I; /* iz */ + /* sqrt(1 - z*z) */ + /* cmul(&ca, &ca, &zz) */ + /* x * x - y * y */ + zz = (x - y) * (x + y) + (2.0L * x * y) * I; + zz = 1.0L - creall(zz) - cimagl(zz) * I; + z2 = csqrtl(zz); + + zz = ct + z2; + zz = clogl(zz); + /* multiply by 1/i = -i */ + w = zz * (-1.0L * I); + return (w); +} diff --git a/openlibm/src/s_catan.c b/openlibm/src/s_catan.c new file mode 100644 index 0000000000..9ee3dbb9ec --- /dev/null +++ b/openlibm/src/s_catan.c @@ -0,0 +1,133 @@ +/* $OpenBSD: s_catan.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* catan() + * + * Complex circular arc tangent + * + * + * + * SYNOPSIS: + * + * double complex catan(); + * double complex z, w; + * + * w = catan (z); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * 1 ( 2x ) + * Re w = - arctan(-----------) + k PI + * 2 ( 2 2) + * (1 - x - y ) + * + * ( 2 2) + * 1 (x + (y+1) ) + * Im w = - log(------------) + * 4 ( 2 2) + * (x + (y-1) ) + * + * Where k is an arbitrary integer. + * + * catan(z) = -i catanh(iz). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5900 1.3e-16 7.8e-18 + * IEEE -10,+10 30000 2.3e-15 8.5e-17 + * The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, + * had peak relative error 1.5e-16, rms relative error + * 2.9e-17. See also clog(). + */ + +#include +#include +#include + +#include "math_private.h" + +#define MAXNUM 1.0e308 + +static const double DP1 = 3.14159265160560607910E0; +static const double DP2 = 1.98418714791870343106E-9; +static const double DP3 = 1.14423774522196636802E-17; + +static double +_redupi(double x) +{ + double t; + long i; + + t = x/M_PI; + if(t >= 0.0) + t += 0.5; + else + t -= 0.5; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return (t); +} + +double complex +catan(double complex z) +{ + double complex w; + double a, t, x, x2, y; + + x = creal (z); + y = cimag (z); + + if ((x == 0.0) && (y > 1.0)) + goto ovrf; + + x2 = x * x; + a = 1.0 - x2 - (y * y); + if (a == 0.0) + goto ovrf; + + t = 0.5 * atan2 (2.0 * x, a); + w = _redupi (t); + + t = y - 1.0; + a = x2 + (t * t); + if (a == 0.0) + goto ovrf; + + t = y + 1.0; + a = (x2 + (t * t))/a; + w = w + (0.25 * log (a)) * I; + return (w); + +ovrf: + /*mtherr ("catan", OVERFLOW);*/ + w = MAXNUM + MAXNUM * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(catan, catanl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_catanf.c b/openlibm/src/s_catanf.c new file mode 100644 index 0000000000..d40cb56c0e --- /dev/null +++ b/openlibm/src/s_catanf.c @@ -0,0 +1,124 @@ +/* $OpenBSD: s_catanf.c,v 1.2 2010/07/18 18:42:26 guenther Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* catanf() + * + * Complex circular arc tangent + * + * + * + * SYNOPSIS: + * + * float complex catanf(); + * float complex z, w; + * + * w = catanf( z ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * 1 ( 2x ) + * Re w = - arctan(-----------) + k PI + * 2 ( 2 2) + * (1 - x - y ) + * + * ( 2 2) + * 1 (x + (y+1) ) + * Im w = - log(------------) + * 4 ( 2 2) + * (x + (y-1) ) + * + * Where k is an arbitrary integer. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 2.3e-6 5.2e-8 + * + */ + +#include +#include + +#define MAXNUMF 1.0e38F + +static const double DP1 = 3.140625; +static const double DP2 = 9.67502593994140625E-4; +static const double DP3 = 1.509957990978376432E-7; + +static float +_redupif(float xx) +{ + float x, t; + long i; + + x = xx; + t = x/(float)M_PI; + if(t >= 0.0) + t += 0.5; + else + t -= 0.5; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return(t); +} + +float complex +catanf(float complex z) +{ + float complex w; + float a, t, x, x2, y; + + x = crealf(z); + y = cimagf(z); + + if((x == 0.0f) && (y > 1.0f)) + goto ovrf; + + x2 = x * x; + a = 1.0f - x2 - (y * y); + if (a == 0.0f) + goto ovrf; + + t = 0.5f * atan2f(2.0f * x, a); + w = _redupif(t); + + t = y - 1.0f; + a = x2 + (t * t); + if(a == 0.0f) + goto ovrf; + + t = y + 1.0f; + a = (x2 + (t * t))/a; + w = w + (0.25f * logf (a)) * I; + return (w); + +ovrf: + /*mtherr( "catanf", OVERFLOW );*/ + w = MAXNUMF + MAXNUMF * I; + return (w); +} diff --git a/openlibm/src/s_catanh.c b/openlibm/src/s_catanh.c new file mode 100644 index 0000000000..3e3eeafe98 --- /dev/null +++ b/openlibm/src/s_catanh.c @@ -0,0 +1,62 @@ +/* $OpenBSD: s_catanh.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* catanh + * + * Complex inverse hyperbolic tangent + * + * + * + * SYNOPSIS: + * + * double complex catanh(); + * double complex z, w; + * + * w = catanh (z); + * + * + * + * DESCRIPTION: + * + * Inverse tanh, equal to -i catan (iz); + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 2.3e-16 6.2e-17 + * + */ + +#include +#include +#include + +#include "math_private.h" + +double complex +catanh(double complex z) +{ + double complex w; + + w = -1.0 * I * catan (z * I); + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(catanh, catanhl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_catanhf.c b/openlibm/src/s_catanhf.c new file mode 100644 index 0000000000..7d43825ba2 --- /dev/null +++ b/openlibm/src/s_catanhf.c @@ -0,0 +1,55 @@ +/* $OpenBSD: s_catanhf.c,v 1.1 2008/09/07 20:36:09 martynas Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* catanhf + * + * Complex inverse hyperbolic tangent + * + * + * + * SYNOPSIS: + * + * float complex catanhf(); + * float complex z, w; + * + * w = catanhf (z); + * + * + * + * DESCRIPTION: + * + * Inverse tanh, equal to -i catan (iz); + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 2.3e-16 6.2e-17 + * + */ + +#include +#include + +float complex +catanhf(float complex z) +{ + float complex w; + + w = -1.0f * I * catanf (z * I); + return (w); +} diff --git a/openlibm/src/s_catanhl.c b/openlibm/src/s_catanhl.c new file mode 100644 index 0000000000..711a2686c5 --- /dev/null +++ b/openlibm/src/s_catanhl.c @@ -0,0 +1,56 @@ +/* $OpenBSD: s_catanhl.c,v 1.1 2011/07/08 19:25:31 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* catanhl + * + * Complex inverse hyperbolic tangent + * + * + * + * SYNOPSIS: + * + * long double complex catanhl(); + * long double complex z, w; + * + * w = catanhl (z); + * + * + * + * DESCRIPTION: + * + * Inverse tanh, equal to -i catan (iz); + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 2.3e-16 6.2e-17 + * + */ + +#include +#include + +long double complex +catanhl(long double complex z) +{ + long double complex w; + + w = -1.0L * I * catanl(z * I); + return (w); +} diff --git a/openlibm/src/s_catanl.c b/openlibm/src/s_catanl.c new file mode 100644 index 0000000000..acd51b0edc --- /dev/null +++ b/openlibm/src/s_catanl.c @@ -0,0 +1,127 @@ +/* $OpenBSD: s_catanl.c,v 1.3 2011/07/20 21:02:51 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* catanl() + * + * Complex circular arc tangent + * + * + * + * SYNOPSIS: + * + * long double complex catanl(); + * long double complex z, w; + * + * w = catanl( z ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * 1 ( 2x ) + * Re w = - arctan(-----------) + k PI + * 2 ( 2 2) + * (1 - x - y ) + * + * ( 2 2) + * 1 (x + (y+1) ) + * Im w = - log(------------) + * 4 ( 2 2) + * (x + (y-1) ) + * + * Where k is an arbitrary integer. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5900 1.3e-16 7.8e-18 + * IEEE -10,+10 30000 2.3e-15 8.5e-17 + * The check catan( ctan(z) ) = z, with |x| and |y| < PI/2, + * had peak relative error 1.5e-16, rms relative error + * 2.9e-17. See also clog(). + */ + +#include +#include +#include + +static const long double PIL = 3.141592653589793238462643383279502884197169L; +static const long double DP1 = 3.14159265358979323829596852490908531763125L; +static const long double DP2 = 1.6667485837041756656403424829301998703007e-19L; +static const long double DP3 = 1.8830410776607851167459095484560349402753e-39L; + +static long double +redupil(long double x) +{ + long double t; + long i; + + t = x / PIL; + if (t >= 0.0L) + t += 0.5L; + else + t -= 0.5L; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return (t); +} + +long double complex +catanl(long double complex z) +{ + long double complex w; + long double a, t, x, x2, y; + + x = creall(z); + y = cimagl(z); + + if ((x == 0.0L) && (y > 1.0L)) + goto ovrf; + + x2 = x * x; + a = 1.0L - x2 - (y * y); + if (a == 0.0L) + goto ovrf; + + t = atan2l(2.0L * x, a) * 0.5L; + w = redupil(t); + + t = y - 1.0L; + a = x2 + (t * t); + if (a == 0.0L) + goto ovrf; + + t = y + 1.0L; + a = (x2 + (t * t)) / a; + w = w + (0.25L * logl(a)) * I; + return (w); + +ovrf: + /*mtherr( "catanl", OVERFLOW );*/ + w = LDBL_MAX + LDBL_MAX * I; + return (w); +} diff --git a/openlibm/src/s_cbrt.c b/openlibm/src/s_cbrt.c new file mode 100644 index 0000000000..b6316adabf --- /dev/null +++ b/openlibm/src/s_cbrt.c @@ -0,0 +1,118 @@ +/* @(#)s_cbrt.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * Optimized by Bruce D. Evans. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cbrt.c,v 1.17 2011/03/12 16:50:39 kargl Exp $"); + +#include + +#include "math_private.h" + +/* cbrt(x) + * Return cube root of x + */ +static const u_int32_t + B1 = 715094163, /* B1 = (1023-1023/3-0.03306235651)*2**20 */ + B2 = 696219795; /* B2 = (1023-1023/3-54/3-0.03306235651)*2**20 */ + +/* |1/cbrt(x) - p(x)| < 2**-23.5 (~[-7.93e-8, 7.929e-8]). */ +static const double +P0 = 1.87595182427177009643, /* 0x3ffe03e6, 0x0f61e692 */ +P1 = -1.88497979543377169875, /* 0xbffe28e0, 0x92f02420 */ +P2 = 1.621429720105354466140, /* 0x3ff9f160, 0x4a49d6c2 */ +P3 = -0.758397934778766047437, /* 0xbfe844cb, 0xbee751d9 */ +P4 = 0.145996192886612446982; /* 0x3fc2b000, 0xd4e4edd7 */ + +OLM_DLLEXPORT double +cbrt(double x) +{ + int32_t hx; + union { + double value; + u_int64_t bits; + } u; + double r,s,t=0.0,w; + u_int32_t sign; + u_int32_t high,low; + + EXTRACT_WORDS(hx,low,x); + sign=hx&0x80000000; /* sign= sign(x) */ + hx ^=sign; + if(hx>=0x7ff00000) return(x+x); /* cbrt(NaN,INF) is itself */ + + /* + * Rough cbrt to 5 bits: + * cbrt(2**e*(1+m) ~= 2**(e/3)*(1+(e%3+m)/3) + * where e is integral and >= 0, m is real and in [0, 1), and "/" and + * "%" are integer division and modulus with rounding towards minus + * infinity. The RHS is always >= the LHS and has a maximum relative + * error of about 1 in 16. Adding a bias of -0.03306235651 to the + * (e%3+m)/3 term reduces the error to about 1 in 32. With the IEEE + * floating point representation, for finite positive normal values, + * ordinary integer divison of the value in bits magically gives + * almost exactly the RHS of the above provided we first subtract the + * exponent bias (1023 for doubles) and later add it back. We do the + * subtraction virtually to keep e >= 0 so that ordinary integer + * division rounds towards minus infinity; this is also efficient. + */ + if(hx<0x00100000) { /* zero or subnormal? */ + if((hx|low)==0) + return(x); /* cbrt(0) is itself */ + SET_HIGH_WORD(t,0x43500000); /* set t= 2**54 */ + t*=x; + GET_HIGH_WORD(high,t); + INSERT_WORDS(t,sign|((high&0x7fffffff)/3+B2),0); + } else + INSERT_WORDS(t,sign|(hx/3+B1),0); + + /* + * New cbrt to 23 bits: + * cbrt(x) = t*cbrt(x/t**3) ~= t*P(t**3/x) + * where P(r) is a polynomial of degree 4 that approximates 1/cbrt(r) + * to within 2**-23.5 when |r - 1| < 1/10. The rough approximation + * has produced t such than |t/cbrt(x) - 1| ~< 1/32, and cubing this + * gives us bounds for r = t**3/x. + * + * Try to optimize for parallel evaluation as in k_tanf.c. + */ + r=(t*t)*(t/x); + t=t*((P0+r*(P1+r*P2))+((r*r)*r)*(P3+r*P4)); + + /* + * Round t away from zero to 23 bits (sloppily except for ensuring that + * the result is larger in magnitude than cbrt(x) but not much more than + * 2 23-bit ulps larger). With rounding towards zero, the error bound + * would be ~5/6 instead of ~4/6. With a maximum error of 2 23-bit ulps + * in the rounded t, the infinite-precision error in the Newton + * approximation barely affects third digit in the final error + * 0.667; the error in the rounded t can be up to about 3 23-bit ulps + * before the final error is larger than 0.667 ulps. + */ + u.value=t; + u.bits=(u.bits+0x80000000)&0xffffffffc0000000ULL; + t=u.value; + + /* one step Newton iteration to 53 bits with error < 0.667 ulps */ + s=t*t; /* t*t is exact */ + r=x/s; /* error <= 0.5 ulps; |r| < |t| */ + w=t+t; /* t+t is exact */ + r=(r-t)/(w+r); /* r-t is exact; w+r ~= 3*t */ + t=t+t*r; /* error <= 0.5 + 0.5/3 + epsilon */ + + return(t); +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(cbrt, cbrtl); +#endif diff --git a/openlibm/src/s_cbrtf.c b/openlibm/src/s_cbrtf.c new file mode 100644 index 0000000000..6a3a762ada --- /dev/null +++ b/openlibm/src/s_cbrtf.c @@ -0,0 +1,74 @@ +/* s_cbrtf.c -- float version of s_cbrt.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Debugged and optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cbrtf.c,v 1.18 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +/* cbrtf(x) + * Return cube root of x + */ +static const unsigned + B1 = 709958130, /* B1 = (127-127.0/3-0.03306235651)*2**23 */ + B2 = 642849266; /* B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */ + +OLM_DLLEXPORT float +cbrtf(float x) +{ + double r,T; + float t; + int32_t hx; + u_int32_t sign; + u_int32_t high; + + GET_FLOAT_WORD(hx,x); + sign=hx&0x80000000; /* sign= sign(x) */ + hx ^=sign; + if(hx>=0x7f800000) return(x+x); /* cbrt(NaN,INF) is itself */ + + /* rough cbrt to 5 bits */ + if(hx<0x00800000) { /* zero or subnormal? */ + if(hx==0) + return(x); /* cbrt(+-0) is itself */ + SET_FLOAT_WORD(t,0x4b800000); /* set t= 2**24 */ + t*=x; + GET_FLOAT_WORD(high,t); + SET_FLOAT_WORD(t,sign|((high&0x7fffffff)/3+B2)); + } else + SET_FLOAT_WORD(t,sign|(hx/3+B1)); + + /* + * First step Newton iteration (solving t*t-x/t == 0) to 16 bits. In + * double precision so that its terms can be arranged for efficiency + * without causing overflow or underflow. + */ + T=t; + r=T*T*T; + T=T*((double)x+x+r)/(x+r+r); + + /* + * Second step Newton iteration to 47 bits. In double precision for + * efficiency and accuracy. + */ + r=T*T*T; + T=T*((double)x+x+r)/(x+r+r); + + /* rounding to 24 bits is perfect in round-to-nearest mode */ + return(T); +} diff --git a/openlibm/src/s_cbrtl.c b/openlibm/src/s_cbrtl.c new file mode 100644 index 0000000000..2fe0360d98 --- /dev/null +++ b/openlibm/src/s_cbrtl.c @@ -0,0 +1,161 @@ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2009-2011, Bruce D. Evans, Steven G. Kargl, David Schultz. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * The argument reduction and testing for exceptional cases was + * written by Steven G. Kargl with input from Bruce D. Evans + * and David A. Schultz. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cbrtl.c,v 1.1 2011/03/12 19:37:35 kargl Exp $"); + +#include +#include +// VBS +//#include + +#include "fpmath.h" +#include "math_private.h" +#if defined(__i386__) +#include "i387/bsd_ieeefp.h" +#endif + +#define BIAS (LDBL_MAX_EXP - 1) + +static const unsigned + B1 = 709958130; /* B1 = (127-127.0/3-0.03306235651)*2**23 */ + +OLM_DLLEXPORT long double +cbrtl(long double x) +{ + union IEEEl2bits u, v; + long double r, s, t, w; + double dr, dt, dx; + float ft, fx; + u_int32_t hx; + u_int16_t expsign; + int k; + + u.e = x; + expsign = u.xbits.expsign; + k = expsign & 0x7fff; + + /* + * If x = +-Inf, then cbrt(x) = +-Inf. + * If x = NaN, then cbrt(x) = NaN. + */ + if (k == BIAS + LDBL_MAX_EXP) + return (x + x); + +#ifdef __i386__ + fp_prec_t oprec; + + oprec = fpgetprec(); + if (oprec != FP_PE) + fpsetprec(FP_PE); +#endif + + if (k == 0) { + /* If x = +-0, then cbrt(x) = +-0. */ + if ((u.bits.manh | u.bits.manl) == 0) { +#ifdef __i386__ + if (oprec != FP_PE) + fpsetprec(oprec); +#endif + return (x); + } + /* Adjust subnormal numbers. */ + u.e *= 0x1.0p514; + k = u.bits.exp; + k -= BIAS + 514; + } else + k -= BIAS; + u.xbits.expsign = BIAS; + v.e = 1; + + x = u.e; + switch (k % 3) { + case 1: + case -2: + x = 2*x; + k--; + break; + case 2: + case -1: + x = 4*x; + k -= 2; + break; + } + v.xbits.expsign = (expsign & 0x8000) | (BIAS + k / 3); + + /* + * The following is the guts of s_cbrtf, with the handling of + * special values removed and extra care for accuracy not taken, + * but with most of the extra accuracy not discarded. + */ + + /* ~5-bit estimate: */ + fx = x; + GET_FLOAT_WORD(hx, fx); + SET_FLOAT_WORD(ft, ((hx & 0x7fffffff) / 3 + B1)); + + /* ~16-bit estimate: */ + dx = x; + dt = ft; + dr = dt * dt * dt; + dt = dt * (dx + dx + dr) / (dx + dr + dr); + + /* ~47-bit estimate: */ + dr = dt * dt * dt; + dt = dt * (dx + dx + dr) / (dx + dr + dr); + +#if LDBL_MANT_DIG == 64 + /* + * dt is cbrtl(x) to ~47 bits (after x has been reduced to 1 <= x < 8). + * Round it away from zero to 32 bits (32 so that t*t is exact, and + * away from zero for technical reasons). + */ + volatile double vd2 = 0x1.0p32; + volatile double vd1 = 0x1.0p-31; + #define vd ((long double)vd2 + vd1) + + t = dt + vd - 0x1.0p32; +#elif LDBL_MANT_DIG == 113 + /* + * Round dt away from zero to 47 bits. Since we don't trust the 47, + * add 2 47-bit ulps instead of 1 to round up. Rounding is slow and + * might be avoidable in this case, since on most machines dt will + * have been evaluated in 53-bit precision and the technical reasons + * for rounding up might not apply to either case in cbrtl() since + * dt is much more accurate than needed. + */ + t = dt + 0x2.0p-46 + 0x1.0p60L - 0x1.0p60; +#else +#error "Unsupported long double format" +#endif + + /* + * Final step Newton iteration to 64 or 113 bits with + * error < 0.667 ulps + */ + s=t*t; /* t*t is exact */ + r=x/s; /* error <= 0.5 ulps; |r| < |t| */ + w=t+t; /* t+t is exact */ + r=(r-t)/(w+r); /* r-t is exact; w+r ~= 3*t */ + t=t+t*r; /* error <= 0.5 + 0.5/3 + epsilon */ + + t *= v.e; +#ifdef __i386__ + if (oprec != FP_PE) + fpsetprec(oprec); +#endif + return (t); +} diff --git a/openlibm/src/s_ccos.c b/openlibm/src/s_ccos.c new file mode 100644 index 0000000000..faafdb0488 --- /dev/null +++ b/openlibm/src/s_ccos.c @@ -0,0 +1,91 @@ +/* $OpenBSD: s_ccos.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ccos() + * + * Complex circular cosine + * + * + * + * SYNOPSIS: + * + * double complex ccos(); + * double complex z, w; + * + * w = ccos (z); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * w = cos x cosh y - i sin x sinh y. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 8400 4.5e-17 1.3e-17 + * IEEE -10,+10 30000 3.8e-16 1.0e-16 + */ + +#include +#include +#include + +#include "math_private.h" + +/* calculate cosh and sinh */ + +static void +_cchsh(double x, double *c, double *s) +{ + double e, ei; + + if (fabs(x) <= 0.5) { + *c = cosh(x); + *s = sinh(x); + } + else { + e = exp(x); + ei = 0.5/e; + e = 0.5 * e; + *s = e - ei; + *c = e + ei; + } +} + +double complex +ccos(double complex z) +{ + double complex w; + double ch, sh; + + _cchsh( cimag(z), &ch, &sh ); + w = cos(creal (z)) * ch - (sin (creal (z)) * sh) * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(ccos, ccosl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_ccosf.c b/openlibm/src/s_ccosf.c new file mode 100644 index 0000000000..ce382f08da --- /dev/null +++ b/openlibm/src/s_ccosf.c @@ -0,0 +1,84 @@ +/* $OpenBSD: s_ccosf.c,v 1.2 2010/07/18 18:42:26 guenther Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ccosf() + * + * Complex circular cosine + * + * + * + * SYNOPSIS: + * + * void ccosf(); + * cmplxf z, w; + * + * ccosf( &z, &w ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * w = cos x cosh y - i sin x sinh y. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.8e-7 5.5e-8 + */ + +#include +#include + +/* calculate cosh and sinh */ + +static void +_cchshf(float xx, float *c, float *s) +{ + float x, e, ei; + + x = xx; + if(fabsf(x) <= 0.5f) { + *c = coshf(x); + *s = sinhf(x); + } + else { + e = expf(x); + ei = 0.5f/e; + e = 0.5f * e; + *s = e - ei; + *c = e + ei; + } +} + +float complex +ccosf(float complex z) +{ + float complex w; + float ch, sh; + + _cchshf( cimagf(z), &ch, &sh ); + w = cosf( crealf(z) ) * ch + ( -sinf( crealf(z) ) * sh) * I; + return (w); +} diff --git a/openlibm/src/s_ccosh.c b/openlibm/src/s_ccosh.c new file mode 100644 index 0000000000..110494af5c --- /dev/null +++ b/openlibm/src/s_ccosh.c @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Hyperbolic cosine of a complex argument z = x + i y. + * + * cosh(z) = cosh(x+iy) + * = cosh(x) cos(y) + i sinh(x) sin(y). + * + * Exceptional values are noted in the comments within the source code. + * These values and the return value were taken from n1124.pdf. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ccosh.c,v 1.2 2011/10/21 06:29:32 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const double huge = 0x1p1023; + +OLM_DLLEXPORT double complex +ccosh(double complex z) +{ + double x, y, h; + int32_t hx, hy, ix, iy, lx, ly; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + /* Handle the nearly-non-exceptional cases where x and y are finite. */ + if (ix < 0x7ff00000 && iy < 0x7ff00000) { + if ((iy | ly) == 0) + return (CMPLX(cosh(x), x * y)); + if (ix < 0x40360000) /* small x: normal case */ + return (CMPLX(cosh(x) * cos(y), sinh(x) * sin(y))); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (ix < 0x40862e42) { + /* x < 710: exp(|x|) won't overflow */ + h = exp(fabs(x)) * 0.5; + return (CMPLX(h * cos(y), copysign(h, x) * sin(y))); + } else if (ix < 0x4096bbaa) { + /* x < 1455: scale to avoid overflow */ + z = __ldexp_cexp(CMPLX(fabs(x), y), -1); + return (CMPLX(creal(z), cimag(z) * copysign(1, x))); + } else { + /* x >= 1455: the result always overflows */ + h = huge * x; + return (CMPLX(h * h * cos(y), h * sin(y))); + } + } + + /* + * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as dNaN. Raise the invalid floating-point exception. + * + * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as d(NaN). + */ + if ((ix | lx) == 0 && iy >= 0x7ff00000) + return (CMPLX(y - y, copysign(0, x * (y - y)))); + + /* + * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0. + * + * cosh(NaN +- I 0) = d(NaN) + I sign(d(NaN, +-0))0. + * The sign of 0 in the result is unspecified. + */ + if ((iy | ly) == 0 && ix >= 0x7ff00000) { + if (((hx & 0xfffff) | lx) == 0) + return (CMPLX(x * x, copysign(0, x) * y)); + return (CMPLX(x * x, copysign(0, (x + x) * y))); + } + + /* + * cosh(x +- I Inf) = dNaN + I dNaN. + * Raise the invalid floating-point exception for finite nonzero x. + * + * cosh(x + I NaN) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero x. Choice = don't raise (except for signaling NaNs). + */ + if (ix < 0x7ff00000 && iy >= 0x7ff00000) + return (CMPLX(y - y, x * (y - y))); + + /* + * cosh(+-Inf + I NaN) = +Inf + I d(NaN). + * + * cosh(+-Inf +- I Inf) = +Inf + I dNaN. + * The sign of Inf in the result is unspecified. Choice = always +. + * Raise the invalid floating-point exception. + * + * cosh(+-Inf + I y) = +Inf cos(y) +- I Inf sin(y) + */ + if (ix >= 0x7ff00000 && ((hx & 0xfffff) | lx) == 0) { + if (iy >= 0x7ff00000) + return (CMPLX(x * x, x * (y - y))); + return (CMPLX((x * x) * cos(y), x * sin(y))); + } + + /* + * cosh(NaN + I NaN) = d(NaN) + I d(NaN). + * + * cosh(NaN +- I Inf) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception. + * Choice = raise. + * + * cosh(NaN + I y) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero y. Choice = don't raise (except for signaling NaNs). + */ + return (CMPLX((x * x) * (y - y), (x + x) * (y - y))); +} + +OLM_DLLEXPORT double complex +ccos(double complex z) +{ + + /* ccos(z) = ccosh(I * z) */ + return (ccosh(CMPLX(-cimag(z), creal(z)))); +} diff --git a/openlibm/src/s_ccoshf.c b/openlibm/src/s_ccoshf.c new file mode 100644 index 0000000000..53c8e7ae6f --- /dev/null +++ b/openlibm/src/s_ccoshf.c @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Hyperbolic cosine of a complex argument. See s_ccosh.c for details. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ccoshf.c,v 1.2 2011/10/21 06:29:32 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const float huge = 0x1p127; + +OLM_DLLEXPORT float complex +ccoshf(float complex z) +{ + float x, y, h; + int32_t hx, hy, ix, iy; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hx, x); + GET_FLOAT_WORD(hy, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + if (ix < 0x7f800000 && iy < 0x7f800000) { + if (iy == 0) + return (CMPLXF(coshf(x), x * y)); + if (ix < 0x41100000) /* small x: normal case */ + return (CMPLXF(coshf(x) * cosf(y), sinhf(x) * sinf(y))); + + /* |x| >= 9, so cosh(x) ~= exp(|x|) */ + if (ix < 0x42b17218) { + /* x < 88.7: expf(|x|) won't overflow */ + h = expf(fabsf(x)) * 0.5f; + return (CMPLXF(h * cosf(y), copysignf(h, x) * sinf(y))); + } else if (ix < 0x4340b1e7) { + /* x < 192.7: scale to avoid overflow */ + z = __ldexp_cexpf(CMPLXF(fabsf(x), y), -1); + return (CMPLXF(crealf(z), cimagf(z) * copysignf(1, x))); + } else { + /* x >= 192.7: the result always overflows */ + h = huge * x; + return (CMPLXF(h * h * cosf(y), h * sinf(y))); + } + } + + if (ix == 0 && iy >= 0x7f800000) + return (CMPLXF(y - y, copysignf(0, x * (y - y)))); + + if (iy == 0 && ix >= 0x7f800000) { + if ((hx & 0x7fffff) == 0) + return (CMPLXF(x * x, copysignf(0, x) * y)); + return (CMPLXF(x * x, copysignf(0, (x + x) * y))); + } + + if (ix < 0x7f800000 && iy >= 0x7f800000) + return (CMPLXF(y - y, x * (y - y))); + + if (ix >= 0x7f800000 && (hx & 0x7fffff) == 0) { + if (iy >= 0x7f800000) + return (CMPLXF(x * x, x * (y - y))); + return (CMPLXF((x * x) * cosf(y), x * sinf(y))); + } + + return (CMPLXF((x * x) * (y - y), (x + x) * (y - y))); +} + +OLM_DLLEXPORT float complex +ccosf(float complex z) +{ + + return (ccoshf(CMPLXF(-cimagf(z), crealf(z)))); +} diff --git a/openlibm/src/s_ccoshl.c b/openlibm/src/s_ccoshl.c new file mode 100644 index 0000000000..0233d192aa --- /dev/null +++ b/openlibm/src/s_ccoshl.c @@ -0,0 +1,59 @@ +/* $OpenBSD: s_ccoshl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ccoshl + * + * Complex hyperbolic cosine + * + * + * + * SYNOPSIS: + * + * long double complex ccoshl(); + * long double complex z, w; + * + * w = ccoshl (z); + * + * + * + * DESCRIPTION: + * + * ccosh(z) = cosh x cos y + i sinh x sin y . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 2.9e-16 8.1e-17 + * + */ + +#include +#include + +long double complex +ccoshl(long double complex z) +{ + long double complex w; + long double x, y; + + x = creall(z); + y = cimagl(z); + w = coshl(x) * cosl(y) + (sinhl(x) * sinl(y)) * I; + return (w); +} diff --git a/openlibm/src/s_ccosl.c b/openlibm/src/s_ccosl.c new file mode 100644 index 0000000000..0d5048369f --- /dev/null +++ b/openlibm/src/s_ccosl.c @@ -0,0 +1,82 @@ +/* $OpenBSD: s_ccosl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ccosl() + * + * Complex circular cosine + * + * + * + * SYNOPSIS: + * + * long double complex ccosl(); + * long double complex z, w; + * + * w = ccosl( z ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * w = cos x cosh y - i sin x sinh y. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 8400 4.5e-17 1.3e-17 + * IEEE -10,+10 30000 3.8e-16 1.0e-16 + */ + +#include +#include + +static void +cchshl(long double x, long double *c, long double *s) +{ + long double e, ei; + + if(fabsl(x) <= 0.5L) { + *c = coshl(x); + *s = sinhl(x); + } else { + e = expl(x); + ei = 0.5L/e; + e = 0.5L * e; + *s = e - ei; + *c = e + ei; + } +} + +long double complex +ccosl(long double complex z) +{ + long double complex w; + long double ch, sh; + + cchshl(cimagl(z), &ch, &sh); + w = cosl(creall(z)) * ch + (-sinl(creall(z)) * sh) * I; + return (w); +} diff --git a/openlibm/src/s_ceil.c b/openlibm/src/s_ceil.c new file mode 100644 index 0000000000..9c70a9761c --- /dev/null +++ b/openlibm/src/s_ceil.c @@ -0,0 +1,77 @@ +/* @(#)s_ceil.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ceil.c,v 1.11 2008/02/15 07:01:40 bde Exp $"); + +/* + * ceil(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to ceil(x). + */ + +#include +#include + +#include "math_private.h" + +static const double huge = 1.0e300; + +OLM_DLLEXPORT double +ceil(double x) +{ + int32_t i0,i1,j0; + u_int32_t i,j; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;i1=0;} + else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) { + if(j0==20) i0+=1; + else { + j = i1 + (1<<(52-j0)); + if(j + +#include "math_private.h" + +static const float huge = 1.0e30; + +OLM_DLLEXPORT float +ceilf(float x) +{ + int32_t i0,j0; + u_int32_t i; + + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;} + else if(i0!=0) { i0=0x3f800000;} + } + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge+x>(float)0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00800000)>>j0; + i0 &= (~i); + } + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} diff --git a/openlibm/src/s_ceill.c b/openlibm/src/s_ceill.c new file mode 100644 index 0000000000..f525411e25 --- /dev/null +++ b/openlibm/src/s_ceill.c @@ -0,0 +1,102 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * From: @(#)s_ceil.c 5.1 93/09/24 + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ceill.c,v 1.9 2008/02/14 15:10:33 bde Exp $"); + +/* + * ceill(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to ceill(x). + */ + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#ifdef LDBL_IMPLICIT_NBIT +#define MANH_SIZE (LDBL_MANH_SIZE + 1) +#define INC_MANH(u, c) do { \ + u_int64_t o = u.bits.manh; \ + u.bits.manh += (c); \ + if (u.bits.manh < o) \ + u.bits.exp++; \ +} while (0) +#else +#define MANH_SIZE LDBL_MANH_SIZE +#define INC_MANH(u, c) do { \ + u_int64_t o = u.bits.manh; \ + u.bits.manh += (c); \ + if (u.bits.manh < o) { \ + u.bits.exp++; \ + u.bits.manh |= 1llu << (LDBL_MANH_SIZE - 1); \ + } \ +} while (0) +#endif + +static const long double huge = 1.0e300; + +OLM_DLLEXPORT long double +ceill(long double x) +{ + union IEEEl2bits u = { .e = x }; + int e = u.bits.exp - LDBL_MAX_EXP + 1; + + if (e < MANH_SIZE - 1) { + if (e < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) + if (u.bits.exp > 0 || + (u.bits.manh | u.bits.manl) != 0) + u.e = u.bits.sign ? -0.0 : 1.0; + } else { + u_int64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1); + if (((u.bits.manh & m) | u.bits.manl) == 0) + return (x); /* x is integral */ + if (!u.bits.sign) { +#ifdef LDBL_IMPLICIT_NBIT + if (e == 0) + u.bits.exp++; + else +#endif + INC_MANH(u, 1llu << (MANH_SIZE - e - 1)); + } + if (huge + x > 0.0) { /* raise inexact flag */ + u.bits.manh &= ~m; + u.bits.manl = 0; + } + } + } else if (e < LDBL_MANT_DIG - 1) { + u_int64_t m = (u_int64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1); + if ((u.bits.manl & m) == 0) + return (x); /* x is integral */ + if (!u.bits.sign) { + if (e == MANH_SIZE - 1) + INC_MANH(u, 1); + else { + u_int64_t o = u.bits.manl; + u.bits.manl += 1llu << (LDBL_MANT_DIG - e - 1); + if (u.bits.manl < o) /* got a carry */ + INC_MANH(u, 1); + } + } + if (huge + x > 0.0) /* raise inexact flag */ + u.bits.manl &= ~m; + } + return (u.e); +} diff --git a/openlibm/src/s_cexp.c b/openlibm/src/s_cexp.c new file mode 100644 index 0000000000..1510721373 --- /dev/null +++ b/openlibm/src/s_cexp.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cexp.c,v 1.3 2011/10/21 06:27:56 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const u_int32_t +exp_ovfl = 0x40862e42, /* high bits of MAX_EXP * ln2 ~= 710 */ +cexp_ovfl = 0x4096b8e4; /* (MAX_EXP - MIN_DENORM_EXP) * ln2 */ + +OLM_DLLEXPORT double complex +cexp(double complex z) +{ + double x, y, exp_x; + u_int32_t hx, hy, lx, ly; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hy, ly, y); + hy &= 0x7fffffff; + + /* cexp(x + I 0) = exp(x) + I 0 */ + if ((hy | ly) == 0) + return (CMPLX(exp(x), y)); + EXTRACT_WORDS(hx, lx, x); + /* cexp(0 + I y) = cos(y) + I sin(y) */ + if (((hx & 0x7fffffff) | lx) == 0) + return (CMPLX(cos(y), sin(y))); + + if (hy >= 0x7ff00000) { + if (lx != 0 || (hx & 0x7fffffff) != 0x7ff00000) { + /* cexp(finite|NaN +- I Inf|NaN) = NaN + I NaN */ + return (CMPLX(y - y, y - y)); + } else if (hx & 0x80000000) { + /* cexp(-Inf +- I Inf|NaN) = 0 + I 0 */ + return (CMPLX(0.0, 0.0)); + } else { + /* cexp(+Inf +- I Inf|NaN) = Inf + I NaN */ + return (CMPLX(x, y - y)); + } + } + + if (hx >= exp_ovfl && hx <= cexp_ovfl) { + /* + * x is between 709.7 and 1454.3, so we must scale to avoid + * overflow in exp(x). + */ + return (__ldexp_cexp(z, 0)); + } else { + /* + * Cases covered here: + * - x < exp_ovfl and exp(x) won't overflow (common case) + * - x > cexp_ovfl, so exp(x) * s overflows for all s > 0 + * - x = +-Inf (generated by exp()) + * - x = NaN (spurious inexact exception from y) + */ + exp_x = exp(x); + return (CMPLX(exp_x * cos(y), exp_x * sin(y))); + } +} diff --git a/openlibm/src/s_cexpf.c b/openlibm/src/s_cexpf.c new file mode 100644 index 0000000000..05f4544007 --- /dev/null +++ b/openlibm/src/s_cexpf.c @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cexpf.c,v 1.3 2011/10/21 06:27:56 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const u_int32_t +exp_ovfl = 0x42b17218, /* MAX_EXP * ln2 ~= 88.722839355 */ +cexp_ovfl = 0x43400074; /* (MAX_EXP - MIN_DENORM_EXP) * ln2 */ + +OLM_DLLEXPORT float complex +cexpf(float complex z) +{ + float x, y, exp_x; + u_int32_t hx, hy; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hy, y); + hy &= 0x7fffffff; + + /* cexp(x + I 0) = exp(x) + I 0 */ + if (hy == 0) + return (CMPLXF(expf(x), y)); + GET_FLOAT_WORD(hx, x); + /* cexp(0 + I y) = cos(y) + I sin(y) */ + if ((hx & 0x7fffffff) == 0) + return (CMPLXF(cosf(y), sinf(y))); + + if (hy >= 0x7f800000) { + if ((hx & 0x7fffffff) != 0x7f800000) { + /* cexp(finite|NaN +- I Inf|NaN) = NaN + I NaN */ + return (CMPLXF(y - y, y - y)); + } else if (hx & 0x80000000) { + /* cexp(-Inf +- I Inf|NaN) = 0 + I 0 */ + return (CMPLXF(0.0, 0.0)); + } else { + /* cexp(+Inf +- I Inf|NaN) = Inf + I NaN */ + return (CMPLXF(x, y - y)); + } + } + + if (hx >= exp_ovfl && hx <= cexp_ovfl) { + /* + * x is between 88.7 and 192, so we must scale to avoid + * overflow in expf(x). + */ + return (__ldexp_cexpf(z, 0)); + } else { + /* + * Cases covered here: + * - x < exp_ovfl and exp(x) won't overflow (common case) + * - x > cexp_ovfl, so exp(x) * s overflows for all s > 0 + * - x = +-Inf (generated by exp()) + * - x = NaN (spurious inexact exception from y) + */ + exp_x = expf(x); + return (CMPLXF(exp_x * cosf(y), exp_x * sinf(y))); + } +} diff --git a/openlibm/src/s_cexpl.c b/openlibm/src/s_cexpl.c new file mode 100644 index 0000000000..f143d88e50 --- /dev/null +++ b/openlibm/src/s_cexpl.c @@ -0,0 +1,69 @@ +/* $OpenBSD: s_cexpl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cexpl() + * + * Complex exponential function + * + * + * + * SYNOPSIS: + * + * long double complex cexpl(); + * long double complex z, w; + * + * w = cexpl( z ); + * + * + * + * DESCRIPTION: + * + * Returns the exponential of the complex argument z + * into the complex result w. + * + * If + * z = x + iy, + * r = exp(x), + * + * then + * + * w = r cos y + i r sin y. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 8700 3.7e-17 1.1e-17 + * IEEE -10,+10 30000 3.0e-16 8.7e-17 + * + */ + +#include +#include + +long double complex +cexpl(long double complex z) +{ + long double complex w; + long double r; + + r = expl(creall(z)); + w = r * cosl(cimagl(z)) + (r * sinl(cimagl(z))) * I; + return (w); +} diff --git a/openlibm/src/s_cimag.c b/openlibm/src/s_cimag.c new file mode 100644 index 0000000000..456ae99e20 --- /dev/null +++ b/openlibm/src/s_cimag.c @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_cimag.c,v 1.3 2009/03/14 18:24:15 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +cimag(double complex z) +{ + return (__imag__ z); +} diff --git a/openlibm/src/s_cimagf.c b/openlibm/src/s_cimagf.c new file mode 100644 index 0000000000..8287412f43 --- /dev/null +++ b/openlibm/src/s_cimagf.c @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_cimagf.c,v 1.3 2009/03/14 18:24:15 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +cimagf(float complex z) +{ + return (__imag__ z); +} diff --git a/openlibm/src/s_cimagl.c b/openlibm/src/s_cimagl.c new file mode 100644 index 0000000000..588c6a3234 --- /dev/null +++ b/openlibm/src/s_cimagl.c @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_cimagl.c,v 1.3 2009/03/14 18:24:15 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +cimagl(long double complex z) +{ + return (__imag__ z); +} diff --git a/openlibm/src/s_clog.c b/openlibm/src/s_clog.c new file mode 100644 index 0000000000..6412df78f3 --- /dev/null +++ b/openlibm/src/s_clog.c @@ -0,0 +1,79 @@ +/* $OpenBSD: s_clog.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* clog.c + * + * Complex natural logarithm + * + * + * + * SYNOPSIS: + * + * double complex clog(); + * double complex z, w; + * + * w = clog (z); + * + * + * + * DESCRIPTION: + * + * Returns complex logarithm to the base e (2.718...) of + * the complex argument x. + * + * If z = x + iy, r = sqrt( x**2 + y**2 ), + * then + * w = log(r) + i arctan(y/x). + * + * The arctangent ranges from -PI to +PI. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 7000 8.5e-17 1.9e-17 + * IEEE -10,+10 30000 5.0e-15 1.1e-16 + * + * Larger relative error can be observed for z near 1 +i0. + * In IEEE arithmetic the peak absolute error is 5.2e-16, rms + * absolute error 1.0e-16. + */ + +#include +#include +#include + +#include "math_private.h" + +double complex +clog(double complex z) +{ + double complex w; + double p, rr; + + /*rr = sqrt( z->r * z->r + z->i * z->i );*/ + rr = cabs(z); + p = log(rr); + rr = atan2 (cimag (z), creal (z)); + w = p + rr * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(clog, clogl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_clogf.c b/openlibm/src/s_clogf.c new file mode 100644 index 0000000000..e157aaeafe --- /dev/null +++ b/openlibm/src/s_clogf.c @@ -0,0 +1,72 @@ +/* $OpenBSD: s_clogf.c,v 1.2 2010/07/18 18:42:26 guenther Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* clogf.c + * + * Complex natural logarithm + * + * + * + * SYNOPSIS: + * + * void clogf(); + * cmplxf z, w; + * + * clogf( &z, &w ); + * + * + * + * DESCRIPTION: + * + * Returns complex logarithm to the base e (2.718...) of + * the complex argument x. + * + * If z = x + iy, r = sqrt( x**2 + y**2 ), + * then + * w = log(r) + i arctan(y/x). + * + * The arctangent ranges from -PI to +PI. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.9e-6 6.2e-8 + * + * Larger relative error can be observed for z near 1 +i0. + * In IEEE arithmetic the peak absolute error is 3.1e-7. + * + */ + +#include +#include + +float complex +clogf(float complex z) +{ + float complex w; + float p, rr, x, y; + + x = crealf(z); + y = cimagf(z); + rr = atan2f(y, x); + p = cabsf(z); + p = logf(p); + w = p + rr * I; + return (w); +} diff --git a/openlibm/src/s_clogl.c b/openlibm/src/s_clogl.c new file mode 100644 index 0000000000..c337103c77 --- /dev/null +++ b/openlibm/src/s_clogl.c @@ -0,0 +1,73 @@ +/* $OpenBSD: s_clogl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* clogl.c + * + * Complex natural logarithm + * + * + * + * SYNOPSIS: + * + * long double complex clogl(); + * long double complex z, w; + * + * w = clogl( z ); + * + * + * + * DESCRIPTION: + * + * Returns complex logarithm to the base e (2.718...) of + * the complex argument x. + * + * If z = x + iy, r = sqrt( x**2 + y**2 ), + * then + * w = log(r) + i arctan(y/x). + * + * The arctangent ranges from -PI to +PI. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 7000 8.5e-17 1.9e-17 + * IEEE -10,+10 30000 5.0e-15 1.1e-16 + * + * Larger relative error can be observed for z near 1 +i0. + * In IEEE arithmetic the peak absolute error is 5.2e-16, rms + * absolute error 1.0e-16. + */ + +#include +#include + +long double complex +clogl(long double complex z) +{ + long double complex w; + long double p, rr; + + /*rr = sqrt(z->r * z->r + z->i * z->i);*/ + p = cabsl(z); + p = logl(p); + rr = atan2l(cimagl(z), creall(z)); + w = p + rr * I; + return (w); +} diff --git a/openlibm/src/s_conj.c b/openlibm/src/s_conj.c new file mode 100644 index 0000000000..a7c09404bf --- /dev/null +++ b/openlibm/src/s_conj.c @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_conj.c,v 1.2 2008/08/07 14:39:56 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double complex +conj(double complex z) +{ + + return (CMPLX(creal(z), -cimag(z))); +} diff --git a/openlibm/src/s_conjf.c b/openlibm/src/s_conjf.c new file mode 100644 index 0000000000..d2ff74362b --- /dev/null +++ b/openlibm/src/s_conjf.c @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_conjf.c,v 1.2 2008/08/07 14:39:56 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float complex +conjf(float complex z) +{ + + return (CMPLXF(crealf(z), -cimagf(z))); +} diff --git a/openlibm/src/s_conjl.c b/openlibm/src/s_conjl.c new file mode 100644 index 0000000000..e4d7a01b7c --- /dev/null +++ b/openlibm/src/s_conjl.c @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_conjl.c,v 1.2 2008/08/07 14:39:56 das Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double complex +conjl(long double complex z) +{ + + return (CMPLXL(creall(z), -cimagl(z))); +} diff --git a/openlibm/src/s_copysign.c b/openlibm/src/s_copysign.c new file mode 100644 index 0000000000..a52701f153 --- /dev/null +++ b/openlibm/src/s_copysign.c @@ -0,0 +1,34 @@ +/* @(#)s_copysign.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_copysign.c,v 1.10 2008/02/22 02:30:35 das Exp $"); + +/* + * copysign(double x, double y) + * copysign(x,y) returns a value with the magnitude of x and + * with the sign bit of y. + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +copysign(double x, double y) +{ + u_int32_t hx,hy; + GET_HIGH_WORD(hx,x); + GET_HIGH_WORD(hy,y); + SET_HIGH_WORD(x,(hx&0x7fffffff)|(hy&0x80000000)); + return x; +} diff --git a/openlibm/src/s_copysignf.c b/openlibm/src/s_copysignf.c new file mode 100644 index 0000000000..945b3fdeb0 --- /dev/null +++ b/openlibm/src/s_copysignf.c @@ -0,0 +1,37 @@ +/* s_copysignf.c -- float version of s_copysign.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_copysignf.c,v 1.10 2008/02/22 02:30:35 das Exp $"); + +/* + * copysignf(float x, float y) + * copysignf(x,y) returns a value with the magnitude of x and + * with the sign bit of y. + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +copysignf(float x, float y) +{ + u_int32_t ix,iy; + GET_FLOAT_WORD(ix,x); + GET_FLOAT_WORD(iy,y); + SET_FLOAT_WORD(x,(ix&0x7fffffff)|(iy&0x80000000)); + return x; +} diff --git a/openlibm/src/s_copysignl.c b/openlibm/src/s_copysignl.c new file mode 100644 index 0000000000..6cae5d3221 --- /dev/null +++ b/openlibm/src/s_copysignl.c @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_copysignl.c,v 1.2 2007/01/07 07:54:21 das Exp $ + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT long double +copysignl(long double x, long double y) +{ + union IEEEl2bits ux, uy; + + ux.e = x; + uy.e = y; + ux.bits.sign = uy.bits.sign; + return (ux.e); +} diff --git a/openlibm/src/s_cos.c b/openlibm/src/s_cos.c new file mode 100644 index 0000000000..9896bd8219 --- /dev/null +++ b/openlibm/src/s_cos.c @@ -0,0 +1,89 @@ +/* @(#)s_cos.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cos.c,v 1.13 2011/02/10 07:37:50 das Exp $"); + +/* cos(x) + * Return cosine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cosine function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include +#include + +//#define INLINE_REM_PIO2 +#include "math_private.h" +//#include "e_rem_pio2.c" + +OLM_DLLEXPORT double +cos(double x) +{ + double y[2],z=0.0; + int32_t n, ix; + + /* High word of x. */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) { + if(ix<0x3e46a09e) /* if x < 2**-27 * sqrt(2) */ + if(((int)x)==0) return 1.0; /* generate inexact */ + return __kernel_cos(x,z); + } + + /* cos(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_cos(y[0],y[1]); + case 1: return -__kernel_sin(y[0],y[1],1); + case 2: return -__kernel_cos(y[0],y[1]); + default: + return __kernel_sin(y[0],y[1],1); + } + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(cos, cosl); +#endif diff --git a/openlibm/src/s_cosf.c b/openlibm/src/s_cosf.c new file mode 100644 index 0000000000..57ec7c7b14 --- /dev/null +++ b/openlibm/src/s_cosf.c @@ -0,0 +1,85 @@ +/* s_cosf.c -- float version of s_cos.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cosf.c,v 1.18 2008/02/25 22:19:17 bde Exp $"); + +#include +#include + +//#define INLINE_KERNEL_COSDF +//#define INLINE_KERNEL_SINDF +//#define INLINE_REM_PIO2F +#include "math_private.h" +//#include "e_rem_pio2f.c" +//#include "k_cosf.c" +//#include "k_sinf.c" + +/* Small multiples of pi/2 rounded to double precision. */ +static const double +c1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */ +c2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */ +c3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */ +c4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */ + +OLM_DLLEXPORT float +cosf(float x) +{ + double y; + int32_t n, hx, ix; + + GET_FLOAT_WORD(hx,x); + ix = hx & 0x7fffffff; + + if(ix <= 0x3f490fda) { /* |x| ~<= pi/4 */ + if(ix<0x39800000) /* |x| < 2**-12 */ + if(((int)x)==0) return 1.0; /* 1 with inexact if x != 0 */ + return __kernel_cosdf(x); + } + if(ix<=0x407b53d1) { /* |x| ~<= 5*pi/4 */ + if(ix<=0x4016cbe3) { /* |x| ~> 3*pi/4 */ + if(hx>0) + return __kernel_sindf(c1pio2 - x); + else + return __kernel_sindf(x + c1pio2); + } else + return -__kernel_cosdf(x + (hx > 0 ? -c2pio2 : c2pio2)); + } + if(ix<=0x40e231d5) { /* |x| ~<= 9*pi/4 */ + if(ix<=0x40afeddf) { /* |x| ~> 7*pi/4 */ + if(hx>0) + return __kernel_sindf(x - c3pio2); + else + return __kernel_sindf(-c3pio2 - x); + } else + return __kernel_cosdf(x + (hx > 0 ? -c4pio2 : c4pio2)); + } + + /* cos(Inf or NaN) is NaN */ + else if (ix>=0x7f800000) return x-x; + + /* general argument reduction needed */ + else { + n = __ieee754_rem_pio2f(x,&y); + switch(n&3) { + case 0: return __kernel_cosdf(y); + case 1: return __kernel_sindf(-y); + case 2: return -__kernel_cosdf(y); + default: + return __kernel_sindf(y); + } + } +} diff --git a/openlibm/src/s_cosl.c b/openlibm/src/s_cosl.c new file mode 100644 index 0000000000..886d3e3ba1 --- /dev/null +++ b/openlibm/src/s_cosl.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2007 Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cosl.c,v 1.3 2011/05/30 19:41:28 kargl Exp $"); + +/* + * Limited testing on pseudorandom numbers drawn within [-2e8:4e8] shows + * an accuracy of <= 0.7412 ULP. + */ + +#include +#include + +#include "math_private.h" +#if LDBL_MANT_DIG == 64 +#include "../ld80/e_rem_pio2l.h" +#elif LDBL_MANT_DIG == 113 +#include "../ld128/e_rem_pio2l.h" +#else +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT long double +cosl(long double x) +{ + union IEEEl2bits z; + int e0; + long double y[2]; + long double hi, lo; + + z.e = x; + z.bits.sign = 0; + + /* If x = +-0 or x is a subnormal number, then cos(x) = 1 */ + if (z.bits.exp == 0) + return (1.0); + + /* If x = NaN or Inf, then cos(x) = NaN. */ + if (z.bits.exp == 32767) + return ((x - x) / (x - x)); + + /* Optimize the case where x is already within range. */ + if (z.e < M_PI_4) + return (__kernel_cosl(z.e, 0)); + + e0 = __ieee754_rem_pio2l(x, y); + hi = y[0]; + lo = y[1]; + + switch (e0 & 3) { + case 0: + hi = __kernel_cosl(hi, lo); + break; + case 1: + hi = - __kernel_sinl(hi, lo, 1); + break; + case 2: + hi = - __kernel_cosl(hi, lo); + break; + case 3: + hi = __kernel_sinl(hi, lo, 1); + break; + } + + return (hi); +} diff --git a/openlibm/src/s_cpow.c b/openlibm/src/s_cpow.c new file mode 100644 index 0000000000..dae6251f63 --- /dev/null +++ b/openlibm/src/s_cpow.c @@ -0,0 +1,78 @@ +/* $OpenBSD: s_cpow.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cpow + * + * Complex power function + * + * + * + * SYNOPSIS: + * + * double complex cpow(); + * double complex a, z, w; + * + * w = cpow (a, z); + * + * + * + * DESCRIPTION: + * + * Raises complex A to the complex Zth power. + * Definition is per AMS55 # 4.2.8, + * analytically equivalent to cpow(a,z) = cexp(z clog(a)). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 9.4e-15 1.5e-15 + * + */ + +#include +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double complex +cpow(double complex a, double complex z) +{ + double complex w; + double x, y, r, theta, absa, arga; + + x = creal (z); + y = cimag (z); + absa = cabs (a); + if (absa == 0.0) { + return (0.0 + 0.0 * I); + } + arga = carg (a); + r = pow (absa, x); + theta = x * arga; + if (y != 0.0) { + r = r * exp (-y * arga); + theta = theta + y * log (absa); + } + w = r * cos (theta) + (r * sin (theta)) * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(cpow, cpowl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_cpowf.c b/openlibm/src/s_cpowf.c new file mode 100644 index 0000000000..764053dc3b --- /dev/null +++ b/openlibm/src/s_cpowf.c @@ -0,0 +1,73 @@ +/* $OpenBSD: s_cpowf.c,v 1.2 2010/07/18 18:42:26 guenther Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cpowf + * + * Complex power function + * + * + * + * SYNOPSIS: + * + * float complex cpowf(); + * float complex a, z, w; + * + * w = cpowf (a, z); + * + * + * + * DESCRIPTION: + * + * Raises complex A to the complex Zth power. + * Definition is per AMS55 # 4.2.8, + * analytically equivalent to cpow(a,z) = cexp(z clog(a)). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 9.4e-15 1.5e-15 + * + */ + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT float complex +cpowf(float complex a, float complex z) +{ + float complex w; + float x, y, r, theta, absa, arga; + + x = crealf(z); + y = cimagf(z); + absa = cabsf (a); + if (absa == 0.0f) { + return (0.0f + 0.0f * I); + } + arga = cargf (a); + r = powf (absa, x); + theta = x * arga; + if (y != 0.0f) { + r = r * expf (-y * arga); + theta = theta + y * logf (absa); + } + w = r * cosf (theta) + (r * sinf (theta)) * I; + return (w); +} diff --git a/openlibm/src/s_cpowl.c b/openlibm/src/s_cpowl.c new file mode 100644 index 0000000000..81c9afda2f --- /dev/null +++ b/openlibm/src/s_cpowl.c @@ -0,0 +1,74 @@ +/* $OpenBSD: s_cpowl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* cpowl + * + * Complex power function + * + * + * + * SYNOPSIS: + * + * long double complex cpowl(); + * long double complex a, z, w; + * + * w = cpowl (a, z); + * + * + * + * DESCRIPTION: + * + * Raises complex A to the complex Zth power. + * Definition is per AMS55 # 4.2.8, + * analytically equivalent to cpow(a,z) = cexp(z clog(a)). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 9.4e-15 1.5e-15 + * + */ + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double complex +cpowl(long double complex a, long double complex z) +{ + long double complex w; + long double x, y, r, theta, absa, arga; + + x = creall(z); + y = cimagl(z); + absa = cabsl(a); + if (absa == 0.0L) { + return (0.0L + 0.0L * I); + } + arga = cargl(a); + r = powl(absa, x); + theta = x * arga; + if (y != 0.0L) { + r = r * expl(-y * arga); + theta = theta + y * logl(absa); + } + w = r * cosl(theta) + (r * sinl(theta)) * I; + return (w); +} diff --git a/openlibm/src/s_cproj.c b/openlibm/src/s_cproj.c new file mode 100644 index 0000000000..b9faa24077 --- /dev/null +++ b/openlibm/src/s_cproj.c @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cproj.c,v 1.1 2008/08/07 15:07:48 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double complex +cproj(double complex z) +{ + + if (!isinf(creal(z)) && !isinf(cimag(z))) + return (z); + else + return (CMPLX(INFINITY, copysign(0.0, cimag(z)))); +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(cproj, cprojl); +#endif diff --git a/openlibm/src/s_cprojf.c b/openlibm/src/s_cprojf.c new file mode 100644 index 0000000000..717c165b8b --- /dev/null +++ b/openlibm/src/s_cprojf.c @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cprojf.c,v 1.1 2008/08/07 15:07:48 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT float complex +cprojf(float complex z) +{ + + if (!isinf(crealf(z)) && !isinf(cimagf(z))) + return (z); + else + return (CMPLXF(INFINITY, copysignf(0.0, cimagf(z)))); +} diff --git a/openlibm/src/s_cprojl.c b/openlibm/src/s_cprojl.c new file mode 100644 index 0000000000..753ab4c3d7 --- /dev/null +++ b/openlibm/src/s_cprojl.c @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_cprojl.c,v 1.1 2008/08/07 15:07:48 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double complex +cprojl(long double complex z) +{ + + if (!isinf(creall(z)) && !isinf(cimagl(z))) + return (z); + else + return (CMPLXL(INFINITY, copysignl(0.0, cimagl(z)))); +} diff --git a/openlibm/src/s_creal.c b/openlibm/src/s_creal.c new file mode 100644 index 0000000000..28a0fbfa75 --- /dev/null +++ b/openlibm/src/s_creal.c @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_creal.c,v 1.1 2004/05/30 09:21:56 stefanf Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +creal(double complex z) +{ + return z; +} diff --git a/openlibm/src/s_crealf.c b/openlibm/src/s_crealf.c new file mode 100644 index 0000000000..9aaed5f889 --- /dev/null +++ b/openlibm/src/s_crealf.c @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_crealf.c,v 1.1 2004/05/30 09:21:56 stefanf Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +crealf(float complex z) +{ + return z; +} diff --git a/openlibm/src/s_creall.c b/openlibm/src/s_creall.c new file mode 100644 index 0000000000..576666ede2 --- /dev/null +++ b/openlibm/src/s_creall.c @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2004 Stefan Farfeleder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_creall.c,v 1.1 2004/05/30 09:21:56 stefanf Exp $ + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +creall(long double complex z) +{ + return z; +} diff --git a/openlibm/src/s_csin.c b/openlibm/src/s_csin.c new file mode 100644 index 0000000000..a905991eab --- /dev/null +++ b/openlibm/src/s_csin.c @@ -0,0 +1,93 @@ +/* $OpenBSD: s_csin.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* csin() + * + * Complex circular sine + * + * + * + * SYNOPSIS: + * + * double complex csin(); + * double complex z, w; + * + * w = csin (z); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * w = sin x cosh y + i cos x sinh y. + * + * csin(z) = -i csinh(iz). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 8400 5.3e-17 1.3e-17 + * IEEE -10,+10 30000 3.8e-16 1.0e-16 + * Also tested by csin(casin(z)) = z. + * + */ + +#include +#include +#include + +#include "math_private.h" + +/* calculate cosh and sinh */ + +static void +cchsh(double x, double *c, double *s) +{ + double e, ei; + + if (fabs(x) <= 0.5) { + *c = cosh(x); + *s = sinh(x); + } + else { + e = exp(x); + ei = 0.5/e; + e = 0.5 * e; + *s = e - ei; + *c = e + ei; + } +} + +double complex +csin(double complex z) +{ + double complex w; + double ch, sh; + + cchsh( cimag (z), &ch, &sh ); + w = sin (creal(z)) * ch + (cos (creal(z)) * sh) * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(csin, csinl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_csinf.c b/openlibm/src/s_csinf.c new file mode 100644 index 0000000000..42aa9817f4 --- /dev/null +++ b/openlibm/src/s_csinf.c @@ -0,0 +1,85 @@ +/* $OpenBSD: s_csinf.c,v 1.2 2010/07/18 18:42:26 guenther Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* csinf() + * + * Complex circular sine + * + * + * + * SYNOPSIS: + * + * void csinf(); + * cmplxf z, w; + * + * csinf( &z, &w ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * w = sin x cosh y + i cos x sinh y. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.9e-7 5.5e-8 + * + */ + +#include +#include + +/* calculate cosh and sinh */ + +static void +cchshf(float xx, float *c, float *s) +{ + float x, e, ei; + + x = xx; + if(fabsf(x) <= 0.5f) { + *c = coshf(x); + *s = sinhf(x); + } + else { + e = expf(x); + ei = 0.5f/e; + e = 0.5f * e; + *s = e - ei; + *c = e + ei; + } +} + +float complex +csinf(float complex z) +{ + float complex w; + float ch, sh; + + cchshf(cimagf(z), &ch, &sh); + w = sinf(crealf(z)) * ch + (cosf(crealf(z)) * sh) * I; + return (w); +} diff --git a/openlibm/src/s_csinh.c b/openlibm/src/s_csinh.c new file mode 100644 index 0000000000..3838f25225 --- /dev/null +++ b/openlibm/src/s_csinh.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Hyperbolic sine of a complex argument z = x + i y. + * + * sinh(z) = sinh(x+iy) + * = sinh(x) cos(y) + i cosh(x) sin(y). + * + * Exceptional values are noted in the comments within the source code. + * These values and the return value were taken from n1124.pdf. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_csinh.c,v 1.2 2011/10/21 06:29:32 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const double huge = 0x1p1023; + +OLM_DLLEXPORT double complex +csinh(double complex z) +{ + double x, y, h; + int32_t hx, hy, ix, iy, lx, ly; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + /* Handle the nearly-non-exceptional cases where x and y are finite. */ + if (ix < 0x7ff00000 && iy < 0x7ff00000) { + if ((iy | ly) == 0) + return (CMPLX(sinh(x), y)); + if (ix < 0x40360000) /* small x: normal case */ + return (CMPLX(sinh(x) * cos(y), cosh(x) * sin(y))); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (ix < 0x40862e42) { + /* x < 710: exp(|x|) won't overflow */ + h = exp(fabs(x)) * 0.5; + return (CMPLX(copysign(h, x) * cos(y), h * sin(y))); + } else if (ix < 0x4096bbaa) { + /* x < 1455: scale to avoid overflow */ + z = __ldexp_cexp(CMPLX(fabs(x), y), -1); + return (CMPLX(creal(z) * copysign(1, x), cimag(z))); + } else { + /* x >= 1455: the result always overflows */ + h = huge * x; + return (CMPLX(h * cos(y), h * h * sin(y))); + } + } + + /* + * sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as dNaN. Raise the invalid floating-point exception. + * + * sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN). + * The sign of 0 in the result is unspecified. Choice = normally + * the same as d(NaN). + */ + if ((ix | lx) == 0 && iy >= 0x7ff00000) + return (CMPLX(copysign(0, x * (y - y)), y - y)); + + /* + * sinh(+-Inf +- I 0) = +-Inf + I +-0. + * + * sinh(NaN +- I 0) = d(NaN) + I +-0. + */ + if ((iy | ly) == 0 && ix >= 0x7ff00000) { + if (((hx & 0xfffff) | lx) == 0) + return (CMPLX(x, y)); + return (CMPLX(x, copysign(0, y))); + } + + /* + * sinh(x +- I Inf) = dNaN + I dNaN. + * Raise the invalid floating-point exception for finite nonzero x. + * + * sinh(x + I NaN) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero x. Choice = don't raise (except for signaling NaNs). + */ + if (ix < 0x7ff00000 && iy >= 0x7ff00000) + return (CMPLX(y - y, x * (y - y))); + + /* + * sinh(+-Inf + I NaN) = +-Inf + I d(NaN). + * The sign of Inf in the result is unspecified. Choice = normally + * the same as d(NaN). + * + * sinh(+-Inf +- I Inf) = +Inf + I dNaN. + * The sign of Inf in the result is unspecified. Choice = always +. + * Raise the invalid floating-point exception. + * + * sinh(+-Inf + I y) = +-Inf cos(y) + I Inf sin(y) + */ + if (ix >= 0x7ff00000 && ((hx & 0xfffff) | lx) == 0) { + if (iy >= 0x7ff00000) + return (CMPLX(x * x, x * (y - y))); + return (CMPLX(x * cos(y), INFINITY * sin(y))); + } + + /* + * sinh(NaN + I NaN) = d(NaN) + I d(NaN). + * + * sinh(NaN +- I Inf) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception. + * Choice = raise. + * + * sinh(NaN + I y) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero y. Choice = don't raise (except for signaling NaNs). + */ + return (CMPLX((x * x) * (y - y), (x + x) * (y - y))); +} + +OLM_DLLEXPORT double complex +csin(double complex z) +{ + + /* csin(z) = -I * csinh(I * z) */ + z = csinh(CMPLX(-cimag(z), creal(z))); + return (CMPLX(cimag(z), -creal(z))); +} diff --git a/openlibm/src/s_csinhf.c b/openlibm/src/s_csinhf.c new file mode 100644 index 0000000000..94954b07b0 --- /dev/null +++ b/openlibm/src/s_csinhf.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2005 Bruce D. Evans and Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Hyperbolic sine of a complex argument z. See s_csinh.c for details. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_csinhf.c,v 1.2 2011/10/21 06:29:32 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const float huge = 0x1p127; + +OLM_DLLEXPORT float complex +csinhf(float complex z) +{ + float x, y, h; + int32_t hx, hy, ix, iy; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hx, x); + GET_FLOAT_WORD(hy, y); + + ix = 0x7fffffff & hx; + iy = 0x7fffffff & hy; + + if (ix < 0x7f800000 && iy < 0x7f800000) { + if (iy == 0) + return (CMPLXF(sinhf(x), y)); + if (ix < 0x41100000) /* small x: normal case */ + return (CMPLXF(sinhf(x) * cosf(y), coshf(x) * sinf(y))); + + /* |x| >= 9, so cosh(x) ~= exp(|x|) */ + if (ix < 0x42b17218) { + /* x < 88.7: expf(|x|) won't overflow */ + h = expf(fabsf(x)) * 0.5f; + return (CMPLXF(copysignf(h, x) * cosf(y), h * sinf(y))); + } else if (ix < 0x4340b1e7) { + /* x < 192.7: scale to avoid overflow */ + z = __ldexp_cexpf(CMPLXF(fabsf(x), y), -1); + return (CMPLXF(crealf(z) * copysignf(1, x), cimagf(z))); + } else { + /* x >= 192.7: the result always overflows */ + h = huge * x; + return (CMPLXF(h * cosf(y), h * h * sinf(y))); + } + } + + if (ix == 0 && iy >= 0x7f800000) + return (CMPLXF(copysignf(0, x * (y - y)), y - y)); + + if (iy == 0 && ix >= 0x7f800000) { + if ((hx & 0x7fffff) == 0) + return (CMPLXF(x, y)); + return (CMPLXF(x, copysignf(0, y))); + } + + if (ix < 0x7f800000 && iy >= 0x7f800000) + return (CMPLXF(y - y, x * (y - y))); + + if (ix >= 0x7f800000 && (hx & 0x7fffff) == 0) { + if (iy >= 0x7f800000) + return (CMPLXF(x * x, x * (y - y))); + return (CMPLXF(x * cosf(y), INFINITY * sinf(y))); + } + + return (CMPLXF((x * x) * (y - y), (x + x) * (y - y))); +} + +OLM_DLLEXPORT float complex +csinf(float complex z) +{ + + z = csinhf(CMPLXF(-cimagf(z), crealf(z))); + return (CMPLXF(cimagf(z), -crealf(z))); +} diff --git a/openlibm/src/s_csinhl.c b/openlibm/src/s_csinhl.c new file mode 100644 index 0000000000..57b577ff41 --- /dev/null +++ b/openlibm/src/s_csinhl.c @@ -0,0 +1,58 @@ +/* $OpenBSD: s_csinhl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* csinhl + * + * Complex hyperbolic sine + * + * + * + * SYNOPSIS: + * + * long double complex csinhl(); + * long double complex z, w; + * + * w = csinhl (z); + * + * DESCRIPTION: + * + * csinh z = (cexp(z) - cexp(-z))/2 + * = sinh x * cos y + i cosh x * sin y . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 3.1e-16 8.2e-17 + * + */ + +#include +#include + +long double complex +csinhl(long double complex z) +{ + long double complex w; + long double x, y; + + x = creall(z); + y = cimagl(z); + w = sinhl(x) * cosl(y) + (coshl(x) * sinl(y)) * I; + return (w); +} diff --git a/openlibm/src/s_csinl.c b/openlibm/src/s_csinl.c new file mode 100644 index 0000000000..3470ac5fd4 --- /dev/null +++ b/openlibm/src/s_csinl.c @@ -0,0 +1,84 @@ +/* $OpenBSD: s_csinl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* csinl() + * + * Complex circular sine + * + * + * + * SYNOPSIS: + * + * long double complex csinl(); + * long double complex z, w; + * + * w = csinl( z ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * w = sin x cosh y + i cos x sinh y. + * + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 8400 5.3e-17 1.3e-17 + * IEEE -10,+10 30000 3.8e-16 1.0e-16 + * Also tested by csin(casin(z)) = z. + * + */ + +#include +#include + +static void +cchshl(long double x, long double *c, long double *s) +{ + long double e, ei; + + if(fabsl(x) <= 0.5L) { + *c = coshl(x); + *s = sinhl(x); + } else { + e = expl(x); + ei = 0.5L/e; + e = 0.5L * e; + *s = e - ei; + *c = e + ei; + } +} + +long double complex +csinl(long double complex z) +{ + long double complex w; + long double ch, sh; + + cchshl(cimagl(z), &ch, &sh); + w = sinl(creall(z)) * ch + (cosl(creall(z)) * sh) * I; + return (w); +} diff --git a/openlibm/src/s_csqrt.c b/openlibm/src/s_csqrt.c new file mode 100644 index 0000000000..9cf42556c1 --- /dev/null +++ b/openlibm/src/s_csqrt.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_csqrt.c,v 1.4 2008/08/08 00:15:16 das Exp $"); + +#include +#include +#include + +#include "math_private.h" + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +#ifndef __GNUC__ +#pragma STDC CX_LIMITED_RANGE ON +#endif + +/* We risk spurious overflow for components >= DBL_MAX / (1 + sqrt(2)). */ +#define THRESH 0x1.a827999fcef32p+1022 + +OLM_DLLEXPORT double complex +csqrt(double complex z) +{ + double complex result; + double a, b; + double t; + int scale; + + a = creal(z); + b = cimag(z); + + /* Handle special cases. */ + if (z == 0) + return (CMPLX(0, b)); + if (isinf(b)) + return (CMPLX(INFINITY, b)); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return (CMPLX(a, t)); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrt(inf + NaN i) = inf + NaN i + * csqrt(inf + y i) = inf + 0 i + * csqrt(-inf + NaN i) = NaN +- inf i + * csqrt(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return (CMPLX(fabs(b - b), copysign(a, b))); + else + return (CMPLX(a, copysign(b - b, b))); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* Scale to avoid overflow. */ + if (fabs(a) >= THRESH || fabs(b) >= THRESH) { + a *= 0.25; + b *= 0.25; + scale = 1; + } else { + scale = 0; + } + + /* Algorithm 312, CACM vol 10, Oct 1967. */ + if (a >= 0) { + t = sqrt((a + hypot(a, b)) * 0.5); + result = CMPLX(t, b / (2 * t)); + } else { + t = sqrt((-a + hypot(a, b)) * 0.5); + result = CMPLX(fabs(b) / (2 * t), copysign(t, b)); + } + + /* Rescale. */ + if (scale) + return (result * 2); + else + return (result); +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(csqrt, csqrtl); +#endif diff --git a/openlibm/src/s_csqrtf.c b/openlibm/src/s_csqrtf.c new file mode 100644 index 0000000000..a1318290ee --- /dev/null +++ b/openlibm/src/s_csqrtf.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_csqrtf.c,v 1.3 2008/08/08 00:15:16 das Exp $"); + +#include +#include + +#include "math_private.h" + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +#ifndef __GNUC__ +#pragma STDC CX_LIMITED_RANGE ON +#endif + +OLM_DLLEXPORT float complex +csqrtf(float complex z) +{ + float a = crealf(z), b = cimagf(z); + double t; + + /* Handle special cases. */ + if (z == 0) + return (CMPLXF(0, b)); + if (isinf(b)) + return (CMPLXF(INFINITY, b)); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return (CMPLXF(a, t)); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrtf(inf + NaN i) = inf + NaN i + * csqrtf(inf + y i) = inf + 0 i + * csqrtf(-inf + NaN i) = NaN +- inf i + * csqrtf(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return (CMPLXF(fabsf(b - b), copysignf(a, b))); + else + return (CMPLXF(a, copysignf(b - b, b))); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* + * We compute t in double precision to avoid overflow and to + * provide correct rounding in nearly all cases. + * This is Algorithm 312, CACM vol 10, Oct 1967. + */ + if (a >= 0) { + t = sqrt((a + hypot(a, b)) * 0.5); + return (CMPLXF(t, b / (2.0 * t))); + } else { + t = sqrt((-a + hypot(a, b)) * 0.5); + return (CMPLXF(fabsf(b) / (2.0 * t), copysignf(t, b))); + } +} diff --git a/openlibm/src/s_csqrtl.c b/openlibm/src/s_csqrtl.c new file mode 100644 index 0000000000..da97fb2dbf --- /dev/null +++ b/openlibm/src/s_csqrtl.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 2007-2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" + +#include +#include +#include + +#include "math_private.h" + +/* + * gcc doesn't implement complex multiplication or division correctly, + * so we need to handle infinities specially. We turn on this pragma to + * notify conforming c99 compilers that the fast-but-incorrect code that + * gcc generates is acceptable, since the special cases have already been + * handled. + */ +#ifndef __GNUC__ +#pragma STDC CX_LIMITED_RANGE ON +#endif + +/* We risk spurious overflow for components >= LDBL_MAX / (1 + sqrt(2)). */ +#define THRESH (LDBL_MAX / 2.414213562373095048801688724209698L) + +OLM_DLLEXPORT long double complex +csqrtl(long double complex z) +{ + long double complex result; + long double a, b; + long double t; + int scale; + + a = creall(z); + b = cimagl(z); + + /* Handle special cases. */ + if (z == 0) + return (CMPLXL(0, b)); + if (isinf(b)) + return (CMPLXL(INFINITY, b)); + if (isnan(a)) { + t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ + return (CMPLXL(a, t)); /* return NaN + NaN i */ + } + if (isinf(a)) { + /* + * csqrt(inf + NaN i) = inf + NaN i + * csqrt(inf + y i) = inf + 0 i + * csqrt(-inf + NaN i) = NaN +- inf i + * csqrt(-inf + y i) = 0 + inf i + */ + if (signbit(a)) + return (CMPLXL(fabsl(b - b), copysignl(a, b))); + else + return (CMPLXL(a, copysignl(b - b, b))); + } + /* + * The remaining special case (b is NaN) is handled just fine by + * the normal code path below. + */ + + /* Scale to avoid overflow. */ + if (fabsl(a) >= THRESH || fabsl(b) >= THRESH) { + a *= 0.25; + b *= 0.25; + scale = 1; + } else { + scale = 0; + } + + /* Algorithm 312, CACM vol 10, Oct 1967. */ + if (a >= 0) { + t = sqrtl((a + hypotl(a, b)) * 0.5); + result = CMPLXL(t, b / (2 * t)); + } else { + t = sqrtl((-a + hypotl(a, b)) * 0.5); + result = CMPLXL(fabsl(b) / (2 * t), copysignl(t, b)); + } + + /* Rescale. */ + if (scale) + return (result * 2); + else + return (result); +} diff --git a/openlibm/src/s_ctan.c b/openlibm/src/s_ctan.c new file mode 100644 index 0000000000..94200959c6 --- /dev/null +++ b/openlibm/src/s_ctan.c @@ -0,0 +1,159 @@ +/* $OpenBSD: s_ctan.c,v 1.6 2013/07/03 04:46:36 espie Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ctan() + * + * Complex circular tangent + * + * + * + * SYNOPSIS: + * + * double complex ctan(); + * double complex z, w; + * + * w = ctan (z); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * sin 2x + i sinh 2y + * w = --------------------. + * cos 2x + cosh 2y + * + * On the real axis the denominator is zero at odd multiples + * of PI/2. The denominator is evaluated by its Taylor + * series near these points. + * + * ctan(z) = -i ctanh(iz). + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5200 7.1e-17 1.6e-17 + * IEEE -10,+10 30000 7.2e-16 1.2e-16 + * Also tested by ctan * ccot = 1 and catan(ctan(z)) = z. + */ + +#include +#include +#include + +#include "math_private.h" + +#define MACHEP 1.1e-16 +#define MAXNUM 1.0e308 + +static const double DP1 = 3.14159265160560607910E0; +static const double DP2 = 1.98418714791870343106E-9; +static const double DP3 = 1.14423774522196636802E-17; + +static double +_redupi(double x) +{ + double t; + long i; + + t = x/M_PI; + if (t >= 0.0) + t += 0.5; + else + t -= 0.5; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return (t); +} + +/* Taylor series expansion for cosh(2y) - cos(2x) */ + +static double +_ctans(double complex z) +{ + double f, x, x2, y, y2, rn, t; + double d; + + x = fabs (2.0 * creal (z)); + y = fabs (2.0 * cimag(z)); + + x = _redupi(x); + + x = x * x; + y = y * y; + x2 = 1.0; + y2 = 1.0; + f = 1.0; + rn = 0.0; + d = 0.0; + do { + rn += 1.0; + f *= rn; + rn += 1.0; + f *= rn; + x2 *= x; + y2 *= y; + t = y2 + x2; + t /= f; + d += t; + + rn += 1.0; + f *= rn; + rn += 1.0; + f *= rn; + x2 *= x; + y2 *= y; + t = y2 - x2; + t /= f; + d += t; + } + while (fabs(t/d) > MACHEP) + ; + return (d); +} + +double complex +ctan(double complex z) +{ + double complex w; + double d; + + d = cos (2.0 * creal (z)) + cosh (2.0 * cimag (z)); + + if (fabs(d) < 0.25) + d = _ctans (z); + + if (d == 0.0) { + /*mtherr ("ctan", OVERFLOW);*/ + w = MAXNUM + MAXNUM * I; + return (w); + } + + w = sin (2.0 * creal(z)) / d + (sinh (2.0 * cimag(z)) / d) * I; + return (w); +} + +#if LDBL_MANT_DIG == DBL_MANT_DIG +openlibm_strong_reference(ctan, ctanl); +#endif /* LDBL_MANT_DIG == DBL_MANT_DIG */ diff --git a/openlibm/src/s_ctanf.c b/openlibm/src/s_ctanf.c new file mode 100644 index 0000000000..3bb37425b0 --- /dev/null +++ b/openlibm/src/s_ctanf.c @@ -0,0 +1,148 @@ +/* $OpenBSD: s_ctanf.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ctanf() + * + * Complex circular tangent + * + * + * + * SYNOPSIS: + * + * void ctanf(); + * cmplxf z, w; + * + * ctanf( &z, &w ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * sin 2x + i sinh 2y + * w = --------------------. + * cos 2x + cosh 2y + * + * On the real axis the denominator is zero at odd multiples + * of PI/2. The denominator is evaluated by its Taylor + * series near these points. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 3.3e-7 5.1e-8 + */ + +#include +#include + +#define MACHEPF 3.0e-8 +#define MAXNUMF 1.0e38f + +static const double DP1 = 3.140625; +static const double DP2 = 9.67502593994140625E-4; +static const double DP3 = 1.509957990978376432E-7; + +static float +_redupif(float xx) +{ + float x, t; + long i; + + x = xx; + t = x/(float)M_PI; + if(t >= 0.0) + t += 0.5; + else + t -= 0.5; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return(t); +} + +/* Taylor series expansion for cosh(2y) - cos(2x) */ + +static float +_ctansf(float complex z) +{ + float f, x, x2, y, y2, rn, t, d; + + x = fabsf(2.0f * crealf(z)); + y = fabsf(2.0f * cimagf(z)); + + x = _redupif(x); + + x = x * x; + y = y * y; + x2 = 1.0f; + y2 = 1.0f; + f = 1.0f; + rn = 0.0f; + d = 0.0f; + do { + rn += 1.0f; + f *= rn; + rn += 1.0f; + f *= rn; + x2 *= x; + y2 *= y; + t = y2 + x2; + t /= f; + d += t; + + rn += 1.0f; + f *= rn; + rn += 1.0f; + f *= rn; + x2 *= x; + y2 *= y; + t = y2 - x2; + t /= f; + d += t; + } + while (fabsf(t/d) > MACHEPF) + ; + return(d); +} + +float complex +ctanf(float complex z) +{ + float complex w; + float d; + + d = cosf( 2.0f * crealf(z) ) + coshf( 2.0f * cimagf(z) ); + + if(fabsf(d) < 0.25f) + d = _ctansf(z); + + if (d == 0.0f) { + /*mtherr( "ctanf", OVERFLOW );*/ + w = MAXNUMF + MAXNUMF * I; + return (w); + } + w = sinf (2.0f * crealf(z)) / d + (sinhf (2.0f * cimagf(z)) / d) * I; + return (w); +} diff --git a/openlibm/src/s_ctanh.c b/openlibm/src/s_ctanh.c new file mode 100644 index 0000000000..8d452ee227 --- /dev/null +++ b/openlibm/src/s_ctanh.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Hyperbolic tangent of a complex argument z = x + i y. + * + * The algorithm is from: + * + * W. Kahan. Branch Cuts for Complex Elementary Functions or Much + * Ado About Nothing's Sign Bit. In The State of the Art in + * Numerical Analysis, pp. 165 ff. Iserles and Powell, eds., 1987. + * + * Method: + * + * Let t = tan(x) + * beta = 1/cos^2(y) + * s = sinh(x) + * rho = cosh(x) + * + * We have: + * + * tanh(z) = sinh(z) / cosh(z) + * + * sinh(x) cos(y) + i cosh(x) sin(y) + * = --------------------------------- + * cosh(x) cos(y) + i sinh(x) sin(y) + * + * cosh(x) sinh(x) / cos^2(y) + i tan(y) + * = ------------------------------------- + * 1 + sinh^2(x) / cos^2(y) + * + * beta rho s + i t + * = ---------------- + * 1 + beta s^2 + * + * Modifications: + * + * I omitted the original algorithm's handling of overflow in tan(x) after + * verifying with nearpi.c that this can't happen in IEEE single or double + * precision. I also handle large x differently. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ctanh.c,v 1.2 2011/10/21 06:30:16 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double complex +ctanh(double complex z) +{ + double x, y; + double t, beta, s, rho, denom; + u_int32_t hx, ix, lx; + + x = creal(z); + y = cimag(z); + + EXTRACT_WORDS(hx, lx, x); + ix = hx & 0x7fffffff; + + /* + * ctanh(NaN + i 0) = NaN + i 0 + * + * ctanh(NaN + i y) = NaN + i NaN for y != 0 + * + * The imaginary part has the sign of x*sin(2*y), but there's no + * special effort to get this right. + * + * ctanh(+-Inf +- i Inf) = +-1 +- 0 + * + * ctanh(+-Inf + i y) = +-1 + 0 sin(2y) for y finite + * + * The imaginary part of the sign is unspecified. This special + * case is only needed to avoid a spurious invalid exception when + * y is infinite. + */ + if (ix >= 0x7ff00000) { + if ((ix & 0xfffff) | lx) /* x is NaN */ + return (CMPLX(x, (y == 0 ? y : x * y))); + SET_HIGH_WORD(x, hx - 0x40000000); /* x = copysign(1, x) */ + return (CMPLX(x, copysign(0, isinf(y) ? y : sin(y) * cos(y)))); + } + + /* + * ctanh(x + i NAN) = NaN + i NaN + * ctanh(x +- i Inf) = NaN + i NaN + */ + if (!isfinite(y)) + return (CMPLX(y - y, y - y)); + + /* + * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the + * approximation sinh^2(huge) ~= exp(2*huge) / 4. + * We use a modified formula to avoid spurious overflow. + */ + if (ix >= 0x40360000) { /* x >= 22 */ + double exp_mx = exp(-fabs(x)); + return (CMPLX(copysign(1, x), + 4 * sin(y) * cos(y) * exp_mx * exp_mx)); + } + + /* Kahan's algorithm */ + t = tan(y); + beta = 1.0 + t * t; /* = 1 / cos^2(y) */ + s = sinh(x); + rho = sqrt(1 + s * s); /* = cosh(x) */ + denom = 1 + beta * s * s; + return (CMPLX((beta * rho * s) / denom, t / denom)); +} + +OLM_DLLEXPORT double complex +ctan(double complex z) +{ + + /* ctan(z) = -I * ctanh(I * z) */ + z = ctanh(CMPLX(-cimag(z), creal(z))); + return (CMPLX(cimag(z), -creal(z))); +} diff --git a/openlibm/src/s_ctanhf.c b/openlibm/src/s_ctanhf.c new file mode 100644 index 0000000000..b2f8f19a9c --- /dev/null +++ b/openlibm/src/s_ctanhf.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Hyperbolic tangent of a complex argument z. See s_ctanh.c for details. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ctanhf.c,v 1.2 2011/10/21 06:30:16 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT float complex +ctanhf(float complex z) +{ + float x, y; + float t, beta, s, rho, denom; + u_int32_t hx, ix; + + x = crealf(z); + y = cimagf(z); + + GET_FLOAT_WORD(hx, x); + ix = hx & 0x7fffffff; + + if (ix >= 0x7f800000) { + if (ix & 0x7fffff) + return (CMPLXF(x, (y == 0 ? y : x * y))); + SET_FLOAT_WORD(x, hx - 0x40000000); + return (CMPLXF(x, + copysignf(0, isinf(y) ? y : sinf(y) * cosf(y)))); + } + + if (!isfinite(y)) + return (CMPLXF(y - y, y - y)); + + if (ix >= 0x41300000) { /* x >= 11 */ + float exp_mx = expf(-fabsf(x)); + return (CMPLXF(copysignf(1, x), + 4 * sinf(y) * cosf(y) * exp_mx * exp_mx)); + } + + t = tanf(y); + beta = 1.0 + t * t; + s = sinhf(x); + rho = sqrtf(1 + s * s); + denom = 1 + beta * s * s; + return (CMPLXF((beta * rho * s) / denom, t / denom)); +} + +OLM_DLLEXPORT float complex +ctanf(float complex z) +{ + + z = ctanhf(CMPLXF(-cimagf(z), crealf(z))); + return (CMPLXF(cimagf(z), -crealf(z))); +} + diff --git a/openlibm/src/s_ctanhl.c b/openlibm/src/s_ctanhl.c new file mode 100644 index 0000000000..6d996d4c84 --- /dev/null +++ b/openlibm/src/s_ctanhl.c @@ -0,0 +1,60 @@ +/* $OpenBSD: s_ctanhl.c,v 1.2 2011/07/20 19:28:33 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ctanhl + * + * Complex hyperbolic tangent + * + * + * + * SYNOPSIS: + * + * long double complex ctanhl(); + * long double complex z, w; + * + * w = ctanhl (z); + * + * + * + * DESCRIPTION: + * + * tanh z = (sinh 2x + i sin 2y) / (cosh 2x + cos 2y) . + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * IEEE -10,+10 30000 1.7e-14 2.4e-16 + * + */ + +#include +#include + +long double complex +ctanhl(long double complex z) +{ + long double complex w; + long double x, y, d; + + x = creall(z); + y = cimagl(z); + d = coshl(2.0L * x) + cosl(2.0L * y); + w = sinhl(2.0L * x) / d + (sinl(2.0L * y) / d) * I; + return (w); +} diff --git a/openlibm/src/s_ctanl.c b/openlibm/src/s_ctanl.c new file mode 100644 index 0000000000..cf33cedd70 --- /dev/null +++ b/openlibm/src/s_ctanl.c @@ -0,0 +1,157 @@ +/* $OpenBSD: s_ctanl.c,v 1.3 2011/07/20 21:02:51 martynas Exp $ */ + +/* + * Copyright (c) 2008 Stephen L. Moshier + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* ctanl() + * + * Complex circular tangent + * + * + * + * SYNOPSIS: + * + * long double complex ctanl(); + * long double complex z, w; + * + * w = ctanl( z ); + * + * + * + * DESCRIPTION: + * + * If + * z = x + iy, + * + * then + * + * sin 2x + i sinh 2y + * w = --------------------. + * cos 2x + cosh 2y + * + * On the real axis the denominator is zero at odd multiples + * of PI/2. The denominator is evaluated by its Taylor + * series near these points. + * + * + * ACCURACY: + * + * Relative error: + * arithmetic domain # trials peak rms + * DEC -10,+10 5200 7.1e-17 1.6e-17 + * IEEE -10,+10 30000 7.2e-16 1.2e-16 + * Also tested by ctan * ccot = 1 and catan(ctan(z)) = z. + */ + +#include +#include +#include + +#if LDBL_MANT_DIG == 64 +static const long double MACHEPL= 5.42101086242752217003726400434970855712890625E-20L; +#elif LDBL_MANT_DIG == 113 +static const long double MACHEPL = 9.629649721936179265279889712924636592690508e-35L; +#endif + +static const long double PIL = 3.141592653589793238462643383279502884197169L; +static const long double DP1 = 3.14159265358979323829596852490908531763125L; +static const long double DP2 = 1.6667485837041756656403424829301998703007e-19L; +static const long double DP3 = 1.8830410776607851167459095484560349402753e-39L; + +static long double +redupil(long double x) +{ + long double t; + long i; + + t = x / PIL; + if (t >= 0.0L) + t += 0.5L; + else + t -= 0.5L; + + i = t; /* the multiple */ + t = i; + t = ((x - t * DP1) - t * DP2) - t * DP3; + return (t); +} + +static long double +ctansl(long double complex z) +{ + long double f, x, x2, y, y2, rn, t; + long double d; + + x = fabsl(2.0L * creall(z)); + y = fabsl(2.0L * cimagl(z)); + + x = redupil(x); + + x = x * x; + y = y * y; + x2 = 1.0L; + y2 = 1.0L; + f = 1.0L; + rn = 0.0L; + d = 0.0L; + do { + rn += 1.0L; + f *= rn; + rn += 1.0L; + f *= rn; + x2 *= x; + y2 *= y; + t = y2 + x2; + t /= f; + d += t; + + rn += 1.0L; + f *= rn; + rn += 1.0L; + f *= rn; + x2 *= x; + y2 *= y; + t = y2 - x2; + t /= f; + d += t; + } + while (fabsl(t/d) > MACHEPL); + return(d); +} + +long double complex +ctanl(long double complex z) +{ + long double complex w; + long double d, x, y; + + x = creall(z); + y = cimagl(z); + d = cosl(2.0L * x) + coshl(2.0L * y); + + if (fabsl(d) < 0.25L) { + d = fabsl(d); + d = ctansl(z); + } + if (d == 0.0L) { + /*mtherr( "ctan", OVERFLOW );*/ + w = LDBL_MAX + LDBL_MAX * I; + return (w); + } + + w = sinl(2.0L * x) / d + (sinhl(2.0L * y) / d) * I; + return (w); +} diff --git a/openlibm/src/s_erf.c b/openlibm/src/s_erf.c new file mode 100644 index 0000000000..67068c4ce6 --- /dev/null +++ b/openlibm/src/s_erf.c @@ -0,0 +1,307 @@ +/* @(#)s_erf.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_erf.c,v 1.8 2008/02/22 02:30:35 das Exp $"); + +/* double erf(double x) + * double erfc(double x) + * x + * 2 |\ + * erf(x) = --------- | exp(-t*t)dt + * sqrt(pi) \| + * 0 + * + * erfc(x) = 1-erf(x) + * Note that + * erf(-x) = -erf(x) + * erfc(-x) = 2 - erfc(x) + * + * Method: + * 1. For |x| in [0, 0.84375] + * erf(x) = x + x*R(x^2) + * erfc(x) = 1 - erf(x) if x in [-.84375,0.25] + * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] + * where R = P/Q where P is an odd poly of degree 8 and + * Q is an odd poly of degree 10. + * -57.90 + * | R - (erf(x)-x)/x | <= 2 + * + * + * Remark. The formula is derived by noting + * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) + * and that + * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 + * is close to one. The interval is chosen because the fix + * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is + * near 0.6174), and by some experiment, 0.84375 is chosen to + * guarantee the error is less than one ulp for erf. + * + * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and + * c = 0.84506291151 rounded to single (24 bits) + * erf(x) = sign(x) * (c + P1(s)/Q1(s)) + * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 + * 1+(c+P1(s)/Q1(s)) if x < 0 + * |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06 + * Remark: here we use the taylor series expansion at x=1. + * erf(1+s) = erf(1) + s*Poly(s) + * = 0.845.. + P1(s)/Q1(s) + * That is, we use rational approximation to approximate + * erf(1+s) - (c = (single)0.84506291151) + * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] + * where + * P1(s) = degree 6 poly in s + * Q1(s) = degree 6 poly in s + * + * 3. For x in [1.25,1/0.35(~2.857143)], + * erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1) + * erf(x) = 1 - erfc(x) + * where + * R1(z) = degree 7 poly in z, (z=1/x^2) + * S1(z) = degree 8 poly in z + * + * 4. For x in [1/0.35,28] + * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 + * = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28 + * erf(x) = sign(x) *(1 - tiny) (raise inexact) + * erfc(x) = tiny*tiny (raise underflow) if x > 0 + * = 2 - tiny if x<0 + * + * 7. Special case: + * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, + * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, + * erfc/erf(NaN) is NaN + */ + +#include +#include + +#include "math_private.h" + +static const double +tiny = 1e-300, +half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +two = 2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */ + /* c = (float)0.84506291151 */ +erx = 8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */ +/* + * Coefficients for approximation to erf on [0,0.84375] + */ +efx = 1.28379167095512586316e-01, /* 0x3FC06EBA, 0x8214DB69 */ +efx8= 1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */ +pp0 = 1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */ +pp1 = -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */ +pp2 = -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */ +pp3 = -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */ +pp4 = -2.37630166566501626084e-05, /* 0xBEF8EAD6, 0x120016AC */ +qq1 = 3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */ +qq2 = 6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */ +qq3 = 5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */ +qq4 = 1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */ +qq5 = -3.96022827877536812320e-06, /* 0xBED09C43, 0x42A26120 */ +/* + * Coefficients for approximation to erf in [0.84375,1.25] + */ +pa0 = -2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */ +pa1 = 4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */ +pa2 = -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */ +pa3 = 3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */ +pa4 = -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */ +pa5 = 3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */ +pa6 = -2.16637559486879084300e-03, /* 0xBF61BF38, 0x0A96073F */ +qa1 = 1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */ +qa2 = 5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */ +qa3 = 7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */ +qa4 = 1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */ +qa5 = 1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */ +qa6 = 1.19844998467991074170e-02, /* 0x3F888B54, 0x5735151D */ +/* + * Coefficients for approximation to erfc in [1.25,1/0.35] + */ +ra0 = -9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */ +ra1 = -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */ +ra2 = -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */ +ra3 = -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */ +ra4 = -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */ +ra5 = -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */ +ra6 = -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */ +ra7 = -9.81432934416914548592e+00, /* 0xC023A0EF, 0xC69AC25C */ +sa1 = 1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */ +sa2 = 1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */ +sa3 = 4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */ +sa4 = 6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */ +sa5 = 4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */ +sa6 = 1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */ +sa7 = 6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */ +sa8 = -6.04244152148580987438e-02, /* 0xBFAEEFF2, 0xEE749A62 */ +/* + * Coefficients for approximation to erfc in [1/.35,28] + */ +rb0 = -9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */ +rb1 = -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */ +rb2 = -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */ +rb3 = -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */ +rb4 = -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */ +rb5 = -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */ +rb6 = -4.83519191608651397019e+02, /* 0xC07E384E, 0x9BDC383F */ +sb1 = 3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */ +sb2 = 3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */ +sb3 = 1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */ +sb4 = 3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */ +sb5 = 2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */ +sb6 = 4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */ +sb7 = -2.24409524465858183362e+01; /* 0xC03670E2, 0x42712D62 */ + +OLM_DLLEXPORT double +erf(double x) +{ + int32_t hx,ix,i; + double R,S,P,Q,s,y,z,r; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) { /* erf(nan)=nan */ + i = ((u_int32_t)hx>>31)<<1; + return (double)(1-i)+one/x; /* erf(+-inf)=+-1 */ + } + + if(ix < 0x3feb0000) { /* |x|<0.84375 */ + if(ix < 0x3e300000) { /* |x|<2**-28 */ + if (ix < 0x00800000) + return 0.125*(8.0*x+efx8*x); /*avoid underflow */ + return x + efx*x; + } + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + return x + x*y; + } + if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ + s = fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) return erx + P/Q; else return -erx - P/Q; + } + if (ix >= 0x40180000) { /* inf>|x|>=6 */ + if(hx>=0) return one-tiny; else return tiny-one; + } + x = fabs(x); + s = one/(x*x); + if(ix< 0x4006DB6E) { /* |x| < 1/0.35 */ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/0.35 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + z = x; + SET_LOW_WORD(z,0); + r = __ieee754_exp(-z*z-0.5625)*__ieee754_exp((z-x)*(z+x)+R/S); + if(hx>=0) return one-r/x; else return r/x-one; +} + +OLM_DLLEXPORT double +erfc(double x) +{ + int32_t hx,ix; + double R,S,P,Q,s,y,z,r; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (double)(((u_int32_t)hx>>31)<<1)+one/x; + } + + if(ix < 0x3feb0000) { /* |x|<0.84375 */ + if(ix < 0x3c700000) /* |x|<2**-56 */ + return one-x; + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + if(hx < 0x3fd00000) { /* x<1/4 */ + return one-(x+x*y); + } else { + r = x*y; + r += (x-half); + return half - r ; + } + } + if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ + s = fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) { + z = one-erx; return z - P/Q; + } else { + z = erx+P/Q; return one+z; + } + } + if (ix < 0x403c0000) { /* |x|<28 */ + x = fabs(x); + s = one/(x*x); + if(ix< 0x4006DB6D) { /* |x| < 1/.35 ~ 2.857143*/ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/.35 ~ 2.857143 */ + if(hx<0&&ix>=0x40180000) return two-tiny;/* x < -6 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + z = x; + SET_LOW_WORD(z,0); + r = __ieee754_exp(-z*z-0.5625)* + __ieee754_exp((z-x)*(z+x)+R/S); + if(hx>0) return r/x; else return two-r/x; + } else { + if(hx>0) return tiny*tiny; else return two-tiny; + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(erf, erfl); +openlibm_weak_reference(erfc, erfcl); +#endif diff --git a/openlibm/src/s_erff.c b/openlibm/src/s_erff.c new file mode 100644 index 0000000000..ed6030f243 --- /dev/null +++ b/openlibm/src/s_erff.c @@ -0,0 +1,184 @@ +/* s_erff.c -- float version of s_erf.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_erff.c,v 1.8 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +static const float +tiny = 1e-30, +half= 5.0000000000e-01, /* 0x3F000000 */ +one = 1.0000000000e+00, /* 0x3F800000 */ +two = 2.0000000000e+00, /* 0x40000000 */ +/* + * Coefficients for approximation to erf on [0,0.84375] + */ +efx = 1.2837916613e-01, /* 0x3e0375d4 */ +efx8= 1.0270333290e+00, /* 0x3f8375d4 */ +/* + * Domain [0, 0.84375], range ~[-5.4446e-10,5.5197e-10]: + * |(erf(x) - x)/x - p(x)/q(x)| < 2**-31. + */ +pp0 = 1.28379166e-01F, /* 0x1.06eba8p-3 */ +pp1 = -3.36030394e-01F, /* -0x1.58185ap-2 */ +pp2 = -1.86260219e-03F, /* -0x1.e8451ep-10 */ +qq1 = 3.12324286e-01F, /* 0x1.3fd1f0p-2 */ +qq2 = 2.16070302e-02F, /* 0x1.620274p-6 */ +qq3 = -1.98859419e-03F, /* -0x1.04a626p-9 */ +/* + * Domain [0.84375, 1.25], range ~[-1.953e-11,1.940e-11]: + * |(erf(x) - erx) - p(x)/q(x)| < 2**-36. + */ +erx = 8.42697144e-01F, /* 0x1.af7600p-1. erf(1) rounded to 16 bits. */ +pa0 = 3.64939137e-06F, /* 0x1.e9d022p-19 */ +pa1 = 4.15109694e-01F, /* 0x1.a91284p-2 */ +pa2 = -1.65179938e-01F, /* -0x1.5249dcp-3 */ +pa3 = 1.10914491e-01F, /* 0x1.c64e46p-4 */ +qa1 = 6.02074385e-01F, /* 0x1.344318p-1 */ +qa2 = 5.35934687e-01F, /* 0x1.126608p-1 */ +qa3 = 1.68576106e-01F, /* 0x1.593e6ep-3 */ +qa4 = 5.62181212e-02F, /* 0x1.cc89f2p-5 */ +/* + * Domain [1.25,1/0.35], range ~[-7.043e-10,7.457e-10]: + * |log(x*erfc(x)) + x**2 + 0.5625 - r(x)/s(x)| < 2**-30 + */ +ra0 = -9.87132732e-03F, /* -0x1.4376b2p-7 */ +ra1 = -5.53605914e-01F, /* -0x1.1b723cp-1 */ +ra2 = -2.17589188e+00F, /* -0x1.1683a0p+1 */ +ra3 = -1.43268085e+00F, /* -0x1.6ec42cp+0 */ +sa1 = 5.45995426e+00F, /* 0x1.5d6fe4p+2 */ +sa2 = 6.69798088e+00F, /* 0x1.acabb8p+2 */ +sa3 = 1.43113089e+00F, /* 0x1.6e5e98p+0 */ +sa4 = -5.77397496e-02F, /* -0x1.d90108p-5 */ +/* + * Domain [1/0.35, 11], range ~[-2.264e-13,2.336e-13]: + * |log(x*erfc(x)) + x**2 + 0.5625 - r(x)/s(x)| < 2**-42 + */ +rb0 = -9.86494310e-03F, /* -0x1.434124p-7 */ +rb1 = -6.25171244e-01F, /* -0x1.401672p-1 */ +rb2 = -6.16498327e+00F, /* -0x1.8a8f16p+2 */ +rb3 = -1.66696873e+01F, /* -0x1.0ab70ap+4 */ +rb4 = -9.53764343e+00F, /* -0x1.313460p+3 */ +sb1 = 1.26884899e+01F, /* 0x1.96081cp+3 */ +sb2 = 4.51839523e+01F, /* 0x1.6978bcp+5 */ +sb3 = 4.72810211e+01F, /* 0x1.7a3f88p+5 */ +sb4 = 8.93033314e+00F; /* 0x1.1dc54ap+3 */ + + +OLM_DLLEXPORT float +erff(float x) +{ + int32_t hx,ix,i; + float R,S,P,Q,s,y,z,r; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) { /* erf(nan)=nan */ + i = ((u_int32_t)hx>>31)<<1; + return (float)(1-i)+one/x; /* erf(+-inf)=+-1 */ + } + + if(ix < 0x3f580000) { /* |x|<0.84375 */ + if(ix < 0x38800000) { /* |x|<2**-14 */ + if (ix < 0x04000000) /* |x|<0x1p-119 */ + return (8*x+efx8*x)/8; /* avoid spurious underflow */ + return x + efx*x; + } + z = x*x; + r = pp0+z*(pp1+z*pp2); + s = one+z*(qq1+z*(qq2+z*qq3)); + y = r/s; + return x + x*y; + } + if(ix < 0x3fa00000) { /* 0.84375 <= |x| < 1.25 */ + s = fabsf(x)-one; + P = pa0+s*(pa1+s*(pa2+s*pa3)); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*qa4))); + if(hx>=0) return erx + P/Q; else return -erx - P/Q; + } + if (ix >= 0x40800000) { /* inf>|x|>=4 */ + if(hx>=0) return one-tiny; else return tiny-one; + } + x = fabsf(x); + s = one/(x*x); + if(ix< 0x4036DB6E) { /* |x| < 1/0.35 */ + R=ra0+s*(ra1+s*(ra2+s*ra3)); + S=one+s*(sa1+s*(sa2+s*(sa3+s*sa4))); + } else { /* |x| >= 1/0.35 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*rb4))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*sb4))); + } + SET_FLOAT_WORD(z,hx&0xffffe000); + r = expf(-z*z-0.5625F)*expf((z-x)*(z+x)+R/S); + if(hx>=0) return one-r/x; else return r/x-one; +} + +OLM_DLLEXPORT float +erfcf(float x) +{ + int32_t hx,ix; + float R,S,P,Q,s,y,z,r; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (float)(((u_int32_t)hx>>31)<<1)+one/x; + } + + if(ix < 0x3f580000) { /* |x|<0.84375 */ + if(ix < 0x33800000) /* |x|<2**-56 */ + return one-x; + z = x*x; + r = pp0+z*(pp1+z*pp2); + s = one+z*(qq1+z*(qq2+z*qq3)); + y = r/s; + if(hx < 0x3e800000) { /* x<1/4 */ + return one-(x+x*y); + } else { + r = x*y; + r += (x-half); + return half - r ; + } + } + if(ix < 0x3fa00000) { /* 0.84375 <= |x| < 1.25 */ + s = fabsf(x)-one; + P = pa0+s*(pa1+s*(pa2+s*pa3)); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*qa4))); + if(hx>=0) { + z = one-erx; return z - P/Q; + } else { + z = erx+P/Q; return one+z; + } + } + if (ix < 0x41300000) { /* |x|<28 */ + x = fabsf(x); + s = one/(x*x); + if(ix< 0x4036DB6D) { /* |x| < 1/.35 ~ 2.857143*/ + R=ra0+s*(ra1+s*(ra2+s*ra3)); + S=one+s*(sa1+s*(sa2+s*(sa3+s*sa4))); + } else { /* |x| >= 1/.35 ~ 2.857143 */ + if(hx<0&&ix>=0x40a00000) return two-tiny;/* x < -5 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*rb4))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*sb4))); + } + SET_FLOAT_WORD(z,hx&0xffffe000); + r = expf(-z*z-0.5625F)*expf((z-x)*(z+x)+R/S); + if(hx>0) return r/x; else return two-r/x; + } else { + if(hx>0) return tiny*tiny; else return two-tiny; + } +} diff --git a/openlibm/src/s_exp2.c b/openlibm/src/s_exp2.c new file mode 100644 index 0000000000..d643dbb3d4 --- /dev/null +++ b/openlibm/src/s_exp2.c @@ -0,0 +1,396 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_exp2.c,v 1.7 2008/02/22 02:27:34 das Exp $"); + +#include +#include + +#include "math_private.h" + +#define TBLBITS 8 +#define TBLSIZE (1 << TBLBITS) + +static const double + huge = 0x1p1000, + redux = 0x1.8p52 / TBLSIZE, + P1 = 0x1.62e42fefa39efp-1, + P2 = 0x1.ebfbdff82c575p-3, + P3 = 0x1.c6b08d704a0a6p-5, + P4 = 0x1.3b2ab88f70400p-7, + P5 = 0x1.5d88003875c74p-10; + +static volatile double twom1000 = 0x1p-1000; + +static const double tbl[TBLSIZE * 2] = { +/* exp2(z + eps) eps */ + 0x1.6a09e667f3d5dp-1, 0x1.9880p-44, + 0x1.6b052fa751744p-1, 0x1.8000p-50, + 0x1.6c012750bd9fep-1, -0x1.8780p-45, + 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46, + 0x1.6dfb23c651a29p-1, -0x1.8000p-50, + 0x1.6ef9298593ae3p-1, -0x1.c000p-52, + 0x1.6ff7df9519386p-1, -0x1.fd80p-45, + 0x1.70f7466f42da3p-1, -0x1.c880p-45, + 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46, + 0x1.72f8286eacf05p-1, -0x1.8300p-44, + 0x1.73f9a48a58152p-1, -0x1.0c00p-47, + 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45, + 0x1.75feb564267f1p-1, 0x1.3e00p-47, + 0x1.77024b1ab6d48p-1, -0x1.7d00p-45, + 0x1.780694fde5d38p-1, -0x1.d000p-50, + 0x1.790b938ac1d00p-1, 0x1.3000p-49, + 0x1.7a11473eb0178p-1, -0x1.d000p-49, + 0x1.7b17b0976d060p-1, 0x1.0400p-45, + 0x1.7c1ed0130c133p-1, 0x1.0000p-53, + 0x1.7d26a62ff8636p-1, -0x1.6900p-45, + 0x1.7e2f336cf4e3bp-1, -0x1.2e00p-47, + 0x1.7f3878491c3e8p-1, -0x1.4580p-45, + 0x1.80427543e1b4ep-1, 0x1.3000p-44, + 0x1.814d2add1071ap-1, 0x1.f000p-47, + 0x1.82589994ccd7ep-1, -0x1.1c00p-45, + 0x1.8364c1eb942d0p-1, 0x1.9d00p-45, + 0x1.8471a4623cab5p-1, 0x1.7100p-43, + 0x1.857f4179f5bbcp-1, 0x1.2600p-45, + 0x1.868d99b4491afp-1, -0x1.2c40p-44, + 0x1.879cad931a395p-1, -0x1.3000p-45, + 0x1.88ac7d98a65b8p-1, -0x1.a800p-45, + 0x1.89bd0a4785800p-1, -0x1.d000p-49, + 0x1.8ace5422aa223p-1, 0x1.3280p-44, + 0x1.8be05bad619fap-1, 0x1.2b40p-43, + 0x1.8cf3216b54383p-1, -0x1.ed00p-45, + 0x1.8e06a5e08664cp-1, -0x1.0500p-45, + 0x1.8f1ae99157807p-1, 0x1.8280p-45, + 0x1.902fed0282c0ep-1, -0x1.cb00p-46, + 0x1.9145b0b91ff96p-1, -0x1.5e00p-47, + 0x1.925c353aa2ff9p-1, 0x1.5400p-48, + 0x1.93737b0cdc64ap-1, 0x1.7200p-46, + 0x1.948b82b5f98aep-1, -0x1.9000p-47, + 0x1.95a44cbc852cbp-1, 0x1.5680p-45, + 0x1.96bdd9a766f21p-1, -0x1.6d00p-44, + 0x1.97d829fde4e2ap-1, -0x1.1000p-47, + 0x1.98f33e47a23a3p-1, 0x1.d000p-45, + 0x1.9a0f170ca0604p-1, -0x1.8a40p-44, + 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44, + 0x1.9c49182a3f15bp-1, 0x1.6b80p-45, + 0x1.9d674194bb8c5p-1, -0x1.c000p-49, + 0x1.9e86319e3238ep-1, 0x1.7d00p-46, + 0x1.9fa5e8d07f302p-1, 0x1.6400p-46, + 0x1.a0c667b5de54dp-1, -0x1.5000p-48, + 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47, + 0x1.a309bec4a2e27p-1, 0x1.ad80p-45, + 0x1.a42c980460a5dp-1, -0x1.af00p-46, + 0x1.a5503b23e259bp-1, 0x1.b600p-47, + 0x1.a674a8af46213p-1, 0x1.8880p-44, + 0x1.a799e1330b3a7p-1, 0x1.1200p-46, + 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47, + 0x1.a9e6b5579fcd2p-1, -0x1.9b80p-45, + 0x1.ab0e521356fb8p-1, 0x1.b700p-45, + 0x1.ac36bbfd3f381p-1, 0x1.9000p-50, + 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49, + 0x1.ae89f995ad2a3p-1, -0x1.c900p-45, + 0x1.afb4ce622f367p-1, 0x1.6500p-46, + 0x1.b0e07298db790p-1, 0x1.fd40p-45, + 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46, + 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43, + 0x1.b468415b747e7p-1, -0x1.8380p-44, + 0x1.b59728de5593ap-1, 0x1.8000p-54, + 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47, + 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50, + 0x1.b928cf22749b2p-1, -0x1.4c00p-47, + 0x1.ba5b030a10603p-1, -0x1.d700p-47, + 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47, + 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47, + 0x1.bdf69c3f3a16fp-1, -0x1.f780p-46, + 0x1.bf2c25bd71db8p-1, -0x1.0a00p-46, + 0x1.c06286141b2e9p-1, -0x1.1400p-46, + 0x1.c199bdd8552e0p-1, 0x1.be00p-47, + 0x1.c2d1cd9fa64eep-1, -0x1.9400p-47, + 0x1.c40ab5fffd02fp-1, -0x1.ed00p-47, + 0x1.c544778fafd15p-1, 0x1.9660p-44, + 0x1.c67f12e57d0cbp-1, -0x1.a100p-46, + 0x1.c7ba88988c1b6p-1, -0x1.8458p-42, + 0x1.c8f6d9406e733p-1, -0x1.a480p-46, + 0x1.ca3405751c4dfp-1, 0x1.b000p-51, + 0x1.cb720dcef9094p-1, 0x1.1400p-47, + 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48, + 0x1.cdf0b555dc412p-1, 0x1.3600p-48, + 0x1.cf3155b5bab3bp-1, -0x1.6900p-47, + 0x1.d072d4a0789bcp-1, 0x1.9a00p-47, + 0x1.d1b532b08c8fap-1, -0x1.5e00p-46, + 0x1.d2f87080d8a85p-1, 0x1.d280p-46, + 0x1.d43c8eacaa203p-1, 0x1.1a00p-47, + 0x1.d5818dcfba491p-1, 0x1.f000p-50, + 0x1.d6c76e862e6a1p-1, -0x1.3a00p-47, + 0x1.d80e316c9834ep-1, -0x1.cd80p-47, + 0x1.d955d71ff6090p-1, 0x1.4c00p-48, + 0x1.da9e603db32aep-1, 0x1.f900p-48, + 0x1.dbe7cd63a8325p-1, 0x1.9800p-49, + 0x1.dd321f301b445p-1, -0x1.5200p-48, + 0x1.de7d5641c05bfp-1, -0x1.d700p-46, + 0x1.dfc97337b9aecp-1, -0x1.6140p-46, + 0x1.e11676b197d5ep-1, 0x1.b480p-47, + 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43, + 0x1.e3b333b16ee5cp-1, 0x1.c680p-47, + 0x1.e502ee78b3fb4p-1, -0x1.9300p-47, + 0x1.e653924676d68p-1, -0x1.5000p-49, + 0x1.e7a51fbc74c44p-1, -0x1.7f80p-47, + 0x1.e8f7977cdb726p-1, -0x1.3700p-48, + 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49, + 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46, + 0x1.ecf482d8e680dp-1, 0x1.5500p-48, + 0x1.ee4aaa2188514p-1, 0x1.6400p-51, + 0x1.efa1bee615a13p-1, -0x1.e800p-49, + 0x1.f0f9c1cb64106p-1, -0x1.a880p-48, + 0x1.f252b376bb963p-1, -0x1.c900p-45, + 0x1.f3ac948dd7275p-1, 0x1.a000p-53, + 0x1.f50765b6e4524p-1, -0x1.4f00p-48, + 0x1.f6632798844fdp-1, 0x1.a800p-51, + 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48, + 0x1.f91d802243c82p-1, -0x1.4600p-50, + 0x1.fa7c1819e908ep-1, -0x1.b0c0p-47, + 0x1.fbdba3692d511p-1, -0x1.0e00p-51, + 0x1.fd3c22b8f7194p-1, -0x1.0de8p-46, + 0x1.fe9d96b2a23eep-1, 0x1.e430p-49, + 0x1.0000000000000p+0, 0x0.0000p+0, + 0x1.00b1afa5abcbep+0, -0x1.3400p-52, + 0x1.0163da9fb3303p+0, -0x1.2170p-46, + 0x1.02168143b0282p+0, 0x1.a400p-52, + 0x1.02c9a3e77806cp+0, 0x1.f980p-49, + 0x1.037d42e11bbcap+0, -0x1.7400p-51, + 0x1.04315e86e7f89p+0, 0x1.8300p-50, + 0x1.04e5f72f65467p+0, -0x1.a3f0p-46, + 0x1.059b0d315855ap+0, -0x1.2840p-47, + 0x1.0650a0e3c1f95p+0, 0x1.1600p-48, + 0x1.0706b29ddf71ap+0, 0x1.5240p-46, + 0x1.07bd42b72a82dp+0, -0x1.9a00p-49, + 0x1.0874518759bd0p+0, 0x1.6400p-49, + 0x1.092bdf66607c8p+0, -0x1.0780p-47, + 0x1.09e3ecac6f383p+0, -0x1.8000p-54, + 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48, + 0x1.0b5586cf988fcp+0, -0x1.ac80p-48, + 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50, + 0x1.0cc922b724816p+0, 0x1.5200p-47, + 0x1.0d83b23395dd8p+0, -0x1.ad00p-48, + 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46, + 0x1.0efa55fdfa9a6p+0, -0x1.4e80p-47, + 0x1.0fb66affed2f0p+0, -0x1.d300p-47, + 0x1.1073028d7234bp+0, 0x1.1500p-48, + 0x1.11301d0125b5bp+0, 0x1.c000p-49, + 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46, + 0x1.12abdc06c31d5p+0, 0x1.8400p-49, + 0x1.136a814f2047dp+0, -0x1.ed00p-47, + 0x1.1429aaea92de9p+0, 0x1.8e00p-49, + 0x1.14e95934f3138p+0, 0x1.b400p-49, + 0x1.15a98c8a58e71p+0, 0x1.5300p-47, + 0x1.166a45471c3dfp+0, 0x1.3380p-47, + 0x1.172b83c7d5211p+0, 0x1.8d40p-45, + 0x1.17ed48695bb9fp+0, -0x1.5d00p-47, + 0x1.18af9388c8d93p+0, -0x1.c880p-46, + 0x1.1972658375d66p+0, 0x1.1f00p-46, + 0x1.1a35beb6fcba7p+0, 0x1.0480p-46, + 0x1.1af99f81387e3p+0, -0x1.7390p-43, + 0x1.1bbe084045d54p+0, 0x1.4e40p-45, + 0x1.1c82f95281c43p+0, -0x1.a200p-47, + 0x1.1d4873168b9b2p+0, 0x1.3800p-49, + 0x1.1e0e75eb44031p+0, 0x1.ac00p-49, + 0x1.1ed5022fcd938p+0, 0x1.1900p-47, + 0x1.1f9c18438cdf7p+0, -0x1.b780p-46, + 0x1.2063b88628d8fp+0, 0x1.d940p-45, + 0x1.212be3578a81ep+0, 0x1.8000p-50, + 0x1.21f49917ddd41p+0, 0x1.b340p-45, + 0x1.22bdda2791323p+0, 0x1.9f80p-46, + 0x1.2387a6e7561e7p+0, -0x1.9c80p-46, + 0x1.2451ffb821427p+0, 0x1.2300p-47, + 0x1.251ce4fb2a602p+0, -0x1.3480p-46, + 0x1.25e85711eceb0p+0, 0x1.2700p-46, + 0x1.26b4565e27d16p+0, 0x1.1d00p-46, + 0x1.2780e341de00fp+0, 0x1.1ee0p-44, + 0x1.284dfe1f5633ep+0, -0x1.4c00p-46, + 0x1.291ba7591bb30p+0, -0x1.3d80p-46, + 0x1.29e9df51fdf09p+0, 0x1.8b00p-47, + 0x1.2ab8a66d10e9bp+0, -0x1.27c0p-45, + 0x1.2b87fd0dada3ap+0, 0x1.a340p-45, + 0x1.2c57e39771af9p+0, -0x1.0800p-46, + 0x1.2d285a6e402d9p+0, -0x1.ed00p-47, + 0x1.2df961f641579p+0, -0x1.4200p-48, + 0x1.2ecafa93e2ecfp+0, -0x1.4980p-45, + 0x1.2f9d24abd8822p+0, -0x1.6300p-46, + 0x1.306fe0a31b625p+0, -0x1.2360p-44, + 0x1.31432edeea50bp+0, -0x1.0df8p-40, + 0x1.32170fc4cd7b8p+0, -0x1.2480p-45, + 0x1.32eb83ba8e9a2p+0, -0x1.5980p-45, + 0x1.33c08b2641766p+0, 0x1.ed00p-46, + 0x1.3496266e3fa27p+0, -0x1.c000p-50, + 0x1.356c55f929f0fp+0, -0x1.0d80p-44, + 0x1.36431a2de88b9p+0, 0x1.2c80p-45, + 0x1.371a7373aaa39p+0, 0x1.0600p-45, + 0x1.37f26231e74fep+0, -0x1.6600p-46, + 0x1.38cae6d05d838p+0, -0x1.ae00p-47, + 0x1.39a401b713ec3p+0, -0x1.4720p-43, + 0x1.3a7db34e5a020p+0, 0x1.8200p-47, + 0x1.3b57fbfec6e95p+0, 0x1.e800p-44, + 0x1.3c32dc313a8f2p+0, 0x1.f800p-49, + 0x1.3d0e544ede122p+0, -0x1.7a00p-46, + 0x1.3dea64c1234bbp+0, 0x1.6300p-45, + 0x1.3ec70df1c4eccp+0, -0x1.8a60p-43, + 0x1.3fa4504ac7e8cp+0, -0x1.cdc0p-44, + 0x1.40822c367a0bbp+0, 0x1.5b80p-45, + 0x1.4160a21f72e95p+0, 0x1.ec00p-46, + 0x1.423fb27094646p+0, -0x1.3600p-46, + 0x1.431f5d950a920p+0, 0x1.3980p-45, + 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48, + 0x1.44e0860618919p+0, -0x1.6c00p-48, + 0x1.45c2042a7d201p+0, -0x1.bc00p-47, + 0x1.46a41ed1d0016p+0, -0x1.2800p-46, + 0x1.4786d668b3326p+0, 0x1.0e00p-44, + 0x1.486a2b5c13c00p+0, -0x1.d400p-45, + 0x1.494e1e192af04p+0, 0x1.c200p-47, + 0x1.4a32af0d7d372p+0, -0x1.e500p-46, + 0x1.4b17dea6db801p+0, 0x1.7800p-47, + 0x1.4bfdad53629e1p+0, -0x1.3800p-46, + 0x1.4ce41b817c132p+0, 0x1.0800p-47, + 0x1.4dcb299fddddbp+0, 0x1.c700p-45, + 0x1.4eb2d81d8ab96p+0, -0x1.ce00p-46, + 0x1.4f9b2769d2d02p+0, 0x1.9200p-46, + 0x1.508417f4531c1p+0, -0x1.8c00p-47, + 0x1.516daa2cf662ap+0, -0x1.a000p-48, + 0x1.5257de83f51eap+0, 0x1.a080p-43, + 0x1.5342b569d4edap+0, -0x1.6d80p-45, + 0x1.542e2f4f6ac1ap+0, -0x1.2440p-44, + 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43, + 0x1.56070dde9116bp+0, 0x1.4b00p-45, + 0x1.56f4736b529dep+0, 0x1.15a0p-43, + 0x1.57e27dbe2c40ep+0, -0x1.9e00p-45, + 0x1.58d12d497c76fp+0, -0x1.3080p-45, + 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43, + 0x1.5ab07dd485427p+0, -0x1.4000p-51, + 0x1.5ba11fba87af4p+0, 0x1.0080p-44, + 0x1.5c9268a59460bp+0, -0x1.6c80p-45, + 0x1.5d84590998e3fp+0, 0x1.69a0p-43, + 0x1.5e76f15ad20e1p+0, -0x1.b400p-46, + 0x1.5f6a320dcebcap+0, 0x1.7700p-46, + 0x1.605e1b976dcb8p+0, 0x1.6f80p-45, + 0x1.6152ae6cdf715p+0, 0x1.1000p-47, + 0x1.6247eb03a5531p+0, -0x1.5d00p-46, + 0x1.633dd1d1929b5p+0, -0x1.2d00p-46, + 0x1.6434634ccc313p+0, -0x1.a800p-49, + 0x1.652b9febc8efap+0, -0x1.8600p-45, + 0x1.6623882553397p+0, 0x1.1fe0p-40, + 0x1.671c1c708328ep+0, -0x1.7200p-44, + 0x1.68155d44ca97ep+0, 0x1.6800p-49, + 0x1.690f4b19e9471p+0, -0x1.9780p-45, +}; + +/* + * exp2(x): compute the base 2 exponential of x + * + * Accuracy: Peak error < 0.503 ulp for normalized results. + * + * Method: (accurate tables) + * + * Reduce x: + * x = 2**k + y, for integer k and |y| <= 1/2. + * Thus we have exp2(x) = 2**k * exp2(y). + * + * Reduce y: + * y = i/TBLSIZE + z - eps[i] for integer i near y * TBLSIZE. + * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z - eps[i]), + * with |z - eps[i]| <= 2**-9 + 2**-39 for the table used. + * + * We compute exp2(i/TBLSIZE) via table lookup and exp2(z - eps[i]) via + * a degree-5 minimax polynomial with maximum error under 1.3 * 2**-61. + * The values in exp2t[] and eps[] are chosen such that + * exp2t[i] = exp2(i/TBLSIZE + eps[i]), and eps[i] is a small offset such + * that exp2t[i] is accurate to 2**-64. + * + * Note that the range of i is +-TBLSIZE/2, so we actually index the tables + * by i0 = i + TBLSIZE/2. For cache efficiency, exp2t[] and eps[] are + * virtual tables, interleaved in the real table tbl[]. + * + * This method is due to Gal, with many details due to Gal and Bachelis: + * + * Gal, S. and Bachelis, B. An Accurate Elementary Mathematical Library + * for the IEEE Floating Point Standard. TOMS 17(1), 26-46 (1991). + */ +OLM_DLLEXPORT double +exp2(double x) +{ + double r, t, twopk, twopkp1000, z; + u_int32_t hx, ix, lx, i0; + int k; + + /* Filter out exceptional cases. */ + GET_HIGH_WORD(hx,x); + ix = hx & 0x7fffffff; /* high word of |x| */ + if(ix >= 0x40900000) { /* |x| >= 1024 */ + if(ix >= 0x7ff00000) { + GET_LOW_WORD(lx,x); + if(((ix & 0xfffff) | lx) != 0 || (hx & 0x80000000) == 0) + return (x + x); /* x is NaN or +Inf */ + else + return (0.0); /* x is -Inf */ + } + if(x >= 0x1.0p10) + return (huge * huge); /* overflow */ + if(x <= -0x1.0ccp10) + return (twom1000 * twom1000); /* underflow */ + } else if (ix < 0x3c900000) { /* |x| < 0x1p-54 */ + return (1.0 + x); + } + + /* Reduce x, computing z, i0, and k. */ + STRICT_ASSIGN(double, t, x + redux); + GET_LOW_WORD(i0, t); + i0 += TBLSIZE / 2; + k = (i0 >> TBLBITS) << 20; + i0 = (i0 & (TBLSIZE - 1)) << 1; + t -= redux; + z = x - t; + + /* Compute r = exp2(y) = exp2t[i0] * p(z - eps[i]). */ + t = tbl[i0]; /* exp2t[i0] */ + z -= tbl[i0 + 1]; /* eps[i0] */ + if (k >= -(1021 << 20)) + INSERT_WORDS(twopk, 0x3ff00000 + k, 0); + else + INSERT_WORDS(twopkp1000, 0x3ff00000 + k + (1000 << 20), 0); + r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))); + + /* Scale by 2**(k>>20). */ + if(k >= -(1021 << 20)) { + if (k == 1024 << 20) + return (r * 2.0 * 0x1p1023); + return (r * twopk); + } else { + return (r * twopkp1000 * twom1000); + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(exp2, exp2l); +#endif diff --git a/openlibm/src/s_exp2f.c b/openlibm/src/s_exp2f.c new file mode 100644 index 0000000000..74bc9f36c7 --- /dev/null +++ b/openlibm/src/s_exp2f.c @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_exp2f.c,v 1.9 2008/02/22 02:27:34 das Exp $"); + +#include +#include + +#include "math_private.h" + +#define TBLBITS 4 +#define TBLSIZE (1 << TBLBITS) + +static const float + huge = 0x1p100f, + redux = 0x1.8p23f / TBLSIZE, + P1 = 0x1.62e430p-1f, + P2 = 0x1.ebfbe0p-3f, + P3 = 0x1.c6b348p-5f, + P4 = 0x1.3b2c9cp-7f; + +static volatile float twom100 = 0x1p-100f; + +static const double exp2ft[TBLSIZE] = { + 0x1.6a09e667f3bcdp-1, + 0x1.7a11473eb0187p-1, + 0x1.8ace5422aa0dbp-1, + 0x1.9c49182a3f090p-1, + 0x1.ae89f995ad3adp-1, + 0x1.c199bdd85529cp-1, + 0x1.d5818dcfba487p-1, + 0x1.ea4afa2a490dap-1, + 0x1.0000000000000p+0, + 0x1.0b5586cf9890fp+0, + 0x1.172b83c7d517bp+0, + 0x1.2387a6e756238p+0, + 0x1.306fe0a31b715p+0, + 0x1.3dea64c123422p+0, + 0x1.4bfdad5362a27p+0, + 0x1.5ab07dd485429p+0, +}; + +/* + * exp2f(x): compute the base 2 exponential of x + * + * Accuracy: Peak error < 0.501 ulp; location of peak: -0.030110927. + * + * Method: (equally-spaced tables) + * + * Reduce x: + * x = 2**k + y, for integer k and |y| <= 1/2. + * Thus we have exp2f(x) = 2**k * exp2(y). + * + * Reduce y: + * y = i/TBLSIZE + z for integer i near y * TBLSIZE. + * Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z), + * with |z| <= 2**-(TBLSIZE+1). + * + * We compute exp2(i/TBLSIZE) via table lookup and exp2(z) via a + * degree-4 minimax polynomial with maximum error under 1.4 * 2**-33. + * Using double precision for everything except the reduction makes + * roundoff error insignificant and simplifies the scaling step. + * + * This method is due to Tang, but I do not use his suggested parameters: + * + * Tang, P. Table-driven Implementation of the Exponential Function + * in IEEE Floating-Point Arithmetic. TOMS 15(2), 144-157 (1989). + */ +OLM_DLLEXPORT float +exp2f(float x) +{ + double tv, twopk, u, z; + float t; + u_int32_t hx, ix, i0; + int32_t k; + + /* Filter out exceptional cases. */ + GET_FLOAT_WORD(hx, x); + ix = hx & 0x7fffffff; /* high word of |x| */ + if(ix >= 0x43000000) { /* |x| >= 128 */ + if(ix >= 0x7f800000) { + if ((ix & 0x7fffff) != 0 || (hx & 0x80000000) == 0) + return (x + x); /* x is NaN or +Inf */ + else + return (0.0); /* x is -Inf */ + } + if(x >= 0x1.0p7f) + return (huge * huge); /* overflow */ + if(x <= -0x1.2cp7f) + return (twom100 * twom100); /* underflow */ + } else if (ix <= 0x33000000) { /* |x| <= 0x1p-25 */ + return (1.0f + x); + } + + /* Reduce x, computing z, i0, and k. */ + STRICT_ASSIGN(float, t, x + redux); + GET_FLOAT_WORD(i0, t); + i0 += TBLSIZE / 2; + k = (i0 >> TBLBITS) << 20; + i0 &= TBLSIZE - 1; + t -= redux; + z = x - t; + INSERT_WORDS(twopk, 0x3ff00000 + k, 0); + + /* Compute r = exp2(y) = exp2ft[i0] * p(z). */ + tv = exp2ft[i0]; + u = tv * z; + tv = tv + u * (P1 + z * P2) + u * (z * z) * (P3 + z * P4); + + /* Scale by 2**(k>>20). */ + return (tv * twopk); +} diff --git a/openlibm/src/s_expm1.c b/openlibm/src/s_expm1.c new file mode 100644 index 0000000000..5798714654 --- /dev/null +++ b/openlibm/src/s_expm1.c @@ -0,0 +1,221 @@ +/* @(#)s_expm1.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_expm1.c,v 1.12 2011/10/21 06:26:38 das Exp $"); + +/* expm1(x) + * Returns exp(x)-1, the exponential of x minus 1. + * + * Method + * 1. Argument reduction: + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658 + * + * Here a correction term c will be computed to compensate + * the error in r when rounded to a floating-point number. + * + * 2. Approximating expm1(r) by a special rational function on + * the interval [0,0.34658]: + * Since + * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ... + * we define R1(r*r) by + * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r) + * That is, + * R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) + * = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) + * = 1 - r^2/60 + r^4/2520 - r^6/100800 + ... + * We use a special Reme algorithm on [0,0.347] to generate + * a polynomial of degree 5 in r*r to approximate R1. The + * maximum error of this polynomial approximation is bounded + * by 2**-61. In other words, + * R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 + * where Q1 = -1.6666666666666567384E-2, + * Q2 = 3.9682539681370365873E-4, + * Q3 = -9.9206344733435987357E-6, + * Q4 = 2.5051361420808517002E-7, + * Q5 = -6.2843505682382617102E-9; + * z = r*r, + * with error bounded by + * | 5 | -61 + * | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 + * | | + * + * expm1(r) = exp(r)-1 is then computed by the following + * specific way which minimize the accumulation rounding error: + * 2 3 + * r r [ 3 - (R1 + R1*r/2) ] + * expm1(r) = r + --- + --- * [--------------------] + * 2 2 [ 6 - r*(3 - R1*r/2) ] + * + * To compensate the error in the argument reduction, we use + * expm1(r+c) = expm1(r) + c + expm1(r)*c + * ~ expm1(r) + c + r*c + * Thus c+r*c will be added in as the correction terms for + * expm1(r+c). Now rearrange the term to avoid optimization + * screw up: + * ( 2 2 ) + * ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) + * expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) + * ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) + * ( ) + * + * = r - E + * 3. Scale back to obtain expm1(x): + * From step 1, we have + * expm1(x) = either 2^k*[expm1(r)+1] - 1 + * = or 2^k*[expm1(r) + (1-2^-k)] + * 4. Implementation notes: + * (A). To save one multiplication, we scale the coefficient Qi + * to Qi*2^i, and replace z by (x^2)/2. + * (B). To achieve maximum accuracy, we compute expm1(x) by + * (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) + * (ii) if k=0, return r-E + * (iii) if k=-1, return 0.5*(r-E)-0.5 + * (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) + * else return 1.0+2.0*(r-E); + * (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1) + * (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else + * (vii) return 2^k(1-((E+2^-k)-r)) + * + * Special cases: + * expm1(INF) is INF, expm1(NaN) is NaN; + * expm1(-INF) is -1, and + * for finite argument, only expm1(0)=0 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then expm1(x) overflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include +#include + +#include "math_private.h" + +static const double +one = 1.0, +huge = 1.0e+300, +tiny = 1.0e-300, +o_threshold = 7.09782712893383973096e+02,/* 0x40862E42, 0xFEFA39EF */ +ln2_hi = 6.93147180369123816490e-01,/* 0x3fe62e42, 0xfee00000 */ +ln2_lo = 1.90821492927058770002e-10,/* 0x3dea39ef, 0x35793c76 */ +invln2 = 1.44269504088896338700e+00,/* 0x3ff71547, 0x652b82fe */ +/* Scaled Q's: Qn_here = 2**n * Qn_above, for R(2*z) where z = hxs = x*x/2: */ +Q1 = -3.33333333333331316428e-02, /* BFA11111 111110F4 */ +Q2 = 1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */ +Q3 = -7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */ +Q4 = 4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */ +Q5 = -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */ + +OLM_DLLEXPORT double +expm1(double x) +{ + double y,hi,lo,c,t,e,hxs,hfx,r1,twopk; + int32_t k,xsb; + u_int32_t hx; + + GET_HIGH_WORD(hx,x); + xsb = hx&0x80000000; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out huge and non-finite argument */ + if(hx >= 0x4043687A) { /* if |x|>=56*ln2 */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + u_int32_t low; + GET_LOW_WORD(low,x); + if(((hx&0xfffff)|low)!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:-1.0;/* exp(+-inf)-1={inf,-1} */ + } + if(x > o_threshold) return huge*huge; /* overflow */ + } + if(xsb!=0) { /* x < -56*ln2, return -1.0 with inexact */ + if(x+tiny<0.0) /* raise inexact */ + return tiny-one; /* return -1 */ + } + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + if(xsb==0) + {hi = x - ln2_hi; lo = ln2_lo; k = 1;} + else + {hi = x + ln2_hi; lo = -ln2_lo; k = -1;} + } else { + k = invln2*x+((xsb==0)?0.5:-0.5); + t = k; + hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ + lo = t*ln2_lo; + } + STRICT_ASSIGN(double, x, hi - lo); + c = (hi-x)-lo; + } + else if(hx < 0x3c900000) { /* when |x|<2**-54, return x */ + t = huge+x; /* return x with inexact flags when x!=0 */ + return x - (t-(huge+x)); + } + else k = 0; + + /* x is now in primary range */ + hfx = 0.5*x; + hxs = x*hfx; + r1 = one+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))); + t = 3.0-r1*hfx; + e = hxs*((r1-t)/(6.0 - x*t)); + if(k==0) return x - (x*e-hxs); /* c is 0 */ + else { + INSERT_WORDS(twopk,0x3ff00000+(k<<20),0); /* 2^k */ + e = (x*(e-c)-c); + e -= hxs; + if(k== -1) return 0.5*(x-e)-0.5; + if(k==1) { + if(x < -0.25) return -2.0*(e-(x+0.5)); + else return one+2.0*(x-e); + } + if (k <= -2 || k>56) { /* suffice to return exp(x)-1 */ + y = one-(e-x); + if (k == 1024) y = y*2.0*0x1p1023; + else y = y*twopk; + return y-one; + } + t = one; + if(k<20) { + SET_HIGH_WORD(t,0x3ff00000 - (0x200000>>k)); /* t=1-2^-k */ + y = t-(e-x); + y = y*twopk; + } else { + SET_HIGH_WORD(t,((0x3ff-k)<<20)); /* 2^-k */ + y = x-(e+t); + y += one; + y = y*twopk; + } + } + return y; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(expm1, expm1l); +#endif diff --git a/openlibm/src/s_expm1f.c b/openlibm/src/s_expm1f.c new file mode 100644 index 0000000000..cb7ceec780 --- /dev/null +++ b/openlibm/src/s_expm1f.c @@ -0,0 +1,123 @@ +/* s_expm1f.c -- float version of s_expm1.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_expm1f.c,v 1.12 2011/10/21 06:26:38 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const float +one = 1.0, +huge = 1.0e+30, +tiny = 1.0e-30, +o_threshold = 8.8721679688e+01,/* 0x42b17180 */ +ln2_hi = 6.9313812256e-01,/* 0x3f317180 */ +ln2_lo = 9.0580006145e-06,/* 0x3717f7d1 */ +invln2 = 1.4426950216e+00,/* 0x3fb8aa3b */ +/* + * Domain [-0.34568, 0.34568], range ~[-6.694e-10, 6.696e-10]: + * |6 / x * (1 + 2 * (1 / (exp(x) - 1) - 1 / x)) - q(x)| < 2**-30.04 + * Scaled coefficients: Qn_here = 2**n * Qn_for_q (see s_expm1.c): + */ +Q1 = -3.3333212137e-2, /* -0x888868.0p-28 */ +Q2 = 1.5807170421e-3; /* 0xcf3010.0p-33 */ + +OLM_DLLEXPORT float +expm1f(float x) +{ + float y,hi,lo,c,t,e,hxs,hfx,r1,twopk; + int32_t k,xsb; + u_int32_t hx; + + GET_FLOAT_WORD(hx,x); + xsb = hx&0x80000000; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out huge and non-finite argument */ + if(hx >= 0x4195b844) { /* if |x|>=27*ln2 */ + if(hx >= 0x42b17218) { /* if |x|>=88.721... */ + if(hx>0x7f800000) + return x+x; /* NaN */ + if(hx==0x7f800000) + return (xsb==0)? x:-1.0;/* exp(+-inf)={inf,-1} */ + if(x > o_threshold) return huge*huge; /* overflow */ + } + if(xsb!=0) { /* x < -27*ln2, return -1.0 with inexact */ + if(x+tiny<(float)0.0) /* raise inexact */ + return tiny-one; /* return -1 */ + } + } + + /* argument reduction */ + if(hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3F851592) { /* and |x| < 1.5 ln2 */ + if(xsb==0) + {hi = x - ln2_hi; lo = ln2_lo; k = 1;} + else + {hi = x + ln2_hi; lo = -ln2_lo; k = -1;} + } else { + k = invln2*x+((xsb==0)?(float)0.5:(float)-0.5); + t = k; + hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ + lo = t*ln2_lo; + } + STRICT_ASSIGN(float, x, hi - lo); + c = (hi-x)-lo; + } + else if(hx < 0x33000000) { /* when |x|<2**-25, return x */ + t = huge+x; /* return x with inexact flags when x!=0 */ + return x - (t-(huge+x)); + } + else k = 0; + + /* x is now in primary range */ + hfx = (float)0.5*x; + hxs = x*hfx; + r1 = one+hxs*(Q1+hxs*Q2); + t = (float)3.0-r1*hfx; + e = hxs*((r1-t)/((float)6.0 - x*t)); + if(k==0) return x - (x*e-hxs); /* c is 0 */ + else { + SET_FLOAT_WORD(twopk,0x3f800000+(k<<23)); /* 2^k */ + e = (x*(e-c)-c); + e -= hxs; + if(k== -1) return (float)0.5*(x-e)-(float)0.5; + if(k==1) { + if(x < (float)-0.25) return -(float)2.0*(e-(x+(float)0.5)); + else return one+(float)2.0*(x-e); + } + if (k <= -2 || k>56) { /* suffice to return exp(x)-1 */ + y = one-(e-x); + if (k == 128) y = y*2.0F*0x1p127F; + else y = y*twopk; + return y-one; + } + t = one; + if(k<23) { + SET_FLOAT_WORD(t,0x3f800000 - (0x1000000>>k)); /* t=1-2^-k */ + y = t-(e-x); + y = y*twopk; + } else { + SET_FLOAT_WORD(t,((0x7f-k)<<23)); /* 2^-k */ + y = x-(e+t); + y += one; + y = y*twopk; + } + } + return y; +} diff --git a/openlibm/src/s_fabs.c b/openlibm/src/s_fabs.c new file mode 100644 index 0000000000..43ffc1abf7 --- /dev/null +++ b/openlibm/src/s_fabs.c @@ -0,0 +1,28 @@ +/* @(#)s_fabs.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fabs(x) returns the absolute value of x. + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +fabs(double x) +{ + u_int32_t high; + GET_HIGH_WORD(high,x); + SET_HIGH_WORD(x,high&0x7fffffff); + return x; +} diff --git a/openlibm/src/s_fabsf.c b/openlibm/src/s_fabsf.c new file mode 100644 index 0000000000..eeceee4179 --- /dev/null +++ b/openlibm/src/s_fabsf.c @@ -0,0 +1,34 @@ +/* s_fabsf.c -- float version of s_fabs.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fabsf.c,v 1.8 2008/02/22 02:30:35 das Exp $"); + +/* + * fabsf(x) returns the absolute value of x. + */ + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +fabsf(float x) +{ + u_int32_t ix; + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(x,ix&0x7fffffff); + return x; +} diff --git a/openlibm/src/s_fabsl.c b/openlibm/src/s_fabsl.c new file mode 100644 index 0000000000..6bc1971bcf --- /dev/null +++ b/openlibm/src/s_fabsl.c @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2003 Dag-Erling Codan Smrgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_fabsl.c,v 1.2 2003/10/25 19:53:28 des Exp $ + */ + +#include +#include "math_private.h" +#include "fpmath.h" + +OLM_DLLEXPORT long double +fabsl(long double x) +{ + union IEEEl2bits u; + + u.e = x; + u.bits.sign = 0; + return (u.e); +} diff --git a/openlibm/src/s_fdim.c b/openlibm/src/s_fdim.c new file mode 100644 index 0000000000..2f05347cf4 --- /dev/null +++ b/openlibm/src/s_fdim.c @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fdim.c,v 1.1 2004/06/30 07:04:01 das Exp $"); +#include +#include "math_private.h" + +#define DECL(type, fn) \ +OLM_DLLEXPORT type \ +fn(type x, type y) \ +{ \ + \ + if (isnan(x)) \ + return (x); \ + if (isnan(y)) \ + return (y); \ + return (x > y ? x - y : 0.0); \ +} + +DECL(double, fdim) +DECL(float, fdimf) +#ifdef OLM_LONG_DOUBLE +DECL(long double, fdiml) +#endif diff --git a/openlibm/src/s_floor.c b/openlibm/src/s_floor.c new file mode 100644 index 0000000000..4a44bd5fe0 --- /dev/null +++ b/openlibm/src/s_floor.c @@ -0,0 +1,78 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_floor.c,v 1.11 2008/02/15 07:01:40 bde Exp $"); + +/* + * floor(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floor(x). + */ + +#include +#include + +#include "math_private.h" + +static const double huge = 1.0e300; + +OLM_DLLEXPORT double +floor(double x) +{ + int32_t i0,i1,j0; + u_int32_t i,j; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=i1=0;} + else if(((i0&0x7fffffff)|i1)!=0) + { i0=0xbff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) { + if(j0==20) i0+=1; + else { + j = i1+(1<<(52-j0)); + if(j + +#include "math_private.h" + +static const float huge = 1.0e30; + +OLM_DLLEXPORT float +floorf(float x) +{ + int32_t i0,j0; + u_int32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=0;} + else if((i0&0x7fffffff)!=0) + { i0=0xbf800000;} + } + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge+x>(float)0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00800000)>>j0; + i0 &= (~i); + } + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} diff --git a/openlibm/src/s_floorl.c b/openlibm/src/s_floorl.c new file mode 100644 index 0000000000..8427edbc76 --- /dev/null +++ b/openlibm/src/s_floorl.c @@ -0,0 +1,102 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * From: @(#)s_floor.c 5.1 93/09/24 + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_floorl.c,v 1.8 2008/02/14 15:10:34 bde Exp $"); + +/* + * floorl(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floorl(x). + */ + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#ifdef LDBL_IMPLICIT_NBIT +#define MANH_SIZE (LDBL_MANH_SIZE + 1) +#define INC_MANH(u, c) do { \ + uint64_t o = u.bits.manh; \ + u.bits.manh += (c); \ + if (u.bits.manh < o) \ + u.bits.exp++; \ +} while (0) +#else +#define MANH_SIZE LDBL_MANH_SIZE +#define INC_MANH(u, c) do { \ + uint64_t o = u.bits.manh; \ + u.bits.manh += (c); \ + if (u.bits.manh < o) { \ + u.bits.exp++; \ + u.bits.manh |= 1llu << (LDBL_MANH_SIZE - 1); \ + } \ +} while (0) +#endif + +static const long double huge = 1.0e300; + +OLM_DLLEXPORT long double +floorl(long double x) +{ + union IEEEl2bits u = { .e = x }; + int e = u.bits.exp - LDBL_MAX_EXP + 1; + + if (e < MANH_SIZE - 1) { + if (e < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) + if (u.bits.exp > 0 || + (u.bits.manh | u.bits.manl) != 0) + u.e = u.bits.sign ? -1.0 : 0.0; + } else { + uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1); + if (((u.bits.manh & m) | u.bits.manl) == 0) + return (x); /* x is integral */ + if (u.bits.sign) { +#ifdef LDBL_IMPLICIT_NBIT + if (e == 0) + u.bits.exp++; + else +#endif + INC_MANH(u, 1llu << (MANH_SIZE - e - 1)); + } + if (huge + x > 0.0) { /* raise inexact flag */ + u.bits.manh &= ~m; + u.bits.manl = 0; + } + } + } else if (e < LDBL_MANT_DIG - 1) { + uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1); + if ((u.bits.manl & m) == 0) + return (x); /* x is integral */ + if (u.bits.sign) { + if (e == MANH_SIZE - 1) + INC_MANH(u, 1); + else { + uint64_t o = u.bits.manl; + u.bits.manl += 1llu << (LDBL_MANT_DIG - e - 1); + if (u.bits.manl < o) /* got a carry */ + INC_MANH(u, 1); + } + } + if (huge + x > 0.0) /* raise inexact flag */ + u.bits.manl &= ~m; + } + return (u.e); +} diff --git a/openlibm/src/s_fma.c b/openlibm/src/s_fma.c new file mode 100644 index 0000000000..63e529e5ff --- /dev/null +++ b/openlibm/src/s_fma.c @@ -0,0 +1,284 @@ +/*- + * Copyright (c) 2005-2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fma.c,v 1.8 2011/10/21 06:30:43 das Exp $"); + +#include +#include +#include + +#include "math_private.h" + +/* + * A struct dd represents a floating-point number with twice the precision + * of a double. We maintain the invariant that "hi" stores the 53 high-order + * bits of the result. + */ +struct dd { + double hi; + double lo; +}; + +/* + * Compute a+b exactly, returning the exact result in a struct dd. We assume + * that both a and b are finite, but make no assumptions about their relative + * magnitudes. + */ +static inline struct dd +dd_add(double a, double b) +{ + struct dd ret; + double s; + + ret.hi = a + b; + s = ret.hi - a; + ret.lo = (a - (ret.hi - s)) + (b - s); + return (ret); +} + +/* + * Compute a+b, with a small tweak: The least significant bit of the + * result is adjusted into a sticky bit summarizing all the bits that + * were lost to rounding. This adjustment negates the effects of double + * rounding when the result is added to another number with a higher + * exponent. For an explanation of round and sticky bits, see any reference + * on FPU design, e.g., + * + * J. Coonen. An Implementation Guide to a Proposed Standard for + * Floating-Point Arithmetic. Computer, vol. 13, no. 1, Jan 1980. + */ +static inline double +add_adjusted(double a, double b) +{ + struct dd sum; + u_int64_t hibits, lobits; + + sum = dd_add(a, b); + if (sum.lo != 0) { + EXTRACT_WORD64(hibits, sum.hi); + if ((hibits & 1) == 0) { + /* hibits += (int)copysign(1.0, sum.hi * sum.lo) */ + EXTRACT_WORD64(lobits, sum.lo); + hibits += 1 - ((hibits ^ lobits) >> 62); + INSERT_WORD64(sum.hi, hibits); + } + } + return (sum.hi); +} + +/* + * Compute ldexp(a+b, scale) with a single rounding error. It is assumed + * that the result will be subnormal, and care is taken to ensure that + * double rounding does not occur. + */ +static inline double +add_and_denormalize(double a, double b, int scale) +{ + struct dd sum; + u_int64_t hibits, lobits; + int bits_lost; + + sum = dd_add(a, b); + + /* + * If we are losing at least two bits of accuracy to denormalization, + * then the first lost bit becomes a round bit, and we adjust the + * lowest bit of sum.hi to make it a sticky bit summarizing all the + * bits in sum.lo. With the sticky bit adjusted, the hardware will + * break any ties in the correct direction. + * + * If we are losing only one bit to denormalization, however, we must + * break the ties manually. + */ + if (sum.lo != 0) { + EXTRACT_WORD64(hibits, sum.hi); + bits_lost = -((int)(hibits >> 52) & 0x7ff) - scale + 1; + if ((bits_lost != 1) ^ (int)(hibits & 1)) { + /* hibits += (int)copysign(1.0, sum.hi * sum.lo) */ + EXTRACT_WORD64(lobits, sum.lo); + hibits += 1 - (((hibits ^ lobits) >> 62) & 2); + INSERT_WORD64(sum.hi, hibits); + } + } + return (ldexp(sum.hi, scale)); +} + +/* + * Compute a*b exactly, returning the exact result in a struct dd. We assume + * that both a and b are normalized, so no underflow or overflow will occur. + * The current rounding mode must be round-to-nearest. + */ +static inline struct dd +dd_mul(double a, double b) +{ + static const double split = 0x1p27 + 1.0; + struct dd ret; + double ha, hb, la, lb, p, q; + + p = a * split; + ha = a - p; + ha += p; + la = a - ha; + + p = b * split; + hb = b - p; + hb += p; + lb = b - hb; + + p = ha * hb; + q = ha * lb + la * hb; + + ret.hi = p + q; + ret.lo = p - ret.hi + q + la * lb; + return (ret); +} + +/* + * Fused multiply-add: Compute x * y + z with a single rounding error. + * + * We use scaling to avoid overflow/underflow, along with the + * canonical precision-doubling technique adapted from: + * + * Dekker, T. A Floating-Point Technique for Extending the + * Available Precision. Numer. Math. 18, 224-242 (1971). + * + * This algorithm is sensitive to the rounding precision. FPUs such + * as the i387 must be set in double-precision mode if variables are + * to be stored in FP registers in order to avoid incorrect results. + * This is the default on FreeBSD, but not on many other systems. + * + * Hardware instructions should be used on architectures that support it, + * since this implementation will likely be several times slower. + */ +OLM_DLLEXPORT double +fma(double x, double y, double z) +{ + double xs, ys, zs, adj; + struct dd xy, r; + int oround; + int ex, ey, ez; + int spread; + + /* + * Handle special cases. The order of operations and the particular + * return values here are crucial in handling special cases involving + * infinities, NaNs, overflows, and signed zeroes correctly. + */ + if (x == 0.0 || y == 0.0) + return (x * y + z); + if (z == 0.0) + return (x * y); + if (!isfinite(x) || !isfinite(y)) + return (x * y + z); + if (!isfinite(z)) + return (z); + + xs = frexp(x, &ex); + ys = frexp(y, &ey); + zs = frexp(z, &ez); + oround = fegetround(); + spread = ex + ey - ez; + + /* + * If x * y and z are many orders of magnitude apart, the scaling + * will overflow, so we handle these cases specially. Rounding + * modes other than FE_TONEAREST are painful. + */ + if (spread < -DBL_MANT_DIG) { + feraiseexcept(FE_INEXACT); + if (!isnormal(z)) + feraiseexcept(FE_UNDERFLOW); + switch (oround) { + case FE_TONEAREST: + return (z); + case FE_TOWARDZERO: + if ((x > 0.0) ^ (y < 0.0) ^ (z < 0.0)) + return (z); + else + return (nextafter(z, 0)); + case FE_DOWNWARD: + if ((x > 0.0) ^ (y < 0.0)) + return (z); + else + return (nextafter(z, -INFINITY)); + default: /* FE_UPWARD */ + if ((x > 0.0) ^ (y < 0.0)) + return (nextafter(z, INFINITY)); + else + return (z); + } + } + if (spread <= DBL_MANT_DIG * 2) + zs = ldexp(zs, -spread); + else + zs = copysign(DBL_MIN, zs); + + fesetround(FE_TONEAREST); + + /* + * Basic approach for round-to-nearest: + * + * (xy.hi, xy.lo) = x * y (exact) + * (r.hi, r.lo) = xy.hi + z (exact) + * adj = xy.lo + r.lo (inexact; low bit is sticky) + * result = r.hi + adj (correctly rounded) + */ + xy = dd_mul(xs, ys); + r = dd_add(xy.hi, zs); + + spread = ex + ey; + + if (r.hi == 0.0) { + /* + * When the addends cancel to 0, ensure that the result has + * the correct sign. + */ + fesetround(oround); + volatile double vzs = zs; /* XXX gcc CSE bug workaround */ + return (xy.hi + vzs + ldexp(xy.lo, spread)); + } + + if (oround != FE_TONEAREST) { + /* + * There is no need to worry about double rounding in directed + * rounding modes. + */ + fesetround(oround); + adj = r.lo + xy.lo; + return (ldexp(r.hi + adj, spread)); + } + + adj = add_adjusted(r.lo, xy.lo); + if (spread + ilogb(r.hi) > -1023) + return (ldexp(r.hi + adj, spread)); + else + return (add_and_denormalize(r.hi, adj, spread)); +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(fma, fmal); +#endif diff --git a/openlibm/src/s_fmaf.c b/openlibm/src/s_fmaf.c new file mode 100644 index 0000000000..b3c8efb5ef --- /dev/null +++ b/openlibm/src/s_fmaf.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2005-2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fmaf.c,v 1.3 2011/10/15 04:16:58 das Exp $"); + +#include +#include + +#include "math_private.h" + +/* + * Fused multiply-add: Compute x * y + z with a single rounding error. + * + * A double has more than twice as much precision than a float, so + * direct double-precision arithmetic suffices, except where double + * rounding occurs. + */ +OLM_DLLEXPORT float +fmaf(float x, float y, float z) +{ + double xy, result; + u_int32_t hr, lr; + + xy = (double)x * y; + result = xy + z; + EXTRACT_WORDS(hr, lr, result); + /* Common case: The double precision result is fine. */ + if ((lr & 0x1fffffff) != 0x10000000 || /* not a halfway case */ + (hr & 0x7ff00000) == 0x7ff00000 || /* NaN */ + result - xy == z || /* exact */ + fegetround() != FE_TONEAREST) /* not round-to-nearest */ + return (result); + + /* + * If result is inexact, and exactly halfway between two float values, + * we need to adjust the low-order bit in the direction of the error. + */ + fesetround(FE_TOWARDZERO); + volatile double vxy = xy; /* XXX work around gcc CSE bug */ + double adjusted_result = vxy + z; + fesetround(FE_TONEAREST); + if (result == adjusted_result) + SET_LOW_WORD(adjusted_result, lr + 1); + return (adjusted_result); +} diff --git a/openlibm/src/s_fmal.c b/openlibm/src/s_fmal.c new file mode 100644 index 0000000000..2fe30c6301 --- /dev/null +++ b/openlibm/src/s_fmal.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 2005-2011 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fmal.c,v 1.7 2011/10/21 06:30:43 das Exp $"); + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +/* + * A struct dd represents a floating-point number with twice the precision + * of a long double. We maintain the invariant that "hi" stores the high-order + * bits of the result. + */ +struct dd { + long double hi; + long double lo; +}; + +/* + * Compute a+b exactly, returning the exact result in a struct dd. We assume + * that both a and b are finite, but make no assumptions about their relative + * magnitudes. + */ +static inline struct dd +dd_add(long double a, long double b) +{ + struct dd ret; + long double s; + + ret.hi = a + b; + s = ret.hi - a; + ret.lo = (a - (ret.hi - s)) + (b - s); + return (ret); +} + +/* + * Compute a+b, with a small tweak: The least significant bit of the + * result is adjusted into a sticky bit summarizing all the bits that + * were lost to rounding. This adjustment negates the effects of double + * rounding when the result is added to another number with a higher + * exponent. For an explanation of round and sticky bits, see any reference + * on FPU design, e.g., + * + * J. Coonen. An Implementation Guide to a Proposed Standard for + * Floating-Point Arithmetic. Computer, vol. 13, no. 1, Jan 1980. + */ +static inline long double +add_adjusted(long double a, long double b) +{ + struct dd sum; + union IEEEl2bits u; + + sum = dd_add(a, b); + if (sum.lo != 0) { + u.e = sum.hi; + if ((u.bits.manl & 1) == 0) + sum.hi = nextafterl(sum.hi, INFINITY * sum.lo); + } + return (sum.hi); +} + +/* + * Compute ldexp(a+b, scale) with a single rounding error. It is assumed + * that the result will be subnormal, and care is taken to ensure that + * double rounding does not occur. + */ +static inline long double +add_and_denormalize(long double a, long double b, int scale) +{ + struct dd sum; + int bits_lost; + union IEEEl2bits u; + + sum = dd_add(a, b); + + /* + * If we are losing at least two bits of accuracy to denormalization, + * then the first lost bit becomes a round bit, and we adjust the + * lowest bit of sum.hi to make it a sticky bit summarizing all the + * bits in sum.lo. With the sticky bit adjusted, the hardware will + * break any ties in the correct direction. + * + * If we are losing only one bit to denormalization, however, we must + * break the ties manually. + */ + if (sum.lo != 0) { + u.e = sum.hi; + bits_lost = -u.bits.exp - scale + 1; + if ((bits_lost != 1) ^ (int)(u.bits.manl & 1)) + sum.hi = nextafterl(sum.hi, INFINITY * sum.lo); + } + return (ldexp(sum.hi, scale)); +} + +/* + * Compute a*b exactly, returning the exact result in a struct dd. We assume + * that both a and b are normalized, so no underflow or overflow will occur. + * The current rounding mode must be round-to-nearest. + */ +static inline struct dd +dd_mul(long double a, long double b) +{ +#if LDBL_MANT_DIG == 64 + static const long double split = 0x1p32L + 1.0; +#elif LDBL_MANT_DIG == 113 + static const long double split = 0x1p57L + 1.0; +#endif + struct dd ret; + long double ha, hb, la, lb, p, q; + + p = a * split; + ha = a - p; + ha += p; + la = a - ha; + + p = b * split; + hb = b - p; + hb += p; + lb = b - hb; + + p = ha * hb; + q = ha * lb + la * hb; + + ret.hi = p + q; + ret.lo = p - ret.hi + q + la * lb; + return (ret); +} + +/* + * Fused multiply-add: Compute x * y + z with a single rounding error. + * + * We use scaling to avoid overflow/underflow, along with the + * canonical precision-doubling technique adapted from: + * + * Dekker, T. A Floating-Point Technique for Extending the + * Available Precision. Numer. Math. 18, 224-242 (1971). + */ +OLM_DLLEXPORT long double +fmal(long double x, long double y, long double z) +{ + long double xs, ys, zs, adj; + struct dd xy, r; + int oround; + int ex, ey, ez; + int spread; + + /* + * Handle special cases. The order of operations and the particular + * return values here are crucial in handling special cases involving + * infinities, NaNs, overflows, and signed zeroes correctly. + */ + if (x == 0.0 || y == 0.0) + return (x * y + z); + if (z == 0.0) + return (x * y); + if (!isfinite(x) || !isfinite(y)) + return (x * y + z); + if (!isfinite(z)) + return (z); + + xs = frexpl(x, &ex); + ys = frexpl(y, &ey); + zs = frexpl(z, &ez); + oround = fegetround(); + spread = ex + ey - ez; + + /* + * If x * y and z are many orders of magnitude apart, the scaling + * will overflow, so we handle these cases specially. Rounding + * modes other than FE_TONEAREST are painful. + */ + if (spread < -LDBL_MANT_DIG) { + feraiseexcept(FE_INEXACT); + if (!isnormal(z)) + feraiseexcept(FE_UNDERFLOW); + switch (oround) { + case FE_TONEAREST: + return (z); + case FE_TOWARDZERO: + if ((x > 0.0) ^ (y < 0.0) ^ (z < 0.0)) + return (z); + else + return (nextafterl(z, 0)); + case FE_DOWNWARD: + if ((x > 0.0) ^ (y < 0.0)) + return (z); + else + return (nextafterl(z, -INFINITY)); + default: /* FE_UPWARD */ + if ((x > 0.0) ^ (y < 0.0)) + return (nextafterl(z, INFINITY)); + else + return (z); + } + } + if (spread <= LDBL_MANT_DIG * 2) + zs = ldexpl(zs, -spread); + else + zs = copysignl(LDBL_MIN, zs); + + fesetround(FE_TONEAREST); + + /* + * Basic approach for round-to-nearest: + * + * (xy.hi, xy.lo) = x * y (exact) + * (r.hi, r.lo) = xy.hi + z (exact) + * adj = xy.lo + r.lo (inexact; low bit is sticky) + * result = r.hi + adj (correctly rounded) + */ + xy = dd_mul(xs, ys); + r = dd_add(xy.hi, zs); + + spread = ex + ey; + + if (r.hi == 0.0) { + /* + * When the addends cancel to 0, ensure that the result has + * the correct sign. + */ + fesetround(oround); + volatile long double vzs = zs; /* XXX gcc CSE bug workaround */ + return (xy.hi + vzs + ldexpl(xy.lo, spread)); + } + + if (oround != FE_TONEAREST) { + /* + * There is no need to worry about double rounding in directed + * rounding modes. + */ + fesetround(oround); + adj = r.lo + xy.lo; + return (ldexpl(r.hi + adj, spread)); + } + + adj = add_adjusted(r.lo, xy.lo); + if (spread + ilogbl(r.hi) > -16383) + return (ldexpl(r.hi + adj, spread)); + else + return (add_and_denormalize(r.hi, adj, spread)); +} diff --git a/openlibm/src/s_fmax.c b/openlibm/src/s_fmax.c new file mode 100644 index 0000000000..73919c6525 --- /dev/null +++ b/openlibm/src/s_fmax.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fmax.c,v 1.1 2004/06/30 07:04:01 das Exp $"); + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT double +fmax(double x, double y) +{ + union IEEEd2bits u[2]; + + u[0].d = x; + u[1].d = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0) + return (y); + if (u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[0].bits.sign].d); + + return (x > y ? x : y); +} diff --git a/openlibm/src/s_fmaxf.c b/openlibm/src/s_fmaxf.c new file mode 100644 index 0000000000..a8034619f1 --- /dev/null +++ b/openlibm/src/s_fmaxf.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fmaxf.c,v 1.1 2004/06/30 07:04:01 das Exp $"); + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT float +fmaxf(float x, float y) +{ + union IEEEf2bits u[2]; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[0].bits.sign].f); + + return (x > y ? x : y); +} diff --git a/openlibm/src/s_fmaxl.c b/openlibm/src/s_fmaxl.c new file mode 100644 index 0000000000..48de991e18 --- /dev/null +++ b/openlibm/src/s_fmaxl.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fmaxl.c,v 1.1 2004/06/30 07:04:01 das Exp $"); + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT long double +fmaxl(long double x, long double y) +{ + union IEEEl2bits u[2]; + + u[0].e = x; + mask_nbit_l(u[0]); + u[1].e = y; + mask_nbit_l(u[1]); + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0) + return (y); + if (u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[0].bits.sign ? y : x); + + return (x > y ? x : y); +} diff --git a/openlibm/src/s_fmin.c b/openlibm/src/s_fmin.c new file mode 100644 index 0000000000..9c527397d1 --- /dev/null +++ b/openlibm/src/s_fmin.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fmin.c,v 1.1 2004/06/30 07:04:01 das Exp $"); + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT double +fmin(double x, double y) +{ + union IEEEd2bits u[2]; + + u[0].d = x; + u[1].d = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 2047 && (u[0].bits.manh | u[0].bits.manl) != 0) + return (y); + if (u[1].bits.exp == 2047 && (u[1].bits.manh | u[1].bits.manl) != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[1].bits.sign].d); + + return (x < y ? x : y); +} diff --git a/openlibm/src/s_fminf.c b/openlibm/src/s_fminf.c new file mode 100644 index 0000000000..cc4017fb4c --- /dev/null +++ b/openlibm/src/s_fminf.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fminf.c,v 1.1 2004/06/30 07:04:01 das Exp $"); + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT float +fminf(float x, float y) +{ + union IEEEf2bits u[2]; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[1].bits.sign].f); + + return (x < y ? x : y); +} diff --git a/openlibm/src/s_fminl.c b/openlibm/src/s_fminl.c new file mode 100644 index 0000000000..043886d301 --- /dev/null +++ b/openlibm/src/s_fminl.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_fminl.c,v 1.1 2004/06/30 07:04:01 das Exp $"); + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT long double +fminl(long double x, long double y) +{ + union IEEEl2bits u[2]; + + u[0].e = x; + mask_nbit_l(u[0]); + u[1].e = y; + mask_nbit_l(u[1]); + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 32767 && (u[0].bits.manh | u[0].bits.manl) != 0) + return (y); + if (u[1].bits.exp == 32767 && (u[1].bits.manh | u[1].bits.manl) != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[1].bits.sign ? y : x); + + return (x < y ? x : y); +} diff --git a/openlibm/src/s_fpclassify.c b/openlibm/src/s_fpclassify.c new file mode 100644 index 0000000000..2b552e175c --- /dev/null +++ b/openlibm/src/s_fpclassify.c @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include "math_private.h" +#include "fpmath.h" + +OLM_DLLEXPORT int +__fpclassifyd(double d) +{ + union IEEEd2bits u; + + u.d = d; + if (u.bits.exp == 2047) { + if (u.bits.manl == 0 && u.bits.manh == 0) { + return FP_INFINITE; + } else { + return FP_NAN; + } + } else if (u.bits.exp != 0) { + return FP_NORMAL; + } else if (u.bits.manl == 0 && u.bits.manh == 0) { + return FP_ZERO; + } else { + return FP_SUBNORMAL; + } +} + + +OLM_DLLEXPORT int +__fpclassifyf(float f) +{ + union IEEEf2bits u; + + u.f = f; + if (u.bits.exp == 255) { + if (u.bits.man == 0) { + return FP_INFINITE; + } else { + return FP_NAN; + } + } else if (u.bits.exp != 0) { + return FP_NORMAL; + } else if (u.bits.man == 0) { + return FP_ZERO; + } else { + return FP_SUBNORMAL; + } +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT int +__fpclassifyl(long double e) +{ + union IEEEl2bits u; + + u.e = e; + mask_nbit_l(u); + if (u.bits.exp == 32767) { + if (u.bits.manl == 0 && u.bits.manh == 0) { + return FP_INFINITE; + } else { + return FP_NAN; + } + } else if (u.bits.exp != 0) { + return FP_NORMAL; + } else if (u.bits.manl == 0 && u.bits.manh == 0) { + return FP_ZERO; + } else { + return FP_SUBNORMAL; + } +} +#endif + diff --git a/openlibm/src/s_frexp.c b/openlibm/src/s_frexp.c new file mode 100644 index 0000000000..0385acfb36 --- /dev/null +++ b/openlibm/src/s_frexp.c @@ -0,0 +1,56 @@ +/* @(#)s_frexp.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_frexp.c,v 1.11 2008/02/22 02:30:35 das Exp $"); + +/* + * for non-zero x + * x = frexp(arg,&exp); + * return a double fp quantity x such that 0.5 <= |x| <1.0 + * and the corresponding binary exponent "exp". That is + * arg = x*2^exp. + * If arg is inf, 0.0, or NaN, then frexp(arg,&exp) returns arg + * with *exp=0. + */ + +#include +#include + +#include "math_private.h" + +static const double +two54 = 1.80143985094819840000e+16; /* 0x43500000, 0x00000000 */ + +OLM_DLLEXPORT double +frexp(double x, int *eptr) +{ + int32_t hx, ix, lx; + EXTRACT_WORDS(hx,lx,x); + ix = 0x7fffffff&hx; + *eptr = 0; + if(ix>=0x7ff00000||((ix|lx)==0)) return x; /* 0,inf,nan */ + if (ix<0x00100000) { /* subnormal */ + x *= two54; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + *eptr = -54; + } + *eptr += (ix>>20)-1022; + hx = (hx&0x800fffff)|0x3fe00000; + SET_HIGH_WORD(x,hx); + return x; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(frexp, frexpl); +#endif diff --git a/openlibm/src/s_frexpf.c b/openlibm/src/s_frexpf.c new file mode 100644 index 0000000000..fe63046a55 --- /dev/null +++ b/openlibm/src/s_frexpf.c @@ -0,0 +1,44 @@ +/* s_frexpf.c -- float version of s_frexp.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_frexpf.c,v 1.10 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +static const float +two25 = 3.3554432000e+07; /* 0x4c000000 */ + +OLM_DLLEXPORT float +frexpf(float x, int *eptr) +{ + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = 0x7fffffff&hx; + *eptr = 0; + if(ix>=0x7f800000||(ix==0)) return x; /* 0,inf,nan */ + if (ix<0x00800000) { /* subnormal */ + x *= two25; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + *eptr = -25; + } + *eptr += (ix>>23)-126; + hx = (hx&0x807fffff)|0x3f000000; + SET_FLOAT_WORD(x,hx); + return x; +} diff --git a/openlibm/src/s_frexpl.c b/openlibm/src/s_frexpl.c new file mode 100644 index 0000000000..ed2d28bc53 --- /dev/null +++ b/openlibm/src/s_frexpl.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2004-2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_frexpl.c,v 1.1 2005/03/07 04:54:51 das Exp $ + */ + +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#if LDBL_MAX_EXP != 0x4000 +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT long double +frexpl(long double x, int *ex) +{ + union IEEEl2bits u; + + u.e = x; + switch (u.bits.exp) { + case 0: /* 0 or subnormal */ + if ((u.bits.manl | u.bits.manh) == 0) { + *ex = 0; + } else { + u.e *= 0x1.0p514; + *ex = u.bits.exp - 0x4200; + u.bits.exp = 0x3ffe; + } + break; + case 0x7fff: /* infinity or NaN; value of *ex is unspecified */ + break; + default: /* normal */ + *ex = u.bits.exp - 0x3ffe; + u.bits.exp = 0x3ffe; + break; + } + return (u.e); +} diff --git a/openlibm/src/s_ilogb.c b/openlibm/src/s_ilogb.c new file mode 100644 index 0000000000..897e6d6ddb --- /dev/null +++ b/openlibm/src/s_ilogb.c @@ -0,0 +1,49 @@ +/* @(#)s_ilogb.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ilogb.c,v 1.10 2008/02/22 02:30:35 das Exp $"); + +/* ilogb(double x) + * return the binary exponent of non-zero x + * ilogb(0) = FP_ILOGB0 + * ilogb(NaN) = FP_ILOGBNAN (no signal is raised) + * ilogb(inf) = INT_MAX (no signal is raised) + */ + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT int +ilogb(double x) +{ + int32_t hx,lx,ix; + + EXTRACT_WORDS(hx,lx,x); + hx &= 0x7fffffff; + if(hx<0x00100000) { + if((hx|lx)==0) + return FP_ILOGB0; + else /* subnormal x */ + if(hx==0) { + for (ix = -1043; lx>0; lx<<=1) ix -=1; + } else { + for (ix = -1022,hx<<=11; hx>0; hx<<=1) ix -=1; + } + return ix; + } + else if (hx<0x7ff00000) return (hx>>20)-1023; + else if (hx>0x7ff00000 || lx!=0) return FP_ILOGBNAN; + else return INT_MAX; +} diff --git a/openlibm/src/s_ilogbf.c b/openlibm/src/s_ilogbf.c new file mode 100644 index 0000000000..79e359ddd3 --- /dev/null +++ b/openlibm/src/s_ilogbf.c @@ -0,0 +1,41 @@ +/* s_ilogbf.c -- float version of s_ilogb.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ilogbf.c,v 1.8 2008/02/22 02:30:35 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT int +ilogbf(float x) +{ + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + hx &= 0x7fffffff; + if(hx<0x00800000) { + if(hx==0) + return FP_ILOGB0; + else /* subnormal x */ + for (ix = -126,hx<<=8; hx>0; hx<<=1) ix -=1; + return ix; + } + else if (hx<0x7f800000) return (hx>>23)-127; + else if (hx>0x7f800000) return FP_ILOGBNAN; + else return INT_MAX; +} diff --git a/openlibm/src/s_ilogbl.c b/openlibm/src/s_ilogbl.c new file mode 100644 index 0000000000..d67eea8623 --- /dev/null +++ b/openlibm/src/s_ilogbl.c @@ -0,0 +1,54 @@ +/* + * From: @(#)s_ilogb.c 5.1 93/09/24 + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_ilogbl.c,v 1.2 2008/02/22 02:30:35 das Exp $"); + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT int +ilogbl(long double x) +{ + union IEEEl2bits u; + unsigned long m; + int b; + + u.e = x; + if (u.bits.exp == 0) { + if ((u.bits.manl | u.bits.manh) == 0) + return (FP_ILOGB0); + /* denormalized */ + if (u.bits.manh == 0) { + m = 1lu << (LDBL_MANL_SIZE - 1); + for (b = LDBL_MANH_SIZE; !(u.bits.manl & m); m >>= 1) + b++; + } else { + m = 1lu << (LDBL_MANH_SIZE - 1); + for (b = 0; !(u.bits.manh & m); m >>= 1) + b++; + } +#ifdef LDBL_IMPLICIT_NBIT + b++; +#endif + return (LDBL_MIN_EXP - b - 1); + } else if (u.bits.exp < (LDBL_MAX_EXP << 1) - 1) + return (u.bits.exp - LDBL_MAX_EXP + 1); + else if (u.bits.manl != 0 || u.bits.manh != 0) + return (FP_ILOGBNAN); + else + return (INT_MAX); +} diff --git a/openlibm/src/s_isfinite.c b/openlibm/src/s_isfinite.c new file mode 100644 index 0000000000..7c1187631a --- /dev/null +++ b/openlibm/src/s_isfinite.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_isfinite.c,v 1.1 2004/07/09 03:32:39 das Exp $ + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT int +__isfinite(double d) +{ + union IEEEd2bits u; + + u.d = d; + return (u.bits.exp != 2047); +} + +OLM_DLLEXPORT int +__isfinitef(float f) +{ + union IEEEf2bits u; + + u.f = f; + return (u.bits.exp != 255); +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT int +__isfinitel(long double e) +{ + union IEEEl2bits u; + + u.e = e; + return (u.bits.exp != 32767); +} +#endif diff --git a/openlibm/src/s_isinf.c b/openlibm/src/s_isinf.c new file mode 100644 index 0000000000..02eaff3621 --- /dev/null +++ b/openlibm/src/s_isinf.c @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +/* Provided by libc */ +#if 1 +OLM_DLLEXPORT int +(isinf) (double d) +{ + union IEEEd2bits u; + + u.d = d; + return (u.bits.exp == 2047 && u.bits.manl == 0 && u.bits.manh == 0); +} +#endif + +OLM_DLLEXPORT int +__isinff(float f) +{ + union IEEEf2bits u; + + u.f = f; + return (u.bits.exp == 255 && u.bits.man == 0); +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT int +__isinfl(long double e) +{ + union IEEEl2bits u; + + u.e = e; + mask_nbit_l(u); + return (u.bits.exp == 32767 && u.bits.manl == 0 && u.bits.manh == 0); +} +#endif + +openlibm_weak_reference(__isinff, isinff); diff --git a/openlibm/src/s_isnan.c b/openlibm/src/s_isnan.c new file mode 100644 index 0000000000..b9066aa5f9 --- /dev/null +++ b/openlibm/src/s_isnan.c @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_isnan.c,v 1.9 2010/06/12 17:32:05 das Exp $ + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +/* Provided by libc */ +#if 1 +OLM_DLLEXPORT int +(isnan) (double d) +{ + union IEEEd2bits u; + + u.d = d; + return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); +} +#endif + +OLM_DLLEXPORT int +__isnanf(float f) +{ + union IEEEf2bits u; + + u.f = f; + return (u.bits.exp == 255 && u.bits.man != 0); +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT int +__isnanl(long double e) +{ + union IEEEl2bits u; + + u.e = e; + mask_nbit_l(u); + return (u.bits.exp == 32767 && (u.bits.manl != 0 || u.bits.manh != 0)); +} +#endif + +openlibm_weak_reference(__isnanf, isnanf); diff --git a/openlibm/src/s_isnormal.c b/openlibm/src/s_isnormal.c new file mode 100644 index 0000000000..3c721d7266 --- /dev/null +++ b/openlibm/src/s_isnormal.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_isnormal.c,v 1.1 2004/07/09 03:32:39 das Exp $ + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT int +__isnormal(double d) +{ + union IEEEd2bits u; + + u.d = d; + return (u.bits.exp != 0 && u.bits.exp != 2047); +} + +OLM_DLLEXPORT int +__isnormalf(float f) +{ + union IEEEf2bits u; + + u.f = f; + return (u.bits.exp != 0 && u.bits.exp != 255); +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT int +__isnormall(long double e) +{ + union IEEEl2bits u; + + u.e = e; + return (u.bits.exp != 0 && u.bits.exp != 32767); +} +#endif diff --git a/openlibm/src/s_llrint.c b/openlibm/src/s_llrint.c new file mode 100644 index 0000000000..dcd6aed909 --- /dev/null +++ b/openlibm/src/s_llrint.c @@ -0,0 +1,9 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_llrint.c,v 1.1 2005/01/11 23:12:55 das Exp $"); + +#define type double +#define roundit rint +#define dtype long long +#define fn llrint + +#include "s_lrint.c" diff --git a/openlibm/src/s_llrintf.c b/openlibm/src/s_llrintf.c new file mode 100644 index 0000000000..81ae8f068f --- /dev/null +++ b/openlibm/src/s_llrintf.c @@ -0,0 +1,9 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_llrintf.c,v 1.1 2005/01/11 23:12:55 das Exp $"); + +#define type float +#define roundit rintf +#define dtype long long +#define fn llrintf + +#include "s_lrint.c" diff --git a/openlibm/src/s_llrintl.c b/openlibm/src/s_llrintl.c new file mode 100644 index 0000000000..3b23238c6e --- /dev/null +++ b/openlibm/src/s_llrintl.c @@ -0,0 +1,9 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_llrintl.c,v 1.1 2008/01/14 02:12:06 das Exp $"); + +#define type long double +#define roundit rintl +#define dtype long long +#define fn llrintl + +#include "s_lrint.c" diff --git a/openlibm/src/s_llround.c b/openlibm/src/s_llround.c new file mode 100644 index 0000000000..717ea96a1a --- /dev/null +++ b/openlibm/src/s_llround.c @@ -0,0 +1,11 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_llround.c,v 1.2 2005/04/08 00:52:27 das Exp $"); + +#define type double +#define roundit round +#define dtype long long +#define DTYPE_MIN LLONG_MIN +#define DTYPE_MAX LLONG_MAX +#define fn llround + +#include "s_lround.c" diff --git a/openlibm/src/s_llroundf.c b/openlibm/src/s_llroundf.c new file mode 100644 index 0000000000..9b8b8b836c --- /dev/null +++ b/openlibm/src/s_llroundf.c @@ -0,0 +1,11 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_llroundf.c,v 1.2 2005/04/08 00:52:27 das Exp $"); + +#define type float +#define roundit roundf +#define dtype long long +#define DTYPE_MIN LLONG_MIN +#define DTYPE_MAX LLONG_MAX +#define fn llroundf + +#include "s_lround.c" diff --git a/openlibm/src/s_llroundl.c b/openlibm/src/s_llroundl.c new file mode 100644 index 0000000000..7a00657180 --- /dev/null +++ b/openlibm/src/s_llroundl.c @@ -0,0 +1,11 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_llroundl.c,v 1.1 2005/04/08 01:24:08 das Exp $"); + +#define type long double +#define roundit roundl +#define dtype long long +#define DTYPE_MIN LLONG_MIN +#define DTYPE_MAX LLONG_MAX +#define fn llroundl + +#include "s_lround.c" diff --git a/openlibm/src/s_log1p.c b/openlibm/src/s_log1p.c new file mode 100644 index 0000000000..1921b6a3b8 --- /dev/null +++ b/openlibm/src/s_log1p.c @@ -0,0 +1,179 @@ +/* @(#)s_log1p.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_log1p.c,v 1.10 2008/03/29 16:37:59 das Exp $"); + +/* double log1p(double x) + * + * Method : + * 1. Argument Reduction: find k and f such that + * 1+x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * Note. If k=0, then f=x is exact. However, if k!=0, then f + * may not be representable exactly. In that case, a correction + * term is need. Let u=1+x rounded. Let c = (1+x)-u, then + * log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), + * and add back the correction term c/u. + * (Note: when x > 2**53, one can simply return log(x)) + * + * 2. Approximation of log1p(f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s + * (the values of Lp1 to Lp7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lp1*s +...+Lp7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log1p(f) = f - (hfsq - s*(hfsq+R)). + * + * 3. Finally, log1p(x) = k*ln2 + log1p(f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log1p(x) is NaN with signal if x < -1 (including -INF) ; + * log1p(+INF) is +INF; log1p(-1) is -INF with signal; + * log1p(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + * + * Note: Assuming log() return accurate answer, the following + * algorithm can be used to compute log1p(x) to within a few ULP: + * + * u = 1+x; + * if(u==1.0) return x ; else + * return log(u)*(x/(u-1.0)); + * + * See HP-15C Advanced Functions Handbook, p.193. + */ + +#include +#include + +#include "math_private.h" + +static const double +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ +ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ +two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ +Lp1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lp2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lp3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lp4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lp5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lp6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lp7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static const double zero = 0.0; + +OLM_DLLEXPORT double +log1p(double x) +{ + double hfsq,f,c,s,z,R,u; + int32_t k,hx,hu,ax; + + GET_HIGH_WORD(hx,x); + ax = hx&0x7fffffff; + + k = 1; + if (hx < 0x3FDA827A) { /* 1+x < sqrt(2)+ */ + if(ax>=0x3ff00000) { /* x <= -1.0 */ + if(x==-1.0) return -two54/zero; /* log1p(-1)=+inf */ + else return (x-x)/(x-x); /* log1p(x<-1)=NaN */ + } + if(ax<0x3e200000) { /* |x| < 2**-29 */ + if(two54+x>zero /* raise inexact */ + &&ax<0x3c900000) /* |x| < 2**-54 */ + return x; + else + return x - x*x*0.5; + } + if(hx>0||hx<=((int32_t)0xbfd2bec4)) { + k=0;f=x;hu=1;} /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ + } + if (hx >= 0x7ff00000) return x+x; + if(k!=0) { + if(hx<0x43400000) { + STRICT_ASSIGN(double,u,1.0+x); + GET_HIGH_WORD(hu,u); + k = (hu>>20)-1023; + c = (k>0)? 1.0-(u-x):x-(u-1.0);/* correction term */ + c /= u; + } else { + u = x; + GET_HIGH_WORD(hu,u); + k = (hu>>20)-1023; + c = 0; + } + hu &= 0x000fffff; + /* + * The approximation to sqrt(2) used in thresholds is not + * critical. However, the ones used above must give less + * strict bounds than the one here so that the k==0 case is + * never reached from here, since here we have committed to + * using the correction term but don't use it if k==0. + */ + if(hu<0x6a09e) { /* u ~< sqrt(2) */ + SET_HIGH_WORD(u,hu|0x3ff00000); /* normalize u */ + } else { + k += 1; + SET_HIGH_WORD(u,hu|0x3fe00000); /* normalize u/2 */ + hu = (0x00100000-hu)>>2; + } + f = u-1.0; + } + hfsq=0.5*f*f; + if(hu==0) { /* |f| < 2**-20 */ + if(f==zero) { + if(k==0) { + return zero; + } else { + c += k*ln2_lo; + return k*ln2_hi+c; + } + } + R = hfsq*(1.0-0.66666666666666666*f); + if(k==0) return f-R; else + return k*ln2_hi-((R-(k*ln2_lo+c))-f); + } + s = f/(2.0+f); + z = s*s; + R = z*(Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))); + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return k*ln2_hi-((hfsq-(s*(hfsq+R)+(k*ln2_lo+c)))-f); +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(log1p, log1pl); +#endif diff --git a/openlibm/src/s_log1pf.c b/openlibm/src/s_log1pf.c new file mode 100644 index 0000000000..53c8f498cc --- /dev/null +++ b/openlibm/src/s_log1pf.c @@ -0,0 +1,114 @@ +/* s_log1pf.c -- float version of s_log1p.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_log1pf.c,v 1.12 2008/03/29 16:37:59 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const float +ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ +ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ +two25 = 3.355443200e+07, /* 0x4c000000 */ +Lp1 = 6.6666668653e-01, /* 3F2AAAAB */ +Lp2 = 4.0000000596e-01, /* 3ECCCCCD */ +Lp3 = 2.8571429849e-01, /* 3E924925 */ +Lp4 = 2.2222198546e-01, /* 3E638E29 */ +Lp5 = 1.8183572590e-01, /* 3E3A3325 */ +Lp6 = 1.5313838422e-01, /* 3E1CD04F */ +Lp7 = 1.4798198640e-01; /* 3E178897 */ + +static const float zero = 0.0; + +OLM_DLLEXPORT float +log1pf(float x) +{ + float hfsq,f,c,s,z,R,u; + int32_t k,hx,hu,ax; + + GET_FLOAT_WORD(hx,x); + ax = hx&0x7fffffff; + + k = 1; + if (hx < 0x3ed413d0) { /* 1+x < sqrt(2)+ */ + if(ax>=0x3f800000) { /* x <= -1.0 */ + if(x==(float)-1.0) return -two25/zero; /* log1p(-1)=+inf */ + else return (x-x)/(x-x); /* log1p(x<-1)=NaN */ + } + if(ax<0x38000000) { /* |x| < 2**-15 */ + if(two25+x>zero /* raise inexact */ + &&ax<0x33800000) /* |x| < 2**-24 */ + return x; + else + return x - x*x*(float)0.5; + } + if(hx>0||hx<=((int32_t)0xbe95f619)) { + k=0;f=x;hu=1;} /* sqrt(2)/2- <= 1+x < sqrt(2)+ */ + } + if (hx >= 0x7f800000) return x+x; + if(k!=0) { + if(hx<0x5a000000) { + STRICT_ASSIGN(float,u,(float)1.0+x); + GET_FLOAT_WORD(hu,u); + k = (hu>>23)-127; + /* correction term */ + c = (k>0)? (float)1.0-(u-x):x-(u-(float)1.0); + c /= u; + } else { + u = x; + GET_FLOAT_WORD(hu,u); + k = (hu>>23)-127; + c = 0; + } + hu &= 0x007fffff; + /* + * The approximation to sqrt(2) used in thresholds is not + * critical. However, the ones used above must give less + * strict bounds than the one here so that the k==0 case is + * never reached from here, since here we have committed to + * using the correction term but don't use it if k==0. + */ + if(hu<0x3504f4) { /* u < sqrt(2) */ + SET_FLOAT_WORD(u,hu|0x3f800000);/* normalize u */ + } else { + k += 1; + SET_FLOAT_WORD(u,hu|0x3f000000); /* normalize u/2 */ + hu = (0x00800000-hu)>>2; + } + f = u-(float)1.0; + } + hfsq=(float)0.5*f*f; + if(hu==0) { /* |f| < 2**-20 */ + if(f==zero) { + if(k==0) { + return zero; + } else { + c += k*ln2_lo; + return k*ln2_hi+c; + } + } + R = hfsq*((float)1.0-(float)0.66666666666666666*f); + if(k==0) return f-R; else + return k*ln2_hi-((R-(k*ln2_lo+c))-f); + } + s = f/((float)2.0+f); + z = s*s; + R = z*(Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))); + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return k*ln2_hi-((hfsq-(s*(hfsq+R)+(k*ln2_lo+c)))-f); +} diff --git a/openlibm/src/s_logb.c b/openlibm/src/s_logb.c new file mode 100644 index 0000000000..116b69635b --- /dev/null +++ b/openlibm/src/s_logb.c @@ -0,0 +1,49 @@ +/* @(#)s_logb.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_logb.c,v 1.12 2008/02/08 01:22:13 bde Exp $"); + +/* + * double logb(x) + * IEEE 754 logb. Included to pass IEEE test suite. Not recommend. + * Use ilogb instead. + */ + +#include +#include + +#include "math_private.h" + +static const double +two54 = 1.80143985094819840000e+16; /* 43500000 00000000 */ + +OLM_DLLEXPORT double +logb(double x) +{ + int32_t lx,ix; + EXTRACT_WORDS(ix,lx,x); + ix &= 0x7fffffff; /* high |x| */ + if((ix|lx)==0) return -1.0/fabs(x); + if(ix>=0x7ff00000) return x*x; + if(ix<0x00100000) { + x *= two54; /* convert subnormal x to normal */ + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + return (double) ((ix>>20)-1023-54); + } else + return (double) ((ix>>20)-1023); +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(logb, logbl); +#endif diff --git a/openlibm/src/s_logbf.c b/openlibm/src/s_logbf.c new file mode 100644 index 0000000000..8b1d1177bc --- /dev/null +++ b/openlibm/src/s_logbf.c @@ -0,0 +1,41 @@ +/* s_logbf.c -- float version of s_logb.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_logbf.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +static const float +two25 = 3.355443200e+07; /* 0x4c000000 */ + +OLM_DLLEXPORT float +logbf(float x) +{ + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; /* high |x| */ + if(ix==0) return (float)-1.0/fabsf(x); + if(ix>=0x7f800000) return x*x; + if(ix<0x00800000) { + x *= two25; /* convert subnormal x to normal */ + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + return (float) ((ix>>23)-127-25); + } else + return (float) ((ix>>23)-127); +} diff --git a/openlibm/src/s_logbl.c b/openlibm/src/s_logbl.c new file mode 100644 index 0000000000..3c1336b7bc --- /dev/null +++ b/openlibm/src/s_logbl.c @@ -0,0 +1,52 @@ +/* + * From: @(#)s_ilogb.c 5.1 93/09/24 + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT long double +logbl(long double x) +{ + union IEEEl2bits u; + unsigned long m; + int b; + + u.e = x; + if (u.bits.exp == 0) { + if ((u.bits.manl | u.bits.manh) == 0) { /* x == 0 */ + u.bits.sign = 1; + return (1.0L / u.e); + } + /* denormalized */ + if (u.bits.manh == 0) { + m = 1lu << (LDBL_MANL_SIZE - 1); + for (b = LDBL_MANH_SIZE; !(u.bits.manl & m); m >>= 1) + b++; + } else { + m = 1lu << (LDBL_MANH_SIZE - 1); + for (b = 0; !(u.bits.manh & m); m >>= 1) + b++; + } +#ifdef LDBL_IMPLICIT_NBIT + b++; +#endif + return ((long double)(LDBL_MIN_EXP - b - 1)); + } + if (u.bits.exp < (LDBL_MAX_EXP << 1) - 1) /* normal */ + return ((long double)(u.bits.exp - LDBL_MAX_EXP + 1)); + else /* +/- inf or nan */ + return (x * x); +} diff --git a/openlibm/src/s_lrint.c b/openlibm/src/s_lrint.c new file mode 100644 index 0000000000..e1c554642e --- /dev/null +++ b/openlibm/src/s_lrint.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" + +#include +#include + +#include "math_private.h" + +#ifndef type +//__FBSDID("$FreeBSD: src/lib/msun/src/s_lrint.c,v 1.1 2005/01/11 23:12:55 das Exp $"); +#define type double +#define roundit rint +#define dtype long +#define fn lrint +#endif + +/* + * C99 says we should not raise a spurious inexact exception when an + * invalid exception is raised. Unfortunately, the set of inputs + * that overflows depends on the rounding mode when 'dtype' has more + * significant bits than 'type'. Hence, we bend over backwards for the + * sake of correctness; an MD implementation could be more efficient. + */ +OLM_DLLEXPORT dtype +fn(type x) +{ + fenv_t env; + dtype d; + + feholdexcept(&env); + d = (dtype)roundit(x); + if (fetestexcept(FE_INVALID)) + feclearexcept(FE_INEXACT); + feupdateenv(&env); + return (d); +} diff --git a/openlibm/src/s_lrintf.c b/openlibm/src/s_lrintf.c new file mode 100644 index 0000000000..f29ad5292b --- /dev/null +++ b/openlibm/src/s_lrintf.c @@ -0,0 +1,9 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_lrintf.c,v 1.1 2005/01/11 23:12:55 das Exp $"); + +#define type float +#define roundit rintf +#define dtype long +#define fn lrintf + +#include "s_lrint.c" diff --git a/openlibm/src/s_lrintl.c b/openlibm/src/s_lrintl.c new file mode 100644 index 0000000000..b57c27d55c --- /dev/null +++ b/openlibm/src/s_lrintl.c @@ -0,0 +1,9 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_lrintl.c,v 1.1 2008/01/14 02:12:06 das Exp $"); + +#define type long double +#define roundit rintl +#define dtype long +#define fn lrintl + +#include "s_lrint.c" diff --git a/openlibm/src/s_lround.c b/openlibm/src/s_lround.c new file mode 100644 index 0000000000..76fa73279a --- /dev/null +++ b/openlibm/src/s_lround.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2005 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" + +#include +#include +#include + +#include "math_private.h" + +#ifndef type +//__FBSDID("$FreeBSD: src/lib/msun/src/s_lround.c,v 1.2 2005/04/08 00:52:16 das Exp $"); +#define type double +#define roundit round +#define dtype long +#define DTYPE_MIN LONG_MIN +#define DTYPE_MAX LONG_MAX +#define fn lround +#endif + +/* + * If type has more precision than dtype, the endpoints dtype_(min|max) are + * of the form xxx.5; they are "out of range" because lround() rounds away + * from 0. On the other hand, if type has less precision than dtype, then + * all values that are out of range are integral, so we might as well assume + * that everything is in range. At compile time, INRANGE(x) should reduce to + * two floating-point comparisons in the former case, or TRUE otherwise. + */ +static const type dtype_min = DTYPE_MIN - 0.5; +static const type dtype_max = DTYPE_MAX + 0.5; +#define INRANGE(x) (dtype_max - DTYPE_MAX != 0.5 || \ + ((x) > dtype_min && (x) < dtype_max)) + +OLM_DLLEXPORT dtype +fn(type x) +{ + + if (INRANGE(x)) { + x = roundit(x); + return ((dtype)x); + } else { + feraiseexcept(FE_INVALID); + return (DTYPE_MAX); + } +} diff --git a/openlibm/src/s_lroundf.c b/openlibm/src/s_lroundf.c new file mode 100644 index 0000000000..f526eef04c --- /dev/null +++ b/openlibm/src/s_lroundf.c @@ -0,0 +1,11 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_lroundf.c,v 1.2 2005/04/08 00:52:27 das Exp $"); + +#define type float +#define roundit roundf +#define dtype long +#define DTYPE_MIN LONG_MIN +#define DTYPE_MAX LONG_MAX +#define fn lroundf + +#include "s_lround.c" diff --git a/openlibm/src/s_lroundl.c b/openlibm/src/s_lroundl.c new file mode 100644 index 0000000000..eb8997c8c7 --- /dev/null +++ b/openlibm/src/s_lroundl.c @@ -0,0 +1,11 @@ +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_lroundl.c,v 1.1 2005/04/08 01:24:08 das Exp $"); + +#define type long double +#define roundit roundl +#define dtype long +#define DTYPE_MIN LONG_MIN +#define DTYPE_MAX LONG_MAX +#define fn lroundl + +#include "s_lround.c" diff --git a/openlibm/src/s_modf.c b/openlibm/src/s_modf.c new file mode 100644 index 0000000000..e3b517b588 --- /dev/null +++ b/openlibm/src/s_modf.c @@ -0,0 +1,76 @@ +/* @(#)s_modf.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * modf(double x, double *iptr) + * return fraction part of x, and return x's integral part in *iptr. + * Method: + * Bit twiddling. + * + * Exception: + * No exception. + */ + +#include + +#include "math_private.h" + +static const double one = 1.0; + +OLM_DLLEXPORT double +modf(double x, double *iptr) +{ + int32_t i0,i1,j0; + u_int32_t i; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; /* exponent of x */ + if(j0<20) { /* integer part in high x */ + if(j0<0) { /* |x|<1 */ + INSERT_WORDS(*iptr,i0&0x80000000,0); /* *iptr = +-0 */ + return x; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) { /* x is integral */ + u_int32_t high; + *iptr = x; + GET_HIGH_WORD(high,x); + INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ + return x; + } else { + INSERT_WORDS(*iptr,i0&(~i),0); + return x - *iptr; + } + } + } else if (j0>51) { /* no fraction part */ + u_int32_t high; + if (j0 == 0x400) { /* inf/NaN */ + *iptr = x; + return 0.0 / x; + } + *iptr = x*one; + GET_HIGH_WORD(high,x); + INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ + return x; + } else { /* fraction part in low x */ + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) { /* x is integral */ + u_int32_t high; + *iptr = x; + GET_HIGH_WORD(high,x); + INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ + return x; + } else { + INSERT_WORDS(*iptr,i0,i1&(~i)); + return x - *iptr; + } + } +} diff --git a/openlibm/src/s_modff.c b/openlibm/src/s_modff.c new file mode 100644 index 0000000000..0a0c840111 --- /dev/null +++ b/openlibm/src/s_modff.c @@ -0,0 +1,58 @@ +/* s_modff.c -- float version of s_modf.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_modff.c,v 1.9 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +static const float one = 1.0; + +OLM_DLLEXPORT float +modff(float x, float *iptr) +{ + int32_t i0,j0; + u_int32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; /* exponent of x */ + if(j0<23) { /* integer part in x */ + if(j0<0) { /* |x|<1 */ + SET_FLOAT_WORD(*iptr,i0&0x80000000); /* *iptr = +-0 */ + return x; + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) { /* x is integral */ + u_int32_t ix; + *iptr = x; + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(x,ix&0x80000000); /* return +-0 */ + return x; + } else { + SET_FLOAT_WORD(*iptr,i0&(~i)); + return x - *iptr; + } + } + } else { /* no fraction part */ + u_int32_t ix; + *iptr = x*one; + if (x != x) /* NaN */ + return x; + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(x,ix&0x80000000); /* return +-0 */ + return x; + } +} diff --git a/openlibm/src/s_modfl.c b/openlibm/src/s_modfl.c new file mode 100644 index 0000000000..44a9363490 --- /dev/null +++ b/openlibm/src/s_modfl.c @@ -0,0 +1,101 @@ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Derived from s_modf.c, which has the following Copyright: + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * $FreeBSD: src/lib/msun/src/s_modfl.c,v 1.1 2007/01/07 07:54:21 das Exp $ + */ + +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#if LDBL_MANL_SIZE > 32 +#define MASK ((u_int64_t)-1) +#else +#define MASK ((u_int32_t)-1) +#endif +/* Return the last n bits of a word, representing the fractional part. */ +#define GETFRAC(bits, n) ((bits) & ~(MASK << (n))) +/* The number of fraction bits in manh, not counting the integer bit */ +#define HIBITS (LDBL_MANT_DIG - LDBL_MANL_SIZE) + +static const long double zero[] = { 0.0L, -0.0L }; + +OLM_DLLEXPORT long double +modfl(long double x, long double *iptr) +{ + union IEEEl2bits ux; + int e; + + ux.e = x; + e = ux.bits.exp - LDBL_MAX_EXP + 1; + if (e < HIBITS) { /* Integer part is in manh. */ + if (e < 0) { /* |x|<1 */ + *iptr = zero[ux.bits.sign]; + return (x); + } else { + if ((GETFRAC(ux.bits.manh, HIBITS - 1 - e) | + ux.bits.manl) == 0) { /* X is an integer. */ + *iptr = x; + return (zero[ux.bits.sign]); + } else { + /* Clear all but the top e+1 bits. */ + ux.bits.manh >>= HIBITS - 1 - e; + ux.bits.manh <<= HIBITS - 1 - e; + ux.bits.manl = 0; + *iptr = ux.e; + return (x - ux.e); + } + } + } else if (e >= LDBL_MANT_DIG - 1) { /* x has no fraction part. */ + *iptr = x; + if (x != x) /* Handle NaNs. */ + return (x); + return (zero[ux.bits.sign]); + } else { /* Fraction part is in manl. */ + if (GETFRAC(ux.bits.manl, LDBL_MANT_DIG - 1 - e) == 0) { + /* x is integral. */ + *iptr = x; + return (zero[ux.bits.sign]); + } else { + /* Clear all but the top e+1 bits. */ + ux.bits.manl >>= LDBL_MANT_DIG - 1 - e; + ux.bits.manl <<= LDBL_MANT_DIG - 1 - e; + *iptr = ux.e; + return (x - ux.e); + } + } +} diff --git a/openlibm/src/s_nan.c b/openlibm/src/s_nan.c new file mode 100644 index 0000000000..dd1c5afd5e --- /dev/null +++ b/openlibm/src/s_nan.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2007 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_nan.c,v 1.2 2007/12/18 23:46:32 das Exp $ + */ + +//VBS +//#include +#include +#include +#include +#include +#include //for memset + +#include "math_private.h" + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__DragonFly__) +static __inline int digittoint(int c) { + if ('0' <= c && c <= '9') + return (c - '0'); + else if ('A' <= c && c <= 'F') + return (c - 'A' + 10); + else if ('a' <= c && c <= 'f') + return (c - 'a' + 10); + return 0; +} +#endif + + +/* + * Scan a string of hexadecimal digits (the format nan(3) expects) and + * make a bit array (using the local endianness). We stop when we + * encounter an invalid character, NUL, etc. If we overflow, we do + * the same as gcc's __builtin_nan(), namely, discard the high order bits. + * + * The format this routine accepts needs to be compatible with what is used + * in contrib/gdtoa/hexnan.c (for strtod/scanf) and what is used in + * __builtin_nan(). In fact, we're only 100% compatible for strings we + * consider valid, so we might be violating the C standard. But it's + * impossible to use nan(3) portably anyway, so this seems good enough. + */ +OLM_DLLEXPORT void +__scan_nan(u_int32_t *words, int num_words, const char *s) +{ + int si; /* index into s */ + int bitpos; /* index into words (in bits) */ + + memset(words, 0, num_words * sizeof(u_int32_t)); + + /* Allow a leading '0x'. (It's expected, but redundant.) */ + if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + s += 2; + + /* Scan forwards in the string, looking for the end of the sequence. */ + for (si = 0; isxdigit(s[si]); si++) + ; + + /* Scan backwards, filling in the bits in words[] as we go. */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + for (bitpos = 0; bitpos < 32 * num_words; bitpos += 4) { +#else + for (bitpos = 32 * num_words - 4; bitpos >= 0; bitpos -= 4) { +#endif + if (--si < 0) + break; + words[bitpos / 32] |= digittoint(s[si]) << (bitpos % 32); + } +} + +OLM_DLLEXPORT double +nan(const char *s) +{ + union { + double d; + u_int32_t bits[2]; + } u; + + __scan_nan(u.bits, 2, s); +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + u.bits[1] |= 0x7ff80000; +#else + u.bits[0] |= 0x7ff80000; +#endif + return (u.d); +} + +OLM_DLLEXPORT float +nanf(const char *s) +{ + union { + float f; + u_int32_t bits[1]; + } u; + + __scan_nan(u.bits, 1, s); + u.bits[0] |= 0x7fc00000; + return (u.f); +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(nan, nanl); +#endif diff --git a/openlibm/src/s_nearbyint.c b/openlibm/src/s_nearbyint.c new file mode 100644 index 0000000000..95162a54cc --- /dev/null +++ b/openlibm/src/s_nearbyint.c @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_nearbyint.c,v 1.2 2008/01/14 02:12:06 das Exp $"); + +#include +#include + +#include "math_private.h" + +/* + * We save and restore the floating-point environment to avoid raising + * an inexact exception. We can get away with using fesetenv() + * instead of feclearexcept()/feupdateenv() to restore the environment + * because the only exception defined for rint() is overflow, and + * rounding can't overflow as long as emax >= p. + */ +#define DECL(type, fn, rint) \ +OLM_DLLEXPORT type \ +fn(type x) \ +{ \ + type ret; \ + fenv_t env; \ + \ + fegetenv(&env); \ + ret = rint(x); \ + fesetenv(&env); \ + return (ret); \ +} + +DECL(double, nearbyint, rint) +DECL(float, nearbyintf, rintf) diff --git a/openlibm/src/s_nextafter.c b/openlibm/src/s_nextafter.c new file mode 100644 index 0000000000..8b2ea05fb1 --- /dev/null +++ b/openlibm/src/s_nextafter.c @@ -0,0 +1,83 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_nextafter.c,v 1.12 2008/02/22 02:30:35 das Exp $"); + +/* IEEE functions + * nextafter(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +nextafter(double x, double y) +{ + volatile double t; + int32_t hx,hy,ix,iy; + u_int32_t lx,ly; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffff; /* |y| */ + + if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) || /* x is nan */ + ((iy>=0x7ff00000)&&((iy-0x7ff00000)|ly)!=0)) /* y is nan */ + return x+y; + if(x==y) return y; /* x=y, return y */ + if((ix|lx)==0) { /* x == 0 */ + INSERT_WORDS(x,hy&0x80000000,1); /* return +-minsubnormal */ + t = x*x; + if(t==x) return t; else return x; /* raise underflow flag */ + } + if(hx>=0) { /* x > 0 */ + if(hx>hy||((hx==hy)&&(lx>ly))) { /* x > y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x < y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } else { /* x < 0 */ + if(hy>=0||hx>hy||((hx==hy)&&(lx>ly))){/* x < y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x > y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } + hy = hx&0x7ff00000; + if(hy>=0x7ff00000) return x+x; /* overflow */ + if(hy<0x00100000) { /* underflow */ + t = x*x; + if(t!=x) { /* raise underflow flag */ + INSERT_WORDS(y,hx,lx); + return y; + } + } + INSERT_WORDS(x,hx,lx); + return x; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(nextafter, nexttoward); +openlibm_weak_reference(nextafter, nexttowardl); +openlibm_weak_reference(nextafter, nextafterl); +#endif diff --git a/openlibm/src/s_nextafterf.c b/openlibm/src/s_nextafterf.c new file mode 100644 index 0000000000..d4cd630f0f --- /dev/null +++ b/openlibm/src/s_nextafterf.c @@ -0,0 +1,67 @@ +/* s_nextafterf.c -- float version of s_nextafter.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_nextafterf.c,v 1.11 2008/02/22 02:30:35 das Exp $"); + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +nextafterf(float x, float y) +{ + volatile float t; + int32_t hx,hy,ix,iy; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffff; /* |y| */ + + if((ix>0x7f800000) || /* x is nan */ + (iy>0x7f800000)) /* y is nan */ + return x+y; + if(x==y) return y; /* x=y, return y */ + if(ix==0) { /* x == 0 */ + SET_FLOAT_WORD(x,(hy&0x80000000)|1);/* return +-minsubnormal */ + t = x*x; + if(t==x) return t; else return x; /* raise underflow flag */ + } + if(hx>=0) { /* x > 0 */ + if(hx>hy) { /* x > y, x -= ulp */ + hx -= 1; + } else { /* x < y, x += ulp */ + hx += 1; + } + } else { /* x < 0 */ + if(hy>=0||hx>hy){ /* x < y, x -= ulp */ + hx -= 1; + } else { /* x > y, x += ulp */ + hx += 1; + } + } + hy = hx&0x7f800000; + if(hy>=0x7f800000) return x+x; /* overflow */ + if(hy<0x00800000) { /* underflow */ + t = x*x; + if(t!=x) { /* raise underflow flag */ + SET_FLOAT_WORD(y,hx); + return y; + } + } + SET_FLOAT_WORD(x,hx); + return x; +} diff --git a/openlibm/src/s_nextafterl.c b/openlibm/src/s_nextafterl.c new file mode 100644 index 0000000000..bb73141e6e --- /dev/null +++ b/openlibm/src/s_nextafterl.c @@ -0,0 +1,80 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_nextafterl.c,v 1.2 2008/02/22 02:30:36 das Exp $"); + +/* IEEE functions + * nextafter(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#if LDBL_MAX_EXP != 0x4000 +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT long double +nextafterl(long double x, long double y) +{ + volatile long double t; + union IEEEl2bits ux, uy; + + ux.e = x; + uy.e = y; + + if ((ux.bits.exp == 0x7fff && + ((ux.bits.manh&~LDBL_NBIT)|ux.bits.manl) != 0) || + (uy.bits.exp == 0x7fff && + ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl) != 0)) + return x+y; /* x or y is nan */ + if(x==y) return y; /* x=y, return y */ + if(x==0.0) { + ux.bits.manh = 0; /* return +-minsubnormal */ + ux.bits.manl = 1; + ux.bits.sign = uy.bits.sign; + t = ux.e*ux.e; + if(t==ux.e) return t; else return ux.e; /* raise underflow flag */ + } + if((x>0.0) ^ (x +#include + +#include "fpmath.h" +#include "math_private.h" + +#if LDBL_MAX_EXP != 0x4000 +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT double +nexttoward(double x, long double y) +{ + union IEEEl2bits uy; + volatile double t; + int32_t hx,ix; + u_int32_t lx; + + EXTRACT_WORDS(hx,lx,x); + ix = hx&0x7fffffff; /* |x| */ + uy.e = y; + + if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) || + (uy.bits.exp == 0x7fff && + ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl) != 0)) + return x+y; /* x or y is nan */ + if(x==y) return (double)y; /* x=y, return y */ + if(x==0.0) { + INSERT_WORDS(x,uy.bits.sign<<31,1); /* return +-minsubnormal */ + t = x*x; + if(t==x) return t; else return x; /* raise underflow flag */ + } + if((hx>0.0) ^ (x < y)) { /* x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + ix = hx&0x7ff00000; + if(ix>=0x7ff00000) return x+x; /* overflow */ + if(ix<0x00100000) { /* underflow */ + t = x*x; + if(t!=x) { /* raise underflow flag */ + INSERT_WORDS(x,hx,lx); + return x; + } + } + INSERT_WORDS(x,hx,lx); + return x; +} diff --git a/openlibm/src/s_nexttowardf.c b/openlibm/src/s_nexttowardf.c new file mode 100644 index 0000000000..bf50862914 --- /dev/null +++ b/openlibm/src/s_nexttowardf.c @@ -0,0 +1,61 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_nexttowardf.c,v 1.3 2011/02/10 07:38:38 das Exp $"); + +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#define LDBL_INFNAN_EXP (LDBL_MAX_EXP * 2 - 1) + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT float +nexttowardf(float x, long double y) +{ + union IEEEl2bits uy; + volatile float t; + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; /* |x| */ + uy.e = y; + + if((ix>0x7f800000) || + (uy.bits.exp == LDBL_INFNAN_EXP && + ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl) != 0)) + return x+y; /* x or y is nan */ + if(x==y) return (float)y; /* x=y, return y */ + if(ix==0) { /* x == 0 */ + SET_FLOAT_WORD(x,(uy.bits.sign<<31)|1);/* return +-minsubnormal */ + t = x*x; + if(t==x) return t; else return x; /* raise underflow flag */ + } + if((hx>=0) ^ (x < y)) /* x -= ulp */ + hx -= 1; + else /* x += ulp */ + hx += 1; + ix = hx&0x7f800000; + if(ix>=0x7f800000) return x+x; /* overflow */ + if(ix<0x00800000) { /* underflow */ + t = x*x; + if(t!=x) { /* raise underflow flag */ + SET_FLOAT_WORD(x,hx); + return x; + } + } + SET_FLOAT_WORD(x,hx); + return x; +} +#endif diff --git a/openlibm/src/s_remquo.c b/openlibm/src/s_remquo.c new file mode 100644 index 0000000000..afc325d9dc --- /dev/null +++ b/openlibm/src/s_remquo.c @@ -0,0 +1,159 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_remquo.c,v 1.2 2008/03/30 20:47:26 das Exp $"); + +#include +#include + +#include "math_private.h" + +static const double Zero[] = {0.0, -0.0,}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + */ +OLM_DLLEXPORT double +remquo(double x, double y, int *quo) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + u_int32_t lx,ly,lz,q,sxy; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + sxy = (hx ^ hy) & 0x80000000; + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if((hy|ly)==0||(hx>=0x7ff00000)|| /* y=0,or x not finite */ + ((hy|((ly|-ly)>>31))>0x7ff00000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<=hy) { + if((hx>31]; /* |x|=|y| return x*0*/ + } + } + + /* determine ix = ilogb(x) */ + if(hx<0x00100000) { /* subnormal x */ + if(hx==0) { + for (ix = -1043, i=lx; i>0; i<<=1) ix -=1; + } else { + for (ix = -1022,i=(hx<<11); i>0; i<<=1) ix -=1; + } + } else ix = (hx>>20)-1023; + + /* determine iy = ilogb(y) */ + if(hy<0x00100000) { /* subnormal y */ + if(hy==0) { + for (iy = -1043, i=ly; i>0; i<<=1) iy -=1; + } else { + for (iy = -1022,i=(hy<<11); i>0; i<<=1) iy -=1; + } + } else iy = (hy>>20)-1023; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -1022) + hx = 0x00100000|(0x000fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -1022-ix; + if(n<=31) { + hx = (hx<>(32-n)); + lx <<= n; + } else { + hx = lx<<(n-32); + lx = 0; + } + } + if(iy >= -1022) + hy = 0x00100000|(0x000fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -1022-iy; + if(n<=31) { + hy = (hy<>(32-n)); + ly <<= n; + } else { + hy = ly<<(n-32); + ly = 0; + } + } + + /* fix point fmod */ + n = ix - iy; + q = 0; + while(n--) { + hz=hx-hy;lz=lx-ly; if(lx>31); lx = lx+lx;} + else {hx = hz+hz+(lz>>31); lx = lz+lz; q++;} + q <<= 1; + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;q++;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) { /* return sign(x)*0 */ + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return Zero[(u_int32_t)sx>>31]; + } + while(hx<0x00100000) { /* normalize x */ + hx = hx+hx+(lx>>31); lx = lx+lx; + iy -= 1; + } + if(iy>= -1022) { /* normalize output */ + hx = ((hx-0x00100000)|((iy+1023)<<20)); + } else { /* subnormal output */ + n = -1022 - iy; + if(n<=20) { + lx = (lx>>n)|((u_int32_t)hx<<(32-n)); + hx >>= n; + } else if (n<=31) { + lx = (hx<<(32-n))|(lx>>n); hx = 0; + } else { + lx = hx>>(n-32); hx = 0; + } + } +fixup: + INSERT_WORDS(x,hx,lx); + y = fabs(y); + if (y < 0x1p-1021) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5*y || (x==0.5*y && (q & 1))) { + q++; + x-=y; + } + GET_HIGH_WORD(hx,x); + SET_HIGH_WORD(x,hx^sx); + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(remquo, remquol); +#endif diff --git a/openlibm/src/s_remquof.c b/openlibm/src/s_remquof.c new file mode 100644 index 0000000000..1983ed5686 --- /dev/null +++ b/openlibm/src/s_remquof.c @@ -0,0 +1,123 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_remquof.c,v 1.1 2005/03/25 04:40:44 das Exp $"); + +#include + +#include "math_private.h" + +static const float Zero[] = {0.0, -0.0,}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + */ +OLM_DLLEXPORT float +remquof(float x, float y, int *quo) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + u_int32_t q,sxy; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + sxy = (hx ^ hy) & 0x80000000; + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if(hy==0||hx>=0x7f800000||hy>0x7f800000) /* y=0,NaN;or x not finite */ + return (x*y)/(x*y); + if(hx>31]; /* |x|=|y| return x*0*/ + } + + /* determine ix = ilogb(x) */ + if(hx<0x00800000) { /* subnormal x */ + for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; + } else ix = (hx>>23)-127; + + /* determine iy = ilogb(y) */ + if(hy<0x00800000) { /* subnormal y */ + for (iy = -126,i=(hy<<8); i>0; i<<=1) iy -=1; + } else iy = (hy>>23)-127; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -126) + hx = 0x00800000|(0x007fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -126-ix; + hx <<= n; + } + if(iy >= -126) + hy = 0x00800000|(0x007fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -126-iy; + hy <<= n; + } + + /* fix point fmod */ + n = ix - iy; + q = 0; + while(n--) { + hz=hx-hy; + if(hz<0) hx = hx << 1; + else {hx = hz << 1; q++;} + q <<= 1; + } + hz=hx-hy; + if(hz>=0) {hx=hz;q++;} + + /* convert back to floating value and restore the sign */ + if(hx==0) { /* return sign(x)*0 */ + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return Zero[(u_int32_t)sx>>31]; + } + while(hx<0x00800000) { /* normalize x */ + hx <<= 1; + iy -= 1; + } + if(iy>= -126) { /* normalize output */ + hx = ((hx-0x00800000)|((iy+127)<<23)); + } else { /* subnormal output */ + n = -126 - iy; + hx >>= n; + } +fixup: + SET_FLOAT_WORD(x,hx); + y = fabsf(y); + if (y < 0x1p-125f) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5f*y || (x==0.5f*y && (q & 1))) { + q++; + x-=y; + } + GET_FLOAT_WORD(hx,x); + SET_FLOAT_WORD(x,hx^sx); + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} diff --git a/openlibm/src/s_remquol.c b/openlibm/src/s_remquol.c new file mode 100644 index 0000000000..b610b671bc --- /dev/null +++ b/openlibm/src/s_remquol.c @@ -0,0 +1,178 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_remquol.c,v 1.2 2008/07/31 20:09:47 das Exp $"); + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#define BIAS (LDBL_MAX_EXP - 1) + +#if LDBL_MANL_SIZE > 32 +typedef u_int64_t manl_t; +#else +typedef u_int32_t manl_t; +#endif + +#if LDBL_MANH_SIZE > 32 +typedef u_int64_t manh_t; +#else +typedef u_int32_t manh_t; +#endif + +/* + * These macros add and remove an explicit integer bit in front of the + * fractional mantissa, if the architecture doesn't have such a bit by + * default already. + */ +#ifdef LDBL_IMPLICIT_NBIT +#define SET_NBIT(hx) ((hx) | (1ULL << LDBL_MANH_SIZE)) +#define HFRAC_BITS LDBL_MANH_SIZE +#else +#define SET_NBIT(hx) (hx) +#define HFRAC_BITS (LDBL_MANH_SIZE - 1) +#endif + +#define MANL_SHIFT (LDBL_MANL_SIZE - 1) + +static const long double Zero[] = {0.0L, -0.0L}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + * + * Assumptions: + * - The low part of the mantissa fits in a manl_t exactly. + * - The high part of the mantissa fits in an int64_t with enough room + * for an explicit integer bit in front of the fractional bits. + */ +OLM_DLLEXPORT long double +remquol(long double x, long double y, int *quo) +{ + union IEEEl2bits ux, uy; + int64_t hx,hz; /* We need a carry bit even if LDBL_MANH_SIZE is 32. */ + manh_t hy; + manl_t lx,ly,lz; + int ix,iy,n,q,sx,sxy; + + ux.e = x; + uy.e = y; + sx = ux.bits.sign; + sxy = sx ^ uy.bits.sign; + ux.bits.sign = 0; /* |x| */ + uy.bits.sign = 0; /* |y| */ + x = ux.e; + + /* purge off exception values */ + if((uy.bits.exp|uy.bits.manh|uy.bits.manl)==0 || /* y=0 */ + (ux.bits.exp == BIAS + LDBL_MAX_EXP) || /* or x not finite */ + (uy.bits.exp == BIAS + LDBL_MAX_EXP && + ((uy.bits.manh&~LDBL_NBIT)|uy.bits.manl)!=0)) /* or y is NaN */ + return (x*y)/(x*y); + if(ux.bits.exp<=uy.bits.exp) { + if((ux.bits.exp>MANL_SHIFT); lx = lx+lx;} + else {hx = hz+hz+(lz>>MANL_SHIFT); lx = lz+lz; q++;} + q <<= 1; + } + hz=hx-hy;lz=lx-ly; if(lx=0) {hx=hz;lx=lz;q++;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) { /* return sign(x)*0 */ + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return Zero[sx]; + } + while(hx<(1ULL<>MANL_SHIFT); lx = lx+lx; + iy -= 1; + } + ux.bits.manh = hx; /* The integer bit is truncated here if needed. */ + ux.bits.manl = lx; + if (iy < LDBL_MIN_EXP) { + ux.bits.exp = iy + (BIAS + 512); + ux.e *= 0x1p-512; + } else { + ux.bits.exp = iy + BIAS; + } + ux.bits.sign = 0; + x = ux.e; +fixup: + y = fabsl(y); + if (y < LDBL_MIN * 2) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5*y || (x==0.5*y && (q & 1))) { + q++; + x-=y; + } + + ux.e = x; + ux.bits.sign ^= sx; + x = ux.e; + + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} diff --git a/openlibm/src/s_rint.c b/openlibm/src/s_rint.c new file mode 100644 index 0000000000..f81e6925c3 --- /dev/null +++ b/openlibm/src/s_rint.c @@ -0,0 +1,92 @@ +/* @(#)s_rint.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_rint.c,v 1.16 2008/02/22 02:30:35 das Exp $"); + +/* + * rint(x) + * Return x rounded to integral value according to the prevailing + * rounding mode. + * Method: + * Using floating addition. + * Exception: + * Inexact flag raised if x not equal to rint(x). + */ + +#include +#include + +#include "math_private.h" + +static const double +TWO52[2]={ + 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ + -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ +}; + +OLM_DLLEXPORT double +rint(double x) +{ + int32_t i0,j0,sx; + u_int32_t i,i1; + double w,t; + EXTRACT_WORDS(i0,i1,x); + sx = (i0>>31)&1; + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { + if(((i0&0x7fffffff)|i1)==0) return x; + i1 |= (i0&0x0fffff); + i0 &= 0xfffe0000; + i0 |= ((i1|-i1)>>12)&0x80000; + SET_HIGH_WORD(x,i0); + STRICT_ASSIGN(double,w,TWO52[sx]+x); + t = w-TWO52[sx]; + GET_HIGH_WORD(i0,t); + SET_HIGH_WORD(t,(i0&0x7fffffff)|(sx<<31)); + return t; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + i>>=1; + if(((i0&i)|i1)!=0) { + /* + * Some bit is set after the 0.5 bit. To avoid the + * possibility of errors from double rounding in + * w = TWO52[sx]+x, adjust the 0.25 bit to a lower + * guard bit. We do this for all j0<=51. The + * adjustment is trickiest for j0==18 and j0==19 + * since then it spans the word boundary. + */ + if(j0==19) i1 = 0x40000000; else + if(j0==18) i1 = 0x80000000; else + i0 = (i0&(~i))|((0x20000)>>j0); + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + i>>=1; + if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); + } + INSERT_WORDS(x,i0,i1); + STRICT_ASSIGN(double,w,TWO52[sx]+x); + return w-TWO52[sx]; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(rint, rintl); +#endif diff --git a/openlibm/src/s_rintf.c b/openlibm/src/s_rintf.c new file mode 100644 index 0000000000..60cdb46f6c --- /dev/null +++ b/openlibm/src/s_rintf.c @@ -0,0 +1,53 @@ +/* s_rintf.c -- float version of s_rint.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_rintf.c,v 1.12 2008/02/22 02:30:35 das Exp $"); + +#include +#include +#include + +#include "math_private.h" + +static const float +TWO23[2]={ + 8.3886080000e+06, /* 0x4b000000 */ + -8.3886080000e+06, /* 0xcb000000 */ +}; + +OLM_DLLEXPORT float +rintf(float x) +{ + int32_t i0,j0,sx; + float w,t; + GET_FLOAT_WORD(i0,x); + sx = (i0>>31)&1; + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { + if((i0&0x7fffffff)==0) return x; + STRICT_ASSIGN(float,w,TWO23[sx]+x); + t = w-TWO23[sx]; + GET_FLOAT_WORD(i0,t); + SET_FLOAT_WORD(t,(i0&0x7fffffff)|(sx<<31)); + return t; + } + STRICT_ASSIGN(float,w,TWO23[sx]+x); + return w-TWO23[sx]; + } + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ +} diff --git a/openlibm/src/s_rintl.c b/openlibm/src/s_rintl.c new file mode 100644 index 0000000000..d20768ee52 --- /dev/null +++ b/openlibm/src/s_rintl.c @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_rintl.c,v 1.5 2008/02/22 11:59:05 bde Exp $"); + +#include +#include +#include + +#include "fpmath.h" + +//VBS +#include "math_private.h" + + +#if LDBL_MAX_EXP != 0x4000 +/* We also require the usual bias, min exp and expsign packing. */ +#error "Unsupported long double format" +#endif + +#define BIAS (LDBL_MAX_EXP - 1) + +static const float +shift[2] = { +#if LDBL_MANT_DIG == 64 + 0x1.0p63, -0x1.0p63 +#elif LDBL_MANT_DIG == 113 + 0x1.0p112, -0x1.0p112 +#else +#error "Unsupported long double format" +#endif +}; +static const float zero[2] = { 0.0, -0.0 }; + +OLM_DLLEXPORT long double +rintl(long double x) +{ + union IEEEl2bits u; + u_int32_t expsign; + int ex, sign; + + u.e = x; + expsign = u.xbits.expsign; + ex = expsign & 0x7fff; + + if (ex >= BIAS + LDBL_MANT_DIG - 1) { + if (ex == BIAS + LDBL_MAX_EXP) + return (x + x); /* Inf, NaN, or unsupported format */ + return (x); /* finite and already an integer */ + } + sign = expsign >> 15; + + /* + * The following code assumes that intermediate results are + * evaluated in long double precision. If they are evaluated in + * greater precision, double rounding may occur, and if they are + * evaluated in less precision (as on i386), results will be + * wildly incorrect. + */ + x += shift[sign]; + x -= shift[sign]; + + /* + * If the result is +-0, then it must have the same sign as x, but + * the above calculation doesn't always give this. Fix up the sign. + */ + if (ex < BIAS && x == 0.0L) + return (zero[sign]); + + return (x); +} + +/* + * We save and restore the floating-point environment to avoid raising + * an inexact exception. We can get away with using fesetenv() + * instead of feclearexcept()/feupdateenv() to restore the environment + * because the only exception defined for rint() is overflow, and + * rounding can't overflow as long as emax >= p. + */ +#define DECL(type, fn, rint) \ +OLM_DLLEXPORT type \ +fn(type x) \ +{ \ + type ret; \ + fenv_t env; \ + \ + fegetenv(&env); \ + ret = rint(x); \ + fesetenv(&env); \ + return (ret); \ +} +DECL(long double, nearbyintl, rintl) diff --git a/openlibm/src/s_round.c b/openlibm/src/s_round.c new file mode 100644 index 0000000000..a982ec3bb2 --- /dev/null +++ b/openlibm/src/s_round.c @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2003, Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_round.c,v 1.4 2005/12/02 13:45:06 bde Exp $"); + +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +round(double x) +{ + double t; + uint32_t hx; + + GET_HIGH_WORD(hx, x); + if ((hx & 0x7fffffff) == 0x7ff00000) + return (x + x); + + if (!(hx & 0x80000000)) { + t = floor(x); + if (t - x <= -0.5) + t += 1; + return (t); + } else { + t = floor(-x); + if (t + x <= -0.5) + t += 1; + return (-t); + } +} diff --git a/openlibm/src/s_roundf.c b/openlibm/src/s_roundf.c new file mode 100644 index 0000000000..784a8a5141 --- /dev/null +++ b/openlibm/src/s_roundf.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2003, Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_roundf.c,v 1.4 2005/12/02 13:45:06 bde Exp $"); + +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +roundf(float x) +{ + float t; + + if (!isfinite(x)) + return (x); + + if (x >= 0.0) { + t = floorf(x); + if (t - x <= -0.5) + t += 1.0; + return (t); + } else { + t = floorf(-x); + if (t + x <= -0.5) + t += 1.0; + return (-t); + } +} diff --git a/openlibm/src/s_roundl.c b/openlibm/src/s_roundl.c new file mode 100644 index 0000000000..dbcd6517d4 --- /dev/null +++ b/openlibm/src/s_roundl.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2003, Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_roundl.c,v 1.2 2005/12/02 13:45:06 bde Exp $"); + +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +roundl(long double x) +{ + long double t; + + if (!isfinite(x)) + return (x); + + if (x >= 0.0) { + t = floorl(x); + if (t - x <= -0.5) + t += 1.0; + return (t); + } else { + t = floorl(-x); + if (t + x <= -0.5) + t += 1.0; + return (-t); + } +} diff --git a/openlibm/src/s_scalbln.c b/openlibm/src/s_scalbln.c new file mode 100644 index 0000000000..8239db5624 --- /dev/null +++ b/openlibm/src/s_scalbln.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_scalbln.c,v 1.2 2005/03/07 04:57:50 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +scalbln (double x, long n) +{ + int in; + + in = (int)n; + if (in != n) { + if (n > 0) + in = INT_MAX; + else + in = INT_MIN; + } + return (scalbn(x, in)); +} + +OLM_DLLEXPORT float +scalblnf (float x, long n) +{ + int in; + + in = (int)n; + if (in != n) { + if (n > 0) + in = INT_MAX; + else + in = INT_MIN; + } + return (scalbnf(x, in)); +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT long double +scalblnl (long double x, long n) +{ + int in; + + in = (int)n; + if (in != n) { + if (n > 0) + in = INT_MAX; + else + in = INT_MIN; + } + return (scalbnl(x, (int)n)); +} +#endif diff --git a/openlibm/src/s_scalbn.c b/openlibm/src/s_scalbn.c new file mode 100644 index 0000000000..dc3a0cfdff --- /dev/null +++ b/openlibm/src/s_scalbn.c @@ -0,0 +1,66 @@ +/* @(#)s_scalbn.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalbn (double x, int n) + * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +#include "cdefs-compat.h" + +#include +#include + +#include "math_private.h" + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ +huge = 1.0e+300, +tiny = 1.0e-300; + +OLM_DLLEXPORT double +scalbn (double x, int n) +{ + int32_t k,hx,lx; + EXTRACT_WORDS(hx,lx,x); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + GET_HIGH_WORD(hx,x); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return huge*copysign(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20)); return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysign(huge,x); /*overflow*/ + else return tiny*copysign(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20)); + return x*twom54; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(scalbn, ldexpl); +openlibm_weak_reference(scalbn, scalbnl); +#endif + +openlibm_strong_reference(scalbn, ldexp); diff --git a/openlibm/src/s_scalbnf.c b/openlibm/src/s_scalbnf.c new file mode 100644 index 0000000000..930ab8fa03 --- /dev/null +++ b/openlibm/src/s_scalbnf.c @@ -0,0 +1,57 @@ +/* s_scalbnf.c -- float version of s_scalbn.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + + +#include "cdefs-compat.h" + +#include + +#include "math_private.h" + +static const float +two25 = 3.355443200e+07, /* 0x4c000000 */ +twom25 = 2.9802322388e-08, /* 0x33000000 */ +huge = 1.0e+30, +tiny = 1.0e-30; + +OLM_DLLEXPORT float +scalbnf (float x, int n) +{ + int32_t k,ix; + GET_FLOAT_WORD(ix,x); + k = (ix&0x7f800000)>>23; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((ix&0x7fffffff)==0) return x; /* +-0 */ + x *= two25; + GET_FLOAT_WORD(ix,x); + k = ((ix&0x7f800000)>>23) - 25; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0xff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0xfe) return huge*copysignf(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {SET_FLOAT_WORD(x,(ix&0x807fffff)|(k<<23)); return x;} + if (k <= -25) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysignf(huge,x); /*overflow*/ + else return tiny*copysignf(tiny,x); /*underflow*/ + } + k += 25; /* subnormal result */ + SET_FLOAT_WORD(x,(ix&0x807fffff)|(k<<23)); + return x*twom25; +} + +openlibm_strong_reference(scalbnf, ldexpf); diff --git a/openlibm/src/s_scalbnl.c b/openlibm/src/s_scalbnl.c new file mode 100644 index 0000000000..7732944f41 --- /dev/null +++ b/openlibm/src/s_scalbnl.c @@ -0,0 +1,70 @@ +/* @(#)s_scalbn.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalbnl (long double x, int n) + * scalbnl(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +/* + * We assume that a long double has a 15-bit exponent. On systems + * where long double is the same as double, scalbnl() is an alias + * for scalbn(), so we don't use this routine. + */ + +#include "cdefs-compat.h" + +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#if LDBL_MAX_EXP != 0x4000 +#error "Unsupported long double format" +#endif + +static const long double +huge = 0x1p16000L, +tiny = 0x1p-16000L; + +OLM_DLLEXPORT long double +scalbnl (long double x, int n) +{ + union IEEEl2bits u; + int k; + u.e = x; + k = u.bits.exp; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((u.bits.manh|u.bits.manl)==0) return x; /* +-0 */ + u.e *= 0x1p+128; + k = u.bits.exp - 128; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7fff) return x+x; /* NaN or Inf */ + k = k+n; + if (k >= 0x7fff) return huge*copysignl(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {u.bits.exp = k; return u.e;} + if (k <= -128) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysign(huge,x); /*overflow*/ + else return tiny*copysign(tiny,x); /*underflow*/ + } + k += 128; /* subnormal result */ + u.bits.exp = k; + return u.e*0x1p-128; +} + +openlibm_strong_reference(scalbnl, ldexpl); diff --git a/openlibm/src/s_signbit.c b/openlibm/src/s_signbit.c new file mode 100644 index 0000000000..87f40f271d --- /dev/null +++ b/openlibm/src/s_signbit.c @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2003 Mike Barcroft + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/msun/src/s_signbit.c,v 1.1 2004/07/19 08:16:10 das Exp $ + */ + +#include + +#include "fpmath.h" +#include "math_private.h" + +OLM_DLLEXPORT int +__signbit(double d) +{ + union IEEEd2bits u; + + u.d = d; + return (u.bits.sign); +} + +OLM_DLLEXPORT int +__signbitf(float f) +{ + union IEEEf2bits u; + + u.f = f; + return (u.bits.sign); +} + +#ifdef OLM_LONG_DOUBLE +OLM_DLLEXPORT int +__signbitl(long double e) +{ + union IEEEl2bits u; + + u.e = e; + return (u.bits.sign); +} +#endif diff --git a/openlibm/src/s_signgam.c b/openlibm/src/s_signgam.c new file mode 100644 index 0000000000..ad72a73042 --- /dev/null +++ b/openlibm/src/s_signgam.c @@ -0,0 +1,7 @@ +#include + +#include "math_private.h" + +#ifndef OPENLIBM_ONLY_THREAD_SAFE +int signgam = 0; +#endif diff --git a/openlibm/src/s_sin.c b/openlibm/src/s_sin.c new file mode 100644 index 0000000000..cc0a5c6fd8 --- /dev/null +++ b/openlibm/src/s_sin.c @@ -0,0 +1,89 @@ +/* @(#)s_sin.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_sin.c,v 1.13 2011/02/10 07:37:50 das Exp $"); + +/* sin(x) + * Return sine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cose function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include +#include + +//#define INLINE_REM_PIO2 +#include "math_private.h" +//#include "e_rem_pio2.c" + +OLM_DLLEXPORT double +sin(double x) +{ + double y[2],z=0.0; + int32_t n, ix; + + /* High word of x. */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) { + if(ix<0x3e500000) /* |x| < 2**-26 */ + {if((int)x==0) return x;} /* generate inexact */ + return __kernel_sin(x,z,0); + } + + /* sin(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_sin(y[0],y[1],1); + case 1: return __kernel_cos(y[0],y[1]); + case 2: return -__kernel_sin(y[0],y[1],1); + default: + return -__kernel_cos(y[0],y[1]); + } + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(sin, sinl); +#endif diff --git a/openlibm/src/s_sincos.c b/openlibm/src/s_sincos.c new file mode 100644 index 0000000000..8628235644 --- /dev/null +++ b/openlibm/src/s_sincos.c @@ -0,0 +1,159 @@ +/* @(#)s_sincos.c 5.1 13/07/15 */ +/* See openlibm LICENSE.md for full license details. + * + * ==================================================== + * This file is derived from fdlibm: + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * + * ==================================================== + * Copyright (C) 2013 Elliot Saba. All rights reserved. + * + * Developed at the University of Washington. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + #include "cdefs-compat.h" + +/* sincos(x, s, c) + * Several applications need sine and cosine of the same + * angle x. This function computes both at the same time, + * and stores the results in *sin and *cos. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cose function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Borrow liberally from s_sin.c and s_cos.c, merging + * efforts where applicable and returning their values in + * appropriate variables, thereby slightly reducing the + * amount of work relative to just calling sin/cos(x) + * separately + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * sincos(+-INF, s, c) is NaN, with signals; + * sincos(NaN, s, c) is that NaN; + */ + +#include +#include + +//#define INLINE_REM_PIO2 +#include "math_private.h" +//#include "e_rem_pio2.c" + +/* Constants used in polynomial approximation of sin/cos */ +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ +S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ +S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ +S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ +S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ +S6 = 1.58969099521155010221e-10, /* 0x3DE5D93A, 0x5ACFD57C */ +C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ +C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ +C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ +C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ +C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ +C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ + +static void +__kernel_sincos( double x, double y, int iy, double * k_s, double * k_c ) +{ + /* Inline calculation of sin/cos, as we can save + some work, and we will always need to calculate + both values, no matter the result of switch */ + double z, w, r, v, hz; + z = x*x; + w = z*z; + + /* cos-specific computation; equivalent to calling + __kernel_cos(x,y) and storing in k_c*/ + r = z*(C1+z*(C2+z*C3)) + w*w*(C4+z*(C5+z*C6)); + hz = 0.5*z; + v = one-hz; + + *k_c = v + (((one-v)-hz) + (z*r-x*y)); + + /* sin-specific computation; equivalent to calling + __kernel_sin(x,y,1) and storing in k_s*/ + r = S2+z*(S3+z*S4) + z*w*(S5+z*S6); + v = z*x; + if(iy == 0) + *k_s = x+v*(S1+z*r); + else + *k_s = x-((z*(half*y-v*r)-y)-v*S1); +} + +OLM_DLLEXPORT void +sincos(double x, double * s, double * c) +{ + double y[2]; + int32_t ix; + + /* Store high word of x in ix */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) { + /* Check for small x for sin and cos */ + if(ix<0x3e46a09e) { + /* Check for exact zero */ + if( (int)x==0 ) { + *s = x; + *c = 1.0; + return; + } + } + /* Call kernel function with 0 extra */ + __kernel_sincos(x,0.0,0, s, c); + } else if( ix >= 0x7ff00000 ) { + /* sincos(Inf or NaN) is NaN */ + *s = x-x; + *c = x-x; + } + + /*argument reduction needed*/ + else { + double k_c, k_s; + + /* Calculate remainer, then sub out to kernel */ + int32_t n = __ieee754_rem_pio2(x,y); + __kernel_sincos( y[0], y[1], 1, &k_s, &k_c ); + + /* Figure out permutation of sin/cos outputs to true outputs */ + switch(n&3) { + case 0: + *c = k_c; + *s = k_s; + break; + case 1: + *c = -k_s; + *s = k_c; + break; + case 2: + *c = -k_c; + *s = -k_s; + break; + default: + *c = k_s; + *s = -k_c; + break; + } + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(sincos, sincosl); +#endif diff --git a/openlibm/src/s_sincosf.c b/openlibm/src/s_sincosf.c new file mode 100644 index 0000000000..62caaac404 --- /dev/null +++ b/openlibm/src/s_sincosf.c @@ -0,0 +1,172 @@ +/* s_sincosf.c -- float version of s_sincos.c + * + * ==================================================== + * This file is derived from fdlibm: + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * + * ==================================================== + * Copyright (C) 2013 Elliot Saba + * Developed at the University of Washington + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== +*/ + +#include "cdefs-compat.h" + +#include +#include + +//#define INLINE_KERNEL_COSDF +//#define INLINE_KERNEL_SINDF +//#define INLINE_REM_PIO2F +#include "math_private.h" +//#include "e_rem_pio2f.c" +//#include "k_cosf.c" +//#include "k_sinf.c" + + +/* Constants used in shortcircuits in sincosf */ +static const double +sc1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */ +sc2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */ +sc3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */ +sc4pio2 = 4*M_PI_2, /* 0x401921FB, 0x54442D18 */ + +/* Constants used in polynomial approximation of sin/cos */ +one = 1.0, +S1 = -0x15555554cbac77.0p-55, /* -0.166666666416265235595 */ +S2 = 0x111110896efbb2.0p-59, /* 0.0083333293858894631756 */ +S3 = -0x1a00f9e2cae774.0p-65, /* -0.000198393348360966317347 */ +S4 = 0x16cd878c3b46a7.0p-71, /* 0.0000027183114939898219064 */ +C0 = -0x1ffffffd0c5e81.0p-54, /* -0.499999997251031003120 */ +C1 = 0x155553e1053a42.0p-57, /* 0.0416666233237390631894 */ +C2 = -0x16c087e80f1e27.0p-62, /* -0.00138867637746099294692 */ +C3 = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */ + +static void +__kernel_sincosdf( double x, float * s, float * c ) +{ + double r, w, z, v; + z = x*x; + w = z*z; + + /* cos-specific computation; equivalent to calling + __kernel_cos(x,y) and storing in k_c*/ + r = C2+z*C3; + double k_c = ((one+z*C0) + w*C1) + (w*z)*r; + + /* sin-specific computation; equivalent to calling + __kernel_sin(x,y,1) and storing in k_s*/ + r = S3+z*S4; + v = z*x; + double k_s = (x + v*(S1+z*S2)) + v*w*r; + + *c = k_c; + *s = k_s; +} + +OLM_DLLEXPORT void +sincosf(float x, float * s, float * c) { + // Worst approximation of sin and cos NA + *s = x; + *c = x; + + double y; + float k_c, k_s; + int32_t n, hx, ix; + + GET_FLOAT_WORD(hx,x); + ix = hx & 0x7fffffff; + + if(ix <= 0x3f490fda) { /* |x| ~<= pi/4 */ + if(ix<0x39800000) { /* |x| < 2**-12 */ + /* Check if x is exactly zero */ + if(((int)x)==0) { + *s = x; + *c = 1.0f; + return; + } + } + __kernel_sincosdf(x, s, c); + return; + } + /* |x| ~<= 5*pi/4 */ + if (ix<=0x407b53d1) { + /* |x| ~<= 3pi/4 */ + if(ix<=0x4016cbe3) { + if(hx>0) { + __kernel_sincosdf( sc1pio2 - x, c, s ); + } + else { + __kernel_sincosdf( sc1pio2 + x, c, &k_s ); + *s = -k_s; + } + } else { + + if(hx>0) { + __kernel_sincosdf( sc2pio2 - x, s, &k_c ); + *c = -k_c; + } else { + __kernel_sincosdf( -sc2pio2 - x, s, &k_c ); + *c = -k_c; + } + } + return; + } + + /* |x| ~<= 9*pi/4 */ + if(ix<=0x40e231d5) { + /* |x| ~> 7*pi/4 */ + if(ix<=0x40afeddf) { + if(hx>0) { + __kernel_sincosdf( x - sc3pio2, c, &k_s ); + *s = -k_s; + } else { + __kernel_sincosdf( x + sc3pio2, &k_c, s ); + *c = -k_c; + } + } + else { + if( hx > 0 ) { + __kernel_sincosdf( x - sc4pio2, s, c ); + } else { + __kernel_sincosdf( x + sc4pio2, s, c ); + } + } + return; + } + + /* cos(Inf or NaN) is NaN */ + else if(ix>=0x7f800000) { + *c = *s = x-x; + } else { + /* general argument reduction needed */ + n = __ieee754_rem_pio2f(x,&y); + + switch(n&3) { + case 0: + __kernel_sincosdf( y, s, c ); + break; + case 1: + __kernel_sincosdf( -y, c, s ); + break; + case 2: + __kernel_sincosdf( -y, s, &k_c); + *c = -k_c; + break; + default: + __kernel_sincosdf( -y, &k_c, &k_s ); + *c = -k_c; + *s = -k_s; + break; + } + } + +} diff --git a/openlibm/src/s_sincosl.c b/openlibm/src/s_sincosl.c new file mode 100644 index 0000000000..e2d3a340d8 --- /dev/null +++ b/openlibm/src/s_sincosl.c @@ -0,0 +1,31 @@ +/* s_sincosl.c -- long double version of s_sincos.c + * + * Copyright (C) 2013 Elliot Saba + * Developed at the University of Washington + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== +*/ + +#include "cdefs-compat.h" + +#include +#include + +#include "math_private.h" +#if LDBL_MANT_DIG == 64 +#include "../ld80/e_rem_pio2l.h" +#elif LDBL_MANT_DIG == 113 +#include "../ld128/e_rem_pio2l.h" +#else +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT void +sincosl( long double x, long double * s, long double * c ) +{ + *s = sinl( x ); + *c = cosl( x ); +} diff --git a/openlibm/src/s_sinf.c b/openlibm/src/s_sinf.c new file mode 100644 index 0000000000..d987369a92 --- /dev/null +++ b/openlibm/src/s_sinf.c @@ -0,0 +1,85 @@ +/* s_sinf.c -- float version of s_sin.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_sinf.c,v 1.17 2008/02/25 22:19:17 bde Exp $"); + +#include +#include + +//#define INLINE_KERNEL_COSDF +//#define INLINE_KERNEL_SINDF +//#define INLINE_REM_PIO2F +#include "math_private.h" +//#include "e_rem_pio2f.c" +//#include "k_cosf.c" +//#include "k_sinf.c" + +/* Small multiples of pi/2 rounded to double precision. */ +static const double +s1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */ +s2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */ +s3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */ +s4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */ + +OLM_DLLEXPORT float +sinf(float x) +{ + double y; + int32_t n, hx, ix; + + GET_FLOAT_WORD(hx,x); + ix = hx & 0x7fffffff; + + if(ix <= 0x3f490fda) { /* |x| ~<= pi/4 */ + if(ix<0x39800000) /* |x| < 2**-12 */ + if(((int)x)==0) return x; /* x with inexact if x != 0 */ + return __kernel_sindf(x); + } + if(ix<=0x407b53d1) { /* |x| ~<= 5*pi/4 */ + if(ix<=0x4016cbe3) { /* |x| ~<= 3pi/4 */ + if(hx>0) + return __kernel_cosdf(x - s1pio2); + else + return -__kernel_cosdf(x + s1pio2); + } else + return __kernel_sindf((hx > 0 ? s2pio2 : -s2pio2) - x); + } + if(ix<=0x40e231d5) { /* |x| ~<= 9*pi/4 */ + if(ix<=0x40afeddf) { /* |x| ~<= 7*pi/4 */ + if(hx>0) + return -__kernel_cosdf(x - s3pio2); + else + return __kernel_cosdf(x + s3pio2); + } else + return __kernel_sindf(x + (hx > 0 ? -s4pio2 : s4pio2)); + } + + /* sin(Inf or NaN) is NaN */ + else if (ix>=0x7f800000) return x-x; + + /* general argument reduction needed */ + else { + n = __ieee754_rem_pio2f(x,&y); + switch(n&3) { + case 0: return __kernel_sindf(y); + case 1: return __kernel_cosdf(y); + case 2: return __kernel_sindf(-y); + default: + return -__kernel_cosdf(y); + } + } +} diff --git a/openlibm/src/s_sinl.c b/openlibm/src/s_sinl.c new file mode 100644 index 0000000000..7304fc746f --- /dev/null +++ b/openlibm/src/s_sinl.c @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 2007 Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_sinl.c,v 1.3 2011/05/30 19:41:28 kargl Exp $"); + +#include +#include + +#include "math_private.h" +#if LDBL_MANT_DIG == 64 +#include "../ld80/e_rem_pio2l.h" +#elif LDBL_MANT_DIG == 113 +#include "../ld128/e_rem_pio2l.h" +#else +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT long double +sinl(long double x) +{ + union IEEEl2bits z; + int e0, s; + long double y[2]; + long double hi, lo; + + z.e = x; + s = z.bits.sign; + z.bits.sign = 0; + + /* If x = +-0 or x is a subnormal number, then sin(x) = x */ + if (z.bits.exp == 0) + return (x); + + /* If x = NaN or Inf, then sin(x) = NaN. */ + if (z.bits.exp == 32767) + return ((x - x) / (x - x)); + + /* Optimize the case where x is already within range. */ + if (z.e < M_PI_4) { + hi = __kernel_sinl(z.e, 0, 0); + return (s ? -hi : hi); + } + + e0 = __ieee754_rem_pio2l(x, y); + hi = y[0]; + lo = y[1]; + + switch (e0 & 3) { + case 0: + hi = __kernel_sinl(hi, lo, 1); + break; + case 1: + hi = __kernel_cosl(hi, lo); + break; + case 2: + hi = - __kernel_sinl(hi, lo, 1); + break; + case 3: + hi = - __kernel_cosl(hi, lo); + break; + } + + return (hi); +} diff --git a/openlibm/src/s_tan.c b/openlibm/src/s_tan.c new file mode 100644 index 0000000000..2b234a2b8a --- /dev/null +++ b/openlibm/src/s_tan.c @@ -0,0 +1,83 @@ +/* @(#)s_tan.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_tan.c,v 1.13 2011/02/10 07:37:50 das Exp $"); + +/* tan(x) + * Return tangent function of x. + * + * kernel function: + * __kernel_tan ... tangent function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include +#include + +//#define INLINE_REM_PIO2 +#include "math_private.h" +//#include "e_rem_pio2.c" + +OLM_DLLEXPORT double +tan(double x) +{ + double y[2],z=0.0; + int32_t n, ix; + + /* High word of x. */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) { + if(ix<0x3e400000) /* x < 2**-27 */ + if((int)x==0) return x; /* generate inexact */ + return __kernel_tan(x,z,1); + } + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; /* NaN */ + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even + -1 -- n odd */ + } +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(tan, tanl); +#endif diff --git a/openlibm/src/s_tanf.c b/openlibm/src/s_tanf.c new file mode 100644 index 0000000000..7ffe193a6a --- /dev/null +++ b/openlibm/src/s_tanf.c @@ -0,0 +1,72 @@ +/* s_tanf.c -- float version of s_tan.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + * Optimized by Bruce D. Evans. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_tanf.c,v 1.17 2008/02/25 22:19:17 bde Exp $"); + +#include +#include + +//#define INLINE_KERNEL_TANDF +//#define INLINE_REM_PIO2F +#include "math_private.h" +//#include "e_rem_pio2f.c" +//#include "k_tanf.c" + +/* Small multiples of pi/2 rounded to double precision. */ +static const double +t1pio2 = 1*M_PI_2, /* 0x3FF921FB, 0x54442D18 */ +t2pio2 = 2*M_PI_2, /* 0x400921FB, 0x54442D18 */ +t3pio2 = 3*M_PI_2, /* 0x4012D97C, 0x7F3321D2 */ +t4pio2 = 4*M_PI_2; /* 0x401921FB, 0x54442D18 */ + +OLM_DLLEXPORT float +tanf(float x) +{ + double y; + int32_t n, hx, ix; + + GET_FLOAT_WORD(hx,x); + ix = hx & 0x7fffffff; + + if(ix <= 0x3f490fda) { /* |x| ~<= pi/4 */ + if(ix<0x39800000) /* |x| < 2**-12 */ + if(((int)x)==0) return x; /* x with inexact if x != 0 */ + return __kernel_tandf(x,1); + } + if(ix<=0x407b53d1) { /* |x| ~<= 5*pi/4 */ + if(ix<=0x4016cbe3) /* |x| ~<= 3pi/4 */ + return __kernel_tandf(x + (hx>0 ? -t1pio2 : t1pio2), -1); + else + return __kernel_tandf(x + (hx>0 ? -t2pio2 : t2pio2), 1); + } + if(ix<=0x40e231d5) { /* |x| ~<= 9*pi/4 */ + if(ix<=0x40afeddf) /* |x| ~<= 7*pi/4 */ + return __kernel_tandf(x + (hx>0 ? -t3pio2 : t3pio2), -1); + else + return __kernel_tandf(x + (hx>0 ? -t4pio2 : t4pio2), 1); + } + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7f800000) return x-x; + + /* general argument reduction needed */ + else { + n = __ieee754_rem_pio2f(x,&y); + /* integer parameter: 1 -- n even; -1 -- n odd */ + return __kernel_tandf(y,1-((n&1)<<1)); + } +} diff --git a/openlibm/src/s_tanh.c b/openlibm/src/s_tanh.c new file mode 100644 index 0000000000..1fa734d29c --- /dev/null +++ b/openlibm/src/s_tanh.c @@ -0,0 +1,83 @@ +/* @(#)s_tanh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_tanh.c,v 1.9 2008/02/22 02:30:36 das Exp $"); + +/* Tanh(x) + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanh(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanh(-x) = -tanh(x). + * 2. 0 <= x < 2**-28 : tanh(x) := x with inexact if x != 0 + * -t + * 2**-28 <= x < 1 : tanh(x) := -----; t = expm1(-2x) + * t + 2 + * 2 + * 1 <= x < 22 : tanh(x) := 1 - -----; t = expm1(2x) + * t + 2 + * 22 <= x <= INF : tanh(x) := 1. + * + * Special cases: + * tanh(NaN) is NaN; + * only tanh(0)=0 is exact for finite argument. + */ + +#include +#include + +#include "math_private.h" + +static const double one = 1.0, two = 2.0, tiny = 1.0e-300, huge = 1.0e300; + +OLM_DLLEXPORT double +tanh(double x) +{ + double t,z; + int32_t jx,ix; + + GET_HIGH_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) { + if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ + else return one/x-one; /* tanh(NaN) = NaN */ + } + + /* |x| < 22 */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3e300000) { /* |x|<2**-28 */ + if(huge+x>one) return x; /* tanh(tiny) = tiny with inexact */ + } + if (ix>=0x3ff00000) { /* |x|>=1 */ + t = expm1(two*fabs(x)); + z = one - two/(t+two); + } else { + t = expm1(-two*fabs(x)); + z= -t/(t+two); + } + /* |x| >= 22, return +-1 */ + } else { + z = one - tiny; /* raise inexact flag */ + } + return (jx>=0)? z: -z; +} + +#if (LDBL_MANT_DIG == 53) +openlibm_weak_reference(tanh, tanhl); +#endif diff --git a/openlibm/src/s_tanhf.c b/openlibm/src/s_tanhf.c new file mode 100644 index 0000000000..cd77e4e76a --- /dev/null +++ b/openlibm/src/s_tanhf.c @@ -0,0 +1,56 @@ +/* s_tanhf.c -- float version of s_tanh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_tanhf.c,v 1.9 2008/02/22 02:30:36 das Exp $"); + +#include + +#include "math_private.h" + +static const float one=1.0, two=2.0, tiny = 1.0e-30, huge = 1.0e30; +OLM_DLLEXPORT float +tanhf(float x) +{ + float t,z; + int32_t jx,ix; + + GET_FLOAT_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7f800000) { + if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ + else return one/x-one; /* tanh(NaN) = NaN */ + } + + /* |x| < 9 */ + if (ix < 0x41100000) { /* |x|<9 */ + if (ix<0x39800000) { /* |x|<2**-12 */ + if(huge+x>one) return x; /* tanh(tiny) = tiny with inexact */ + } + if (ix>=0x3f800000) { /* |x|>=1 */ + t = expm1f(two*fabsf(x)); + z = one - two/(t+two); + } else { + t = expm1f(-two*fabsf(x)); + z= -t/(t+two); + } + /* |x| >= 9, return +-1 */ + } else { + z = one - tiny; /* raise inexact flag */ + } + return (jx>=0)? z: -z; +} diff --git a/openlibm/src/s_tanl.c b/openlibm/src/s_tanl.c new file mode 100644 index 0000000000..0370e6b844 --- /dev/null +++ b/openlibm/src/s_tanl.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2007 Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_tanl.c,v 1.3 2011/05/30 19:41:28 kargl Exp $"); + +/* + * Limited testing on pseudorandom numbers drawn within [0:4e8] shows + * an accuracy of <= 1.5 ULP where 247024 values of x out of 40 million + * possibles resulted in tan(x) that exceeded 0.5 ULP (ie., 0.6%). + */ + +#include +#include + +#include "math_private.h" +#if LDBL_MANT_DIG == 64 +#include "../ld80/e_rem_pio2l.h" +#elif LDBL_MANT_DIG == 113 +#include "../ld128/e_rem_pio2l.h" +#else +#error "Unsupported long double format" +#endif + +OLM_DLLEXPORT long double +tanl(long double x) +{ + union IEEEl2bits z; + int e0, s; + long double y[2]; + long double hi, lo; + + z.e = x; + s = z.bits.sign; + z.bits.sign = 0; + + /* If x = +-0 or x is subnormal, then tan(x) = x. */ + if (z.bits.exp == 0) + return (x); + + /* If x = NaN or Inf, then tan(x) = NaN. */ + if (z.bits.exp == 32767) + return ((x - x) / (x - x)); + + /* Optimize the case where x is already within range. */ + if (z.e < M_PI_4) { + hi = __kernel_tanl(z.e, 0, 0); + return (s ? -hi : hi); + } + + e0 = __ieee754_rem_pio2l(x, y); + hi = y[0]; + lo = y[1]; + + switch (e0 & 3) { + case 0: + case 2: + hi = __kernel_tanl(hi, lo, 0); + break; + case 1: + case 3: + hi = __kernel_tanl(hi, lo, 1); + break; + } + + return (hi); +} diff --git a/openlibm/src/s_tgammaf.c b/openlibm/src/s_tgammaf.c new file mode 100644 index 0000000000..fbfa3fea37 --- /dev/null +++ b/openlibm/src/s_tgammaf.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2008 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_tgammaf.c,v 1.1 2008/02/18 17:27:10 das Exp $"); + +#include + +#include "math_private.h" + +/* + * We simply call tgamma() rather than bloating the math library with + * a float-optimized version of it. The reason is that tgammaf() is + * essentially useless, since the function is superexponential and + * floats have very limited range. + */ +OLM_DLLEXPORT float +tgammaf(float x) +{ + + return (tgamma(x)); +} diff --git a/openlibm/src/s_trunc.c b/openlibm/src/s_trunc.c new file mode 100644 index 0000000000..c460cc925d --- /dev/null +++ b/openlibm/src/s_trunc.c @@ -0,0 +1,67 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_trunc.c,v 1.4 2008/02/22 02:27:34 das Exp $"); + +/* + * trunc(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to trunc(x). + */ + +#include +#include + +#include "math_private.h" + +static const double huge = 1.0e300; + +OLM_DLLEXPORT double +trunc(double x) +{ + int32_t i0,i1,j0; + u_int32_t i; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* |x|<1, so return 0*sign(x) */ + i0 &= 0x80000000U; + i1 = 0; + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) /* raise inexact flag */ + i1 &= (~i); + } + INSERT_WORDS(x,i0,i1); + return x; +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(trunc, truncl); +#endif diff --git a/openlibm/src/s_truncf.c b/openlibm/src/s_truncf.c new file mode 100644 index 0000000000..d9fc62e212 --- /dev/null +++ b/openlibm/src/s_truncf.c @@ -0,0 +1,54 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_truncf.c,v 1.1 2004/06/20 09:25:43 das Exp $"); + +/* + * truncf(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to truncf(x). + */ + +#include + +#include "math_private.h" + +static const float huge = 1.0e30F; + +OLM_DLLEXPORT float +truncf(float x) +{ + int32_t i0,j0; + u_int32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0F) /* |x|<1, so return 0*sign(x) */ + i0 &= 0x80000000; + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge+x>0.0F) /* raise inexact flag */ + i0 &= (~i); + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} diff --git a/openlibm/src/s_truncl.c b/openlibm/src/s_truncl.c new file mode 100644 index 0000000000..34d7b6530f --- /dev/null +++ b/openlibm/src/s_truncl.c @@ -0,0 +1,69 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + * From: @(#)s_floor.c 5.1 93/09/24 + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/s_truncl.c,v 1.9 2008/02/14 15:10:34 bde Exp $"); + +/* + * truncl(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to truncl(x). + */ + +#include +#include +#include + +#include "fpmath.h" +#include "math_private.h" + +#ifdef LDBL_IMPLICIT_NBIT +#define MANH_SIZE (LDBL_MANH_SIZE + 1) +#else +#define MANH_SIZE LDBL_MANH_SIZE +#endif + +static const long double huge = 1.0e300; +static const float zero[] = { 0.0, -0.0 }; + +OLM_DLLEXPORT long double +truncl(long double x) +{ + union IEEEl2bits u = { .e = x }; + int e = u.bits.exp - LDBL_MAX_EXP + 1; + + if (e < MANH_SIZE - 1) { + if (e < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) + u.e = zero[u.bits.sign]; + } else { + uint64_t m = ((1llu << MANH_SIZE) - 1) >> (e + 1); + if (((u.bits.manh & m) | u.bits.manl) == 0) + return (x); /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + u.bits.manh &= ~m; + u.bits.manl = 0; + } + } + } else if (e < LDBL_MANT_DIG - 1) { + uint64_t m = (uint64_t)-1 >> (64 - LDBL_MANT_DIG + e + 1); + if ((u.bits.manl & m) == 0) + return (x); /* x is integral */ + if (huge + x > 0.0) /* raise inexact flag */ + u.bits.manl &= ~m; + } + return (u.e); +} diff --git a/openlibm/src/types-compat.h b/openlibm/src/types-compat.h new file mode 100644 index 0000000000..b20b5aeac2 --- /dev/null +++ b/openlibm/src/types-compat.h @@ -0,0 +1,13 @@ +#ifndef _TYPES_COMPAT_H_ +#define _TYPES_COMPAT_H_ + +#include +#include + +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; +typedef uint64_t u_int64_t; + + +#endif diff --git a/openlibm/src/w_cabs.c b/openlibm/src/w_cabs.c new file mode 100644 index 0000000000..6dc9bdee70 --- /dev/null +++ b/openlibm/src/w_cabs.c @@ -0,0 +1,25 @@ +/* + * cabs() wrapper for hypot(). + * + * Written by J.T. Conklin, + * Placed into the Public Domain, 1994. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/w_cabs.c,v 1.7 2008/03/30 20:03:06 das Exp $"); + +#include +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT double +cabs(double complex z) +{ + return hypot(creal(z), cimag(z)); +} + +#if LDBL_MANT_DIG == 53 +openlibm_weak_reference(cabs, cabsl); +#endif diff --git a/openlibm/src/w_cabsf.c b/openlibm/src/w_cabsf.c new file mode 100644 index 0000000000..f14c71aa97 --- /dev/null +++ b/openlibm/src/w_cabsf.c @@ -0,0 +1,19 @@ +/* + * cabsf() wrapper for hypotf(). + * + * Written by J.T. Conklin, + * Placed into the Public Domain, 1994. + */ + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT float +cabsf(z) + float complex z; +{ + + return hypotf(crealf(z), cimagf(z)); +} diff --git a/openlibm/src/w_cabsl.c b/openlibm/src/w_cabsl.c new file mode 100644 index 0000000000..c10f1d4f8e --- /dev/null +++ b/openlibm/src/w_cabsl.c @@ -0,0 +1,22 @@ +/* + * cabs() wrapper for hypot(). + * + * Written by J.T. Conklin, + * Placed into the Public Domain, 1994. + * + * Modified by Steven G. Kargl for the long double type. + */ + +#include "cdefs-compat.h" +//__FBSDID("$FreeBSD: src/lib/msun/src/w_cabsl.c,v 1.1 2008/03/30 20:02:03 das Exp $"); + +#include +#include + +#include "math_private.h" + +OLM_DLLEXPORT long double +cabsl(long double complex z) +{ + return hypotl(creall(z), cimagl(z)); +} diff --git a/openlibm/test/.gitignore b/openlibm/test/.gitignore new file mode 100644 index 0000000000..2d85b23e33 --- /dev/null +++ b/openlibm/test/.gitignore @@ -0,0 +1,9 @@ +/test-float +/test-float-system +/test-float.dSYM +/test-double +/test-double-system +/test-double.dSYM +/bench-openlibm +/bench-syslibm +/*.exe diff --git a/openlibm/test/Makefile b/openlibm/test/Makefile new file mode 100644 index 0000000000..a61ce5a254 --- /dev/null +++ b/openlibm/test/Makefile @@ -0,0 +1,37 @@ +OPENLIBM_HOME=$(abspath ..) +include ../Make.inc + +# Set rpath of tests to builddir for loading shared library +OPENLIBM_LIB = -L.. -lopenlibm +ifneq ($(OS),WINNT) +ifneq ($(OS),Darwin) +OPENLIBM_LIB += -Wl,-rpath=$(OPENLIBM_HOME) +endif +else # WINNT +CFLAGS_add += -DIMPORT_EXPORTS +endif + +all: test-double test-float # test-double-system test-float-system + +bench: bench-syslibm bench-openlibm + +test-double: test-double.c libm-test.c libm-test-ulps.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) $(LDFLAGS) $(LDFLAGS_arch) $@.c -D__BSD_VISIBLE -I ../include -I../src $(OPENLIBM_LIB) -o $@ + +test-float: test-float.c libm-test.c libm-test-ulps.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) $(LDFLAGS) $(LDFLAGS_arch) $@.c -D__BSD_VISIBLE -I ../include -I../src $(OPENLIBM_LIB) -o $@ + +test-double-system: test-double.c libm-test.c libm-test-ulps.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) $(LDFLAGS) $(LDFLAGS_arch) $< -DSYS_MATH_H -lm -o $@ + +test-float-system: test-float.c libm-test.c libm-test-ulps.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) $(LDFLAGS) $(LDFLAGS_arch) $< -DSYS_MATH_H -lm -o $@ + +bench-openlibm: libm-bench.cpp + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) $(LDFLAGS) $(LDFLAGS_arch) $< $(OPENLIBM_LIB) -o $@ + +bench-syslibm: libm-bench.cpp + $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_add) $(LDFLAGS) $(LDFLAGS_arch) $< -lm -o $@ + +clean: + rm -fr test-double test-float test-double-system test-float-system bench-openlibm bench-syslibm *.dSYM diff --git a/openlibm/test/inf_torture.c b/openlibm/test/inf_torture.c new file mode 100644 index 0000000000..f2b0124f39 --- /dev/null +++ b/openlibm/test/inf_torture.c @@ -0,0 +1,76 @@ +#include +#include + +int main(); +int main2(); +int main3(); +int main4(); + +int main() { + printf("+inf:\n"); + float fx = (float)INFINITY; unsigned int *fxi = (unsigned int*)&fx; + double dx = (double)INFINITY; long unsigned long int *dxi = (long unsigned long int*)&dx; + long double ldx = (long double)INFINITY; long unsigned long int *ldxi1 = (long unsigned long int*)&ldx; long unsigned long int *ldxi2 = &(ldxi1[1]); + printf("\t\tf d ld\n"); + printf("correct:\t%x %x %x\n", isinf(fx), isinf(dx), isinf(ldx)); + printf("as floats:\t%x %x %x\n", isinf(*(float*)fxi), isinf(*(float*)dxi), isinf(*(float*)ldxi1)); + printf("as double:\t%x %x %x\n", isinf(*(double*)fxi), isinf(*(double*)dxi), isinf(*(double*)ldxi1)); + printf("as long double:\t%x %x %x\n", isinf(*(long double*)fxi), isinf(*(long double*)dxi), isinf(*(long double*)ldxi1)); + printf("sizes ?4 8 12?:\t%d %d %d\n", (int)sizeof(fx), (int)sizeof(dx), (int)sizeof(ldx)); + printf("sizes:\t%d %d %d\n", (int)sizeof(*fxi), (int)sizeof(*dxi), (int)sizeof(*ldxi1)*2); + printf("bit repr:\n f: %x\n d: %llx\n ld: %llx%llx\n", *fxi, *dxi, (0xFFFF)&*ldxi2, *ldxi1); + printf("\n"); + main2(); + return 0; +} + +int main2() { + printf("-inf:\n"); + float fx = (float)-INFINITY; unsigned int *fxi = (unsigned int*)&fx; + double dx = (double)-INFINITY; long unsigned long int *dxi = (long unsigned long int*)&dx; + long double ldx = (long double)-INFINITY; long unsigned long int *ldxi1 = (long unsigned long int*)&ldx; long unsigned long int *ldxi2 = &(ldxi1[1]); + printf("\t\tf d ld\n"); + printf("correct:\t%x %x %x\n", isinf(fx), isinf(dx), isinf(ldx)); + printf("as floats:\t%x %x %x\n", isinf(*(float*)fxi), isinf(*(float*)dxi), isinf(*(float*)ldxi1)); + printf("as double:\t%x %x %x\n", isinf(*(double*)fxi), isinf(*(double*)dxi), isinf(*(double*)ldxi1)); + printf("as long double:\t%x %x %x\n", isinf(*(long double*)fxi), isinf(*(long double*)dxi), isinf(*(long double*)ldxi1)); + printf("sizes ?4 8 12?:\t%d %d %d\n", (int)sizeof(fx), (int)sizeof(dx), (int)sizeof(ldx)); + printf("bit repr:\n f: %x\n d: %llx\n ld: %llx%llx\n", *fxi, *dxi, (0xFFFF)&*ldxi2, *ldxi1); + printf("\n"); + main3(); + return 0; +} + +int main3() { + printf("+NaN:\n"); + float fx = (float)NAN; unsigned int *fxi = (unsigned int*)&fx; + double dx = (double)NAN; long unsigned long int *dxi = (long unsigned long int*)&dx; + long double ldx = (long double)NAN; long unsigned long int *ldxi1 = (long unsigned long int*)&ldx; long unsigned long int *ldxi2 = &(ldxi1[1]); + printf("\t\tf d ld\n"); + printf("correct:\t%x %x %x\n", isnan(fx), isnan(dx), isnan(ldx)); + printf("as floats:\t%x %x %x\n", isnan(*(float*)fxi), isnan(*(float*)dxi), isnan(*(float*)ldxi1)); + printf("as double:\t%x %x %x\n", isnan(*(double*)fxi), isnan(*(double*)dxi), isnan(*(double*)ldxi1)); + printf("as long double:\t%x %x %x\n", isnan(*(long double*)fxi), isnan(*(long double*)dxi), isnan(*(long double*)ldxi1)); + printf("sizes ?4 8 12?:\t%d %d %d\n", (int)sizeof(fx), (int)sizeof(dx), (int)sizeof(ldx)); + printf("sizes:\t%d %d %d\n", (int)sizeof(*fxi), (int)sizeof(*dxi), (int)sizeof(*ldxi1)*2); + printf("bit repr:\n f: %x\n d: %llx\n ld: %llx%llx\n", *fxi, *dxi, (0xFFFF)&*ldxi2, *ldxi1); + printf("\n"); + main4(); + return 0; +} + +int main4() { + printf("-NaN:\n"); + float fx = (float)-NAN; unsigned int *fxi = (unsigned int*)&fx; + double dx = (double)-NAN; long unsigned long int *dxi = (long unsigned long int*)&dx; + long double ldx = (long double)-NAN; long unsigned long int *ldxi1 = (long unsigned long int*)&ldx; long unsigned long int *ldxi2 = &(ldxi1[1]); + printf("\t\tf d ld\n"); + printf("correct:\t%x %x %x\n", isnan(fx), isnan(dx), isnan(ldx)); + printf("as floats:\t%x %x %x\n", isnan(*(float*)fxi), isnan(*(float*)dxi), isnan(*(float*)ldxi1)); + printf("as double:\t%x %x %x\n", isnan(*(double*)fxi), isnan(*(double*)dxi), isnan(*(double*)ldxi1)); + printf("as long double:\t%x %x %x\n", isnan(*(long double*)fxi), isnan(*(long double*)dxi), isnan(*(long double*)ldxi1)); + printf("sizes ?4 8 12?:\t%d %d %d\n", (int)sizeof(fx), (int)sizeof(dx), (int)sizeof(ldx)); + printf("bit repr:\n f: %x\n d: %llx\n ld: %llx%llx\n", *fxi, *dxi, (0xFFFF)&*ldxi2, *ldxi1); + printf("\n"); + return 0; +} diff --git a/openlibm/test/libm-bench.cpp b/openlibm/test/libm-bench.cpp new file mode 100644 index 0000000000..04876deccd --- /dev/null +++ b/openlibm/test/libm-bench.cpp @@ -0,0 +1,144 @@ +// Copyright (C) Dahua Lin, 2014. Provided under the MIT license. + +// Benchmark on libm functions + +#include +#include +#include +#include + + +// Timing facilities + +#ifdef __MACH__ + +#include + +class stimer +{ +public: + typedef uint64_t time_type; + + stimer() + { + ::mach_timebase_info(&m_baseinfo); + } + + time_type current() const + { + return ::mach_absolute_time(); + } + + double span(const time_type& t0, const time_type& t1) const + { + uint64_t d = (m_baseinfo.numer * (t1 - t0)) / m_baseinfo.denom; + return static_cast(d) / 1.0e9; + } + +private: + mach_timebase_info_data_t m_baseinfo; +}; + +#else + +class stimer +{ +public: + typedef timespec time_type; + + time_type current() const + { + time_type t; + ::clock_gettime(CLOCK_REALTIME, &t); + return t; + } + + double span(const time_type& t0, const time_type& t1) const + { + return double(t1.tv_sec - t0.tv_sec) + + double(t1.tv_nsec - t0.tv_nsec) * 1.0e-9; + } +}; + +#endif + + +inline double sec2mps(double s, long n) +{ + return n / (s * 1e6); +} + + +const long ARR_LEN = 1024; + +double a[ARR_LEN]; +double b[ARR_LEN]; +double r[ARR_LEN]; + +#define TFUN1(FNAME) \ + void test_##FNAME(long n) { \ + for (int j = 0; j < ARR_LEN; ++j) r[j] = FNAME(a[j]); \ + stimer tm; \ + stimer::time_type t0 = tm.current(); \ + for(int i = 0; i < n; ++i) { \ + for (int j = 0; j < ARR_LEN; ++j) r[j] = FNAME(a[j]); \ + } \ + double s = tm.span(t0, tm.current()); \ + double mps = sec2mps(s, n * ARR_LEN); \ + printf(" %-8s: %7.4f MPS\n", #FNAME, mps); } + +#define TFUN2(FNAME) \ + void test_##FNAME(long n) { \ + for (int j = 0; j < ARR_LEN; ++j) r[j] = FNAME(a[j], b[j]); \ + stimer tm; \ + stimer::time_type t0 = tm.current(); \ + for(int i = 0; i < n; ++i) { \ + for (int j = 0; j < ARR_LEN; ++j) r[j] = FNAME(a[j], b[j]); \ + } \ + double s = tm.span(t0, tm.current()); \ + double mps = sec2mps(s, n * ARR_LEN); \ + printf(" %-8s: %7.4f MPS\n", #FNAME, mps); } + + +#define TCALL(FNAME) test_##FNAME(20000) + +// define benchmark functions + +TFUN2(pow) +TFUN2(hypot) + +TFUN1(exp) +TFUN1(log) +TFUN1(log10) +TFUN1(sin) +TFUN1(cos) +TFUN1(tan) +TFUN1(asin) +TFUN1(acos) +TFUN1(atan) +TFUN2(atan2) + +int main(int argc, char *argv[]) +{ + // initialize array contents + for (int i = 0; i < ARR_LEN; ++i) + { + a[i] = rand() / (double) RAND_MAX; + b[i] = rand() / (double) RAND_MAX; + } + + TCALL(pow); + TCALL(hypot); + TCALL(exp); + TCALL(log); + TCALL(log10); + TCALL(sin); + TCALL(cos); + TCALL(tan); + TCALL(asin); + TCALL(acos); + TCALL(atan); + TCALL(atan2); + + return 0; +} diff --git a/openlibm/test/libm-test-ulps.h b/openlibm/test/libm-test-ulps.h new file mode 100644 index 0000000000..67a415c008 --- /dev/null +++ b/openlibm/test/libm-test-ulps.h @@ -0,0 +1,254 @@ +/* This file is automatically generated + from libm-test-ulps with gen-libm-test.pl. + Don't change it - change instead the master files. */ + + +/* Maximal error of functions. */ +#define DELTAacos CHOOSE(1150, 1, 1, 1150, 0, 0) /* acos */ +#define DELTAacosh CHOOSE(1, 0, 0, 1, 0, 0) /* acosh */ +#define DELTAasin CHOOSE(1, 1, 0, 1, 0, 0) /* asin */ +#define DELTAasinh CHOOSE(656, 0, 0, 656, 0, 0) /* asinh */ +#define DELTAatan CHOOSE(549, 0, 0, 549, 0, 0) /* atan */ +#define DELTAatanh CHOOSE(1605, 1, 0, 1605, 1, 0) /* atanh */ +#define DELTAatan2 CHOOSE(549, 0, 0, 549, 0, 0) /* atan2 */ +#define DELTAcabs CHOOSE(560, 1, 1, 560, 1, 1) /* cabs */ +#define DELTAcacos CHOOSE(BUILD_COMPLEX (151, 329), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 2), BUILD_COMPLEX (151, 329), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 2)) /* cacos */ +#define DELTAcacosh CHOOSE(BUILD_COMPLEX (328, 151), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (4, 4), BUILD_COMPLEX (328, 151), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (4, 4)) /* cacosh */ +#define DELTAcasin CHOOSE(BUILD_COMPLEX (603, 329), BUILD_COMPLEX (3, 0), BUILD_COMPLEX (2, 2), BUILD_COMPLEX (603, 329), BUILD_COMPLEX (3, 0), BUILD_COMPLEX (2, 2)) /* casin */ +#define DELTAcasinh CHOOSE(BUILD_COMPLEX (892, 12), BUILD_COMPLEX (5, 3), BUILD_COMPLEX (1, 6), BUILD_COMPLEX (892, 12), BUILD_COMPLEX (5, 3), BUILD_COMPLEX (1, 6)) /* casinh */ +#define DELTAcatan CHOOSE(BUILD_COMPLEX (251, 474), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (251, 474), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* catan */ +#define DELTAcatanh CHOOSE(BUILD_COMPLEX (66, 447), BUILD_COMPLEX (2, 0), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (66, 447), BUILD_COMPLEX (2, 0), BUILD_COMPLEX (1, 0)) /* catanh */ +#define DELTAcbrt CHOOSE(716, 1, 0, 716, 1, 0) /* cbrt */ +#define DELTAccos CHOOSE(BUILD_COMPLEX (5, 1901), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (5, 1901), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1)) /* ccos */ +#define DELTAccosh CHOOSE(BUILD_COMPLEX (1467, 1183), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1467, 1183), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1)) /* ccosh */ +#define DELTAcexp CHOOSE(BUILD_COMPLEX (940, 1067), 0, BUILD_COMPLEX (1, 0), BUILD_COMPLEX (940, 1067), 0, BUILD_COMPLEX (1, 0)) /* cexp */ +#define DELTAclog CHOOSE(BUILD_COMPLEX (0, 1), 0, 0, BUILD_COMPLEX (0, 1), 0, 0) /* clog */ +#define DELTAclog10 CHOOSE(BUILD_COMPLEX (1403, 186), BUILD_COMPLEX (2, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1403, 186), BUILD_COMPLEX (2, 1), BUILD_COMPLEX (1, 1)) /* clog10 */ +#define DELTAcos CHOOSE(529, 2, 1, 529, 2, 1) /* cos */ +#define DELTAcosh CHOOSE(309, 0, 0, 309, 0, 0) /* cosh */ +#define DELTAcpow CHOOSE(BUILD_COMPLEX (2, 9), BUILD_COMPLEX (1, 1.104), BUILD_COMPLEX (4, 2.5333), BUILD_COMPLEX (2, 9), BUILD_COMPLEX (1, 1.104), BUILD_COMPLEX (4, 2.5333)) /* cpow */ +#define DELTAcsin CHOOSE(BUILD_COMPLEX (966, 168), 0, 0, BUILD_COMPLEX (966, 168), 0, 0) /* csin */ +#define DELTAcsinh CHOOSE(BUILD_COMPLEX (413, 477), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (413, 477), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1)) /* csinh */ +#define DELTAcsqrt CHOOSE(BUILD_COMPLEX (237, 128), BUILD_COMPLEX (1, 0), 0, BUILD_COMPLEX (237, 128), BUILD_COMPLEX (1, 0), 0) /* csqrt */ +#define DELTActan CHOOSE(BUILD_COMPLEX (690, 367), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (690, 367), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 1)) /* ctan */ +#define DELTActanh CHOOSE(BUILD_COMPLEX (286, 3074), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (286, 3074), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (1, 1)) /* ctanh */ +#define DELTAerfc CHOOSE(36, 24, 12, 36, 24, 12) /* erfc */ +#define DELTAexp CHOOSE(754, 0, 0, 754, 0, 0) /* exp */ +#define DELTAexp10 CHOOSE(1182, 1, 0, 1182, 1, 0) /* exp10 */ +#define DELTAexp2 CHOOSE(462, 0, 0, 462, 0, 0) /* exp2 */ +#define DELTAexpm1 CHOOSE(825, 1, 1, 825, 0, 0) /* expm1 */ +#define DELTAfmod CHOOSE(4096, 2, 1, 4096, 2, 1) /* fmod */ +#define DELTAgamma CHOOSE(1, 1, 0, 1, 1, 0) /* gamma */ +#define DELTAhypot CHOOSE(560, 1, 1, 560, 0, 0) /* hypot */ +#define DELTAj0 CHOOSE(0, 2, 2, 0, 2, 1) /* j0 */ +#define DELTAj1 CHOOSE(2, 2, 2, 2, 2, 1) /* j1 */ +#define DELTAjn CHOOSE(2, 6, 4, 2, 5, 2) /* jn */ +#define DELTAlgamma CHOOSE(1, 1, 2, 1, 1, 2) /* lgamma */ +#define DELTAlog CHOOSE(2341, 1, 1, 2341, 1, 1) /* log */ +#define DELTAlog10 CHOOSE(2033, 1, 1, 2033, 1, 1) /* log10 */ +#define DELTAlog1p CHOOSE(585, 1, 1, 585, 1, 1) /* log1p */ +#define DELTAlog2 CHOOSE(1688, 1, 1, 1688, 1, 1) /* log2 */ +#define DELTApow CHOOSE(725, 0, 0, 725, 0, 0) /* pow */ +#define DELTAsin CHOOSE(627, 0, 0, 627, 0, 0) /* sin */ +#define DELTAsincos CHOOSE(627, 1, 1, 627, 1, 1) /* sincos */ +#define DELTAsinh CHOOSE(1029, 1, 1, 1028, 0, 1) /* sinh */ +#define DELTAsqrt CHOOSE(489, 0, 0, 489, 0, 0) /* sqrt */ +#define DELTAtan CHOOSE(1401, 1, 1, 1401, 0.5, 0) /* tan */ +#define DELTAtanh CHOOSE(521, 1, 1, 521, 0, 0) /* tanh */ +#define DELTAtgamma CHOOSE(2, 2, 1, 2, 2, 1) /* tgamma */ +#define DELTAy0 CHOOSE(2, 3, 1, 2, 3, 1) /* y0 */ +#define DELTAy1 CHOOSE(2, 3, 2, 2, 3, 2) /* y1 */ +#define DELTAyn CHOOSE(7, 6, 3, 7, 6, 3) /* yn */ + +/* Error of single function calls. */ +#define DELTA16 CHOOSE(1, 0, 0, 1, 0, 0) /* acosh (7) == 2.633915793849633417250092694615937 */ +#define DELTA24 CHOOSE(1, 1, 0, 1, 0, 0) /* asin (0.5) == pi/6 */ +#define DELTA25 CHOOSE(1, 1, 0, 1, 0, 0) /* asin (-0.5) == -pi/6 */ +#define DELTA26 CHOOSE(1, 0, 0, 1, 0, 0) /* asin (1.0) == pi/2 */ +#define DELTA27 CHOOSE(1, 0, 0, 1, 0, 0) /* asin (-1.0) == -pi/2 */ +#define DELTA28 CHOOSE(1, 1, 0, 1, 0, 0) /* asin (0.7) == 0.77539749661075306374035335271498708 */ +#define DELTA34 CHOOSE(656, 1, 0, 656, 0, 0) /* asinh (0.7) == 0.652666566082355786 */ +#define DELTA42 CHOOSE(549, 0, 0, 549, 0, 0) /* atan (0.7) == 0.61072596438920861654375887649023613 */ +#define DELTA50 CHOOSE(1605, 1, 0, 1605, 1, 0) /* atanh (0.7) == 0.8673005276940531944 */ +#define DELTA74 CHOOSE(549, 0, 0, 549, 0, 0) /* atan2 (0.7, 1) == 0.61072596438920861654375887649023613 */ +#define DELTA78 CHOOSE(1, 0, 0, 1, 0, 0) /* atan2 (0.4, 0.0003) == 1.5700463269355215717704032607580829 */ +#define DELTA85 CHOOSE(0, 0, 1, 0, 0, 1) /* cabs (0.7 + 12.4 i) == 12.419742348374220601176836866763271 */ +#define DELTA86 CHOOSE(0, 0, 1, 0, 0, 1) /* cabs (-12.4 + 0.7 i) == 12.419742348374220601176836866763271 */ +#define DELTA87 CHOOSE(0, 0, 1, 0, 0, 1) /* cabs (-0.7 + 12.4 i) == 12.419742348374220601176836866763271 */ +#define DELTA88 CHOOSE(0, 0, 1, 0, 0, 1) /* cabs (-12.4 - 0.7 i) == 12.419742348374220601176836866763271 */ +#define DELTA89 CHOOSE(0, 0, 1, 0, 0, 1) /* cabs (-0.7 - 12.4 i) == 12.419742348374220601176836866763271 */ +#define DELTA96 CHOOSE(560, 1, 0, 560, 1, 0) /* cabs (0.7 + 1.2 i) == 1.3892443989449804508432547041028554 */ +#define DELTA130 CHOOSE(BUILD_COMPLEX (151, 329), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 2), BUILD_COMPLEX (151, 329), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 2)) /* cacos (0.7 + 1.2 i) == 1.1351827477151551088992008271819053 - 1.0927647857577371459105272080819308 i */ +#define DELTA131 CHOOSE(BUILD_COMPLEX (0, 1), 0, 0, BUILD_COMPLEX (0, 1), 0, 0) /* cacos (-2 - 3 i) == 2.1414491111159960199416055713254211 + 1.9833870299165354323470769028940395 i */ +#define DELTA165 CHOOSE(BUILD_COMPLEX (328, 151), BUILD_COMPLEX (1, 0), 0, BUILD_COMPLEX (328, 151), BUILD_COMPLEX (1, 0), 0) /* cacosh (0.7 + 1.2 i) == 1.0927647857577371459105272080819308 + 1.1351827477151551088992008271819053 i */ +#define DELTA166 CHOOSE(BUILD_COMPLEX (6, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (4, 4), BUILD_COMPLEX (6, 1), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (4, 4)) /* cacosh (-2 - 3 i) == -1.9833870299165354323470769028940395 + 2.1414491111159960199416055713254211 i */ +#define DELTA225 CHOOSE(BUILD_COMPLEX (603, 329), BUILD_COMPLEX (3, 0), BUILD_COMPLEX (2, 2), BUILD_COMPLEX (603, 329), BUILD_COMPLEX (3, 0), BUILD_COMPLEX (2, 2)) /* casin (0.7 + 1.2 i) == 0.4356135790797415103321208644578462 + 1.0927647857577371459105272080819308 i */ +#define DELTA226 CHOOSE(BUILD_COMPLEX (0, 1), 0, 0, BUILD_COMPLEX (0, 1), 0, 0) /* casin (-2 - 3 i) == -0.57065278432109940071028387968566963 - 1.9833870299165354323470769028940395 i */ +#define DELTA262 CHOOSE(BUILD_COMPLEX (892, 12), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (892, 12), 0, BUILD_COMPLEX (0, 1)) /* casinh (0.7 + 1.2 i) == 0.97865459559367387689317593222160964 + 0.91135418953156011567903546856170941 i */ +#define DELTA263 CHOOSE(BUILD_COMPLEX (6, 6), BUILD_COMPLEX (5, 3), BUILD_COMPLEX (1, 6), BUILD_COMPLEX (6, 6), BUILD_COMPLEX (5, 3), BUILD_COMPLEX (1, 6)) /* casinh (-2 - 3 i) == -1.9686379257930962917886650952454982 - 0.96465850440760279204541105949953237 i */ +#define DELTA301 CHOOSE(BUILD_COMPLEX (251, 474), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (251, 474), 0, BUILD_COMPLEX (0, 1)) /* catan (0.7 + 1.2 i) == 1.0785743834118921877443707996386368 + 0.57705737765343067644394541889341712 i */ +#define DELTA302 CHOOSE(BUILD_COMPLEX (0, 7), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 7), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* catan (-2 - 3 i) == -1.4099210495965755225306193844604208 - 0.22907268296853876629588180294200276 i */ +#define DELTA340 CHOOSE(BUILD_COMPLEX (66, 447), BUILD_COMPLEX (1, 0), 0, BUILD_COMPLEX (66, 447), BUILD_COMPLEX (1, 0), 0) /* catanh (0.7 + 1.2 i) == 0.2600749516525135959200648705635915 + 0.97024030779509898497385130162655963 i */ +#define DELTA341 CHOOSE(BUILD_COMPLEX (6, 0), BUILD_COMPLEX (2, 0), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (6, 0), BUILD_COMPLEX (2, 0), BUILD_COMPLEX (1, 0)) /* catanh (-2 - 3 i) == -0.14694666622552975204743278515471595 - 1.3389725222944935611241935759091443 i */ +#define DELTA347 CHOOSE(716, 0, 0, 716, 0, 0) /* cbrt (-0.001) == -0.1 */ +#define DELTA349 CHOOSE(1, 0, 0, 1, 0, 0) /* cbrt (-27.0) == -3.0 */ +#define DELTA350 CHOOSE(306, 0, 0, 306, 0, 0) /* cbrt (0.970299) == 0.99 */ +#define DELTA351 CHOOSE(346, 1, 0, 346, 1, 0) /* cbrt (0.7) == 0.8879040017426007084 */ +#define DELTA389 CHOOSE(BUILD_COMPLEX (5, 1901), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (5, 1901), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 0)) /* ccos (0.7 + 1.2 i) == 1.3848657645312111080 - 0.97242170335830028619 i */ +#define DELTA390 CHOOSE(BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1)) /* ccos (-2 - 3 i) == -4.1896256909688072301 - 9.1092278937553365979 i */ +#define DELTA428 CHOOSE(BUILD_COMPLEX (1467, 1183), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1467, 1183), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 0)) /* ccosh (0.7 + 1.2 i) == 0.4548202223691477654 + 0.7070296600921537682 i */ +#define DELTA429 CHOOSE(BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* ccosh (-2 - 3 i) == -3.7245455049153225654 + 0.5118225699873846088 i */ +#define DELTA469 CHOOSE(BUILD_COMPLEX (940, 0), 0, BUILD_COMPLEX (1, 0), BUILD_COMPLEX (940, 0), 0, BUILD_COMPLEX (1, 0)) /* cexp (0.7 + 1.2 i) == 0.72969890915032360123451688642930727 + 1.8768962328348102821139467908203072 i */ +#define DELTA470 CHOOSE(BUILD_COMPLEX (4, 18), 0, 0, BUILD_COMPLEX (4, 18), 0, 0) /* cexp (-2.0 - 3.0 i) == -0.13398091492954261346140525546115575 - 0.019098516261135196432576240858800925 i */ +#define DELTA515 CHOOSE(BUILD_COMPLEX (0, 1), 0, 0, BUILD_COMPLEX (0, 1), 0, 0) /* clog (-2 - 3 i) == 1.2824746787307683680267437207826593 - 2.1587989303424641704769327722648368 i */ +#define DELTA520 CHOOSE(0, BUILD_COMPLEX (0, 1), 0, 0, BUILD_COMPLEX (0, 1), 0) /* clog10 (-inf + inf i) == inf + 3/4 pi*log10(e) i */ +#define DELTA521 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (inf + inf i) == inf + pi/4*log10(e) i */ +#define DELTA522 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (inf - inf i) == inf - pi/4*log10(e) i */ +#define DELTA523 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (0 + inf i) == inf + pi/2*log10(e) i */ +#define DELTA524 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (3 + inf i) == inf + pi/2*log10(e) i */ +#define DELTA525 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-0 + inf i) == inf + pi/2*log10(e) i */ +#define DELTA526 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-3 + inf i) == inf + pi/2*log10(e) i */ +#define DELTA527 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (0 - inf i) == inf - pi/2*log10(e) i */ +#define DELTA528 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (3 - inf i) == inf - pi/2*log10(e) i */ +#define DELTA529 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-0 - inf i) == inf - pi/2*log10(e) i */ +#define DELTA530 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-3 - inf i) == inf - pi/2*log10(e) i */ +#define DELTA531 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-inf + 0 i) == inf + pi*log10(e) i */ +#define DELTA532 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-inf + 1 i) == inf + pi*log10(e) i */ +#define DELTA533 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-inf - 0 i) == inf - pi*log10(e) i */ +#define DELTA534 CHOOSE(0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1)) /* clog10 (-inf - 1 i) == inf - pi*log10(e) i */ +#define DELTA552 CHOOSE(BUILD_COMPLEX (1403, 186), BUILD_COMPLEX (2, 1), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1403, 186), BUILD_COMPLEX (2, 1), BUILD_COMPLEX (1, 0)) /* clog10 (0.7 + 1.2 i) == 0.1427786545038868803 + 0.4528483579352493248 i */ +#define DELTA553 CHOOSE(BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 1), 0) /* clog10 (-2 - 3 i) == 0.5569716761534183846 - 0.9375544629863747085 i */ +#define DELTA582 CHOOSE(0, 1, 0.5, 0, 1, 0.5) /* cos (M_PI_6l * 2.0) == 0.5 */ +#define DELTA583 CHOOSE(0.5, 2, 1, 0.5, 2, 1) /* cos (M_PI_6l * 4.0) == -0.5 */ +#define DELTA584 CHOOSE(0.25, 0.2758, 0.3667, 0.25, 0.2758, 0.3667) /* cos (pi/2) == 0 */ +#define DELTA585 CHOOSE(529, 1, 0, 529, 1, 0) /* cos (0.7) == 0.76484218728448842625585999019186495 */ +#define DELTA591 CHOOSE(309, 0, 0, 309, 0, 0) /* cosh (0.7) == 1.255169005630943018 */ +#define DELTA594 CHOOSE(BUILD_COMPLEX (0, 9), BUILD_COMPLEX (0, 1.104), BUILD_COMPLEX (0, 2.5333), BUILD_COMPLEX (0, 9), BUILD_COMPLEX (0, 1.104), BUILD_COMPLEX (0, 2.5333)) /* cpow (e + 0 i, 0 + 2 * M_PIl i) == 1.0 + 0.0 i */ +#define DELTA595 CHOOSE(BUILD_COMPLEX (2, 5), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (4, 1), BUILD_COMPLEX (2, 5), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (4, 1)) /* cpow (2 + 3 i, 4 + 0 i) == -119.0 - 120.0 i */ +#define DELTA652 CHOOSE(BUILD_COMPLEX (966, 168), 0, 0, BUILD_COMPLEX (966, 168), 0, 0) /* csin (0.7 + 1.2 i) == 1.1664563419657581376 + 1.1544997246948547371 i */ +#define DELTA691 CHOOSE(BUILD_COMPLEX (413, 477), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (413, 477), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (1, 0)) /* csinh (0.7 + 1.2 i) == 0.27487868678117583582 + 1.1698665727426565139 i */ +#define DELTA692 CHOOSE(BUILD_COMPLEX (0, 2), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (0, 2), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (0, 1)) /* csinh (-2 - 3 i) == 3.5905645899857799520 - 0.5309210862485198052 i */ +#define DELTA732 CHOOSE(BUILD_COMPLEX (237, 128), BUILD_COMPLEX (1, 0), 0, BUILD_COMPLEX (237, 128), BUILD_COMPLEX (1, 0), 0) /* csqrt (0.7 + 1.2 i) == 1.022067610030026450706487883081139 + 0.58704531296356521154977678719838035 i */ +#define DELTA733 CHOOSE(BUILD_COMPLEX (1, 0), 0, 0, BUILD_COMPLEX (1, 0), 0, 0) /* csqrt (-2 - 3 i) == 0.89597747612983812471573375529004348 - 1.6741492280355400404480393008490519 i */ +#define DELTA734 CHOOSE(BUILD_COMPLEX (1, 0), 0, 0, BUILD_COMPLEX (1, 0), 0, 0) /* csqrt (-2 + 3 i) == 0.89597747612983812471573375529004348 + 1.6741492280355400404480393008490519 i */ +#define DELTA766 CHOOSE(BUILD_COMPLEX (690, 367), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (690, 367), BUILD_COMPLEX (1, 1), BUILD_COMPLEX (1, 0)) /* ctan (0.7 + 1.2 i) == 0.1720734197630349001 + 0.9544807059989405538 i */ +#define DELTA767 CHOOSE(BUILD_COMPLEX (439, 2), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (439, 2), 0, BUILD_COMPLEX (0, 1)) /* ctan (-2 - 3 i) == 0.0037640256415042482 - 1.0032386273536098014 i */ +#define DELTA799 CHOOSE(0, BUILD_COMPLEX (0, 0.5), BUILD_COMPLEX (0, 1), 0, BUILD_COMPLEX (0, 0.5), BUILD_COMPLEX (0, 1)) /* ctanh (0 + pi/4 i) == 0.0 + 1.0 i */ +#define DELTA800 CHOOSE(BUILD_COMPLEX (286, 3074), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (1, 0), BUILD_COMPLEX (286, 3074), BUILD_COMPLEX (0, 1), BUILD_COMPLEX (1, 0)) /* ctanh (0.7 + 1.2 i) == 1.3472197399061191630 + 0.4778641038326365540 i */ +#define DELTA801 CHOOSE(BUILD_COMPLEX (5, 25), 0, BUILD_COMPLEX (0, 1), BUILD_COMPLEX (5, 25), 0, BUILD_COMPLEX (0, 1)) /* ctanh (-2 - 3 i) == -0.9653858790221331242 + 0.0098843750383224937 i */ +#define DELTA817 CHOOSE(1, 1, 0, 1, 1, 0) /* erfc (0.7) == 0.32219880616258152702 */ +#define DELTA818 CHOOSE(3, 1, 2, 3, 1, 2) /* erfc (1.2) == 0.089686021770364619762 */ +#define DELTA819 CHOOSE(0, 1, 1, 0, 1, 0) /* erfc (2.0) == 0.0046777349810472658379 */ +#define DELTA820 CHOOSE(12, 24, 12, 12, 24, 12) /* erfc (4.1) == 0.67000276540848983727e-8 */ +#define DELTA821 CHOOSE(36, 0, 0, 36, 0, 0) /* erfc (9) == 0.41370317465138102381e-36 */ +#define DELTA830 CHOOSE(412, 0, 0, 412, 0, 0) /* exp (0.7) == 2.0137527074704765216 */ +#define DELTA831 CHOOSE(16, 0, 0, 16, 0, 0) /* exp (50.0) == 5184705528587072464087.45332293348538 */ +#define DELTA832 CHOOSE(754, 0, 0, 754, 0, 0) /* exp (1000.0) == 0.197007111401704699388887935224332313e435 */ +#define DELTA838 CHOOSE(8, 0, 0, 8, 0, 0) /* exp10 (3) == 1000 */ +#define DELTA839 CHOOSE(818, 0, 0, 818, 0, 0) /* exp10 (-1) == 0.1 */ +#define DELTA842 CHOOSE(1182, 1, 0, 1182, 1, 0) /* exp10 (0.7) == 5.0118723362727228500155418688494574 */ +#define DELTA852 CHOOSE(462, 0, 0, 462, 0, 0) /* exp2 (0.7) == 1.6245047927124710452 */ +#define DELTA859 CHOOSE(825, 0, 0, 825, 0, 0) /* expm1 (0.7) == 1.0137527074704765216 */ +#define DELTA972 CHOOSE(4096, 2, 1, 4096, 2, 1) /* fmod (6.5, 2.3) == 1.9 */ +#define DELTA973 CHOOSE(4096, 2, 1, 4096, 2, 1) /* fmod (-6.5, 2.3) == -1.9 */ +#define DELTA974 CHOOSE(4096, 2, 1, 4096, 2, 1) /* fmod (6.5, -2.3) == 1.9 */ +#define DELTA975 CHOOSE(4096, 2, 1, 4096, 2, 1) /* fmod (-6.5, -2.3) == -1.9 */ +#define DELTA1004 CHOOSE(1, 1, 0, 1, 1, 0) /* gamma (-0.5) == log(2*sqrt(pi)) */ +#define DELTA1013 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (0.7, 12.4) == 12.419742348374220601176836866763271 */ +#define DELTA1014 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (-0.7, 12.4) == 12.419742348374220601176836866763271 */ +#define DELTA1015 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (0.7, -12.4) == 12.419742348374220601176836866763271 */ +#define DELTA1016 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (-0.7, -12.4) == 12.419742348374220601176836866763271 */ +#define DELTA1017 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (12.4, 0.7) == 12.419742348374220601176836866763271 */ +#define DELTA1018 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (-12.4, 0.7) == 12.419742348374220601176836866763271 */ +#define DELTA1019 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (12.4, -0.7) == 12.419742348374220601176836866763271 */ +#define DELTA1020 CHOOSE(406, 0, 1, 406, 0, 0) /* hypot (-12.4, -0.7) == 12.419742348374220601176836866763271 */ +#define DELTA1024 CHOOSE(560, 1, 0, 560, 0, 0) /* hypot (0.7, 1.2) == 1.3892443989449804508432547041028554 */ +#define DELTA1053 CHOOSE(0, 2, 2, 0, 1, 1) /* j0 (2.0) == 0.22389077914123566805 */ +#define DELTA1054 CHOOSE(0, 0, 1, 0, 0, 1) /* j0 (8.0) == 0.17165080713755390609 */ +#define DELTA1055 CHOOSE(0, 2, 2, 0, 2, 1) /* j0 (10.0) == -0.24593576445134833520 */ +#define DELTA1064 CHOOSE(0, 1, 0, 0, 1, 0) /* j1 (2.0) == 0.57672480775687338720 */ +#define DELTA1065 CHOOSE(1, 1, 1, 1, 0, 1) /* j1 (8.0) == 0.23463634685391462438 */ +#define DELTA1066 CHOOSE(2, 2, 2, 2, 2, 1) /* j1 (10.0) == 0.043472746168861436670 */ +#define DELTA1075 CHOOSE(0, 2, 2, 0, 1, 1) /* jn (0, 2.0) == 0.22389077914123566805 */ +#define DELTA1076 CHOOSE(1, 0, 1, 1, 0, 1) /* jn (0, 8.0) == 0.17165080713755390609 */ +#define DELTA1077 CHOOSE(2, 2, 1, 2, 2, 1) /* jn (0, 10.0) == -0.24593576445134833520 */ +#define DELTA1086 CHOOSE(0, 1, 0, 0, 1, 0) /* jn (1, 2.0) == 0.57672480775687338720 */ +#define DELTA1087 CHOOSE(1, 1, 1, 1, 0, 1) /* jn (1, 8.0) == 0.23463634685391462438 */ +#define DELTA1088 CHOOSE(2, 2, 2, 2, 2, 1) /* jn (1, 10.0) == 0.043472746168861436670 */ +#define DELTA1091 CHOOSE(1, 0, 0, 1, 0, 0) /* jn (3, -1.0) == -0.019563353982668405919 */ +#define DELTA1093 CHOOSE(1, 1, 0, 1, 1, 0) /* jn (3, 0.1) == 0.000020820315754756261429 */ +#define DELTA1094 CHOOSE(0, 2, 1, 0, 2, 0) /* jn (3, 0.7) == 0.0069296548267508408077 */ +#define DELTA1095 CHOOSE(1, 0, 0, 1, 0, 0) /* jn (3, 1.0) == 0.019563353982668405919 */ +#define DELTA1096 CHOOSE(0, 1, 1, 0, 1, 1) /* jn (3, 2.0) == 0.12894324947440205110 */ +#define DELTA1097 CHOOSE(1, 3, 1, 1, 3, 1) /* jn (3, 10.0) == 0.058379379305186812343 */ +#define DELTA1100 CHOOSE(1, 1, 1, 1, 1, 1) /* jn (10, -1.0) == 0.26306151236874532070e-9 */ +#define DELTA1102 CHOOSE(1, 6, 4, 1, 5, 2) /* jn (10, 0.1) == 0.26905328954342155795e-19 */ +#define DELTA1103 CHOOSE(2, 4, 1, 2, 4, 1) /* jn (10, 0.7) == 0.75175911502153953928e-11 */ +#define DELTA1104 CHOOSE(1, 1, 1, 1, 1, 1) /* jn (10, 1.0) == 0.26306151236874532070e-9 */ +#define DELTA1105 CHOOSE(1, 2, 2, 1, 2, 1) /* jn (10, 2.0) == 0.25153862827167367096e-6 */ +#define DELTA1106 CHOOSE(2, 4, 3, 2, 4, 2) /* jn (10, 10.0) == 0.20748610663335885770 */ +#define DELTA1126 CHOOSE(1, 1, 0, 1, 1, 0) /* lgamma (-0.5) == log(2*sqrt(pi)) */ +#define DELTA1128 CHOOSE(0, 1, 1, 0, 1, 1) /* lgamma (0.7) == 0.26086724653166651439 */ +#define DELTA1130 CHOOSE(1, 1, 2, 1, 1, 2) /* lgamma (1.2) == -0.853740900033158497197e-1 */ +#define DELTA1163 CHOOSE(1, 0, 0.5, 1, 0, 0.5) /* log (e) == 1 */ +#define DELTA1164 CHOOSE(1, 0, 0, 1, 0, 0) /* log (1.0 / M_El) == -1 */ +#define DELTA1167 CHOOSE(2341, 1, 1, 2341, 1, 1) /* log (0.7) == -0.35667494393873237891263871124118447 */ +#define DELTA1178 CHOOSE(1, 0, 1, 1, 0, 1) /* log10 (e) == log10(e) */ +#define DELTA1179 CHOOSE(2033, 1, 0, 2033, 1, 0) /* log10 (0.7) == -0.15490195998574316929 */ +#define DELTA1186 CHOOSE(1, 0, 0, 1, 0, 0) /* log1p (M_El - 1.0) == 1 */ +#define DELTA1187 CHOOSE(585, 1, 1, 585, 1, 1) /* log1p (-0.3) == -0.35667494393873237891263871124118447 */ +#define DELTA1198 CHOOSE(1688, 1, 1, 1688, 1, 1) /* log2 (0.7) == -0.51457317282975824043 */ +#define DELTA1398 CHOOSE(725, 0, 0, 725, 0, 0) /* pow (0.7, 1.2) == 0.65180494056638638188 */ +#define DELTA1524 CHOOSE(627, 0, 0, 627, 0, 0) /* sin (0.7) == 0.64421768723769105367261435139872014 */ +#define DELTA1536 CHOOSE(0.25, 0.2758, 0.3667, 0.25, 0.2758, 0.3667) /* sincos (pi/2, &sin_res, &cos_res) puts 0 in cos_res */ +#define DELTA1539 CHOOSE(1, 1, 1, 1, 1, 1) /* sincos (M_PI_6l*2.0, &sin_res, &cos_res) puts 0.86602540378443864676372317075293616 in sin_res */ +#define DELTA1540 CHOOSE(0, 1, 0.5, 0, 1, 0.5) /* sincos (M_PI_6l*2.0, &sin_res, &cos_res) puts 0.5 in cos_res */ +#define DELTA1541 CHOOSE(627, 0, 0, 627, 0, 0) /* sincos (0.7, &sin_res, &cos_res) puts 0.64421768723769105367261435139872014 in sin_res */ +#define DELTA1542 CHOOSE(528, 1, 0, 528, 1, 0) /* sincos (0.7, &sin_res, &cos_res) puts 0.76484218728448842625585999019186495 in cos_res */ +#define DELTA1548 CHOOSE(1029, 1, 1, 1028, 0, 1) /* sinh (0.7) == 0.75858370183953350346 */ +#define DELTA1562 CHOOSE(325, 0, 0, 325, 0, 0) /* sqrt (15239.9025) == 123.45 */ +#define DELTA1569 CHOOSE(0, 0.5, 0, 0, 0.5, 0) /* tan (pi/4) == 1 */ +#define DELTA1570 CHOOSE(1401, 1, 1, 1401, 0, 0) /* tan (0.7) == 0.84228838046307944812813500221293775 */ +#define DELTA1576 CHOOSE(521, 1, 1, 521, 0, 0) /* tanh (0.7) == 0.60436777711716349631 */ +#define DELTA1577 CHOOSE(1, 1, 1, 1, 0, 0) /* tanh (-0.7) == -0.60436777711716349631 */ +#define DELTA1587 CHOOSE(0, 0, 1, 0, 0, 1) /* tgamma (0.5) == sqrt (pi) */ +#define DELTA1588 CHOOSE(2, 2, 1, 2, 2, 1) /* tgamma (-0.5) == -2 sqrt (pi) */ +#define DELTA1590 CHOOSE(2, 0, 0, 2, 0, 0) /* tgamma (4) == 6 */ +#define DELTA1591 CHOOSE(0, 1, 1, 0, 1, 1) /* tgamma (0.7) == 1.29805533264755778568 */ +#define DELTA1614 CHOOSE(0, 1, 1, 0, 1, 1) /* y0 (0.1) == -1.5342386513503668441 */ +#define DELTA1615 CHOOSE(2, 3, 1, 2, 3, 1) /* y0 (0.7) == -0.19066492933739506743 */ +#define DELTA1616 CHOOSE(0, 2, 1, 0, 2, 1) /* y0 (1.0) == 0.088256964215676957983 */ +#define DELTA1617 CHOOSE(0, 1, 1, 0, 1, 1) /* y0 (1.5) == 0.38244892379775884396 */ +#define DELTA1618 CHOOSE(0, 1, 0, 0, 1, 0) /* y0 (2.0) == 0.51037567264974511960 */ +#define DELTA1619 CHOOSE(1, 1, 1, 1, 1, 1) /* y0 (8.0) == 0.22352148938756622053 */ +#define DELTA1620 CHOOSE(1, 2, 1, 2, 2, 1) /* y0 (10.0) == 0.055671167283599391424 */ +#define DELTA1625 CHOOSE(1, 1, 1, 1, 1, 1) /* y1 (0.1) == -6.4589510947020269877 */ +#define DELTA1626 CHOOSE(0, 1, 1, 0, 1, 0) /* y1 (0.7) == -1.1032498719076333697 */ +#define DELTA1627 CHOOSE(0, 1, 0, 0, 1, 0) /* y1 (1.0) == -0.78121282130028871655 */ +#define DELTA1628 CHOOSE(0, 0, 1, 0, 0, 1) /* y1 (1.5) == -0.41230862697391129595 */ +#define DELTA1629 CHOOSE(1, 1, 2, 1, 1, 2) /* y1 (2.0) == -0.10703243154093754689 */ +#define DELTA1630 CHOOSE(2, 1, 2, 2, 0, 2) /* y1 (8.0) == -0.15806046173124749426 */ +#define DELTA1631 CHOOSE(0, 3, 2, 0, 3, 2) /* y1 (10.0) == 0.24901542420695388392 */ +#define DELTA1636 CHOOSE(0, 1, 1, 0, 1, 1) /* yn (0, 0.1) == -1.5342386513503668441 */ +#define DELTA1637 CHOOSE(2, 3, 1, 2, 3, 1) /* yn (0, 0.7) == -0.19066492933739506743 */ +#define DELTA1638 CHOOSE(0, 2, 1, 0, 2, 1) /* yn (0, 1.0) == 0.088256964215676957983 */ +#define DELTA1639 CHOOSE(0, 1, 1, 0, 1, 1) /* yn (0, 1.5) == 0.38244892379775884396 */ +#define DELTA1640 CHOOSE(0, 1, 0, 0, 1, 0) /* yn (0, 2.0) == 0.51037567264974511960 */ +#define DELTA1641 CHOOSE(1, 1, 1, 1, 1, 1) /* yn (0, 8.0) == 0.22352148938756622053 */ +#define DELTA1642 CHOOSE(1, 2, 1, 1, 2, 1) /* yn (0, 10.0) == 0.055671167283599391424 */ +#define DELTA1647 CHOOSE(1, 1, 1, 1, 1, 1) /* yn (1, 0.1) == -6.4589510947020269877 */ +#define DELTA1648 CHOOSE(0, 1, 1, 0, 1, 0) /* yn (1, 0.7) == -1.1032498719076333697 */ +#define DELTA1649 CHOOSE(0, 1, 0, 0, 1, 0) /* yn (1, 1.0) == -0.78121282130028871655 */ +#define DELTA1650 CHOOSE(0, 0, 1, 0, 0, 1) /* yn (1, 1.5) == -0.41230862697391129595 */ +#define DELTA1651 CHOOSE(1, 1, 2, 1, 1, 2) /* yn (1, 2.0) == -0.10703243154093754689 */ +#define DELTA1652 CHOOSE(2, 1, 2, 2, 0, 2) /* yn (1, 8.0) == -0.15806046173124749426 */ +#define DELTA1653 CHOOSE(0, 3, 2, 0, 3, 2) /* yn (1, 10.0) == 0.24901542420695388392 */ +#define DELTA1656 CHOOSE(2, 1, 2, 2, 1, 1) /* yn (3, 0.1) == -5099.3323786129048894 */ +#define DELTA1657 CHOOSE(2, 3, 1, 2, 3, 1) /* yn (3, 0.7) == -15.819479052819633505 */ +#define DELTA1659 CHOOSE(0, 1, 1, 0, 1, 1) /* yn (3, 2.0) == -1.1277837768404277861 */ +#define DELTA1660 CHOOSE(0, 1, 1, 0, 1, 1) /* yn (3, 10.0) == -0.25136265718383732978 */ +#define DELTA1663 CHOOSE(2, 2, 2, 2, 2, 1) /* yn (10, 0.1) == -0.11831335132045197885e19 */ +#define DELTA1664 CHOOSE(7, 6, 3, 7, 6, 3) /* yn (10, 0.7) == -0.42447194260703866924e10 */ +#define DELTA1665 CHOOSE(0, 1, 2, 0, 1, 1) /* yn (10, 1.0) == -0.12161801427868918929e9 */ +#define DELTA1666 CHOOSE(1, 3, 3, 1, 2, 1) /* yn (10, 2.0) == -129184.54220803928264 */ +#define DELTA1667 CHOOSE(0, 2, 1, 0, 2, 1) /* yn (10, 10.0) == -0.35981415218340272205 */ diff --git a/openlibm/test/libm-test.c b/openlibm/test/libm-test.c new file mode 100644 index 0000000000..0cef0250ac --- /dev/null +++ b/openlibm/test/libm-test.c @@ -0,0 +1,4537 @@ +/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger , 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Part of testsuite for libm. + + This file is processed by a perl script. The resulting file has to + be included by a master file that defines: + + Makros: + FUNC(function): converts general function name (like cos) to + name with correct suffix (e.g. cosl or cosf) + MATHCONST(x): like FUNC but for constants (e.g convert 0.0 to 0.0L) + FLOAT: floating point type to test + - TEST_MSG: informal message to be displayed + CHOOSE(Clongdouble,Cdouble,Cfloat,Cinlinelongdouble,Cinlinedouble,Cinlinefloat): + chooses one of the parameters as delta for testing + equality + PRINTF_EXPR Floating point conversion specification to print a variable + of type FLOAT with printf. PRINTF_EXPR just contains + the specifier, not the percent and width arguments, + e.g. "f". + PRINTF_XEXPR Like PRINTF_EXPR, but print in hexadecimal format. + PRINTF_NEXPR Like PRINTF_EXPR, but print nice. */ + +/* This testsuite has currently tests for: + acos, acosh, asin, asinh, atan, atan2, atanh, + cbrt, ceil, copysign, cos, cosh, erf, erfc, exp, exp10, exp2, expm1, + fabs, fdim, floor, fma, fmax, fmin, fmod, fpclassify, + frexp, gamma, hypot, + ilogb, isfinite, isinf, isnan, isnormal, + isless, islessequal, isgreater, isgreaterequal, islessgreater, isunordered, + j0, j1, jn, + ldexp, lgamma, log, log10, log1p, log2, logb, + modf, nearbyint, nextafter, + pow, remainder, remquo, rint, lrint, llrint, + round, lround, llround, + scalb, scalbn, scalbln, signbit, sin, sincos, sinh, sqrt, tan, tanh, tgamma, trunc, + y0, y1, yn + + and for the following complex math functions: + cabs, cacos, cacosh, carg, casin, casinh, catan, catanh, + ccos, ccosh, cexp, clog, cpow, cproj, csin, csinh, csqrt, ctan, ctanh. + + At the moment the following functions aren't tested: + drem, significand, nan + + Parameter handling is primitive in the moment: + --verbose=[0..3] for different levels of output: + 0: only error count + 1: basic report on failed tests (default) + 2: full report on all tests + -v for full output (equals --verbose=3) + -u for generation of an ULPs file + */ + +/* "Philosophy": + + This suite tests some aspects of the correct implementation of + mathematical functions in libm. Some simple, specific parameters + are tested for correctness but there's no exhaustive + testing. Handling of specific inputs (e.g. infinity, not-a-number) + is also tested. Correct handling of exceptions is checked + against. These implemented tests should check all cases that are + specified in ISO C99. + + Exception testing: At the moment only divide-by-zero and invalid + exceptions are tested. Overflow/underflow and inexact exceptions + aren't checked at the moment. + + NaN values: There exist signalling and quiet NaNs. This implementation + only uses signalling NaN as parameter but does not differenciate + between the two kinds of NaNs as result. + + Inline functions: Inlining functions should give an improvement in + speed - but not in precission. The inlined functions return + reasonable values for a reasonable range of input values. The + result is not necessarily correct for all values and exceptions are + not correctly raised in all cases. Problematic input and return + values are infinity, not-a-number and minus zero. This suite + therefore does not check these specific inputs and the exception + handling for inlined mathematical functions - just the "reasonable" + values are checked. + + Beware: The tests might fail for any of the following reasons: + - Tests are wrong + - Functions are wrong + - Floating Point Unit not working properly + - Compiler has errors + + With e.g. gcc 2.7.2.2 the test for cexp fails because of a compiler error. + + + To Do: All parameter should be numbers that can be represented as + exact floating point values. Currently some values cannot be represented + exactly and therefore the result is not the expected result. +*/ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include "libm-test-ulps.h" +#include +#ifdef SYS_MATH_H +#include +#include +#else +#include +#endif + +#if 0 /* XXX scp XXX */ +#define FE_INEXACT FE_INEXACT +#define FE_DIVBYZERO FE_DIVBYZERO +#define FE_UNDERFLOW FE_UNDERFLOW +#define FE_OVERFLOW FE_OVERFLOW +#define FE_INVALID FE_INVALID +#endif + +#include + +#include +#include +#include +#include +#if 0 /* XXX scp XXX */ +#include +#endif + +// Some native libm implementations don't have sincos defined, so we have to do it ourselves +void FUNC(sincos) (FLOAT x, FLOAT * s, FLOAT * c); + +#ifdef __APPLE__ +#ifdef SYS_MATH_H +void sincos(FLOAT x, FLOAT * s, FLOAT * c) +{ + *s = sin(x); + *c = cos(x); +} +#endif +#endif + +/* Possible exceptions */ +#define NO_EXCEPTION 0x0 +#define INVALID_EXCEPTION 0x1 +#define DIVIDE_BY_ZERO_EXCEPTION 0x2 +/* The next flags signals that those exceptions are allowed but not required. */ +#define INVALID_EXCEPTION_OK 0x4 +#define DIVIDE_BY_ZERO_EXCEPTION_OK 0x8 +#define EXCEPTIONS_OK INVALID_EXCEPTION_OK+DIVIDE_BY_ZERO_EXCEPTION_OK +/* Some special test flags, passed togther with exceptions. */ +#define IGNORE_ZERO_INF_SIGN 0x10 + +/* Various constants (we must supply them precalculated for accuracy). */ +#define M_PI_6l .52359877559829887307710723054658383L +#define M_E2l 7.389056098930650227230427460575008L +#define M_E3l 20.085536923187667740928529654581719L +#define M_2_SQRT_PIl 3.5449077018110320545963349666822903L /* 2 sqrt (M_PIl) */ +#define M_SQRT_PIl 1.7724538509055160272981674833411451L /* sqrt (M_PIl) */ +#define M_LOG_SQRT_PIl 0.57236494292470008707171367567652933L /* log(sqrt(M_PIl)) */ +#define M_LOG_2_SQRT_PIl 1.265512123484645396488945797134706L /* log(2*sqrt(M_PIl)) */ +#define M_PI_34l (M_PIl - M_PI_4l) /* 3*pi/4 */ +#define M_PI_34_LOG10El (M_PIl - M_PI_4l) * M_LOG10El +#define M_PI2_LOG10El M_PI_2l * M_LOG10El +#define M_PI4_LOG10El M_PI_4l * M_LOG10El +#define M_PI_LOG10El M_PIl * M_LOG10El + +#if 1 /* XXX scp XXX */ +# define M_El 2.7182818284590452353602874713526625L /* e */ +# define M_LOG2El 1.4426950408889634073599246810018922L /* log_2 e */ +# define M_LOG10El 0.4342944819032518276511289189166051L /* log_10 e */ +# define M_LN2l 0.6931471805599453094172321214581766L /* log_e 2 */ +# define M_LN10l 2.3025850929940456840179914546843642L /* log_e 10 */ +# define M_PIl 3.1415926535897932384626433832795029L /* pi */ +# define M_PI_2l 1.5707963267948966192313216916397514L /* pi/2 */ +# define M_PI_4l 0.7853981633974483096156608458198757L /* pi/4 */ +# define M_1_PIl 0.3183098861837906715377675267450287L /* 1/pi */ +# define M_2_PIl 0.6366197723675813430755350534900574L /* 2/pi */ +# define M_2_SQRTPIl 1.1283791670955125738961589031215452L /* 2/sqrt(pi) */ +# define M_SQRT2l 1.4142135623730950488016887242096981L /* sqrt(2) */ +# define M_SQRT1_2l 0.7071067811865475244008443621048490L /* 1/sqrt(2) */ +#endif + +static FILE *ulps_file; /* File to document difference. */ +static int output_ulps; /* Should ulps printed? */ + +static int noErrors; /* number of errors */ +static int noTests; /* number of tests (without testing exceptions) */ +static int noExcTests; /* number of tests for exception flags */ +static int noXFails; /* number of expected failures. */ +static int noXPasses; /* number of unexpected passes. */ + +static int verbose; +static int output_max_error; /* Should the maximal errors printed? */ +static int output_points; /* Should the single function results printed? */ +static int ignore_max_ulp; /* Should we ignore max_ulp? */ + +static FLOAT minus_zero, plus_zero; +static FLOAT plus_infty, minus_infty, nan_value; + +static FLOAT max_error, real_max_error, imag_max_error; + + +#if 0 /* XXX scp XXX */ +#define BUILD_COMPLEX(real, imag) \ + ({ __complex__ FLOAT __retval; \ + __real__ __retval = (real); \ + __imag__ __retval = (imag); \ + __retval; }) + +#define BUILD_COMPLEX_INT(real, imag) \ + ({ __complex__ int __retval; \ + __real__ __retval = (real); \ + __imag__ __retval = (imag); \ + __retval; }) +#endif + + +#define MANT_DIG CHOOSE ((LDBL_MANT_DIG-1), (DBL_MANT_DIG-1), (FLT_MANT_DIG-1), \ + (LDBL_MANT_DIG-1), (DBL_MANT_DIG-1), (FLT_MANT_DIG-1)) + + +static void +init_max_error (void) +{ + max_error = 0; + real_max_error = 0; + imag_max_error = 0; + feclearexcept (FE_ALL_EXCEPT); +} + +static void +set_max_error (FLOAT current, FLOAT *curr_max_error) +{ + if (current > *curr_max_error) + *curr_max_error = current; +} + + +/* Should the message print to screen? This depends on the verbose flag, + and the test status. */ +static int +print_screen (int ok, int xfail) +{ + if (output_points + && (verbose > 1 + || (verbose == 1 && ok == xfail))) + return 1; + return 0; +} + + +/* Should the message print to screen? This depends on the verbose flag, + and the test status. */ +static int +print_screen_max_error (int ok, int xfail) +{ + if (output_max_error + && (verbose > 1 + || ((verbose == 1) && (ok == xfail)))) + return 1; + return 0; +} + +/* Update statistic counters. */ +static void +update_stats (int ok, int xfail) +{ + ++noTests; + if (ok && xfail) + ++noXPasses; + else if (!ok && xfail) + ++noXFails; + else if (!ok && !xfail) + ++noErrors; +} + +static void +print_ulps (const char *test_name, FLOAT ulp) +{ + if (output_ulps) + { + fprintf (ulps_file, "Test \"%s\":\n", test_name); + fprintf (ulps_file, "%s: % .4" PRINTF_NEXPR "\n", + CHOOSE("ldouble", "double", "float", + "ildouble", "idouble", "ifloat"), ulp); + } +} + +static void +print_function_ulps (const char *function_name, FLOAT ulp) +{ + if (output_ulps) + { + fprintf (ulps_file, "Function: \"%s\":\n", function_name); + fprintf (ulps_file, "%s: % .4" PRINTF_NEXPR "\n", + CHOOSE("ldouble", "double", "float", + "ildouble", "idouble", "ifloat"), ulp); + } +} + + +#if 0 /* XXX scp XXX */ +static void +print_complex_function_ulps (const char *function_name, FLOAT real_ulp, + FLOAT imag_ulp) +{ + if (output_ulps) + { + if (real_ulp != 0.0) + { + fprintf (ulps_file, "Function: Real part of \"%s\":\n", function_name); + fprintf (ulps_file, "%s: % .4" PRINTF_NEXPR "\n", + CHOOSE("ldouble", "double", "float", + "ildouble", "idouble", "ifloat"), real_ulp); + } + if (imag_ulp != 0.0) + { + fprintf (ulps_file, "Function: Imaginary part of \"%s\":\n", function_name); + fprintf (ulps_file, "%s: % .4" PRINTF_NEXPR "\n", + CHOOSE("ldouble", "double", "float", + "ildouble", "idouble", "ifloat"), imag_ulp); + } + + + } +} +#endif + + +static void +print_max_error (const char *func_name, FLOAT allowed, int xfail) +{ + int ok = 0; + + if (max_error == 0.0 || (max_error <= allowed && !ignore_max_ulp)) + { + ok = 1; + } + + if (!ok) + print_function_ulps (func_name, max_error); + + + if (print_screen_max_error (ok, xfail)) + { + printf ("Maximal error of `%s'\n", func_name); + printf (" is : % .4" PRINTF_NEXPR " ulp\n", max_error); + printf (" accepted: % .4" PRINTF_NEXPR " ulp\n", allowed); + } + + update_stats (ok, xfail); +} + + +#if 0 /* XXX scp XXX */ +static void +print_complex_max_error (const char *func_name, __complex__ FLOAT allowed, + __complex__ int xfail) +{ + int ok = 0; + + if ((real_max_error <= __real__ allowed) + && (imag_max_error <= __imag__ allowed)) + { + ok = 1; + } + + if (!ok) + print_complex_function_ulps (func_name, real_max_error, imag_max_error); + + + if (print_screen_max_error (ok, xfail)) + { + printf ("Maximal error of real part of: %s\n", func_name); + printf (" is : % .4" PRINTF_NEXPR " ulp\n", real_max_error); + printf (" accepted: % .4" PRINTF_NEXPR " ulp\n", __real__ allowed); + printf ("Maximal error of imaginary part of: %s\n", func_name); + printf (" is : % .4" PRINTF_NEXPR " ulp\n", imag_max_error); + printf (" accepted: % .4" PRINTF_NEXPR " ulp\n", __imag__ allowed); + } + + update_stats (ok, xfail); +} +#endif + + +/* Test whether a given exception was raised. */ +static void +test_single_exception (const char *test_name, + int exception, + int exc_flag, + int fe_flag, + const char *flag_name) +{ +/* Don't perform these checks if we're compiling with clang, because clang + doesn't bother to set floating-point exceptions properly */ +#ifndef __clang__ +#ifndef TEST_INLINE + int ok = 1; + if (exception & exc_flag) + { + if (fetestexcept (fe_flag)) + { + if (print_screen (1, 0)) + printf ("Pass: %s: Exception \"%s\" set\n", test_name, flag_name); + } + else + { + ok = 0; + if (print_screen (0, 0)) + printf ("Failure: %s: Exception \"%s\" not set\n", + test_name, flag_name); + } + } + else + { + if (fetestexcept (fe_flag)) + { + ok = 0; + if (print_screen (0, 0)) + printf ("Failure: %s: Exception \"%s\" set\n", + test_name, flag_name); + } + else + { + if (print_screen (1, 0)) + printf ("%s: Exception \"%s\" not set\n", test_name, + flag_name); + } + } + if (!ok) + ++noErrors; + +#endif +#endif // __clang__ +} + + +/* Test whether exceptions given by EXCEPTION are raised. Ignore thereby + allowed but not required exceptions. +*/ +static void +test_exceptions (const char *test_name, int exception) +{ + ++noExcTests; +#ifdef FE_DIVBYZERO + if ((exception & DIVIDE_BY_ZERO_EXCEPTION_OK) == 0) + test_single_exception (test_name, exception, + DIVIDE_BY_ZERO_EXCEPTION, FE_DIVBYZERO, + "Divide by zero"); +#endif +#ifdef FE_INVALID + if ((exception & INVALID_EXCEPTION_OK) == 0) + test_single_exception (test_name, exception, INVALID_EXCEPTION, FE_INVALID, + "Invalid operation"); +#endif + feclearexcept (FE_ALL_EXCEPT); +} + + +static void +check_float_internal (const char *test_name, FLOAT computed, FLOAT expected, + FLOAT max_ulp, int xfail, int exceptions, + FLOAT *curr_max_error) +{ + int ok = 0; + int print_diff = 0; + FLOAT diff = 0; + FLOAT ulp = 0; + + test_exceptions (test_name, exceptions); + if (isnan (computed) && isnan (expected)) + ok = 1; + else if (isinf (computed) && isinf (expected)) + { + /* Test for sign of infinities. */ + if ((exceptions & IGNORE_ZERO_INF_SIGN) == 0 + && signbit (computed) != signbit (expected)) + { + ok = 0; + printf ("infinity has wrong sign.\n"); + } + else + ok = 1; + } + /* Don't calc ulp for NaNs or infinities. */ + else if (isinf (computed) || isnan (computed) || isinf (expected) || isnan (expected)) + ok = 0; + else + { + diff = FUNC(fabs) (computed - expected); + /* ilogb (0) isn't allowed. */ + if (expected == 0.0) + ulp = diff / FUNC(ldexp) (1.0, - MANT_DIG); + else + ulp = diff / FUNC(ldexp) (1.0, FUNC(ilogb) (expected) - MANT_DIG); + set_max_error (ulp, curr_max_error); + print_diff = 1; + if ((exceptions & IGNORE_ZERO_INF_SIGN) == 0 + && computed == 0.0 && expected == 0.0 + && signbit(computed) != signbit (expected)) + ok = 0; + else if (ulp == 0.0 || (ulp <= max_ulp && !ignore_max_ulp)) + ok = 1; + else + { + ok = 0; + print_ulps (test_name, ulp); + } + + } + if (print_screen (ok, xfail)) + { + if (!ok) + printf ("Failure: "); + printf ("Test: %s\n", test_name); + printf ("Result:\n"); + printf (" is: % .20" PRINTF_EXPR " % .20" PRINTF_XEXPR "\n", + computed, computed); + printf (" should be: % .20" PRINTF_EXPR " % .20" PRINTF_XEXPR "\n", + expected, expected); + if (print_diff) + { + printf (" difference: % .20" PRINTF_EXPR " % .20" PRINTF_XEXPR + "\n", diff, diff); + printf (" ulp : % .4" PRINTF_NEXPR "\n", ulp); + printf (" max.ulp : % .4" PRINTF_NEXPR "\n", max_ulp); + } + } + update_stats (ok, xfail); +} + + +static void +check_float (const char *test_name, FLOAT computed, FLOAT expected, + FLOAT max_ulp, int xfail, int exceptions) +{ + check_float_internal (test_name, computed, expected, max_ulp, xfail, + exceptions, &max_error); +} + +#if 0 /* XXX scp XXX */ +static void +check_complex (const char *test_name, __complex__ FLOAT computed, + __complex__ FLOAT expected, + __complex__ FLOAT max_ulp, __complex__ int xfail, + int exception) +{ + FLOAT part_comp, part_exp, part_max_ulp; + int part_xfail; + char str[200]; + + sprintf (str, "Real part of: %s", test_name); + part_comp = __real__ computed; + part_exp = __real__ expected; + part_max_ulp = __real__ max_ulp; + part_xfail = __real__ xfail; + + check_float_internal (str, part_comp, part_exp, part_max_ulp, part_xfail, + exception, &real_max_error); + + sprintf (str, "Imaginary part of: %s", test_name); + part_comp = __imag__ computed; + part_exp = __imag__ expected; + part_max_ulp = __imag__ max_ulp; + part_xfail = __imag__ xfail; + + /* Don't check again for exceptions, just pass through the + zero/inf sign test. */ + check_float_internal (str, part_comp, part_exp, part_max_ulp, part_xfail, + exception & IGNORE_ZERO_INF_SIGN, + &imag_max_error); +} +#endif + +/* Check that computed and expected values are equal (int values). */ +static void +check_int (const char *test_name, int computed, int expected, int max_ulp, + int xfail, int exceptions) +{ + int diff = computed - expected; + int ok = 0; + + test_exceptions (test_name, exceptions); + noTests++; + if (abs (diff) <= max_ulp) + ok = 1; + + if (!ok) + print_ulps (test_name, diff); + + if (print_screen (ok, xfail)) + { + if (!ok) + printf ("Failure: "); + printf ("Test: %s\n", test_name); + printf ("Result:\n"); + printf (" is: %d\n", computed); + printf (" should be: %d\n", expected); + } + + update_stats (ok, xfail); +} + + +/* Check that computed and expected values are equal (long int values). */ +static void +check_long (const char *test_name, long int computed, long int expected, + long int max_ulp, int xfail, int exceptions) +{ + long int diff = computed - expected; + int ok = 0; + + test_exceptions (test_name, exceptions); + noTests++; + if (labs (diff) <= max_ulp) + ok = 1; + + if (!ok) + print_ulps (test_name, diff); + + if (print_screen (ok, xfail)) + { + if (!ok) + printf ("Failure: "); + printf ("Test: %s\n", test_name); + printf ("Result:\n"); + printf (" is: %ld\n", computed); + printf (" should be: %ld\n", expected); + } + + update_stats (ok, xfail); +} + + +/* Check that computed value is true/false. */ +static void +check_bool (const char *test_name, int computed, int expected, + long int max_ulp, int xfail, int exceptions) +{ + int ok = 0; + + test_exceptions (test_name, exceptions); + noTests++; + if ((computed == 0) == (expected == 0)) + ok = 1; + + if (print_screen (ok, xfail)) + { + if (!ok) + printf ("Failure: "); + printf ("Test: %s\n", test_name); + printf ("Result:\n"); + printf (" is: %d\n", computed); + printf (" should be: %d\n", expected); + } + + update_stats (ok, xfail); +} + + +/* check that computed and expected values are equal (long int values) */ +static void +check_longlong (const char *test_name, long long int computed, + long long int expected, + long long int max_ulp, int xfail, + int exceptions) +{ + long long int diff = computed - expected; + int ok = 0; + + test_exceptions (test_name, exceptions); + noTests++; + if (llabs (diff) <= max_ulp) + ok = 1; + + if (!ok) + print_ulps (test_name, diff); + + if (print_screen (ok, xfail)) + { + if (!ok) + printf ("Failure:"); + printf ("Test: %s\n", test_name); + printf ("Result:\n"); + printf (" is: %lld\n", computed); + printf (" should be: %lld\n", expected); + } + + update_stats (ok, xfail); +} + + +#if 0 /* XXX scp XXX */ +/* This is to prevent messages from the SVID libm emulation. */ +int +matherr (struct exception *x __attribute__ ((unused))) +{ + return 1; +} +#endif + +/**************************************************************************** + Tests for single functions of libm. + Please keep them alphabetically sorted! +****************************************************************************/ + +static void +acos_test (void) +{ + errno = 0; + FUNC(acos) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("acos (inf) == NaN plus invalid exception", FUNC(acos) (plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("acos (-inf) == NaN plus invalid exception", FUNC(acos) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("acos (NaN) == NaN", FUNC(acos) (nan_value), nan_value, 0, 0, 0); + + /* |x| > 1: */ + check_float ("acos (1.1) == NaN plus invalid exception", FUNC(acos) (1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("acos (-1.1) == NaN plus invalid exception", FUNC(acos) (-1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("acos (0) == pi/2", FUNC(acos) (0), M_PI_2l, 0, 0, 0); + check_float ("acos (-0) == pi/2", FUNC(acos) (minus_zero), M_PI_2l, 0, 0, 0); + check_float ("acos (1) == 0", FUNC(acos) (1), 0, 0, 0, 0); + check_float ("acos (-1) == pi", FUNC(acos) (-1), M_PIl, 0, 0, 0); + check_float ("acos (0.5) == M_PI_6l*2.0", FUNC(acos) (0.5), M_PI_6l*2.0, 1, 0, 0); + check_float ("acos (-0.5) == M_PI_6l*4.0", FUNC(acos) (-0.5), M_PI_6l*4.0, 0, 0, 0); + check_float ("acos (0.7) == 0.79539883018414355549096833892476432", FUNC(acos) (0.7L), 0.79539883018414355549096833892476432L, 0, 0, 0); + + print_max_error ("acos", DELTAacos, 0); +} + +static void +acosh_test (void) +{ + errno = 0; + FUNC(acosh) (7); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("acosh (inf) == inf", FUNC(acosh) (plus_infty), plus_infty, 0, 0, 0); + check_float ("acosh (-inf) == NaN plus invalid exception", FUNC(acosh) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + + /* x < 1: */ + check_float ("acosh (-1.1) == NaN plus invalid exception", FUNC(acosh) (-1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("acosh (1) == 0", FUNC(acosh) (1), 0, 0, 0, 0); + check_float ("acosh (7) == 2.633915793849633417250092694615937", FUNC(acosh) (7), 2.633915793849633417250092694615937L, DELTA16, 0, 0); + + print_max_error ("acosh", DELTAacosh, 0); +} + +static void +asin_test (void) +{ + errno = 0; + FUNC(asin) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("asin (inf) == NaN plus invalid exception", FUNC(asin) (plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("asin (-inf) == NaN plus invalid exception", FUNC(asin) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("asin (NaN) == NaN", FUNC(asin) (nan_value), nan_value, 0, 0, 0); + + /* asin x == NaN plus invalid exception for |x| > 1. */ + check_float ("asin (1.1) == NaN plus invalid exception", FUNC(asin) (1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("asin (-1.1) == NaN plus invalid exception", FUNC(asin) (-1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("asin (0) == 0", FUNC(asin) (0), 0, 0, 0, 0); + check_float ("asin (-0) == -0", FUNC(asin) (minus_zero), minus_zero, 0, 0, 0); + check_float ("asin (0.5) == pi/6", FUNC(asin) (0.5), M_PI_6l, DELTA24, 0, 0); + check_float ("asin (-0.5) == -pi/6", FUNC(asin) (-0.5), -M_PI_6l, DELTA25, 0, 0); + check_float ("asin (1.0) == pi/2", FUNC(asin) (1.0), M_PI_2l, DELTA26, 0, 0); + check_float ("asin (-1.0) == -pi/2", FUNC(asin) (-1.0), -M_PI_2l, DELTA27, 0, 0); + check_float ("asin (0.7) == 0.77539749661075306374035335271498708", FUNC(asin) (0.7L), 0.77539749661075306374035335271498708L, DELTA28, 0, 0); + + print_max_error ("asin", DELTAasin, 0); +} + +static void +asinh_test (void) +{ + errno = 0; + FUNC(asinh) (0.7L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("asinh (0) == 0", FUNC(asinh) (0), 0, 0, 0, 0); + check_float ("asinh (-0) == -0", FUNC(asinh) (minus_zero), minus_zero, 0, 0, 0); +#ifndef TEST_INLINE + check_float ("asinh (inf) == inf", FUNC(asinh) (plus_infty), plus_infty, 0, 0, 0); + check_float ("asinh (-inf) == -inf", FUNC(asinh) (minus_infty), minus_infty, 0, 0, 0); +#endif + check_float ("asinh (NaN) == NaN", FUNC(asinh) (nan_value), nan_value, 0, 0, 0); + check_float ("asinh (0.7) == 0.652666566082355786", FUNC(asinh) (0.7L), 0.652666566082355786L, DELTA34, 0, 0); + + print_max_error ("asinh", DELTAasinh, 0); +} + +static void +atan_test (void) +{ + errno = 0; + FUNC(atan) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("atan (0) == 0", FUNC(atan) (0), 0, 0, 0, 0); + check_float ("atan (-0) == -0", FUNC(atan) (minus_zero), minus_zero, 0, 0, 0); + + check_float ("atan (inf) == pi/2", FUNC(atan) (plus_infty), M_PI_2l, 0, 0, 0); + check_float ("atan (-inf) == -pi/2", FUNC(atan) (minus_infty), -M_PI_2l, 0, 0, 0); + check_float ("atan (NaN) == NaN", FUNC(atan) (nan_value), nan_value, 0, 0, 0); + + check_float ("atan (1) == pi/4", FUNC(atan) (1), M_PI_4l, 0, 0, 0); + check_float ("atan (-1) == -pi/4", FUNC(atan) (-1), -M_PI_4l, 0, 0, 0); + + check_float ("atan (0.7) == 0.61072596438920861654375887649023613", FUNC(atan) (0.7L), 0.61072596438920861654375887649023613L, DELTA42, 0, 0); + + print_max_error ("atan", DELTAatan, 0); +} + + + +static void +atanh_test (void) +{ + errno = 0; + FUNC(atanh) (0.7L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + + check_float ("atanh (0) == 0", FUNC(atanh) (0), 0, 0, 0, 0); + check_float ("atanh (-0) == -0", FUNC(atanh) (minus_zero), minus_zero, 0, 0, 0); + + check_float ("atanh (1) == inf plus division by zero exception", FUNC(atanh) (1), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("atanh (-1) == -inf plus division by zero exception", FUNC(atanh) (-1), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("atanh (NaN) == NaN", FUNC(atanh) (nan_value), nan_value, 0, 0, 0); + + /* atanh (x) == NaN plus invalid exception if |x| > 1. */ + check_float ("atanh (1.1) == NaN plus invalid exception", FUNC(atanh) (1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("atanh (-1.1) == NaN plus invalid exception", FUNC(atanh) (-1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("atanh (0.7) == 0.8673005276940531944", FUNC(atanh) (0.7L), 0.8673005276940531944L, DELTA50, 0, 0); + + print_max_error ("atanh", DELTAatanh, 0); +} + +static void +atan2_test (void) +{ + errno = 0; + FUNC(atan2) (-0, 1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + /* atan2 (0,x) == 0 for x > 0. */ + check_float ("atan2 (0, 1) == 0", FUNC(atan2) (0, 1), 0, 0, 0, 0); + + /* atan2 (-0,x) == -0 for x > 0. */ + check_float ("atan2 (-0, 1) == -0", FUNC(atan2) (minus_zero, 1), minus_zero, 0, 0, 0); + + check_float ("atan2 (0, 0) == 0", FUNC(atan2) (0, 0), 0, 0, 0, 0); + check_float ("atan2 (-0, 0) == -0", FUNC(atan2) (minus_zero, 0), minus_zero, 0, 0, 0); + + /* atan2 (+0,x) == +pi for x < 0. */ + check_float ("atan2 (0, -1) == pi", FUNC(atan2) (0, -1), M_PIl, 0, 0, 0); + + /* atan2 (-0,x) == -pi for x < 0. */ + check_float ("atan2 (-0, -1) == -pi", FUNC(atan2) (minus_zero, -1), -M_PIl, 0, 0, 0); + + check_float ("atan2 (0, -0) == pi", FUNC(atan2) (0, minus_zero), M_PIl, 0, 0, 0); + check_float ("atan2 (-0, -0) == -pi", FUNC(atan2) (minus_zero, minus_zero), -M_PIl, 0, 0, 0); + + /* atan2 (y,+0) == pi/2 for y > 0. */ + check_float ("atan2 (1, 0) == pi/2", FUNC(atan2) (1, 0), M_PI_2l, 0, 0, 0); + + /* atan2 (y,-0) == pi/2 for y > 0. */ + check_float ("atan2 (1, -0) == pi/2", FUNC(atan2) (1, minus_zero), M_PI_2l, 0, 0, 0); + + /* atan2 (y,+0) == -pi/2 for y < 0. */ + check_float ("atan2 (-1, 0) == -pi/2", FUNC(atan2) (-1, 0), -M_PI_2l, 0, 0, 0); + + /* atan2 (y,-0) == -pi/2 for y < 0. */ + check_float ("atan2 (-1, -0) == -pi/2", FUNC(atan2) (-1, minus_zero), -M_PI_2l, 0, 0, 0); + + /* atan2 (y,inf) == +0 for finite y > 0. */ + check_float ("atan2 (1, inf) == 0", FUNC(atan2) (1, plus_infty), 0, 0, 0, 0); + + /* atan2 (y,inf) == -0 for finite y < 0. */ + check_float ("atan2 (-1, inf) == -0", FUNC(atan2) (-1, plus_infty), minus_zero, 0, 0, 0); + + /* atan2(+inf, x) == pi/2 for finite x. */ + check_float ("atan2 (inf, -1) == pi/2", FUNC(atan2) (plus_infty, -1), M_PI_2l, 0, 0, 0); + + /* atan2(-inf, x) == -pi/2 for finite x. */ + check_float ("atan2 (-inf, 1) == -pi/2", FUNC(atan2) (minus_infty, 1), -M_PI_2l, 0, 0, 0); + + /* atan2 (y,-inf) == +pi for finite y > 0. */ + check_float ("atan2 (1, -inf) == pi", FUNC(atan2) (1, minus_infty), M_PIl, 0, 0, 0); + + /* atan2 (y,-inf) == -pi for finite y < 0. */ + check_float ("atan2 (-1, -inf) == -pi", FUNC(atan2) (-1, minus_infty), -M_PIl, 0, 0, 0); + + check_float ("atan2 (inf, inf) == pi/4", FUNC(atan2) (plus_infty, plus_infty), M_PI_4l, 0, 0, 0); + check_float ("atan2 (-inf, inf) == -pi/4", FUNC(atan2) (minus_infty, plus_infty), -M_PI_4l, 0, 0, 0); + check_float ("atan2 (inf, -inf) == 3/4 pi", FUNC(atan2) (plus_infty, minus_infty), M_PI_34l, 0, 0, 0); + check_float ("atan2 (-inf, -inf) == -3/4 pi", FUNC(atan2) (minus_infty, minus_infty), -M_PI_34l, 0, 0, 0); + check_float ("atan2 (NaN, NaN) == NaN", FUNC(atan2) (nan_value, nan_value), nan_value, 0, 0, 0); + + check_float ("atan2 (0.7, 1) == 0.61072596438920861654375887649023613", FUNC(atan2) (0.7L, 1), 0.61072596438920861654375887649023613L, DELTA74, 0, 0); + check_float ("atan2 (-0.7, 1.0) == -0.61072596438920861654375887649023613", FUNC(atan2) (-0.7L, 1.0L), -0.61072596438920861654375887649023613L, 0, 0, 0); + check_float ("atan2 (0.7, -1.0) == 2.530866689200584621918884506789267", FUNC(atan2) (0.7L, -1.0L), 2.530866689200584621918884506789267L, 0, 0, 0); + check_float ("atan2 (-0.7, -1.0) == -2.530866689200584621918884506789267", FUNC(atan2) (-0.7L, -1.0L), -2.530866689200584621918884506789267L, 0, 0, 0); + check_float ("atan2 (0.4, 0.0003) == 1.5700463269355215717704032607580829", FUNC(atan2) (0.4L, 0.0003L), 1.5700463269355215717704032607580829L, DELTA78, 0, 0); + check_float ("atan2 (1.4, -0.93) == 2.1571487668237843754887415992772736", FUNC(atan2) (1.4L, -0.93L), 2.1571487668237843754887415992772736L, 0, 0, 0); + + print_max_error ("atan2", DELTAatan2, 0); +} + + +#if 0 /* XXX scp XXX */ +static void +cabs_test (void) +{ + errno = 0; + FUNC(cabs) (BUILD_COMPLEX (0.7L, 12.4L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + /* cabs (x + iy) is specified as hypot (x,y) */ + + /* cabs (+inf + i x) == +inf. */ + check_float ("cabs (inf + 1.0 i) == inf", FUNC(cabs) (BUILD_COMPLEX (plus_infty, 1.0)), plus_infty, 0, 0, 0); + /* cabs (-inf + i x) == +inf. */ + check_float ("cabs (-inf + 1.0 i) == inf", FUNC(cabs) (BUILD_COMPLEX (minus_infty, 1.0)), plus_infty, 0, 0, 0); + + check_float ("cabs (-inf + NaN i) == inf", FUNC(cabs) (BUILD_COMPLEX (minus_infty, nan_value)), plus_infty, 0, 0, 0); + check_float ("cabs (-inf + NaN i) == inf", FUNC(cabs) (BUILD_COMPLEX (minus_infty, nan_value)), plus_infty, 0, 0, 0); + + check_float ("cabs (NaN + NaN i) == NaN", FUNC(cabs) (BUILD_COMPLEX (nan_value, nan_value)), nan_value, 0, 0, 0); + + /* cabs (x,y) == cabs (y,x). */ + check_float ("cabs (0.7 + 12.4 i) == 12.419742348374220601176836866763271", FUNC(cabs) (BUILD_COMPLEX (0.7L, 12.4L)), 12.419742348374220601176836866763271L, DELTA85, 0, 0); + /* cabs (x,y) == cabs (-x,y). */ + check_float ("cabs (-12.4 + 0.7 i) == 12.419742348374220601176836866763271", FUNC(cabs) (BUILD_COMPLEX (-12.4L, 0.7L)), 12.419742348374220601176836866763271L, DELTA86, 0, 0); + /* cabs (x,y) == cabs (-y,x). */ + check_float ("cabs (-0.7 + 12.4 i) == 12.419742348374220601176836866763271", FUNC(cabs) (BUILD_COMPLEX (-0.7L, 12.4L)), 12.419742348374220601176836866763271L, DELTA87, 0, 0); + /* cabs (x,y) == cabs (-x,-y). */ + check_float ("cabs (-12.4 - 0.7 i) == 12.419742348374220601176836866763271", FUNC(cabs) (BUILD_COMPLEX (-12.4L, -0.7L)), 12.419742348374220601176836866763271L, DELTA88, 0, 0); + /* cabs (x,y) == cabs (-y,-x). */ + check_float ("cabs (-0.7 - 12.4 i) == 12.419742348374220601176836866763271", FUNC(cabs) (BUILD_COMPLEX (-0.7L, -12.4L)), 12.419742348374220601176836866763271L, DELTA89, 0, 0); + /* cabs (x,0) == fabs (x). */ + check_float ("cabs (-0.7 + 0 i) == 0.7", FUNC(cabs) (BUILD_COMPLEX (-0.7L, 0)), 0.7L, 0, 0, 0); + check_float ("cabs (0.7 + 0 i) == 0.7", FUNC(cabs) (BUILD_COMPLEX (0.7L, 0)), 0.7L, 0, 0, 0); + check_float ("cabs (-1.0 + 0 i) == 1.0", FUNC(cabs) (BUILD_COMPLEX (-1.0L, 0)), 1.0L, 0, 0, 0); + check_float ("cabs (1.0 + 0 i) == 1.0", FUNC(cabs) (BUILD_COMPLEX (1.0L, 0)), 1.0L, 0, 0, 0); + check_float ("cabs (-5.7e7 + 0 i) == 5.7e7", FUNC(cabs) (BUILD_COMPLEX (-5.7e7L, 0)), 5.7e7L, 0, 0, 0); + check_float ("cabs (5.7e7 + 0 i) == 5.7e7", FUNC(cabs) (BUILD_COMPLEX (5.7e7L, 0)), 5.7e7L, 0, 0, 0); + + check_float ("cabs (0.7 + 1.2 i) == 1.3892443989449804508432547041028554", FUNC(cabs) (BUILD_COMPLEX (0.7L, 1.2L)), 1.3892443989449804508432547041028554L, DELTA96, 0, 0); + + print_max_error ("cabs", DELTAcabs, 0); +} + +static void +cacos_test (void) +{ + errno = 0; + FUNC(cacos) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + + check_complex ("cacos (0 + 0 i) == pi/2 - 0 i", FUNC(cacos) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("cacos (-0 + 0 i) == pi/2 - 0 i", FUNC(cacos) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("cacos (-0 - 0 i) == pi/2 + 0.0 i", FUNC(cacos) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (M_PI_2l, 0.0), 0, 0, 0); + check_complex ("cacos (0 - 0 i) == pi/2 + 0.0 i", FUNC(cacos) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (M_PI_2l, 0.0), 0, 0, 0); + + check_complex ("cacos (-inf + inf i) == 3/4 pi - inf i", FUNC(cacos) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (M_PI_34l, minus_infty), 0, 0, 0); + check_complex ("cacos (-inf - inf i) == 3/4 pi + inf i", FUNC(cacos) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (M_PI_34l, plus_infty), 0, 0, 0); + + check_complex ("cacos (inf + inf i) == pi/4 - inf i", FUNC(cacos) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (M_PI_4l, minus_infty), 0, 0, 0); + check_complex ("cacos (inf - inf i) == pi/4 + inf i", FUNC(cacos) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (M_PI_4l, plus_infty), 0, 0, 0); + + check_complex ("cacos (-10.0 + inf i) == pi/2 - inf i", FUNC(cacos) (BUILD_COMPLEX (-10.0, plus_infty)), BUILD_COMPLEX (M_PI_2l, minus_infty), 0, 0, 0); + check_complex ("cacos (-10.0 - inf i) == pi/2 + inf i", FUNC(cacos) (BUILD_COMPLEX (-10.0, minus_infty)), BUILD_COMPLEX (M_PI_2l, plus_infty), 0, 0, 0); + check_complex ("cacos (0 + inf i) == pi/2 - inf i", FUNC(cacos) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (M_PI_2l, minus_infty), 0, 0, 0); + check_complex ("cacos (0 - inf i) == pi/2 + inf i", FUNC(cacos) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (M_PI_2l, plus_infty), 0, 0, 0); + check_complex ("cacos (0.1 + inf i) == pi/2 - inf i", FUNC(cacos) (BUILD_COMPLEX (0.1L, plus_infty)), BUILD_COMPLEX (M_PI_2l, minus_infty), 0, 0, 0); + check_complex ("cacos (0.1 - inf i) == pi/2 + inf i", FUNC(cacos) (BUILD_COMPLEX (0.1L, minus_infty)), BUILD_COMPLEX (M_PI_2l, plus_infty), 0, 0, 0); + + check_complex ("cacos (-inf + 0 i) == pi - inf i", FUNC(cacos) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (M_PIl, minus_infty), 0, 0, 0); + check_complex ("cacos (-inf - 0 i) == pi + inf i", FUNC(cacos) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (M_PIl, plus_infty), 0, 0, 0); + check_complex ("cacos (-inf + 100 i) == pi - inf i", FUNC(cacos) (BUILD_COMPLEX (minus_infty, 100)), BUILD_COMPLEX (M_PIl, minus_infty), 0, 0, 0); + check_complex ("cacos (-inf - 100 i) == pi + inf i", FUNC(cacos) (BUILD_COMPLEX (minus_infty, -100)), BUILD_COMPLEX (M_PIl, plus_infty), 0, 0, 0); + + check_complex ("cacos (inf + 0 i) == 0.0 - inf i", FUNC(cacos) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + check_complex ("cacos (inf - 0 i) == 0.0 + inf i", FUNC(cacos) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + check_complex ("cacos (inf + 0.5 i) == 0.0 - inf i", FUNC(cacos) (BUILD_COMPLEX (plus_infty, 0.5)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + check_complex ("cacos (inf - 0.5 i) == 0.0 + inf i", FUNC(cacos) (BUILD_COMPLEX (plus_infty, -0.5)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + + check_complex ("cacos (inf + NaN i) == NaN + inf i plus sign of zero/inf not specified", FUNC(cacos) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("cacos (-inf + NaN i) == NaN + inf i plus sign of zero/inf not specified", FUNC(cacos) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("cacos (0 + NaN i) == pi/2 + NaN i", FUNC(cacos) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (M_PI_2l, nan_value), 0, 0, 0); + check_complex ("cacos (-0 + NaN i) == pi/2 + NaN i", FUNC(cacos) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (M_PI_2l, nan_value), 0, 0, 0); + + check_complex ("cacos (NaN + inf i) == NaN - inf i", FUNC(cacos) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, minus_infty), 0, 0, 0); + check_complex ("cacos (NaN - inf i) == NaN + inf i", FUNC(cacos) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, 0); + + check_complex ("cacos (10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(cacos) (BUILD_COMPLEX (10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cacos (-10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(cacos) (BUILD_COMPLEX (-10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("cacos (NaN + 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(cacos) (BUILD_COMPLEX (nan_value, 0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cacos (NaN - 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(cacos) (BUILD_COMPLEX (nan_value, -0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("cacos (NaN + NaN i) == NaN + NaN i", FUNC(cacos) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("cacos (0.7 + 1.2 i) == 1.1351827477151551088992008271819053 - 1.0927647857577371459105272080819308 i", FUNC(cacos) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.1351827477151551088992008271819053L, -1.0927647857577371459105272080819308L), DELTA130, 0, 0); + check_complex ("cacos (-2 - 3 i) == 2.1414491111159960199416055713254211 + 1.9833870299165354323470769028940395 i", FUNC(cacos) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (2.1414491111159960199416055713254211L, 1.9833870299165354323470769028940395L), DELTA131, 0, 0); + + print_complex_max_error ("cacos", DELTAcacos, 0); +} + + +static void +cacosh_test (void) +{ + errno = 0; + FUNC(cacosh) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + + check_complex ("cacosh (0 + 0 i) == 0.0 + pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("cacosh (-0 + 0 i) == 0.0 + pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("cacosh (0 - 0 i) == 0.0 - pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + check_complex ("cacosh (-0 - 0 i) == 0.0 - pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + check_complex ("cacosh (-inf + inf i) == inf + 3/4 pi i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_34l), 0, 0, 0); + check_complex ("cacosh (-inf - inf i) == inf - 3/4 pi i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_34l), 0, 0, 0); + + check_complex ("cacosh (inf + inf i) == inf + pi/4 i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_4l), 0, 0, 0); + check_complex ("cacosh (inf - inf i) == inf - pi/4 i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_4l), 0, 0, 0); + + check_complex ("cacosh (-10.0 + inf i) == inf + pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (-10.0, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("cacosh (-10.0 - inf i) == inf - pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (-10.0, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("cacosh (0 + inf i) == inf + pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("cacosh (0 - inf i) == inf - pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("cacosh (0.1 + inf i) == inf + pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (0.1L, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("cacosh (0.1 - inf i) == inf - pi/2 i", FUNC(cacosh) (BUILD_COMPLEX (0.1L, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + + check_complex ("cacosh (-inf + 0 i) == inf + pi i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (plus_infty, M_PIl), 0, 0, 0); + check_complex ("cacosh (-inf - 0 i) == inf - pi i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, -M_PIl), 0, 0, 0); + check_complex ("cacosh (-inf + 100 i) == inf + pi i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, 100)), BUILD_COMPLEX (plus_infty, M_PIl), 0, 0, 0); + check_complex ("cacosh (-inf - 100 i) == inf - pi i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, -100)), BUILD_COMPLEX (plus_infty, -M_PIl), 0, 0, 0); + + check_complex ("cacosh (inf + 0 i) == inf + 0.0 i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("cacosh (inf - 0 i) == inf - 0 i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("cacosh (inf + 0.5 i) == inf + 0.0 i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, 0.5)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("cacosh (inf - 0.5 i) == inf - 0 i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, -0.5)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("cacosh (inf + NaN i) == inf + NaN i", FUNC(cacosh) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("cacosh (-inf + NaN i) == inf + NaN i", FUNC(cacosh) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("cacosh (0 + NaN i) == NaN + NaN i", FUNC(cacosh) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + check_complex ("cacosh (-0 + NaN i) == NaN + NaN i", FUNC(cacosh) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("cacosh (NaN + inf i) == inf + NaN i", FUNC(cacosh) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("cacosh (NaN - inf i) == inf + NaN i", FUNC(cacosh) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("cacosh (10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(cacosh) (BUILD_COMPLEX (10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cacosh (-10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(cacosh) (BUILD_COMPLEX (-10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("cacosh (NaN + 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(cacosh) (BUILD_COMPLEX (nan_value, 0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cacosh (NaN - 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(cacosh) (BUILD_COMPLEX (nan_value, -0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("cacosh (NaN + NaN i) == NaN + NaN i", FUNC(cacosh) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("cacosh (0.7 + 1.2 i) == 1.0927647857577371459105272080819308 + 1.1351827477151551088992008271819053 i", FUNC(cacosh) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.0927647857577371459105272080819308L, 1.1351827477151551088992008271819053L), DELTA165, 0, 0); + check_complex ("cacosh (-2 - 3 i) == -1.9833870299165354323470769028940395 + 2.1414491111159960199416055713254211 i", FUNC(cacosh) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-1.9833870299165354323470769028940395L, 2.1414491111159960199416055713254211L), DELTA166, 0, 0); + + print_complex_max_error ("cacosh", DELTAcacosh, 0); +} + +static void +carg_test (void) +{ + init_max_error (); + + /* carg (x + iy) is specified as atan2 (y, x) */ + + /* carg (x + i 0) == 0 for x > 0. */ + check_float ("carg (2.0 + 0 i) == 0", FUNC(carg) (BUILD_COMPLEX (2.0, 0)), 0, 0, 0, 0); + /* carg (x - i 0) == -0 for x > 0. */ + check_float ("carg (2.0 - 0 i) == -0", FUNC(carg) (BUILD_COMPLEX (2.0, minus_zero)), minus_zero, 0, 0, 0); + + check_float ("carg (0 + 0 i) == 0", FUNC(carg) (BUILD_COMPLEX (0, 0)), 0, 0, 0, 0); + check_float ("carg (0 - 0 i) == -0", FUNC(carg) (BUILD_COMPLEX (0, minus_zero)), minus_zero, 0, 0, 0); + + /* carg (x + i 0) == +pi for x < 0. */ + check_float ("carg (-2.0 + 0 i) == pi", FUNC(carg) (BUILD_COMPLEX (-2.0, 0)), M_PIl, 0, 0, 0); + + /* carg (x - i 0) == -pi for x < 0. */ + check_float ("carg (-2.0 - 0 i) == -pi", FUNC(carg) (BUILD_COMPLEX (-2.0, minus_zero)), -M_PIl, 0, 0, 0); + + check_float ("carg (-0 + 0 i) == pi", FUNC(carg) (BUILD_COMPLEX (minus_zero, 0)), M_PIl, 0, 0, 0); + check_float ("carg (-0 - 0 i) == -pi", FUNC(carg) (BUILD_COMPLEX (minus_zero, minus_zero)), -M_PIl, 0, 0, 0); + + /* carg (+0 + i y) == pi/2 for y > 0. */ + check_float ("carg (0 + 2.0 i) == pi/2", FUNC(carg) (BUILD_COMPLEX (0, 2.0)), M_PI_2l, 0, 0, 0); + + /* carg (-0 + i y) == pi/2 for y > 0. */ + check_float ("carg (-0 + 2.0 i) == pi/2", FUNC(carg) (BUILD_COMPLEX (minus_zero, 2.0)), M_PI_2l, 0, 0, 0); + + /* carg (+0 + i y) == -pi/2 for y < 0. */ + check_float ("carg (0 - 2.0 i) == -pi/2", FUNC(carg) (BUILD_COMPLEX (0, -2.0)), -M_PI_2l, 0, 0, 0); + + /* carg (-0 + i y) == -pi/2 for y < 0. */ + check_float ("carg (-0 - 2.0 i) == -pi/2", FUNC(carg) (BUILD_COMPLEX (minus_zero, -2.0)), -M_PI_2l, 0, 0, 0); + + /* carg (inf + i y) == +0 for finite y > 0. */ + check_float ("carg (inf + 2.0 i) == 0", FUNC(carg) (BUILD_COMPLEX (plus_infty, 2.0)), 0, 0, 0, 0); + + /* carg (inf + i y) == -0 for finite y < 0. */ + check_float ("carg (inf - 2.0 i) == -0", FUNC(carg) (BUILD_COMPLEX (plus_infty, -2.0)), minus_zero, 0, 0, 0); + + /* carg(x + i inf) == pi/2 for finite x. */ + check_float ("carg (10.0 + inf i) == pi/2", FUNC(carg) (BUILD_COMPLEX (10.0, plus_infty)), M_PI_2l, 0, 0, 0); + + /* carg(x - i inf) == -pi/2 for finite x. */ + check_float ("carg (10.0 - inf i) == -pi/2", FUNC(carg) (BUILD_COMPLEX (10.0, minus_infty)), -M_PI_2l, 0, 0, 0); + + /* carg (-inf + i y) == +pi for finite y > 0. */ + check_float ("carg (-inf + 10.0 i) == pi", FUNC(carg) (BUILD_COMPLEX (minus_infty, 10.0)), M_PIl, 0, 0, 0); + + /* carg (-inf + i y) == -pi for finite y < 0. */ + check_float ("carg (-inf - 10.0 i) == -pi", FUNC(carg) (BUILD_COMPLEX (minus_infty, -10.0)), -M_PIl, 0, 0, 0); + + check_float ("carg (inf + inf i) == pi/4", FUNC(carg) (BUILD_COMPLEX (plus_infty, plus_infty)), M_PI_4l, 0, 0, 0); + + check_float ("carg (inf - inf i) == -pi/4", FUNC(carg) (BUILD_COMPLEX (plus_infty, minus_infty)), -M_PI_4l, 0, 0, 0); + + check_float ("carg (-inf + inf i) == 3 * M_PI_4l", FUNC(carg) (BUILD_COMPLEX (minus_infty, plus_infty)), 3 * M_PI_4l, 0, 0, 0); + + check_float ("carg (-inf - inf i) == -3 * M_PI_4l", FUNC(carg) (BUILD_COMPLEX (minus_infty, minus_infty)), -3 * M_PI_4l, 0, 0, 0); + + check_float ("carg (NaN + NaN i) == NaN", FUNC(carg) (BUILD_COMPLEX (nan_value, nan_value)), nan_value, 0, 0, 0); + + print_max_error ("carg", 0, 0); +} + +static void +casin_test (void) +{ + errno = 0; + FUNC(casin) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("casin (0 + 0 i) == 0.0 + 0.0 i", FUNC(casin) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("casin (-0 + 0 i) == -0 + 0.0 i", FUNC(casin) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("casin (0 - 0 i) == 0.0 - 0 i", FUNC(casin) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("casin (-0 - 0 i) == -0 - 0 i", FUNC(casin) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("casin (inf + inf i) == pi/4 + inf i", FUNC(casin) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (M_PI_4l, plus_infty), 0, 0, 0); + check_complex ("casin (inf - inf i) == pi/4 - inf i", FUNC(casin) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (M_PI_4l, minus_infty), 0, 0, 0); + check_complex ("casin (-inf + inf i) == -pi/4 + inf i", FUNC(casin) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (-M_PI_4l, plus_infty), 0, 0, 0); + check_complex ("casin (-inf - inf i) == -pi/4 - inf i", FUNC(casin) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (-M_PI_4l, minus_infty), 0, 0, 0); + + check_complex ("casin (-10.0 + inf i) == -0 + inf i", FUNC(casin) (BUILD_COMPLEX (-10.0, plus_infty)), BUILD_COMPLEX (minus_zero, plus_infty), 0, 0, 0); + check_complex ("casin (-10.0 - inf i) == -0 - inf i", FUNC(casin) (BUILD_COMPLEX (-10.0, minus_infty)), BUILD_COMPLEX (minus_zero, minus_infty), 0, 0, 0); + check_complex ("casin (0 + inf i) == 0.0 + inf i", FUNC(casin) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + check_complex ("casin (0 - inf i) == 0.0 - inf i", FUNC(casin) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + check_complex ("casin (-0 + inf i) == -0 + inf i", FUNC(casin) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (minus_zero, plus_infty), 0, 0, 0); + check_complex ("casin (-0 - inf i) == -0 - inf i", FUNC(casin) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (minus_zero, minus_infty), 0, 0, 0); + check_complex ("casin (0.1 + inf i) == 0.0 + inf i", FUNC(casin) (BUILD_COMPLEX (0.1L, plus_infty)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + check_complex ("casin (0.1 - inf i) == 0.0 - inf i", FUNC(casin) (BUILD_COMPLEX (0.1L, minus_infty)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + + check_complex ("casin (-inf + 0 i) == -pi/2 + inf i", FUNC(casin) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (-M_PI_2l, plus_infty), 0, 0, 0); + check_complex ("casin (-inf - 0 i) == -pi/2 - inf i", FUNC(casin) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (-M_PI_2l, minus_infty), 0, 0, 0); + check_complex ("casin (-inf + 100 i) == -pi/2 + inf i", FUNC(casin) (BUILD_COMPLEX (minus_infty, 100)), BUILD_COMPLEX (-M_PI_2l, plus_infty), 0, 0, 0); + check_complex ("casin (-inf - 100 i) == -pi/2 - inf i", FUNC(casin) (BUILD_COMPLEX (minus_infty, -100)), BUILD_COMPLEX (-M_PI_2l, minus_infty), 0, 0, 0); + + check_complex ("casin (inf + 0 i) == pi/2 + inf i", FUNC(casin) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (M_PI_2l, plus_infty), 0, 0, 0); + check_complex ("casin (inf - 0 i) == pi/2 - inf i", FUNC(casin) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (M_PI_2l, minus_infty), 0, 0, 0); + check_complex ("casin (inf + 0.5 i) == pi/2 + inf i", FUNC(casin) (BUILD_COMPLEX (plus_infty, 0.5)), BUILD_COMPLEX (M_PI_2l, plus_infty), 0, 0, 0); + check_complex ("casin (inf - 0.5 i) == pi/2 - inf i", FUNC(casin) (BUILD_COMPLEX (plus_infty, -0.5)), BUILD_COMPLEX (M_PI_2l, minus_infty), 0, 0, 0); + + check_complex ("casin (NaN + inf i) == NaN + inf i", FUNC(casin) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, 0); + check_complex ("casin (NaN - inf i) == NaN - inf i", FUNC(casin) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (nan_value, minus_infty), 0, 0, 0); + + check_complex ("casin (0.0 + NaN i) == 0.0 + NaN i", FUNC(casin) (BUILD_COMPLEX (0.0, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, 0); + check_complex ("casin (-0 + NaN i) == -0 + NaN i", FUNC(casin) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (minus_zero, nan_value), 0, 0, 0); + + check_complex ("casin (inf + NaN i) == NaN + inf i plus sign of zero/inf not specified", FUNC(casin) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("casin (-inf + NaN i) == NaN + inf i plus sign of zero/inf not specified", FUNC(casin) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("casin (NaN + 10.5 i) == NaN + NaN i plus invalid exception allowed", FUNC(casin) (BUILD_COMPLEX (nan_value, 10.5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("casin (NaN - 10.5 i) == NaN + NaN i plus invalid exception allowed", FUNC(casin) (BUILD_COMPLEX (nan_value, -10.5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("casin (0.75 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(casin) (BUILD_COMPLEX (0.75, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("casin (-0.75 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(casin) (BUILD_COMPLEX (-0.75, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("casin (NaN + NaN i) == NaN + NaN i", FUNC(casin) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("casin (0.7 + 1.2 i) == 0.4356135790797415103321208644578462 + 1.0927647857577371459105272080819308 i", FUNC(casin) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.4356135790797415103321208644578462L, 1.0927647857577371459105272080819308L), DELTA225, 0, 0); + check_complex ("casin (-2 - 3 i) == -0.57065278432109940071028387968566963 - 1.9833870299165354323470769028940395 i", FUNC(casin) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-0.57065278432109940071028387968566963L, -1.9833870299165354323470769028940395L), DELTA226, 0, 0); + + print_complex_max_error ("casin", DELTAcasin, 0); +} + + +static void +casinh_test (void) +{ + errno = 0; + FUNC(casinh) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("casinh (0 + 0 i) == 0.0 + 0.0 i", FUNC(casinh) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("casinh (-0 + 0 i) == -0 + 0 i", FUNC(casinh) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_zero, 0), 0, 0, 0); + check_complex ("casinh (0 - 0 i) == 0.0 - 0 i", FUNC(casinh) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("casinh (-0 - 0 i) == -0 - 0 i", FUNC(casinh) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("casinh (inf + inf i) == inf + pi/4 i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_4l), 0, 0, 0); + check_complex ("casinh (inf - inf i) == inf - pi/4 i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_4l), 0, 0, 0); + check_complex ("casinh (-inf + inf i) == -inf + pi/4 i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (minus_infty, M_PI_4l), 0, 0, 0); + check_complex ("casinh (-inf - inf i) == -inf - pi/4 i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (minus_infty, -M_PI_4l), 0, 0, 0); + + check_complex ("casinh (-10.0 + inf i) == -inf + pi/2 i", FUNC(casinh) (BUILD_COMPLEX (-10.0, plus_infty)), BUILD_COMPLEX (minus_infty, M_PI_2l), 0, 0, 0); + check_complex ("casinh (-10.0 - inf i) == -inf - pi/2 i", FUNC(casinh) (BUILD_COMPLEX (-10.0, minus_infty)), BUILD_COMPLEX (minus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("casinh (0 + inf i) == inf + pi/2 i", FUNC(casinh) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("casinh (0 - inf i) == inf - pi/2 i", FUNC(casinh) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("casinh (-0 + inf i) == -inf + pi/2 i", FUNC(casinh) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (minus_infty, M_PI_2l), 0, 0, 0); + check_complex ("casinh (-0 - inf i) == -inf - pi/2 i", FUNC(casinh) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (minus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("casinh (0.1 + inf i) == inf + pi/2 i", FUNC(casinh) (BUILD_COMPLEX (0.1L, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("casinh (0.1 - inf i) == inf - pi/2 i", FUNC(casinh) (BUILD_COMPLEX (0.1L, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + + check_complex ("casinh (-inf + 0 i) == -inf + 0.0 i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (minus_infty, 0.0), 0, 0, 0); + check_complex ("casinh (-inf - 0 i) == -inf - 0 i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (minus_infty, minus_zero), 0, 0, 0); + check_complex ("casinh (-inf + 100 i) == -inf + 0.0 i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, 100)), BUILD_COMPLEX (minus_infty, 0.0), 0, 0, 0); + check_complex ("casinh (-inf - 100 i) == -inf - 0 i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, -100)), BUILD_COMPLEX (minus_infty, minus_zero), 0, 0, 0); + + check_complex ("casinh (inf + 0 i) == inf + 0.0 i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("casinh (inf - 0 i) == inf - 0 i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("casinh (inf + 0.5 i) == inf + 0.0 i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, 0.5)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("casinh (inf - 0.5 i) == inf - 0 i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, -0.5)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("casinh (inf + NaN i) == inf + NaN i", FUNC(casinh) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("casinh (-inf + NaN i) == -inf + NaN i", FUNC(casinh) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (minus_infty, nan_value), 0, 0, 0); + + check_complex ("casinh (NaN + 0 i) == NaN + 0.0 i", FUNC(casinh) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, 0); + check_complex ("casinh (NaN - 0 i) == NaN - 0 i", FUNC(casinh) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, minus_zero), 0, 0, 0); + + check_complex ("casinh (NaN + inf i) == inf + NaN i plus sign of zero/inf not specified", FUNC(casinh) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("casinh (NaN - inf i) == inf + NaN i plus sign of zero/inf not specified", FUNC(casinh) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("casinh (10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(casinh) (BUILD_COMPLEX (10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("casinh (-10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(casinh) (BUILD_COMPLEX (-10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("casinh (NaN + 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(casinh) (BUILD_COMPLEX (nan_value, 0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("casinh (-0.75 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(casinh) (BUILD_COMPLEX (-0.75, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("casinh (NaN + NaN i) == NaN + NaN i", FUNC(casinh) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("casinh (0.7 + 1.2 i) == 0.97865459559367387689317593222160964 + 0.91135418953156011567903546856170941 i", FUNC(casinh) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.97865459559367387689317593222160964L, 0.91135418953156011567903546856170941L), DELTA262, 0, 0); + check_complex ("casinh (-2 - 3 i) == -1.9686379257930962917886650952454982 - 0.96465850440760279204541105949953237 i", FUNC(casinh) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-1.9686379257930962917886650952454982L, -0.96465850440760279204541105949953237L), DELTA263, 0, 0); + + print_complex_max_error ("casinh", DELTAcasinh, 0); +} + + +static void +catan_test (void) +{ + errno = 0; + FUNC(catan) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("catan (0 + 0 i) == 0 + 0 i", FUNC(catan) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0, 0), 0, 0, 0); + check_complex ("catan (-0 + 0 i) == -0 + 0 i", FUNC(catan) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_zero, 0), 0, 0, 0); + check_complex ("catan (0 - 0 i) == 0 - 0 i", FUNC(catan) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0, minus_zero), 0, 0, 0); + check_complex ("catan (-0 - 0 i) == -0 - 0 i", FUNC(catan) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("catan (inf + inf i) == pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (inf - inf i) == pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (-inf + inf i) == -pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (-M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (-inf - inf i) == -pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (-M_PI_2l, minus_zero), 0, 0, 0); + + + check_complex ("catan (inf - 10.0 i) == pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (plus_infty, -10.0)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (-inf - 10.0 i) == -pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (minus_infty, -10.0)), BUILD_COMPLEX (-M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (inf - 0 i) == pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (-inf - 0 i) == -pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (-M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (inf + 0.0 i) == pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (plus_infty, 0.0)), BUILD_COMPLEX (M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (-inf + 0.0 i) == -pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (minus_infty, 0.0)), BUILD_COMPLEX (-M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (inf + 0.1 i) == pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (plus_infty, 0.1L)), BUILD_COMPLEX (M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (-inf + 0.1 i) == -pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (minus_infty, 0.1L)), BUILD_COMPLEX (-M_PI_2l, 0), 0, 0, 0); + + check_complex ("catan (0.0 - inf i) == pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (0.0, minus_infty)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (-0 - inf i) == -pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (-M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (100.0 - inf i) == pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (100.0, minus_infty)), BUILD_COMPLEX (M_PI_2l, minus_zero), 0, 0, 0); + check_complex ("catan (-100.0 - inf i) == -pi/2 - 0 i", FUNC(catan) (BUILD_COMPLEX (-100.0, minus_infty)), BUILD_COMPLEX (-M_PI_2l, minus_zero), 0, 0, 0); + + check_complex ("catan (0.0 + inf i) == pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (0.0, plus_infty)), BUILD_COMPLEX (M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (-0 + inf i) == -pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (-M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (0.5 + inf i) == pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (0.5, plus_infty)), BUILD_COMPLEX (M_PI_2l, 0), 0, 0, 0); + check_complex ("catan (-0.5 + inf i) == -pi/2 + 0 i", FUNC(catan) (BUILD_COMPLEX (-0.5, plus_infty)), BUILD_COMPLEX (-M_PI_2l, 0), 0, 0, 0); + + check_complex ("catan (NaN + 0.0 i) == NaN + 0 i", FUNC(catan) (BUILD_COMPLEX (nan_value, 0.0)), BUILD_COMPLEX (nan_value, 0), 0, 0, 0); + check_complex ("catan (NaN - 0 i) == NaN - 0 i", FUNC(catan) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, minus_zero), 0, 0, 0); + + check_complex ("catan (NaN + inf i) == NaN + 0 i", FUNC(catan) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, 0), 0, 0, 0); + check_complex ("catan (NaN - inf i) == NaN - 0 i", FUNC(catan) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (nan_value, minus_zero), 0, 0, 0); + + check_complex ("catan (0.0 + NaN i) == NaN + NaN i", FUNC(catan) (BUILD_COMPLEX (0.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + check_complex ("catan (-0 + NaN i) == NaN + NaN i", FUNC(catan) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("catan (inf + NaN i) == pi/2 + 0 i plus sign of zero/inf not specified", FUNC(catan) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (M_PI_2l, 0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("catan (-inf + NaN i) == -pi/2 + 0 i plus sign of zero/inf not specified", FUNC(catan) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (-M_PI_2l, 0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("catan (NaN + 10.5 i) == NaN + NaN i plus invalid exception allowed", FUNC(catan) (BUILD_COMPLEX (nan_value, 10.5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("catan (NaN - 10.5 i) == NaN + NaN i plus invalid exception allowed", FUNC(catan) (BUILD_COMPLEX (nan_value, -10.5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("catan (0.75 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(catan) (BUILD_COMPLEX (0.75, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("catan (-0.75 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(catan) (BUILD_COMPLEX (-0.75, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("catan (NaN + NaN i) == NaN + NaN i", FUNC(catan) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("catan (0.7 + 1.2 i) == 1.0785743834118921877443707996386368 + 0.57705737765343067644394541889341712 i", FUNC(catan) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.0785743834118921877443707996386368L, 0.57705737765343067644394541889341712L), DELTA301, 0, 0); + + check_complex ("catan (-2 - 3 i) == -1.4099210495965755225306193844604208 - 0.22907268296853876629588180294200276 i", FUNC(catan) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-1.4099210495965755225306193844604208L, -0.22907268296853876629588180294200276L), DELTA302, 0, 0); + + print_complex_max_error ("catan", DELTAcatan, 0); +} + +static void +catanh_test (void) +{ + errno = 0; + FUNC(catanh) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("catanh (0 + 0 i) == 0.0 + 0.0 i", FUNC(catanh) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("catanh (-0 + 0 i) == -0 + 0.0 i", FUNC(catanh) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("catanh (0 - 0 i) == 0.0 - 0 i", FUNC(catanh) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("catanh (-0 - 0 i) == -0 - 0 i", FUNC(catanh) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("catanh (inf + inf i) == 0.0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("catanh (inf - inf i) == 0.0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + check_complex ("catanh (-inf + inf i) == -0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (minus_zero, M_PI_2l), 0, 0, 0); + check_complex ("catanh (-inf - inf i) == -0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (minus_zero, -M_PI_2l), 0, 0, 0); + + check_complex ("catanh (-10.0 + inf i) == -0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (-10.0, plus_infty)), BUILD_COMPLEX (minus_zero, M_PI_2l), 0, 0, 0); + check_complex ("catanh (-10.0 - inf i) == -0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (-10.0, minus_infty)), BUILD_COMPLEX (minus_zero, -M_PI_2l), 0, 0, 0); + check_complex ("catanh (-0 + inf i) == -0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (minus_zero, M_PI_2l), 0, 0, 0); + check_complex ("catanh (-0 - inf i) == -0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (minus_zero, -M_PI_2l), 0, 0, 0); + check_complex ("catanh (0 + inf i) == 0.0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("catanh (0 - inf i) == 0.0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + check_complex ("catanh (0.1 + inf i) == 0.0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (0.1L, plus_infty)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("catanh (0.1 - inf i) == 0.0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (0.1L, minus_infty)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + + check_complex ("catanh (-inf + 0 i) == -0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (minus_zero, M_PI_2l), 0, 0, 0); + check_complex ("catanh (-inf - 0 i) == -0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (minus_zero, -M_PI_2l), 0, 0, 0); + check_complex ("catanh (-inf + 100 i) == -0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, 100)), BUILD_COMPLEX (minus_zero, M_PI_2l), 0, 0, 0); + check_complex ("catanh (-inf - 100 i) == -0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, -100)), BUILD_COMPLEX (minus_zero, -M_PI_2l), 0, 0, 0); + + check_complex ("catanh (inf + 0 i) == 0.0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("catanh (inf - 0 i) == 0.0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + check_complex ("catanh (inf + 0.5 i) == 0.0 + pi/2 i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, 0.5)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, 0); + check_complex ("catanh (inf - 0.5 i) == 0.0 - pi/2 i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, -0.5)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, 0); + + check_complex ("catanh (0 + NaN i) == 0.0 + NaN i", FUNC(catanh) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, 0); + check_complex ("catanh (-0 + NaN i) == -0 + NaN i", FUNC(catanh) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (minus_zero, nan_value), 0, 0, 0); + + check_complex ("catanh (inf + NaN i) == 0.0 + NaN i", FUNC(catanh) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, 0); + check_complex ("catanh (-inf + NaN i) == -0 + NaN i", FUNC(catanh) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (minus_zero, nan_value), 0, 0, 0); + + check_complex ("catanh (NaN + 0 i) == NaN + NaN i", FUNC(catanh) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + check_complex ("catanh (NaN - 0 i) == NaN + NaN i", FUNC(catanh) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("catanh (NaN + inf i) == 0.0 + pi/2 i plus sign of zero/inf not specified", FUNC(catanh) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (0.0, M_PI_2l), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("catanh (NaN - inf i) == 0.0 - pi/2 i plus sign of zero/inf not specified", FUNC(catanh) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (0.0, -M_PI_2l), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("catanh (10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(catanh) (BUILD_COMPLEX (10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("catanh (-10.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(catanh) (BUILD_COMPLEX (-10.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("catanh (NaN + 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(catanh) (BUILD_COMPLEX (nan_value, 0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("catanh (NaN - 0.75 i) == NaN + NaN i plus invalid exception allowed", FUNC(catanh) (BUILD_COMPLEX (nan_value, -0.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("catanh (NaN + NaN i) == NaN + NaN i", FUNC(catanh) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("catanh (0.7 + 1.2 i) == 0.2600749516525135959200648705635915 + 0.97024030779509898497385130162655963 i", FUNC(catanh) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.2600749516525135959200648705635915L, 0.97024030779509898497385130162655963L), DELTA340, 0, 0); + check_complex ("catanh (-2 - 3 i) == -0.14694666622552975204743278515471595 - 1.3389725222944935611241935759091443 i", FUNC(catanh) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-0.14694666622552975204743278515471595L, -1.3389725222944935611241935759091443L), DELTA341, 0, 0); + + print_complex_max_error ("catanh", DELTAcatanh, 0); +} +#endif + +static void +cbrt_test (void) +{ + errno = 0; + FUNC(cbrt) (8); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("cbrt (0.0) == 0.0", FUNC(cbrt) (0.0), 0.0, 0, 0, 0); + check_float ("cbrt (-0) == -0", FUNC(cbrt) (minus_zero), minus_zero, 0, 0, 0); + + check_float ("cbrt (inf) == inf", FUNC(cbrt) (plus_infty), plus_infty, 0, 0, 0); + check_float ("cbrt (-inf) == -inf", FUNC(cbrt) (minus_infty), minus_infty, 0, 0, 0); + check_float ("cbrt (NaN) == NaN", FUNC(cbrt) (nan_value), nan_value, 0, 0, 0); + + check_float ("cbrt (-0.001) == -0.1", FUNC(cbrt) (-0.001L), -0.1L, DELTA347, 0, 0); + check_float ("cbrt (8) == 2", FUNC(cbrt) (8), 2, 0, 0, 0); + check_float ("cbrt (-27.0) == -3.0", FUNC(cbrt) (-27.0), -3.0, DELTA349, 0, 0); + check_float ("cbrt (0.970299) == 0.99", FUNC(cbrt) (0.970299L), 0.99L, DELTA350, 0, 0); + check_float ("cbrt (0.7) == 0.8879040017426007084", FUNC(cbrt) (0.7L), 0.8879040017426007084L, DELTA351, 0, 0); + + print_max_error ("cbrt", DELTAcbrt, 0); +} + +#if 0 /* XXX scp XXX */ +static void +ccos_test (void) +{ + errno = 0; + FUNC(ccos) (BUILD_COMPLEX (0, 0)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("ccos (0.0 + 0.0 i) == 1.0 - 0 i", FUNC(ccos) (BUILD_COMPLEX (0.0, 0.0)), BUILD_COMPLEX (1.0, minus_zero), 0, 0, 0); + check_complex ("ccos (-0 + 0.0 i) == 1.0 + 0.0 i", FUNC(ccos) (BUILD_COMPLEX (minus_zero, 0.0)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("ccos (0.0 - 0 i) == 1.0 + 0.0 i", FUNC(ccos) (BUILD_COMPLEX (0.0, minus_zero)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("ccos (-0 - 0 i) == 1.0 - 0 i", FUNC(ccos) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (1.0, minus_zero), 0, 0, 0); + + check_complex ("ccos (inf + 0.0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (plus_infty, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("ccos (inf - 0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("ccos (-inf + 0.0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (minus_infty, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("ccos (-inf - 0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("ccos (0.0 + inf i) == inf - 0 i", FUNC(ccos) (BUILD_COMPLEX (0.0, plus_infty)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("ccos (0.0 - inf i) == inf + 0.0 i", FUNC(ccos) (BUILD_COMPLEX (0.0, minus_infty)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("ccos (-0 + inf i) == inf + 0.0 i", FUNC(ccos) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("ccos (-0 - inf i) == inf - 0 i", FUNC(ccos) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("ccos (inf + inf i) == inf + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccos (-inf + inf i) == inf + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccos (inf - inf i) == inf + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccos (-inf - inf i) == inf + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("ccos (4.625 + inf i) == -inf + inf i", FUNC(ccos) (BUILD_COMPLEX (4.625, plus_infty)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + check_complex ("ccos (4.625 - inf i) == -inf - inf i", FUNC(ccos) (BUILD_COMPLEX (4.625, minus_infty)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + check_complex ("ccos (-4.625 + inf i) == -inf - inf i", FUNC(ccos) (BUILD_COMPLEX (-4.625, plus_infty)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + check_complex ("ccos (-4.625 - inf i) == -inf + inf i", FUNC(ccos) (BUILD_COMPLEX (-4.625, minus_infty)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + + check_complex ("ccos (inf + 6.75 i) == NaN + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (plus_infty, 6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccos (inf - 6.75 i) == NaN + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (plus_infty, -6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccos (-inf + 6.75 i) == NaN + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (minus_infty, 6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccos (-inf - 6.75 i) == NaN + NaN i plus invalid exception", FUNC(ccos) (BUILD_COMPLEX (minus_infty, -6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("ccos (NaN + 0.0 i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (nan_value, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("ccos (NaN - 0 i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("ccos (NaN + inf i) == inf + NaN i", FUNC(ccos) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("ccos (NaN - inf i) == inf + NaN i", FUNC(ccos) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("ccos (NaN + 9.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(ccos) (BUILD_COMPLEX (nan_value, 9.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ccos (NaN - 9.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(ccos) (BUILD_COMPLEX (nan_value, -9.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ccos (0.0 + NaN i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (0.0, nan_value)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("ccos (-0 + NaN i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccos) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("ccos (10.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ccos) (BUILD_COMPLEX (10.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ccos (-10.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ccos) (BUILD_COMPLEX (-10.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ccos (inf + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ccos) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ccos (-inf + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ccos) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ccos (NaN + NaN i) == NaN + NaN i", FUNC(ccos) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("ccos (0.7 + 1.2 i) == 1.3848657645312111080 - 0.97242170335830028619 i", FUNC(ccos) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.3848657645312111080L, -0.97242170335830028619L), DELTA389, 0, 0); + + check_complex ("ccos (-2 - 3 i) == -4.1896256909688072301 - 9.1092278937553365979 i", FUNC(ccos) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-4.1896256909688072301L, -9.1092278937553365979L), DELTA390, 0, 0); + + print_complex_max_error ("ccos", DELTAccos, 0); +} + + +static void +ccosh_test (void) +{ + errno = 0; + FUNC(ccosh) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("ccosh (0.0 + 0.0 i) == 1.0 + 0.0 i", FUNC(ccosh) (BUILD_COMPLEX (0.0, 0.0)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("ccosh (-0 + 0.0 i) == 1.0 - 0 i", FUNC(ccosh) (BUILD_COMPLEX (minus_zero, 0.0)), BUILD_COMPLEX (1.0, minus_zero), 0, 0, 0); + check_complex ("ccosh (0.0 - 0 i) == 1.0 - 0 i", FUNC(ccosh) (BUILD_COMPLEX (0.0, minus_zero)), BUILD_COMPLEX (1.0, minus_zero), 0, 0, 0); + check_complex ("ccosh (-0 - 0 i) == 1.0 + 0.0 i", FUNC(ccosh) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + + check_complex ("ccosh (0.0 + inf i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (0.0, plus_infty)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("ccosh (-0 + inf i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("ccosh (0.0 - inf i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (0.0, minus_infty)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("ccosh (-0 - inf i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("ccosh (inf + 0.0 i) == inf + 0.0 i", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, 0.0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("ccosh (-inf + 0.0 i) == inf - 0 i", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, 0.0)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("ccosh (inf - 0 i) == inf - 0 i", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("ccosh (-inf - 0 i) == inf + 0.0 i", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + + check_complex ("ccosh (inf + inf i) == inf + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccosh (-inf + inf i) == inf + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccosh (inf - inf i) == inf + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccosh (-inf - inf i) == inf + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("ccosh (inf + 4.625 i) == -inf - inf i", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, 4.625)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + check_complex ("ccosh (-inf + 4.625 i) == -inf + inf i", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, 4.625)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + check_complex ("ccosh (inf - 4.625 i) == -inf + inf i", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, -4.625)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + check_complex ("ccosh (-inf - 4.625 i) == -inf - inf i", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, -4.625)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + + check_complex ("ccosh (6.75 + inf i) == NaN + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (6.75, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccosh (-6.75 + inf i) == NaN + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (-6.75, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccosh (6.75 - inf i) == NaN + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (6.75, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ccosh (-6.75 - inf i) == NaN + NaN i plus invalid exception", FUNC(ccosh) (BUILD_COMPLEX (-6.75, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("ccosh (0.0 + NaN i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (0.0, nan_value)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("ccosh (-0 + NaN i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("ccosh (inf + NaN i) == inf + NaN i", FUNC(ccosh) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("ccosh (-inf + NaN i) == inf + NaN i", FUNC(ccosh) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("ccosh (9.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ccosh) (BUILD_COMPLEX (9.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ccosh (-9.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ccosh) (BUILD_COMPLEX (-9.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ccosh (NaN + 0.0 i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (nan_value, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("ccosh (NaN - 0 i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(ccosh) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("ccosh (NaN + 10.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(ccosh) (BUILD_COMPLEX (nan_value, 10.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ccosh (NaN - 10.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(ccosh) (BUILD_COMPLEX (nan_value, -10.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ccosh (NaN + inf i) == NaN + NaN i plus invalid exception allowed", FUNC(ccosh) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ccosh (NaN - inf i) == NaN + NaN i plus invalid exception allowed", FUNC(ccosh) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ccosh (NaN + NaN i) == NaN + NaN i", FUNC(ccosh) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("ccosh (0.7 + 1.2 i) == 0.4548202223691477654 + 0.7070296600921537682 i", FUNC(ccosh) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.4548202223691477654L, 0.7070296600921537682L), DELTA428, 0, 0); + + check_complex ("ccosh (-2 - 3 i) == -3.7245455049153225654 + 0.5118225699873846088 i", FUNC(ccosh) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-3.7245455049153225654L, 0.5118225699873846088L), DELTA429, 0, 0); + + print_complex_max_error ("ccosh", DELTAccosh, 0); +} +#endif + + +static void +ceil_test (void) +{ + init_max_error (); + + check_float ("ceil (0.0) == 0.0", FUNC(ceil) (0.0), 0.0, 0, 0, 0); + check_float ("ceil (-0) == -0", FUNC(ceil) (minus_zero), minus_zero, 0, 0, 0); + check_float ("ceil (inf) == inf", FUNC(ceil) (plus_infty), plus_infty, 0, 0, 0); + check_float ("ceil (-inf) == -inf", FUNC(ceil) (minus_infty), minus_infty, 0, 0, 0); + check_float ("ceil (NaN) == NaN", FUNC(ceil) (nan_value), nan_value, 0, 0, 0); + + check_float ("ceil (pi) == 4.0", FUNC(ceil) (M_PIl), 4.0, 0, 0, 0); + check_float ("ceil (-pi) == -3.0", FUNC(ceil) (-M_PIl), -3.0, 0, 0, 0); + + print_max_error ("ceil", 0, 0); +} + + +#if 0 /* XXX scp XXX */ +static void +cexp_test (void) +{ + errno = 0; + FUNC(cexp) (BUILD_COMPLEX (0, 0)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("cexp (+0 + +0 i) == 1 + 0.0 i", FUNC(cexp) (BUILD_COMPLEX (plus_zero, plus_zero)), BUILD_COMPLEX (1, 0.0), 0, 0, 0); + check_complex ("cexp (-0 + +0 i) == 1 + 0.0 i", FUNC(cexp) (BUILD_COMPLEX (minus_zero, plus_zero)), BUILD_COMPLEX (1, 0.0), 0, 0, 0); + check_complex ("cexp (+0 - 0 i) == 1 - 0 i", FUNC(cexp) (BUILD_COMPLEX (plus_zero, minus_zero)), BUILD_COMPLEX (1, minus_zero), 0, 0, 0); + check_complex ("cexp (-0 - 0 i) == 1 - 0 i", FUNC(cexp) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (1, minus_zero), 0, 0, 0); + + check_complex ("cexp (inf + +0 i) == inf + 0.0 i", FUNC(cexp) (BUILD_COMPLEX (plus_infty, plus_zero)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("cexp (inf - 0 i) == inf - 0 i", FUNC(cexp) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("cexp (-inf + +0 i) == 0.0 + 0.0 i", FUNC(cexp) (BUILD_COMPLEX (minus_infty, plus_zero)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("cexp (-inf - 0 i) == 0.0 - 0 i", FUNC(cexp) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + + check_complex ("cexp (0.0 + inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (0.0, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("cexp (-0 + inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("cexp (0.0 - inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (0.0, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("cexp (-0 - inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("cexp (100.0 + inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (100.0, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("cexp (-100.0 + inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (-100.0, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("cexp (100.0 - inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (100.0, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("cexp (-100.0 - inf i) == NaN + NaN i plus invalid exception", FUNC(cexp) (BUILD_COMPLEX (-100.0, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("cexp (-inf + 2.0 i) == -0 + 0.0 i", FUNC(cexp) (BUILD_COMPLEX (minus_infty, 2.0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("cexp (-inf + 4.0 i) == -0 - 0 i", FUNC(cexp) (BUILD_COMPLEX (minus_infty, 4.0)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + check_complex ("cexp (inf + 2.0 i) == -inf + inf i", FUNC(cexp) (BUILD_COMPLEX (plus_infty, 2.0)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + check_complex ("cexp (inf + 4.0 i) == -inf - inf i", FUNC(cexp) (BUILD_COMPLEX (plus_infty, 4.0)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + + check_complex ("cexp (inf + inf i) == inf + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(cexp) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("cexp (inf - inf i) == inf + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(cexp) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("cexp (-inf + inf i) == 0.0 + 0.0 i plus sign of zero/inf not specified", FUNC(cexp) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (0.0, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("cexp (-inf - inf i) == 0.0 - 0 i plus sign of zero/inf not specified", FUNC(cexp) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("cexp (-inf + NaN i) == 0 + 0 i plus sign of zero/inf not specified", FUNC(cexp) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (0, 0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("cexp (inf + NaN i) == inf + NaN i", FUNC(cexp) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("cexp (NaN + 0.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(cexp) (BUILD_COMPLEX (nan_value, 0.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cexp (NaN + 1.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(cexp) (BUILD_COMPLEX (nan_value, 1.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("cexp (NaN + inf i) == NaN + NaN i plus invalid exception allowed", FUNC(cexp) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cexp (0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(cexp) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cexp (1 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(cexp) (BUILD_COMPLEX (1, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("cexp (NaN + NaN i) == NaN + NaN i", FUNC(cexp) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("cexp (0.7 + 1.2 i) == 0.72969890915032360123451688642930727 + 1.8768962328348102821139467908203072 i", FUNC(cexp) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.72969890915032360123451688642930727L, 1.8768962328348102821139467908203072L), DELTA469, 0, 0); + check_complex ("cexp (-2.0 - 3.0 i) == -0.13398091492954261346140525546115575 - 0.019098516261135196432576240858800925 i", FUNC(cexp) (BUILD_COMPLEX (-2.0, -3.0)), BUILD_COMPLEX (-0.13398091492954261346140525546115575L, -0.019098516261135196432576240858800925L), DELTA470, 0, 0); + + print_complex_max_error ("cexp", DELTAcexp, 0); +} + +static void +cimag_test (void) +{ + init_max_error (); + check_float ("cimag (1.0 + 0.0 i) == 0.0", FUNC(cimag) (BUILD_COMPLEX (1.0, 0.0)), 0.0, 0, 0, 0); + check_float ("cimag (1.0 - 0 i) == -0", FUNC(cimag) (BUILD_COMPLEX (1.0, minus_zero)), minus_zero, 0, 0, 0); + check_float ("cimag (1.0 + NaN i) == NaN", FUNC(cimag) (BUILD_COMPLEX (1.0, nan_value)), nan_value, 0, 0, 0); + check_float ("cimag (NaN + NaN i) == NaN", FUNC(cimag) (BUILD_COMPLEX (nan_value, nan_value)), nan_value, 0, 0, 0); + check_float ("cimag (1.0 + inf i) == inf", FUNC(cimag) (BUILD_COMPLEX (1.0, plus_infty)), plus_infty, 0, 0, 0); + check_float ("cimag (1.0 - inf i) == -inf", FUNC(cimag) (BUILD_COMPLEX (1.0, minus_infty)), minus_infty, 0, 0, 0); + check_float ("cimag (2.0 + 3.0 i) == 3.0", FUNC(cimag) (BUILD_COMPLEX (2.0, 3.0)), 3.0, 0, 0, 0); + + print_max_error ("cimag", 0, 0); +} + +static void +clog_test (void) +{ + errno = 0; + FUNC(clog) (BUILD_COMPLEX (-2, -3)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("clog (-0 + 0 i) == -inf + pi i plus division by zero exception", FUNC(clog) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_infty, M_PIl), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_complex ("clog (-0 - 0 i) == -inf - pi i plus division by zero exception", FUNC(clog) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_infty, -M_PIl), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_complex ("clog (0 + 0 i) == -inf + 0.0 i plus division by zero exception", FUNC(clog) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (minus_infty, 0.0), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_complex ("clog (0 - 0 i) == -inf - 0 i plus division by zero exception", FUNC(clog) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (minus_infty, minus_zero), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_complex ("clog (-inf + inf i) == inf + 3/4 pi i", FUNC(clog) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_34l), 0, 0, 0); + check_complex ("clog (-inf - inf i) == inf - 3/4 pi i", FUNC(clog) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_34l), 0, 0, 0); + + check_complex ("clog (inf + inf i) == inf + pi/4 i", FUNC(clog) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_4l), 0, 0, 0); + check_complex ("clog (inf - inf i) == inf - pi/4 i", FUNC(clog) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_4l), 0, 0, 0); + + check_complex ("clog (0 + inf i) == inf + pi/2 i", FUNC(clog) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("clog (3 + inf i) == inf + pi/2 i", FUNC(clog) (BUILD_COMPLEX (3, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("clog (-0 + inf i) == inf + pi/2 i", FUNC(clog) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("clog (-3 + inf i) == inf + pi/2 i", FUNC(clog) (BUILD_COMPLEX (-3, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_2l), 0, 0, 0); + check_complex ("clog (0 - inf i) == inf - pi/2 i", FUNC(clog) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("clog (3 - inf i) == inf - pi/2 i", FUNC(clog) (BUILD_COMPLEX (3, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("clog (-0 - inf i) == inf - pi/2 i", FUNC(clog) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + check_complex ("clog (-3 - inf i) == inf - pi/2 i", FUNC(clog) (BUILD_COMPLEX (-3, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI_2l), 0, 0, 0); + + check_complex ("clog (-inf + 0 i) == inf + pi i", FUNC(clog) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (plus_infty, M_PIl), 0, 0, 0); + check_complex ("clog (-inf + 1 i) == inf + pi i", FUNC(clog) (BUILD_COMPLEX (minus_infty, 1)), BUILD_COMPLEX (plus_infty, M_PIl), 0, 0, 0); + check_complex ("clog (-inf - 0 i) == inf - pi i", FUNC(clog) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, -M_PIl), 0, 0, 0); + check_complex ("clog (-inf - 1 i) == inf - pi i", FUNC(clog) (BUILD_COMPLEX (minus_infty, -1)), BUILD_COMPLEX (plus_infty, -M_PIl), 0, 0, 0); + + check_complex ("clog (inf + 0 i) == inf + 0.0 i", FUNC(clog) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("clog (inf + 1 i) == inf + 0.0 i", FUNC(clog) (BUILD_COMPLEX (plus_infty, 1)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("clog (inf - 0 i) == inf - 0 i", FUNC(clog) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("clog (inf - 1 i) == inf - 0 i", FUNC(clog) (BUILD_COMPLEX (plus_infty, -1)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("clog (inf + NaN i) == inf + NaN i", FUNC(clog) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("clog (-inf + NaN i) == inf + NaN i", FUNC(clog) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("clog (NaN + inf i) == inf + NaN i", FUNC(clog) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("clog (NaN - inf i) == inf + NaN i", FUNC(clog) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("clog (0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog (3 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (3, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog (-0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog (-3 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (-3, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("clog (NaN + 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog (NaN + 5 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (nan_value, 5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog (NaN - 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog (NaN - 5 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog) (BUILD_COMPLEX (nan_value, -5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("clog (NaN + NaN i) == NaN + NaN i", FUNC(clog) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + check_complex ("clog (-2 - 3 i) == 1.2824746787307683680267437207826593 - 2.1587989303424641704769327722648368 i", FUNC(clog) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (1.2824746787307683680267437207826593L, -2.1587989303424641704769327722648368L), DELTA515, 0, 0); + + print_complex_max_error ("clog", DELTAclog, 0); +} + + +static void +clog10_test (void) +{ + errno = 0; + FUNC(clog10) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("clog10 (-0 + 0 i) == -inf + pi i plus division by zero exception", FUNC(clog10) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_infty, M_PIl), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_complex ("clog10 (-0 - 0 i) == -inf - pi i plus division by zero exception", FUNC(clog10) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_infty, -M_PIl), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_complex ("clog10 (0 + 0 i) == -inf + 0.0 i plus division by zero exception", FUNC(clog10) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (minus_infty, 0.0), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_complex ("clog10 (0 - 0 i) == -inf - 0 i plus division by zero exception", FUNC(clog10) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (minus_infty, minus_zero), 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_complex ("clog10 (-inf + inf i) == inf + 3/4 pi*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI_34_LOG10El), DELTA520, 0, 0); + + check_complex ("clog10 (inf + inf i) == inf + pi/4*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI4_LOG10El), DELTA521, 0, 0); + check_complex ("clog10 (inf - inf i) == inf - pi/4*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI4_LOG10El), DELTA522, 0, 0); + + check_complex ("clog10 (0 + inf i) == inf + pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI2_LOG10El), DELTA523, 0, 0); + check_complex ("clog10 (3 + inf i) == inf + pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (3, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI2_LOG10El), DELTA524, 0, 0); + check_complex ("clog10 (-0 + inf i) == inf + pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI2_LOG10El), DELTA525, 0, 0); + check_complex ("clog10 (-3 + inf i) == inf + pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (-3, plus_infty)), BUILD_COMPLEX (plus_infty, M_PI2_LOG10El), DELTA526, 0, 0); + check_complex ("clog10 (0 - inf i) == inf - pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI2_LOG10El), DELTA527, 0, 0); + check_complex ("clog10 (3 - inf i) == inf - pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (3, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI2_LOG10El), DELTA528, 0, 0); + check_complex ("clog10 (-0 - inf i) == inf - pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI2_LOG10El), DELTA529, 0, 0); + check_complex ("clog10 (-3 - inf i) == inf - pi/2*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (-3, minus_infty)), BUILD_COMPLEX (plus_infty, -M_PI2_LOG10El), DELTA530, 0, 0); + + check_complex ("clog10 (-inf + 0 i) == inf + pi*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (plus_infty, M_PI_LOG10El), DELTA531, 0, 0); + check_complex ("clog10 (-inf + 1 i) == inf + pi*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_infty, 1)), BUILD_COMPLEX (plus_infty, M_PI_LOG10El), DELTA532, 0, 0); + check_complex ("clog10 (-inf - 0 i) == inf - pi*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, -M_PI_LOG10El), DELTA533, 0, 0); + check_complex ("clog10 (-inf - 1 i) == inf - pi*log10(e) i", FUNC(clog10) (BUILD_COMPLEX (minus_infty, -1)), BUILD_COMPLEX (plus_infty, -M_PI_LOG10El), DELTA534, 0, 0); + + check_complex ("clog10 (inf + 0 i) == inf + 0.0 i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("clog10 (inf + 1 i) == inf + 0.0 i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, 1)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("clog10 (inf - 0 i) == inf - 0 i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("clog10 (inf - 1 i) == inf - 0 i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, -1)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("clog10 (inf + NaN i) == inf + NaN i", FUNC(clog10) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("clog10 (-inf + NaN i) == inf + NaN i", FUNC(clog10) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("clog10 (NaN + inf i) == inf + NaN i", FUNC(clog10) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + check_complex ("clog10 (NaN - inf i) == inf + NaN i", FUNC(clog10) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("clog10 (0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog10 (3 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (3, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog10 (-0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog10 (-3 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (-3, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("clog10 (NaN + 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog10 (NaN + 5 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (nan_value, 5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog10 (NaN - 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("clog10 (NaN - 5 i) == NaN + NaN i plus invalid exception allowed", FUNC(clog10) (BUILD_COMPLEX (nan_value, -5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("clog10 (NaN + NaN i) == NaN + NaN i", FUNC(clog10) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("clog10 (0.7 + 1.2 i) == 0.1427786545038868803 + 0.4528483579352493248 i", FUNC(clog10) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.1427786545038868803L, 0.4528483579352493248L), DELTA552, 0, 0); + check_complex ("clog10 (-2 - 3 i) == 0.5569716761534183846 - 0.9375544629863747085 i", FUNC(clog10) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (0.5569716761534183846L, -0.9375544629863747085L), DELTA553, 0, 0); + + print_complex_max_error ("clog10", DELTAclog10, 0); +} + +static void +conj_test (void) +{ + init_max_error (); + check_complex ("conj (0.0 + 0.0 i) == 0.0 - 0 i", FUNC(conj) (BUILD_COMPLEX (0.0, 0.0)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("conj (0.0 - 0 i) == 0.0 + 0.0 i", FUNC(conj) (BUILD_COMPLEX (0.0, minus_zero)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("conj (NaN + NaN i) == NaN + NaN i", FUNC(conj) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + check_complex ("conj (inf - inf i) == inf + inf i", FUNC(conj) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("conj (inf + inf i) == inf - inf i", FUNC(conj) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("conj (1.0 + 2.0 i) == 1.0 - 2.0 i", FUNC(conj) (BUILD_COMPLEX (1.0, 2.0)), BUILD_COMPLEX (1.0, -2.0), 0, 0, 0); + check_complex ("conj (3.0 - 4.0 i) == 3.0 + 4.0 i", FUNC(conj) (BUILD_COMPLEX (3.0, -4.0)), BUILD_COMPLEX (3.0, 4.0), 0, 0, 0); + + print_complex_max_error ("conj", 0, 0); +} +#endif + + +static void +copysign_test (void) +{ + init_max_error (); + + check_float ("copysign (0, 4) == 0", FUNC(copysign) (0, 4), 0, 0, 0, 0); + check_float ("copysign (0, -4) == -0", FUNC(copysign) (0, -4), minus_zero, 0, 0, 0); + check_float ("copysign (-0, 4) == 0", FUNC(copysign) (minus_zero, 4), 0, 0, 0, 0); + check_float ("copysign (-0, -4) == -0", FUNC(copysign) (minus_zero, -4), minus_zero, 0, 0, 0); + + check_float ("copysign (inf, 0) == inf", FUNC(copysign) (plus_infty, 0), plus_infty, 0, 0, 0); + check_float ("copysign (inf, -0) == -inf", FUNC(copysign) (plus_infty, minus_zero), minus_infty, 0, 0, 0); + check_float ("copysign (-inf, 0) == inf", FUNC(copysign) (minus_infty, 0), plus_infty, 0, 0, 0); + check_float ("copysign (-inf, -0) == -inf", FUNC(copysign) (minus_infty, minus_zero), minus_infty, 0, 0, 0); + + check_float ("copysign (0, inf) == 0", FUNC(copysign) (0, plus_infty), 0, 0, 0, 0); + check_float ("copysign (0, -0) == -0", FUNC(copysign) (0, minus_zero), minus_zero, 0, 0, 0); + check_float ("copysign (-0, inf) == 0", FUNC(copysign) (minus_zero, plus_infty), 0, 0, 0, 0); + check_float ("copysign (-0, -0) == -0", FUNC(copysign) (minus_zero, minus_zero), minus_zero, 0, 0, 0); + + /* XXX More correctly we would have to check the sign of the NaN. */ + check_float ("copysign (NaN, 0) == NaN", FUNC(copysign) (nan_value, 0), nan_value, 0, 0, 0); + check_float ("copysign (NaN, -0) == NaN", FUNC(copysign) (nan_value, minus_zero), nan_value, 0, 0, 0); + check_float ("copysign (-NaN, 0) == NaN", FUNC(copysign) (-nan_value, 0), nan_value, 0, 0, 0); + check_float ("copysign (-NaN, -0) == NaN", FUNC(copysign) (-nan_value, minus_zero), nan_value, 0, 0, 0); + + print_max_error ("copysign", 0, 0); +} + +static void +cos_test (void) +{ + errno = 0; + FUNC(cos) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("cos (0) == 1", FUNC(cos) (0), 1, 0, 0, 0); + check_float ("cos (-0) == 1", FUNC(cos) (minus_zero), 1, 0, 0, 0); + check_float ("cos (inf) == NaN plus invalid exception", FUNC(cos) (plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("cos (-inf) == NaN plus invalid exception", FUNC(cos) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("cos (NaN) == NaN", FUNC(cos) (nan_value), nan_value, 0, 0, 0); + + check_float ("cos (M_PI_6l * 2.0) == 0.5", FUNC(cos) (M_PI_6l * 2.0), 0.5, DELTA582, 0, 0); + check_float ("cos (M_PI_6l * 4.0) == -0.5", FUNC(cos) (M_PI_6l * 4.0), -0.5, DELTA583, 0, 0); + check_float ("cos (pi/2) == 0", FUNC(cos) (M_PI_2l), 0, DELTA584, 0, 0); + + check_float ("cos (0.7) == 0.76484218728448842625585999019186495", FUNC(cos) (0.7L), 0.76484218728448842625585999019186495L, DELTA585, 0, 0); + + print_max_error ("cos", DELTAcos, 0); +} + +static void +cosh_test (void) +{ + errno = 0; + FUNC(cosh) (0.7L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + check_float ("cosh (0) == 1", FUNC(cosh) (0), 1, 0, 0, 0); + check_float ("cosh (-0) == 1", FUNC(cosh) (minus_zero), 1, 0, 0, 0); + +#ifndef TEST_INLINE + check_float ("cosh (inf) == inf", FUNC(cosh) (plus_infty), plus_infty, 0, 0, 0); + check_float ("cosh (-inf) == inf", FUNC(cosh) (minus_infty), plus_infty, 0, 0, 0); +#endif + check_float ("cosh (NaN) == NaN", FUNC(cosh) (nan_value), nan_value, 0, 0, 0); + + check_float ("cosh (0.7) == 1.255169005630943018", FUNC(cosh) (0.7L), 1.255169005630943018L, DELTA591, 0, 0); + print_max_error ("cosh", DELTAcosh, 0); +} + + +#if 0 /* XXX scp XXX */ +static void +cpow_test (void) +{ + errno = 0; + FUNC(cpow) (BUILD_COMPLEX (1, 0), BUILD_COMPLEX (0, 0)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("cpow (1 + 0 i, 0 + 0 i) == 1.0 + 0.0 i", FUNC(cpow) (BUILD_COMPLEX (1, 0), BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("cpow (2 + 0 i, 10 + 0 i) == 1024.0 + 0.0 i", FUNC(cpow) (BUILD_COMPLEX (2, 0), BUILD_COMPLEX (10, 0)), BUILD_COMPLEX (1024.0, 0.0), 0, 0, 0); + + check_complex ("cpow (e + 0 i, 0 + 2 * M_PIl i) == 1.0 + 0.0 i", FUNC(cpow) (BUILD_COMPLEX (M_El, 0), BUILD_COMPLEX (0, 2 * M_PIl)), BUILD_COMPLEX (1.0, 0.0), DELTA594, 0, 0); + check_complex ("cpow (2 + 3 i, 4 + 0 i) == -119.0 - 120.0 i", FUNC(cpow) (BUILD_COMPLEX (2, 3), BUILD_COMPLEX (4, 0)), BUILD_COMPLEX (-119.0, -120.0), DELTA595, 0, 0); + + check_complex ("cpow (NaN + NaN i, NaN + NaN i) == NaN + NaN i", FUNC(cpow) (BUILD_COMPLEX (nan_value, nan_value), BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + print_complex_max_error ("cpow", DELTAcpow, 0); +} + +static void +cproj_test (void) +{ + init_max_error (); + check_complex ("cproj (0.0 + 0.0 i) == 0.0 + 0.0 i", FUNC(cproj) (BUILD_COMPLEX (0.0, 0.0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("cproj (-0 - 0 i) == -0 - 0 i", FUNC(cproj) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + check_complex ("cproj (0.0 - 0 i) == 0.0 - 0 i", FUNC(cproj) (BUILD_COMPLEX (0.0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("cproj (-0 + 0.0 i) == -0 + 0.0 i", FUNC(cproj) (BUILD_COMPLEX (minus_zero, 0.0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + + check_complex ("cproj (NaN + NaN i) == NaN + NaN i", FUNC(cproj) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("cproj (inf + inf i) == inf + 0.0 i", FUNC(cproj) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("cproj (inf - inf i) == inf - 0 i", FUNC(cproj) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("cproj (-inf + inf i) == inf + 0.0 i", FUNC(cproj) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("cproj (-inf - inf i) == inf - 0 i", FUNC(cproj) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("cproj (1.0 + 0.0 i) == 1.0 + 0.0 i", FUNC(cproj) (BUILD_COMPLEX (1.0, 0.0)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("cproj (2.0 + 3.0 i) == 0.2857142857142857142857142857142857 + 0.42857142857142857142857142857142855 i", FUNC(cproj) (BUILD_COMPLEX (2.0, 3.0)), BUILD_COMPLEX (0.2857142857142857142857142857142857L, 0.42857142857142857142857142857142855L), 0, 0, 0); + + print_complex_max_error ("cproj", 0, 0); +} + +static void +creal_test (void) +{ + init_max_error (); + check_float ("creal (0.0 + 1.0 i) == 0.0", FUNC(creal) (BUILD_COMPLEX (0.0, 1.0)), 0.0, 0, 0, 0); + check_float ("creal (-0 + 1.0 i) == -0", FUNC(creal) (BUILD_COMPLEX (minus_zero, 1.0)), minus_zero, 0, 0, 0); + check_float ("creal (NaN + 1.0 i) == NaN", FUNC(creal) (BUILD_COMPLEX (nan_value, 1.0)), nan_value, 0, 0, 0); + check_float ("creal (NaN + NaN i) == NaN", FUNC(creal) (BUILD_COMPLEX (nan_value, nan_value)), nan_value, 0, 0, 0); + check_float ("creal (inf + 1.0 i) == inf", FUNC(creal) (BUILD_COMPLEX (plus_infty, 1.0)), plus_infty, 0, 0, 0); + check_float ("creal (-inf + 1.0 i) == -inf", FUNC(creal) (BUILD_COMPLEX (minus_infty, 1.0)), minus_infty, 0, 0, 0); + check_float ("creal (2.0 + 3.0 i) == 2.0", FUNC(creal) (BUILD_COMPLEX (2.0, 3.0)), 2.0, 0, 0, 0); + + print_max_error ("creal", 0, 0); +} + +static void +csin_test (void) +{ + errno = 0; + FUNC(csin) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("csin (0.0 + 0.0 i) == 0.0 + 0.0 i", FUNC(csin) (BUILD_COMPLEX (0.0, 0.0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("csin (-0 + 0.0 i) == -0 + 0.0 i", FUNC(csin) (BUILD_COMPLEX (minus_zero, 0.0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("csin (0.0 - 0 i) == 0 - 0 i", FUNC(csin) (BUILD_COMPLEX (0.0, minus_zero)), BUILD_COMPLEX (0, minus_zero), 0, 0, 0); + check_complex ("csin (-0 - 0 i) == -0 - 0 i", FUNC(csin) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("csin (0.0 + inf i) == 0.0 + inf i", FUNC(csin) (BUILD_COMPLEX (0.0, plus_infty)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + check_complex ("csin (-0 + inf i) == -0 + inf i", FUNC(csin) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (minus_zero, plus_infty), 0, 0, 0); + check_complex ("csin (0.0 - inf i) == 0.0 - inf i", FUNC(csin) (BUILD_COMPLEX (0.0, minus_infty)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + check_complex ("csin (-0 - inf i) == -0 - inf i", FUNC(csin) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (minus_zero, minus_infty), 0, 0, 0); + + check_complex ("csin (inf + 0.0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (plus_infty, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csin (-inf + 0.0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (minus_infty, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csin (inf - 0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csin (-inf - 0 i) == NaN + 0.0 i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("csin (inf + inf i) == NaN + inf i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csin (-inf + inf i) == NaN + inf i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csin (inf - inf i) == NaN + inf i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csin (-inf - inf i) == NaN + inf i plus invalid exception and sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("csin (inf + 6.75 i) == NaN + NaN i plus invalid exception", FUNC(csin) (BUILD_COMPLEX (plus_infty, 6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("csin (inf - 6.75 i) == NaN + NaN i plus invalid exception", FUNC(csin) (BUILD_COMPLEX (plus_infty, -6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("csin (-inf + 6.75 i) == NaN + NaN i plus invalid exception", FUNC(csin) (BUILD_COMPLEX (minus_infty, 6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("csin (-inf - 6.75 i) == NaN + NaN i plus invalid exception", FUNC(csin) (BUILD_COMPLEX (minus_infty, -6.75)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("csin (4.625 + inf i) == -inf - inf i", FUNC(csin) (BUILD_COMPLEX (4.625, plus_infty)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + check_complex ("csin (4.625 - inf i) == -inf + inf i", FUNC(csin) (BUILD_COMPLEX (4.625, minus_infty)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + check_complex ("csin (-4.625 + inf i) == inf - inf i", FUNC(csin) (BUILD_COMPLEX (-4.625, plus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csin (-4.625 - inf i) == inf + inf i", FUNC(csin) (BUILD_COMPLEX (-4.625, minus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + + check_complex ("csin (NaN + 0.0 i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (nan_value, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("csin (NaN - 0 i) == NaN + 0.0 i plus sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("csin (NaN + inf i) == NaN + inf i plus sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("csin (NaN - inf i) == NaN + inf i plus sign of zero/inf not specified", FUNC(csin) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("csin (NaN + 9.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csin) (BUILD_COMPLEX (nan_value, 9.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csin (NaN - 9.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csin) (BUILD_COMPLEX (nan_value, -9.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csin (0.0 + NaN i) == 0.0 + NaN i", FUNC(csin) (BUILD_COMPLEX (0.0, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, 0); + check_complex ("csin (-0 + NaN i) == -0 + NaN i", FUNC(csin) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (minus_zero, nan_value), 0, 0, 0); + + check_complex ("csin (10.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csin) (BUILD_COMPLEX (10.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csin (NaN - 10.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csin) (BUILD_COMPLEX (nan_value, -10.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csin (inf + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csin) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csin (-inf + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csin) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csin (NaN + NaN i) == NaN + NaN i", FUNC(csin) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("csin (0.7 + 1.2 i) == 1.1664563419657581376 + 1.1544997246948547371 i", FUNC(csin) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.1664563419657581376L, 1.1544997246948547371L), DELTA652, 0, 0); + + check_complex ("csin (-2 - 3 i) == -9.1544991469114295734 + 4.1689069599665643507 i", FUNC(csin) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-9.1544991469114295734L, 4.1689069599665643507L), 0, 0, 0); + + print_complex_max_error ("csin", DELTAcsin, 0); +} + + +static void +csinh_test (void) +{ + errno = 0; + FUNC(csinh) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("csinh (0.0 + 0.0 i) == 0.0 + 0.0 i", FUNC(csinh) (BUILD_COMPLEX (0.0, 0.0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("csinh (-0 + 0.0 i) == -0 + 0.0 i", FUNC(csinh) (BUILD_COMPLEX (minus_zero, 0.0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("csinh (0.0 - 0 i) == 0.0 - 0 i", FUNC(csinh) (BUILD_COMPLEX (0.0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("csinh (-0 - 0 i) == -0 - 0 i", FUNC(csinh) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("csinh (0.0 + inf i) == 0.0 + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (0.0, plus_infty)), BUILD_COMPLEX (0.0, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (-0 + inf i) == 0.0 + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (0.0, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (0.0 - inf i) == 0.0 + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (0.0, minus_infty)), BUILD_COMPLEX (0.0, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (-0 - inf i) == 0.0 + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (0.0, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("csinh (inf + 0.0 i) == inf + 0.0 i", FUNC(csinh) (BUILD_COMPLEX (plus_infty, 0.0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("csinh (-inf + 0.0 i) == -inf + 0.0 i", FUNC(csinh) (BUILD_COMPLEX (minus_infty, 0.0)), BUILD_COMPLEX (minus_infty, 0.0), 0, 0, 0); + check_complex ("csinh (inf - 0 i) == inf - 0 i", FUNC(csinh) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("csinh (-inf - 0 i) == -inf - 0 i", FUNC(csinh) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (minus_infty, minus_zero), 0, 0, 0); + + check_complex ("csinh (inf + inf i) == inf + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (-inf + inf i) == inf + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (inf - inf i) == inf + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (-inf - inf i) == inf + NaN i plus invalid exception and sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN); + + check_complex ("csinh (inf + 4.625 i) == -inf - inf i", FUNC(csinh) (BUILD_COMPLEX (plus_infty, 4.625)), BUILD_COMPLEX (minus_infty, minus_infty), 0, 0, 0); + check_complex ("csinh (-inf + 4.625 i) == inf - inf i", FUNC(csinh) (BUILD_COMPLEX (minus_infty, 4.625)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csinh (inf - 4.625 i) == -inf + inf i", FUNC(csinh) (BUILD_COMPLEX (plus_infty, -4.625)), BUILD_COMPLEX (minus_infty, plus_infty), 0, 0, 0); + check_complex ("csinh (-inf - 4.625 i) == inf + inf i", FUNC(csinh) (BUILD_COMPLEX (minus_infty, -4.625)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + + check_complex ("csinh (6.75 + inf i) == NaN + NaN i plus invalid exception", FUNC(csinh) (BUILD_COMPLEX (6.75, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("csinh (-6.75 + inf i) == NaN + NaN i plus invalid exception", FUNC(csinh) (BUILD_COMPLEX (-6.75, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("csinh (6.75 - inf i) == NaN + NaN i plus invalid exception", FUNC(csinh) (BUILD_COMPLEX (6.75, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("csinh (-6.75 - inf i) == NaN + NaN i plus invalid exception", FUNC(csinh) (BUILD_COMPLEX (-6.75, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("csinh (0.0 + NaN i) == 0.0 + NaN i plus sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (0.0, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (-0 + NaN i) == 0.0 + NaN i plus sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("csinh (inf + NaN i) == inf + NaN i plus sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("csinh (-inf + NaN i) == inf + NaN i plus sign of zero/inf not specified", FUNC(csinh) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("csinh (9.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csinh) (BUILD_COMPLEX (9.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csinh (-9.0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csinh) (BUILD_COMPLEX (-9.0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csinh (NaN + 0.0 i) == NaN + 0.0 i", FUNC(csinh) (BUILD_COMPLEX (nan_value, 0.0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, 0); + check_complex ("csinh (NaN - 0 i) == NaN - 0 i", FUNC(csinh) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, minus_zero), 0, 0, 0); + + check_complex ("csinh (NaN + 10.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csinh) (BUILD_COMPLEX (nan_value, 10.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csinh (NaN - 10.0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csinh) (BUILD_COMPLEX (nan_value, -10.0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csinh (NaN + inf i) == NaN + NaN i plus invalid exception allowed", FUNC(csinh) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csinh (NaN - inf i) == NaN + NaN i plus invalid exception allowed", FUNC(csinh) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csinh (NaN + NaN i) == NaN + NaN i", FUNC(csinh) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("csinh (0.7 + 1.2 i) == 0.27487868678117583582 + 1.1698665727426565139 i", FUNC(csinh) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.27487868678117583582L, 1.1698665727426565139L), DELTA691, 0, 0); + check_complex ("csinh (-2 - 3 i) == 3.5905645899857799520 - 0.5309210862485198052 i", FUNC(csinh) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (3.5905645899857799520L, -0.5309210862485198052L), DELTA692, 0, 0); + + print_complex_max_error ("csinh", DELTAcsinh, 0); +} + +static void +csqrt_test (void) +{ + errno = 0; + FUNC(csqrt) (BUILD_COMPLEX (-1, 0)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("csqrt (0 + 0 i) == 0.0 + 0.0 i", FUNC(csqrt) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("csqrt (0 - 0 i) == 0 - 0 i", FUNC(csqrt) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0, minus_zero), 0, 0, 0); + check_complex ("csqrt (-0 + 0 i) == 0.0 + 0.0 i", FUNC(csqrt) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("csqrt (-0 - 0 i) == 0.0 - 0 i", FUNC(csqrt) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + + check_complex ("csqrt (-inf + 0 i) == 0.0 + inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + check_complex ("csqrt (-inf + 6 i) == 0.0 + inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, 6)), BUILD_COMPLEX (0.0, plus_infty), 0, 0, 0); + check_complex ("csqrt (-inf - 0 i) == 0.0 - inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + check_complex ("csqrt (-inf - 6 i) == 0.0 - inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, -6)), BUILD_COMPLEX (0.0, minus_infty), 0, 0, 0); + + check_complex ("csqrt (inf + 0 i) == inf + 0.0 i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("csqrt (inf + 6 i) == inf + 0.0 i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, 6)), BUILD_COMPLEX (plus_infty, 0.0), 0, 0, 0); + check_complex ("csqrt (inf - 0 i) == inf - 0 i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + check_complex ("csqrt (inf - 6 i) == inf - 0 i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, -6)), BUILD_COMPLEX (plus_infty, minus_zero), 0, 0, 0); + + check_complex ("csqrt (0 + inf i) == inf + inf i", FUNC(csqrt) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("csqrt (4 + inf i) == inf + inf i", FUNC(csqrt) (BUILD_COMPLEX (4, plus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("csqrt (inf + inf i) == inf + inf i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("csqrt (-0 + inf i) == inf + inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("csqrt (-4 + inf i) == inf + inf i", FUNC(csqrt) (BUILD_COMPLEX (-4, plus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("csqrt (-inf + inf i) == inf + inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, plus_infty)), BUILD_COMPLEX (plus_infty, plus_infty), 0, 0, 0); + check_complex ("csqrt (0 - inf i) == inf - inf i", FUNC(csqrt) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csqrt (4 - inf i) == inf - inf i", FUNC(csqrt) (BUILD_COMPLEX (4, minus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csqrt (inf - inf i) == inf - inf i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csqrt (-0 - inf i) == inf - inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csqrt (-4 - inf i) == inf - inf i", FUNC(csqrt) (BUILD_COMPLEX (-4, minus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + check_complex ("csqrt (-inf - inf i) == inf - inf i", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, minus_infty)), BUILD_COMPLEX (plus_infty, minus_infty), 0, 0, 0); + + check_complex ("csqrt (-inf + NaN i) == NaN + inf i plus sign of zero/inf not specified", FUNC(csqrt) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (nan_value, plus_infty), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("csqrt (inf + NaN i) == inf + NaN i", FUNC(csqrt) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (plus_infty, nan_value), 0, 0, 0); + + check_complex ("csqrt (0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csqrt (1 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (1, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csqrt (-0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csqrt (-1 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (-1, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csqrt (NaN + 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csqrt (NaN + 8 i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (nan_value, 8)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csqrt (NaN - 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("csqrt (NaN - 8 i) == NaN + NaN i plus invalid exception allowed", FUNC(csqrt) (BUILD_COMPLEX (nan_value, -8)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("csqrt (NaN + NaN i) == NaN + NaN i", FUNC(csqrt) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("csqrt (16.0 - 30.0 i) == 5.0 - 3.0 i", FUNC(csqrt) (BUILD_COMPLEX (16.0, -30.0)), BUILD_COMPLEX (5.0, -3.0), 0, 0, 0); + check_complex ("csqrt (-1 + 0 i) == 0.0 + 1.0 i", FUNC(csqrt) (BUILD_COMPLEX (-1, 0)), BUILD_COMPLEX (0.0, 1.0), 0, 0, 0); + check_complex ("csqrt (0 + 2 i) == 1.0 + 1.0 i", FUNC(csqrt) (BUILD_COMPLEX (0, 2)), BUILD_COMPLEX (1.0, 1.0), 0, 0, 0); + check_complex ("csqrt (119 + 120 i) == 12.0 + 5.0 i", FUNC(csqrt) (BUILD_COMPLEX (119, 120)), BUILD_COMPLEX (12.0, 5.0), 0, 0, 0); + check_complex ("csqrt (0.7 + 1.2 i) == 1.022067610030026450706487883081139 + 0.58704531296356521154977678719838035 i", FUNC(csqrt) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.022067610030026450706487883081139L, 0.58704531296356521154977678719838035L), DELTA732, 0, 0); + check_complex ("csqrt (-2 - 3 i) == 0.89597747612983812471573375529004348 - 1.6741492280355400404480393008490519 i", FUNC(csqrt) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (0.89597747612983812471573375529004348L, -1.6741492280355400404480393008490519L), DELTA733, 0, 0); + check_complex ("csqrt (-2 + 3 i) == 0.89597747612983812471573375529004348 + 1.6741492280355400404480393008490519 i", FUNC(csqrt) (BUILD_COMPLEX (-2, 3)), BUILD_COMPLEX (0.89597747612983812471573375529004348L, 1.6741492280355400404480393008490519L), DELTA734, 0, 0); + + print_complex_max_error ("csqrt", DELTAcsqrt, 0); +} + +static void +ctan_test (void) +{ + errno = 0; + FUNC(ctan) (BUILD_COMPLEX (0.7L, 1.2L)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("ctan (0 + 0 i) == 0.0 + 0.0 i", FUNC(ctan) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("ctan (0 - 0 i) == 0.0 - 0 i", FUNC(ctan) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("ctan (-0 + 0 i) == -0 + 0.0 i", FUNC(ctan) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("ctan (-0 - 0 i) == -0 - 0 i", FUNC(ctan) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("ctan (0 + inf i) == 0.0 + 1.0 i", FUNC(ctan) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (0.0, 1.0), 0, 0, 0); + check_complex ("ctan (1 + inf i) == 0.0 + 1.0 i", FUNC(ctan) (BUILD_COMPLEX (1, plus_infty)), BUILD_COMPLEX (0.0, 1.0), 0, 0, 0); + check_complex ("ctan (-0 + inf i) == -0 + 1.0 i", FUNC(ctan) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (minus_zero, 1.0), 0, 0, 0); + check_complex ("ctan (-1 + inf i) == -0 + 1.0 i", FUNC(ctan) (BUILD_COMPLEX (-1, plus_infty)), BUILD_COMPLEX (minus_zero, 1.0), 0, 0, 0); + + check_complex ("ctan (0 - inf i) == 0.0 - 1.0 i", FUNC(ctan) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (0.0, -1.0), 0, 0, 0); + check_complex ("ctan (1 - inf i) == 0.0 - 1.0 i", FUNC(ctan) (BUILD_COMPLEX (1, minus_infty)), BUILD_COMPLEX (0.0, -1.0), 0, 0, 0); + check_complex ("ctan (-0 - inf i) == -0 - 1.0 i", FUNC(ctan) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (minus_zero, -1.0), 0, 0, 0); + check_complex ("ctan (-1 - inf i) == -0 - 1.0 i", FUNC(ctan) (BUILD_COMPLEX (-1, minus_infty)), BUILD_COMPLEX (minus_zero, -1.0), 0, 0, 0); + + check_complex ("ctan (inf + 0 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (inf + 2 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (plus_infty, 2)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (-inf + 0 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (-inf + 2 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (minus_infty, 2)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (inf - 0 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (inf - 2 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (plus_infty, -2)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (-inf - 0 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctan (-inf - 2 i) == NaN + NaN i plus invalid exception", FUNC(ctan) (BUILD_COMPLEX (minus_infty, -2)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("ctan (NaN + inf i) == 0.0 + 1.0 i plus sign of zero/inf not specified", FUNC(ctan) (BUILD_COMPLEX (nan_value, plus_infty)), BUILD_COMPLEX (0.0, 1.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("ctan (NaN - inf i) == 0.0 - 1.0 i plus sign of zero/inf not specified", FUNC(ctan) (BUILD_COMPLEX (nan_value, minus_infty)), BUILD_COMPLEX (0.0, -1.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("ctan (0 + NaN i) == 0.0 + NaN i", FUNC(ctan) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (0.0, nan_value), 0, 0, 0); + check_complex ("ctan (-0 + NaN i) == -0 + NaN i", FUNC(ctan) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (minus_zero, nan_value), 0, 0, 0); + + check_complex ("ctan (0.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ctan) (BUILD_COMPLEX (0.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctan (-4.5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ctan) (BUILD_COMPLEX (-4.5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ctan (NaN + 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(ctan) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctan (NaN + 5 i) == NaN + NaN i plus invalid exception allowed", FUNC(ctan) (BUILD_COMPLEX (nan_value, 5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctan (NaN - 0 i) == NaN + NaN i plus invalid exception allowed", FUNC(ctan) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctan (NaN - 0.25 i) == NaN + NaN i plus invalid exception allowed", FUNC(ctan) (BUILD_COMPLEX (nan_value, -0.25)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ctan (NaN + NaN i) == NaN + NaN i", FUNC(ctan) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("ctan (0.7 + 1.2 i) == 0.1720734197630349001 + 0.9544807059989405538 i", FUNC(ctan) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (0.1720734197630349001L, 0.9544807059989405538L), DELTA766, 0, 0); + check_complex ("ctan (-2 - 3 i) == 0.0037640256415042482 - 1.0032386273536098014 i", FUNC(ctan) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (0.0037640256415042482L, -1.0032386273536098014L), DELTA767, 0, 0); + + print_complex_max_error ("ctan", DELTActan, 0); +} + + +static void +ctanh_test (void) +{ + errno = 0; + FUNC(ctanh) (BUILD_COMPLEX (0, 0)); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_complex ("ctanh (0 + 0 i) == 0.0 + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (0, 0)), BUILD_COMPLEX (0.0, 0.0), 0, 0, 0); + check_complex ("ctanh (0 - 0 i) == 0.0 - 0 i", FUNC(ctanh) (BUILD_COMPLEX (0, minus_zero)), BUILD_COMPLEX (0.0, minus_zero), 0, 0, 0); + check_complex ("ctanh (-0 + 0 i) == -0 + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (minus_zero, 0)), BUILD_COMPLEX (minus_zero, 0.0), 0, 0, 0); + check_complex ("ctanh (-0 - 0 i) == -0 - 0 i", FUNC(ctanh) (BUILD_COMPLEX (minus_zero, minus_zero)), BUILD_COMPLEX (minus_zero, minus_zero), 0, 0, 0); + + check_complex ("ctanh (inf + 0 i) == 1.0 + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (plus_infty, 0)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("ctanh (inf + 1 i) == 1.0 + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (plus_infty, 1)), BUILD_COMPLEX (1.0, 0.0), 0, 0, 0); + check_complex ("ctanh (inf - 0 i) == 1.0 - 0 i", FUNC(ctanh) (BUILD_COMPLEX (plus_infty, minus_zero)), BUILD_COMPLEX (1.0, minus_zero), 0, 0, 0); + check_complex ("ctanh (inf - 1 i) == 1.0 - 0 i", FUNC(ctanh) (BUILD_COMPLEX (plus_infty, -1)), BUILD_COMPLEX (1.0, minus_zero), 0, 0, 0); + check_complex ("ctanh (-inf + 0 i) == -1.0 + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (minus_infty, 0)), BUILD_COMPLEX (-1.0, 0.0), 0, 0, 0); + check_complex ("ctanh (-inf + 1 i) == -1.0 + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (minus_infty, 1)), BUILD_COMPLEX (-1.0, 0.0), 0, 0, 0); + check_complex ("ctanh (-inf - 0 i) == -1.0 - 0 i", FUNC(ctanh) (BUILD_COMPLEX (minus_infty, minus_zero)), BUILD_COMPLEX (-1.0, minus_zero), 0, 0, 0); + check_complex ("ctanh (-inf - 1 i) == -1.0 - 0 i", FUNC(ctanh) (BUILD_COMPLEX (minus_infty, -1)), BUILD_COMPLEX (-1.0, minus_zero), 0, 0, 0); + + check_complex ("ctanh (0 + inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (0, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (2 + inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (2, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (0 - inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (0, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (2 - inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (2, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (-0 + inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (minus_zero, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (-2 + inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (-2, plus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (-0 - inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (minus_zero, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + check_complex ("ctanh (-2 - inf i) == NaN + NaN i plus invalid exception", FUNC(ctanh) (BUILD_COMPLEX (-2, minus_infty)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION); + + check_complex ("ctanh (inf + NaN i) == 1.0 + 0.0 i plus sign of zero/inf not specified", FUNC(ctanh) (BUILD_COMPLEX (plus_infty, nan_value)), BUILD_COMPLEX (1.0, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + check_complex ("ctanh (-inf + NaN i) == -1.0 + 0.0 i plus sign of zero/inf not specified", FUNC(ctanh) (BUILD_COMPLEX (minus_infty, nan_value)), BUILD_COMPLEX (-1.0, 0.0), 0, 0, IGNORE_ZERO_INF_SIGN); + + check_complex ("ctanh (NaN + 0 i) == NaN + 0.0 i", FUNC(ctanh) (BUILD_COMPLEX (nan_value, 0)), BUILD_COMPLEX (nan_value, 0.0), 0, 0, 0); + check_complex ("ctanh (NaN - 0 i) == NaN - 0 i", FUNC(ctanh) (BUILD_COMPLEX (nan_value, minus_zero)), BUILD_COMPLEX (nan_value, minus_zero), 0, 0, 0); + + check_complex ("ctanh (NaN + 0.5 i) == NaN + NaN i plus invalid exception allowed", FUNC(ctanh) (BUILD_COMPLEX (nan_value, 0.5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctanh (NaN - 4.5 i) == NaN + NaN i plus invalid exception allowed", FUNC(ctanh) (BUILD_COMPLEX (nan_value, -4.5)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ctanh (0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ctanh) (BUILD_COMPLEX (0, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctanh (5 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ctanh) (BUILD_COMPLEX (5, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctanh (-0 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ctanh) (BUILD_COMPLEX (minus_zero, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + check_complex ("ctanh (-0.25 + NaN i) == NaN + NaN i plus invalid exception allowed", FUNC(ctanh) (BUILD_COMPLEX (-0.25, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, INVALID_EXCEPTION_OK); + + check_complex ("ctanh (NaN + NaN i) == NaN + NaN i", FUNC(ctanh) (BUILD_COMPLEX (nan_value, nan_value)), BUILD_COMPLEX (nan_value, nan_value), 0, 0, 0); + + check_complex ("ctanh (0 + pi/4 i) == 0.0 + 1.0 i", FUNC(ctanh) (BUILD_COMPLEX (0, M_PI_4l)), BUILD_COMPLEX (0.0, 1.0), DELTA799, 0, 0); + + check_complex ("ctanh (0.7 + 1.2 i) == 1.3472197399061191630 + 0.4778641038326365540 i", FUNC(ctanh) (BUILD_COMPLEX (0.7L, 1.2L)), BUILD_COMPLEX (1.3472197399061191630L, 0.4778641038326365540L), DELTA800, 0, 0); + check_complex ("ctanh (-2 - 3 i) == -0.9653858790221331242 + 0.0098843750383224937 i", FUNC(ctanh) (BUILD_COMPLEX (-2, -3)), BUILD_COMPLEX (-0.9653858790221331242L, 0.0098843750383224937L), DELTA801, 0, 0); + + print_complex_max_error ("ctanh", DELTActanh, 0); +} +#endif + +static void +erf_test (void) +{ + errno = 0; + FUNC(erf) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("erf (0) == 0", FUNC(erf) (0), 0, 0, 0, 0); + check_float ("erf (-0) == -0", FUNC(erf) (minus_zero), minus_zero, 0, 0, 0); + check_float ("erf (inf) == 1", FUNC(erf) (plus_infty), 1, 0, 0, 0); + check_float ("erf (-inf) == -1", FUNC(erf) (minus_infty), -1, 0, 0, 0); + check_float ("erf (NaN) == NaN", FUNC(erf) (nan_value), nan_value, 0, 0, 0); + + check_float ("erf (0.7) == 0.67780119383741847297", FUNC(erf) (0.7L), 0.67780119383741847297L, 0, 0, 0); + + check_float ("erf (1.2) == 0.91031397822963538024", FUNC(erf) (1.2L), 0.91031397822963538024L, 0, 0, 0); + check_float ("erf (2.0) == 0.99532226501895273416", FUNC(erf) (2.0), 0.99532226501895273416L, 0, 0, 0); + check_float ("erf (4.1) == 0.99999999329997234592", FUNC(erf) (4.1L), 0.99999999329997234592L, 0, 0, 0); + check_float ("erf (27) == 1.0", FUNC(erf) (27), 1.0L, 0, 0, 0); + + print_max_error ("erf", 0, 0); +} + + +static void +erfc_test (void) +{ + errno = 0; + FUNC(erfc) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("erfc (inf) == 0.0", FUNC(erfc) (plus_infty), 0.0, 0, 0, 0); + check_float ("erfc (-inf) == 2.0", FUNC(erfc) (minus_infty), 2.0, 0, 0, 0); + check_float ("erfc (0.0) == 1.0", FUNC(erfc) (0.0), 1.0, 0, 0, 0); + check_float ("erfc (-0) == 1.0", FUNC(erfc) (minus_zero), 1.0, 0, 0, 0); + check_float ("erfc (NaN) == NaN", FUNC(erfc) (nan_value), nan_value, 0, 0, 0); + + check_float ("erfc (0.7) == 0.32219880616258152702", FUNC(erfc) (0.7L), 0.32219880616258152702L, DELTA817, 0, 0); + + check_float ("erfc (1.2) == 0.089686021770364619762", FUNC(erfc) (1.2L), 0.089686021770364619762L, DELTA818, 0, 0); + check_float ("erfc (2.0) == 0.0046777349810472658379", FUNC(erfc) (2.0), 0.0046777349810472658379L, DELTA819, 0, 0); + check_float ("erfc (4.1) == 0.67000276540848983727e-8", FUNC(erfc) (4.1L), 0.67000276540848983727e-8L, DELTA820, 0, 0); + check_float ("erfc (9) == 0.41370317465138102381e-36", FUNC(erfc) (9), 0.41370317465138102381e-36L, DELTA821, 0, 0); + + print_max_error ("erfc", DELTAerfc, 0); +} + +static void +exp_test (void) +{ + errno = 0; + FUNC(exp) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("exp (0) == 1", FUNC(exp) (0), 1, 0, 0, 0); + check_float ("exp (-0) == 1", FUNC(exp) (minus_zero), 1, 0, 0, 0); + +#ifndef TEST_INLINE + check_float ("exp (inf) == inf", FUNC(exp) (plus_infty), plus_infty, 0, 0, 0); + check_float ("exp (-inf) == 0", FUNC(exp) (minus_infty), 0, 0, 0, 0); +#endif + check_float ("exp (NaN) == NaN", FUNC(exp) (nan_value), nan_value, 0, 0, 0); + check_float ("exp (1) == e", FUNC(exp) (1), M_El, 0, 0, 0); + + check_float ("exp (2) == e^2", FUNC(exp) (2), M_E2l, 0, 0, 0); + check_float ("exp (3) == e^3", FUNC(exp) (3), M_E3l, 0, 0, 0); + check_float ("exp (0.7) == 2.0137527074704765216", FUNC(exp) (0.7L), 2.0137527074704765216L, DELTA830, 0, 0); + check_float ("exp (50.0) == 5184705528587072464087.45332293348538", FUNC(exp) (50.0L), 5184705528587072464087.45332293348538L, DELTA831, 0, 0); +#ifdef TEST_LDOUBLE + /* The result can only be represented in long double. */ + check_float ("exp (1000.0) == 0.197007111401704699388887935224332313e435", FUNC(exp) (1000.0L), 0.197007111401704699388887935224332313e435L, DELTA832, 0, 0); +#endif + print_max_error ("exp", DELTAexp, 0); +} + + +#if 0 /* XXX scp XXX */ +static void +exp10_test (void) +{ + errno = 0; + FUNC(exp10) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("exp10 (0) == 1", FUNC(exp10) (0), 1, 0, 0, 0); + check_float ("exp10 (-0) == 1", FUNC(exp10) (minus_zero), 1, 0, 0, 0); + + check_float ("exp10 (inf) == inf", FUNC(exp10) (plus_infty), plus_infty, 0, 0, 0); + check_float ("exp10 (-inf) == 0", FUNC(exp10) (minus_infty), 0, 0, 0, 0); + check_float ("exp10 (NaN) == NaN", FUNC(exp10) (nan_value), nan_value, 0, 0, 0); + check_float ("exp10 (3) == 1000", FUNC(exp10) (3), 1000, DELTA838, 0, 0); + check_float ("exp10 (-1) == 0.1", FUNC(exp10) (-1), 0.1L, DELTA839, 0, 0); + check_float ("exp10 (1e6) == inf", FUNC(exp10) (1e6), plus_infty, 0, 0, 0); + check_float ("exp10 (-1e6) == 0", FUNC(exp10) (-1e6), 0, 0, 0, 0); + check_float ("exp10 (0.7) == 5.0118723362727228500155418688494574", FUNC(exp10) (0.7L), 5.0118723362727228500155418688494574L, DELTA842, 0, 0); + + print_max_error ("exp10", DELTAexp10, 0); +} +#endif + +static void +exp2_test (void) +{ + errno = 0; + FUNC(exp2) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("exp2 (0) == 1", FUNC(exp2) (0), 1, 0, 0, 0); + check_float ("exp2 (-0) == 1", FUNC(exp2) (minus_zero), 1, 0, 0, 0); + check_float ("exp2 (inf) == inf", FUNC(exp2) (plus_infty), plus_infty, 0, 0, 0); + check_float ("exp2 (-inf) == 0", FUNC(exp2) (minus_infty), 0, 0, 0, 0); + check_float ("exp2 (NaN) == NaN", FUNC(exp2) (nan_value), nan_value, 0, 0, 0); + + check_float ("exp2 (10) == 1024", FUNC(exp2) (10), 1024, 0, 0, 0); + check_float ("exp2 (-1) == 0.5", FUNC(exp2) (-1), 0.5, 0, 0, 0); + check_float ("exp2 (1e6) == inf", FUNC(exp2) (1e6), plus_infty, 0, 0, 0); + check_float ("exp2 (-1e6) == 0", FUNC(exp2) (-1e6), 0, 0, 0, 0); + check_float ("exp2 (0.7) == 1.6245047927124710452", FUNC(exp2) (0.7L), 1.6245047927124710452L, DELTA852, 0, 0); + + print_max_error ("exp2", DELTAexp2, 0); +} + +static void +expm1_test (void) +{ + errno = 0; + FUNC(expm1) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("expm1 (0) == 0", FUNC(expm1) (0), 0, 0, 0, 0); + check_float ("expm1 (-0) == -0", FUNC(expm1) (minus_zero), minus_zero, 0, 0, 0); + +#ifndef TEST_INLINE + check_float ("expm1 (inf) == inf", FUNC(expm1) (plus_infty), plus_infty, 0, 0, 0); + check_float ("expm1 (-inf) == -1", FUNC(expm1) (minus_infty), -1, 0, 0, 0); +#endif + check_float ("expm1 (NaN) == NaN", FUNC(expm1) (nan_value), nan_value, 0, 0, 0); + + check_float ("expm1 (1) == M_El - 1.0", FUNC(expm1) (1), M_El - 1.0, 1, 0, 0); + check_float ("expm1 (0.7) == 1.0137527074704765216", FUNC(expm1) (0.7L), 1.0137527074704765216L, DELTA859, 0, 0); + + print_max_error ("expm1", DELTAexpm1, 0); +} + +static void +fabs_test (void) +{ + init_max_error (); + + check_float ("fabs (0) == 0", FUNC(fabs) ((FLOAT)0.0), 0, 0, 0, 0); + check_float ("fabs (-0) == 0", FUNC(fabs) (minus_zero), 0, 0, 0, 0); + + check_float ("fabs (inf) == inf", FUNC(fabs) (plus_infty), plus_infty, 0, 0, 0); + check_float ("fabs (-inf) == inf", FUNC(fabs) (minus_infty), plus_infty, 0, 0, 0); + check_float ("fabs (NaN) == NaN", FUNC(fabs) (nan_value), nan_value, 0, 0, 0); + + check_float ("fabs (38.0) == 38.0", FUNC(fabs) ((FLOAT)38.0), 38.0, 0, 0, 0); + check_float ("fabs (-e) == e", FUNC(fabs) ((FLOAT)-M_El), M_El, 0, 0, 0); + + print_max_error ("fabs", 0, 0); +} + +static void +fdim_test (void) +{ + init_max_error (); + + check_float ("fdim (0, 0) == 0", FUNC(fdim) (0, 0), 0, 0, 0, 0); + check_float ("fdim (9, 0) == 9", FUNC(fdim) (9, 0), 9, 0, 0, 0); + check_float ("fdim (0, 9) == 0", FUNC(fdim) (0, 9), 0, 0, 0, 0); + check_float ("fdim (-9, 0) == 0", FUNC(fdim) (-9, 0), 0, 0, 0, 0); + check_float ("fdim (0, -9) == 9", FUNC(fdim) (0, -9), 9, 0, 0, 0); + + check_float ("fdim (inf, 9) == inf", FUNC(fdim) (plus_infty, 9), plus_infty, 0, 0, 0); + check_float ("fdim (inf, -9) == inf", FUNC(fdim) (plus_infty, -9), plus_infty, 0, 0, 0); + check_float ("fdim (-inf, 9) == 0", FUNC(fdim) (minus_infty, 9), 0, 0, 0, 0); + check_float ("fdim (-inf, -9) == 0", FUNC(fdim) (minus_infty, -9), 0, 0, 0, 0); + check_float ("fdim (9, -inf) == inf", FUNC(fdim) (9, minus_infty), plus_infty, 0, 0, 0); + check_float ("fdim (-9, -inf) == inf", FUNC(fdim) (-9, minus_infty), plus_infty, 0, 0, 0); + check_float ("fdim (9, inf) == 0", FUNC(fdim) (9, plus_infty), 0, 0, 0, 0); + check_float ("fdim (-9, inf) == 0", FUNC(fdim) (-9, plus_infty), 0, 0, 0, 0); + + check_float ("fdim (0, NaN) == NaN", FUNC(fdim) (0, nan_value), nan_value, 0, 0, 0); + check_float ("fdim (9, NaN) == NaN", FUNC(fdim) (9, nan_value), nan_value, 0, 0, 0); + check_float ("fdim (-9, NaN) == NaN", FUNC(fdim) (-9, nan_value), nan_value, 0, 0, 0); + check_float ("fdim (NaN, 9) == NaN", FUNC(fdim) (nan_value, 9), nan_value, 0, 0, 0); + check_float ("fdim (NaN, -9) == NaN", FUNC(fdim) (nan_value, -9), nan_value, 0, 0, 0); + check_float ("fdim (inf, NaN) == NaN", FUNC(fdim) (plus_infty, nan_value), nan_value, 0, 0, 0); + check_float ("fdim (-inf, NaN) == NaN", FUNC(fdim) (minus_infty, nan_value), nan_value, 0, 0, 0); + check_float ("fdim (NaN, inf) == NaN", FUNC(fdim) (nan_value, plus_infty), nan_value, 0, 0, 0); + check_float ("fdim (NaN, -inf) == NaN", FUNC(fdim) (nan_value, minus_infty), nan_value, 0, 0, 0); + check_float ("fdim (NaN, NaN) == NaN", FUNC(fdim) (nan_value, nan_value), nan_value, 0, 0, 0); + + print_max_error ("fdim", 0, 0); +} + +static void +floor_test (void) +{ + init_max_error (); + + check_float ("floor (0.0) == 0.0", FUNC(floor) (0.0), 0.0, 0, 0, 0); + check_float ("floor (-0) == -0", FUNC(floor) (minus_zero), minus_zero, 0, 0, 0); + check_float ("floor (inf) == inf", FUNC(floor) (plus_infty), plus_infty, 0, 0, 0); + check_float ("floor (-inf) == -inf", FUNC(floor) (minus_infty), minus_infty, 0, 0, 0); + check_float ("floor (NaN) == NaN", FUNC(floor) (nan_value), nan_value, 0, 0, 0); + + check_float ("floor (pi) == 3.0", FUNC(floor) (M_PIl), 3.0, 0, 0, 0); + check_float ("floor (-pi) == -4.0", FUNC(floor) (-M_PIl), -4.0, 0, 0, 0); + + print_max_error ("floor", 0, 0); +} + +static void +fma_test (void) +{ + init_max_error (); + + check_float ("fma (1.0, 2.0, 3.0) == 5.0", FUNC(fma) (1.0, 2.0, 3.0), 5.0, 0, 0, 0); + check_float ("fma (NaN, 2.0, 3.0) == NaN", FUNC(fma) (nan_value, 2.0, 3.0), nan_value, 0, 0, 0); + check_float ("fma (1.0, NaN, 3.0) == NaN", FUNC(fma) (1.0, nan_value, 3.0), nan_value, 0, 0, 0); + check_float ("fma (1.0, 2.0, NaN) == NaN plus invalid exception allowed", FUNC(fma) (1.0, 2.0, nan_value), nan_value, 0, 0, INVALID_EXCEPTION_OK); + check_float ("fma (inf, 0.0, NaN) == NaN plus invalid exception allowed", FUNC(fma) (plus_infty, 0.0, nan_value), nan_value, 0, 0, INVALID_EXCEPTION_OK); + check_float ("fma (-inf, 0.0, NaN) == NaN plus invalid exception allowed", FUNC(fma) (minus_infty, 0.0, nan_value), nan_value, 0, 0, INVALID_EXCEPTION_OK); + check_float ("fma (0.0, inf, NaN) == NaN plus invalid exception allowed", FUNC(fma) (0.0, plus_infty, nan_value), nan_value, 0, 0, INVALID_EXCEPTION_OK); + check_float ("fma (0.0, -inf, NaN) == NaN plus invalid exception allowed", FUNC(fma) (0.0, minus_infty, nan_value), nan_value, 0, 0, INVALID_EXCEPTION_OK); + check_float ("fma (inf, 0.0, 1.0) == NaN plus invalid exception", FUNC(fma) (plus_infty, 0.0, 1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("fma (-inf, 0.0, 1.0) == NaN plus invalid exception", FUNC(fma) (minus_infty, 0.0, 1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("fma (0.0, inf, 1.0) == NaN plus invalid exception", FUNC(fma) (0.0, plus_infty, 1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("fma (0.0, -inf, 1.0) == NaN plus invalid exception", FUNC(fma) (0.0, minus_infty, 1.0), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("fma (inf, inf, -inf) == NaN plus invalid exception", FUNC(fma) (plus_infty, plus_infty, minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("fma (-inf, inf, inf) == NaN plus invalid exception", FUNC(fma) (minus_infty, plus_infty, plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("fma (inf, -inf, inf) == NaN plus invalid exception", FUNC(fma) (plus_infty, minus_infty, plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("fma (-inf, -inf, -inf) == NaN plus invalid exception", FUNC(fma) (minus_infty, minus_infty, minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + + print_max_error ("fma", 0, 0); +} + + +static void +fmax_test (void) +{ + init_max_error (); + + check_float ("fmax (0, 0) == 0", FUNC(fmax) (0, 0), 0, 0, 0, 0); + check_float ("fmax (-0, -0) == -0", FUNC(fmax) (minus_zero, minus_zero), minus_zero, 0, 0, 0); + check_float ("fmax (9, 0) == 9", FUNC(fmax) (9, 0), 9, 0, 0, 0); + check_float ("fmax (0, 9) == 9", FUNC(fmax) (0, 9), 9, 0, 0, 0); + check_float ("fmax (-9, 0) == 0", FUNC(fmax) (-9, 0), 0, 0, 0, 0); + check_float ("fmax (0, -9) == 0", FUNC(fmax) (0, -9), 0, 0, 0, 0); + + check_float ("fmax (inf, 9) == inf", FUNC(fmax) (plus_infty, 9), plus_infty, 0, 0, 0); + check_float ("fmax (0, inf) == inf", FUNC(fmax) (0, plus_infty), plus_infty, 0, 0, 0); + check_float ("fmax (-9, inf) == inf", FUNC(fmax) (-9, plus_infty), plus_infty, 0, 0, 0); + check_float ("fmax (inf, -9) == inf", FUNC(fmax) (plus_infty, -9), plus_infty, 0, 0, 0); + + check_float ("fmax (-inf, 9) == 9", FUNC(fmax) (minus_infty, 9), 9, 0, 0, 0); + check_float ("fmax (-inf, -9) == -9", FUNC(fmax) (minus_infty, -9), -9, 0, 0, 0); + check_float ("fmax (9, -inf) == 9", FUNC(fmax) (9, minus_infty), 9, 0, 0, 0); + check_float ("fmax (-9, -inf) == -9", FUNC(fmax) (-9, minus_infty), -9, 0, 0, 0); + + check_float ("fmax (0, NaN) == 0", FUNC(fmax) (0, nan_value), 0, 0, 0, 0); + check_float ("fmax (9, NaN) == 9", FUNC(fmax) (9, nan_value), 9, 0, 0, 0); + check_float ("fmax (-9, NaN) == -9", FUNC(fmax) (-9, nan_value), -9, 0, 0, 0); + check_float ("fmax (NaN, 0) == 0", FUNC(fmax) (nan_value, 0), 0, 0, 0, 0); + check_float ("fmax (NaN, 9) == 9", FUNC(fmax) (nan_value, 9), 9, 0, 0, 0); + check_float ("fmax (NaN, -9) == -9", FUNC(fmax) (nan_value, -9), -9, 0, 0, 0); + check_float ("fmax (inf, NaN) == inf", FUNC(fmax) (plus_infty, nan_value), plus_infty, 0, 0, 0); + check_float ("fmax (-inf, NaN) == -inf", FUNC(fmax) (minus_infty, nan_value), minus_infty, 0, 0, 0); + check_float ("fmax (NaN, inf) == inf", FUNC(fmax) (nan_value, plus_infty), plus_infty, 0, 0, 0); + check_float ("fmax (NaN, -inf) == -inf", FUNC(fmax) (nan_value, minus_infty), minus_infty, 0, 0, 0); + check_float ("fmax (NaN, NaN) == NaN", FUNC(fmax) (nan_value, nan_value), nan_value, 0, 0, 0); + + print_max_error ("fmax", 0, 0); +} + + +static void +fmin_test (void) +{ + init_max_error (); + + check_float ("fmin (0, 0) == 0", FUNC(fmin) (0, 0), 0, 0, 0, 0); + check_float ("fmin (-0, -0) == -0", FUNC(fmin) (minus_zero, minus_zero), minus_zero, 0, 0, 0); + check_float ("fmin (9, 0) == 0", FUNC(fmin) (9, 0), 0, 0, 0, 0); + check_float ("fmin (0, 9) == 0", FUNC(fmin) (0, 9), 0, 0, 0, 0); + check_float ("fmin (-9, 0) == -9", FUNC(fmin) (-9, 0), -9, 0, 0, 0); + check_float ("fmin (0, -9) == -9", FUNC(fmin) (0, -9), -9, 0, 0, 0); + + check_float ("fmin (inf, 9) == 9", FUNC(fmin) (plus_infty, 9), 9, 0, 0, 0); + check_float ("fmin (9, inf) == 9", FUNC(fmin) (9, plus_infty), 9, 0, 0, 0); + check_float ("fmin (inf, -9) == -9", FUNC(fmin) (plus_infty, -9), -9, 0, 0, 0); + check_float ("fmin (-9, inf) == -9", FUNC(fmin) (-9, plus_infty), -9, 0, 0, 0); + check_float ("fmin (-inf, 9) == -inf", FUNC(fmin) (minus_infty, 9), minus_infty, 0, 0, 0); + check_float ("fmin (-inf, -9) == -inf", FUNC(fmin) (minus_infty, -9), minus_infty, 0, 0, 0); + check_float ("fmin (9, -inf) == -inf", FUNC(fmin) (9, minus_infty), minus_infty, 0, 0, 0); + check_float ("fmin (-9, -inf) == -inf", FUNC(fmin) (-9, minus_infty), minus_infty, 0, 0, 0); + + check_float ("fmin (0, NaN) == 0", FUNC(fmin) (0, nan_value), 0, 0, 0, 0); + check_float ("fmin (9, NaN) == 9", FUNC(fmin) (9, nan_value), 9, 0, 0, 0); + check_float ("fmin (-9, NaN) == -9", FUNC(fmin) (-9, nan_value), -9, 0, 0, 0); + check_float ("fmin (NaN, 0) == 0", FUNC(fmin) (nan_value, 0), 0, 0, 0, 0); + check_float ("fmin (NaN, 9) == 9", FUNC(fmin) (nan_value, 9), 9, 0, 0, 0); + check_float ("fmin (NaN, -9) == -9", FUNC(fmin) (nan_value, -9), -9, 0, 0, 0); + check_float ("fmin (inf, NaN) == inf", FUNC(fmin) (plus_infty, nan_value), plus_infty, 0, 0, 0); + check_float ("fmin (-inf, NaN) == -inf", FUNC(fmin) (minus_infty, nan_value), minus_infty, 0, 0, 0); + check_float ("fmin (NaN, inf) == inf", FUNC(fmin) (nan_value, plus_infty), plus_infty, 0, 0, 0); + check_float ("fmin (NaN, -inf) == -inf", FUNC(fmin) (nan_value, minus_infty), minus_infty, 0, 0, 0); + check_float ("fmin (NaN, NaN) == NaN", FUNC(fmin) (nan_value, nan_value), nan_value, 0, 0, 0); + + print_max_error ("fmin", 0, 0); +} + + +static void +fmod_test (void) +{ + errno = 0; + FUNC(fmod) (6.5, 2.3L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + /* fmod (+0, y) == +0 for y != 0. */ + check_float ("fmod (0, 3) == 0", FUNC(fmod) (0, 3), 0, 0, 0, 0); + + /* fmod (-0, y) == -0 for y != 0. */ + check_float ("fmod (-0, 3) == -0", FUNC(fmod) (minus_zero, 3), minus_zero, 0, 0, 0); + + /* fmod (+inf, y) == NaN plus invalid exception. */ + check_float ("fmod (inf, 3) == NaN plus invalid exception", FUNC(fmod) (plus_infty, 3), nan_value, 0, 0, INVALID_EXCEPTION); + /* fmod (-inf, y) == NaN plus invalid exception. */ + check_float ("fmod (-inf, 3) == NaN plus invalid exception", FUNC(fmod) (minus_infty, 3), nan_value, 0, 0, INVALID_EXCEPTION); + /* fmod (x, +0) == NaN plus invalid exception. */ + check_float ("fmod (3, 0) == NaN plus invalid exception", FUNC(fmod) (3, 0), nan_value, 0, 0, INVALID_EXCEPTION); + /* fmod (x, -0) == NaN plus invalid exception. */ + check_float ("fmod (3, -0) == NaN plus invalid exception", FUNC(fmod) (3, minus_zero), nan_value, 0, 0, INVALID_EXCEPTION); + + /* fmod (x, +inf) == x for x not infinite. */ + check_float ("fmod (3.0, inf) == 3.0", FUNC(fmod) (3.0, plus_infty), 3.0, 0, 0, 0); + /* fmod (x, -inf) == x for x not infinite. */ + check_float ("fmod (3.0, -inf) == 3.0", FUNC(fmod) (3.0, minus_infty), 3.0, 0, 0, 0); + + check_float ("fmod (NaN, NaN) == NaN", FUNC(fmod) (nan_value, nan_value), nan_value, 0, 0, 0); + + check_float ("fmod (6.5, 2.3) == 1.9", FUNC(fmod) (6.5, 2.3L), 1.9L, DELTA972, 0, 0); + check_float ("fmod (-6.5, 2.3) == -1.9", FUNC(fmod) (-6.5, 2.3L), -1.9L, DELTA973, 0, 0); + check_float ("fmod (6.5, -2.3) == 1.9", FUNC(fmod) (6.5, -2.3L), 1.9L, DELTA974, 0, 0); + check_float ("fmod (-6.5, -2.3) == -1.9", FUNC(fmod) (-6.5, -2.3L), -1.9L, DELTA975, 0, 0); + + print_max_error ("fmod", DELTAfmod, 0); +} + +static void +fpclassify_test (void) +{ + init_max_error (); + + check_int ("fpclassify (NaN) == FP_NAN", fpclassify (nan_value), FP_NAN, 0, 0, 0); + check_int ("fpclassify (inf) == FP_INFINITE", fpclassify (plus_infty), FP_INFINITE, 0, 0, 0); + check_int ("fpclassify (-inf) == FP_INFINITE", fpclassify (minus_infty), FP_INFINITE, 0, 0, 0); + check_int ("fpclassify (+0) == FP_ZERO", fpclassify (plus_zero), FP_ZERO, 0, 0, 0); + check_int ("fpclassify (-0) == FP_ZERO", fpclassify (minus_zero), FP_ZERO, 0, 0, 0); + check_int ("fpclassify (1000) == FP_NORMAL", fpclassify (1000.0), FP_NORMAL, 0, 0, 0); + + print_max_error ("fpclassify", 0, 0); +} + + +static void +frexp_test (void) +{ + int x; + + init_max_error (); + + check_float ("frexp (inf, &x) == inf", FUNC(frexp) (plus_infty, &x), plus_infty, 0, 0, 0); + check_float ("frexp (-inf, &x) == -inf", FUNC(frexp) (minus_infty, &x), minus_infty, 0, 0, 0); + check_float ("frexp (NaN, &x) == NaN", FUNC(frexp) (nan_value, &x), nan_value, 0, 0, 0); + + check_float ("frexp (0.0, &x) == 0.0", FUNC(frexp) (0.0, &x), 0.0, 0, 0, 0); + check_int ("frexp (0.0, &x) sets x to 0.0", x, 0.0, 0, 0, 0); + check_float ("frexp (-0, &x) == -0", FUNC(frexp) (minus_zero, &x), minus_zero, 0, 0, 0); + check_int ("frexp (-0, &x) sets x to 0.0", x, 0.0, 0, 0, 0); + + check_float ("frexp (12.8, &x) == 0.8", FUNC(frexp) (12.8L, &x), 0.8L, 0, 0, 0); + check_int ("frexp (12.8, &x) sets x to 4", x, 4, 0, 0, 0); + check_float ("frexp (-27.34, &x) == -0.854375", FUNC(frexp) (-27.34L, &x), -0.854375L, 0, 0, 0); + check_int ("frexp (-27.34, &x) sets x to 5", x, 5, 0, 0, 0); + + print_max_error ("frexp", 0, 0); +} + +#define gamma lgamma /* XXX scp XXX */ +#define gammaf lgammaf /* XXX scp XXX */ +static void +gamma_test (void) +{ + errno = 0; + FUNC(gamma) (1); + + if (errno == ENOSYS) + /* Function not implemented. */ + return; + feclearexcept (FE_ALL_EXCEPT); + + init_max_error (); + + signgam = 0; + check_float ("gamma (inf) == inf", FUNC(gamma) (plus_infty), plus_infty, 0, 0, 0); + signgam = 0; + check_float ("gamma (0) == inf plus division by zero exception", FUNC(gamma) (0), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + signgam = 0; + check_float ("gamma (-3) == inf plus division by zero exception", FUNC(gamma) (-3), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + signgam = 0; + check_float ("gamma (-inf) == inf", FUNC(gamma) (minus_infty), plus_infty, 0, 0, 0); + signgam = 0; + check_float ("gamma (NaN) == NaN", FUNC(gamma) (nan_value), nan_value, 0, 0, 0); + + signgam = 0; + check_float ("gamma (1) == 0", FUNC(gamma) (1), 0, 0, 0, 0); + check_int ("gamma (1) sets signgam to 1", signgam, 1, 0, 0, 0); + signgam = 0; + check_float ("gamma (3) == M_LN2l", FUNC(gamma) (3), M_LN2l, 0, 0, 0); + check_int ("gamma (3) sets signgam to 1", signgam, 1, 0, 0, 0); + + signgam = 0; + check_float ("gamma (0.5) == log(sqrt(pi))", FUNC(gamma) (0.5), M_LOG_SQRT_PIl, 0, 0, 0); + check_int ("gamma (0.5) sets signgam to 1", signgam, 1, 0, 0, 0); + signgam = 0; + check_float ("gamma (-0.5) == log(2*sqrt(pi))", FUNC(gamma) (-0.5), M_LOG_2_SQRT_PIl, DELTA1004, 0, 0); + check_int ("gamma (-0.5) sets signgam to -1", signgam, -1, 0, 0, 0); + + print_max_error ("gamma", DELTAgamma, 0); +} +#undef gamma /* XXX scp XXX */ +#undef gammaf /* XXX scp XXX */ + +static void +hypot_test (void) +{ + errno = 0; + FUNC(hypot) (0.7L, 12.4L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("hypot (inf, 1) == inf plus sign of zero/inf not specified", FUNC(hypot) (plus_infty, 1), plus_infty, 0, 0, IGNORE_ZERO_INF_SIGN); + check_float ("hypot (-inf, 1) == inf plus sign of zero/inf not specified", FUNC(hypot) (minus_infty, 1), plus_infty, 0, 0, IGNORE_ZERO_INF_SIGN); + +#ifndef TEST_INLINE + check_float ("hypot (inf, NaN) == inf", FUNC(hypot) (plus_infty, nan_value), plus_infty, 0, 0, 0); + check_float ("hypot (-inf, NaN) == inf", FUNC(hypot) (minus_infty, nan_value), plus_infty, 0, 0, 0); + check_float ("hypot (NaN, inf) == inf", FUNC(hypot) (nan_value, plus_infty), plus_infty, 0, 0, 0); + check_float ("hypot (NaN, -inf) == inf", FUNC(hypot) (nan_value, minus_infty), plus_infty, 0, 0, 0); +#endif + + check_float ("hypot (NaN, NaN) == NaN", FUNC(hypot) (nan_value, nan_value), nan_value, 0, 0, 0); + + /* hypot (x,y) == hypot (+-x, +-y) */ + check_float ("hypot (0.7, 12.4) == 12.419742348374220601176836866763271", FUNC(hypot) (0.7L, 12.4L), 12.419742348374220601176836866763271L, DELTA1013, 0, 0); + check_float ("hypot (-0.7, 12.4) == 12.419742348374220601176836866763271", FUNC(hypot) (-0.7L, 12.4L), 12.419742348374220601176836866763271L, DELTA1014, 0, 0); + check_float ("hypot (0.7, -12.4) == 12.419742348374220601176836866763271", FUNC(hypot) (0.7L, -12.4L), 12.419742348374220601176836866763271L, DELTA1015, 0, 0); + check_float ("hypot (-0.7, -12.4) == 12.419742348374220601176836866763271", FUNC(hypot) (-0.7L, -12.4L), 12.419742348374220601176836866763271L, DELTA1016, 0, 0); + check_float ("hypot (12.4, 0.7) == 12.419742348374220601176836866763271", FUNC(hypot) (12.4L, 0.7L), 12.419742348374220601176836866763271L, DELTA1017, 0, 0); + check_float ("hypot (-12.4, 0.7) == 12.419742348374220601176836866763271", FUNC(hypot) (-12.4L, 0.7L), 12.419742348374220601176836866763271L, DELTA1018, 0, 0); + check_float ("hypot (12.4, -0.7) == 12.419742348374220601176836866763271", FUNC(hypot) (12.4L, -0.7L), 12.419742348374220601176836866763271L, DELTA1019, 0, 0); + check_float ("hypot (-12.4, -0.7) == 12.419742348374220601176836866763271", FUNC(hypot) (-12.4L, -0.7L), 12.419742348374220601176836866763271L, DELTA1020, 0, 0); + + /* hypot (x,0) == fabs (x) */ + check_float ("hypot (0.7, 0) == 0.7", FUNC(hypot) (0.7L, 0), 0.7L, 0, 0, 0); + check_float ("hypot (-0.7, 0) == 0.7", FUNC(hypot) (-0.7L, 0), 0.7L, 0, 0, 0); + check_float ("hypot (-5.7e7, 0) == 5.7e7", FUNC(hypot) (-5.7e7, 0), 5.7e7L, 0, 0, 0); + + check_float ("hypot (0.7, 1.2) == 1.3892443989449804508432547041028554", FUNC(hypot) (0.7L, 1.2L), 1.3892443989449804508432547041028554L, DELTA1024, 0, 0); + + print_max_error ("hypot", DELTAhypot, 0); +} + + +static void +ilogb_test (void) +{ + init_max_error (); + + check_int ("ilogb (1) == 0", FUNC(ilogb) (1), 0, 0, 0, 0); + check_int ("ilogb (e) == 1", FUNC(ilogb) (M_El), 1, 0, 0, 0); + check_int ("ilogb (1024) == 10", FUNC(ilogb) (1024), 10, 0, 0, 0); + check_int ("ilogb (-2000) == 10", FUNC(ilogb) (-2000), 10, 0, 0, 0); + + /* XXX We have a problem here: the standard does not tell us whether + exceptions are allowed/required. ignore them for now. */ + + check_int ("ilogb (0.0) == FP_ILOGB0 plus exceptions allowed", FUNC(ilogb) (0.0), FP_ILOGB0, 0, 0, EXCEPTIONS_OK); + check_int ("ilogb (NaN) == FP_ILOGBNAN plus exceptions allowed", FUNC(ilogb) (nan_value), FP_ILOGBNAN, 0, 0, EXCEPTIONS_OK); + check_int ("ilogb (inf) == INT_MAX plus exceptions allowed", FUNC(ilogb) (plus_infty), INT_MAX, 0, 0, EXCEPTIONS_OK); + check_int ("ilogb (-inf) == INT_MAX plus exceptions allowed", FUNC(ilogb) (minus_infty), INT_MAX, 0, 0, EXCEPTIONS_OK); + + print_max_error ("ilogb", 0, 0); +} + +static void +isfinite_test (void) +{ + init_max_error (); + + check_bool ("isfinite (0) == true", isfinite (0.0), 1, 0, 0, 0); + check_bool ("isfinite (-0) == true", isfinite (minus_zero), 1, 0, 0, 0); + check_bool ("isfinite (10) == true", isfinite (10.0), 1, 0, 0, 0); + check_bool ("isfinite (inf) == false", isfinite (plus_infty), 0, 0, 0, 0); + check_bool ("isfinite (-inf) == false", isfinite (minus_infty), 0, 0, 0, 0); + check_bool ("isfinite (NaN) == false", isfinite (nan_value), 0, 0, 0, 0); + + print_max_error ("isfinite", 0, 0); +} + +static void +isnormal_test (void) +{ + init_max_error (); + + check_bool ("isnormal (0) == false", isnormal (0.0), 0, 0, 0, 0); + check_bool ("isnormal (-0) == false", isnormal (minus_zero), 0, 0, 0, 0); + check_bool ("isnormal (10) == true", isnormal (10.0), 1, 0, 0, 0); + check_bool ("isnormal (inf) == false", isnormal (plus_infty), 0, 0, 0, 0); + check_bool ("isnormal (-inf) == false", isnormal (minus_infty), 0, 0, 0, 0); + check_bool ("isnormal (NaN) == false", isnormal (nan_value), 0, 0, 0, 0); + + print_max_error ("isnormal", 0, 0); +} + +static void +j0_test (void) +{ + FLOAT s, c; + errno = 0; + FUNC (sincos) (0, &s, &c); + if (errno == ENOSYS) + /* Required function not implemented. */ + return; + FUNC(j0) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + /* j0 is the Bessel function of the first kind of order 0 */ + check_float ("j0 (NaN) == NaN", FUNC(j0) (nan_value), nan_value, 0, 0, 0); + check_float ("j0 (inf) == 0", FUNC(j0) (plus_infty), 0, 0, 0, 0); + check_float ("j0 (-1.0) == 0.76519768655796655145", FUNC(j0) (-1.0), 0.76519768655796655145L, 0, 0, 0); + check_float ("j0 (0.0) == 1.0", FUNC(j0) (0.0), 1.0, 0, 0, 0); + check_float ("j0 (0.1) == 0.99750156206604003228", FUNC(j0) (0.1L), 0.99750156206604003228L, 0, 0, 0); + check_float ("j0 (0.7) == 0.88120088860740528084", FUNC(j0) (0.7L), 0.88120088860740528084L, 0, 0, 0); + check_float ("j0 (1.0) == 0.76519768655796655145", FUNC(j0) (1.0), 0.76519768655796655145L, 0, 0, 0); + check_float ("j0 (1.5) == 0.51182767173591812875", FUNC(j0) (1.5), 0.51182767173591812875L, 0, 0, 0); + check_float ("j0 (2.0) == 0.22389077914123566805", FUNC(j0) (2.0), 0.22389077914123566805L, DELTA1053, 0, 0); + check_float ("j0 (8.0) == 0.17165080713755390609", FUNC(j0) (8.0), 0.17165080713755390609L, DELTA1054, 0, 0); + check_float ("j0 (10.0) == -0.24593576445134833520", FUNC(j0) (10.0), -0.24593576445134833520L, DELTA1055, 0, 0); + + print_max_error ("j0", DELTAj0, 0); +} + + +static void +j1_test (void) +{ + FLOAT s, c; + errno = 0; + FUNC (sincos) (0, &s, &c); + if (errno == ENOSYS) + /* Required function not implemented. */ + return; + FUNC(j1) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + /* j1 is the Bessel function of the first kind of order 1 */ + + init_max_error (); + + check_float ("j1 (NaN) == NaN", FUNC(j1) (nan_value), nan_value, 0, 0, 0); + check_float ("j1 (inf) == 0", FUNC(j1) (plus_infty), 0, 0, 0, 0); + + check_float ("j1 (-1.0) == -0.44005058574493351596", FUNC(j1) (-1.0), -0.44005058574493351596L, 0, 0, 0); + check_float ("j1 (0.0) == 0.0", FUNC(j1) (0.0), 0.0, 0, 0, 0); + check_float ("j1 (0.1) == 0.049937526036241997556", FUNC(j1) (0.1L), 0.049937526036241997556L, 0, 0, 0); + check_float ("j1 (0.7) == 0.32899574154005894785", FUNC(j1) (0.7L), 0.32899574154005894785L, 0, 0, 0); + check_float ("j1 (1.0) == 0.44005058574493351596", FUNC(j1) (1.0), 0.44005058574493351596L, 0, 0, 0); + check_float ("j1 (1.5) == 0.55793650791009964199", FUNC(j1) (1.5), 0.55793650791009964199L, 0, 0, 0); + check_float ("j1 (2.0) == 0.57672480775687338720", FUNC(j1) (2.0), 0.57672480775687338720L, DELTA1064, 0, 0); + check_float ("j1 (8.0) == 0.23463634685391462438", FUNC(j1) (8.0), 0.23463634685391462438L, DELTA1065, 0, 0); + check_float ("j1 (10.0) == 0.043472746168861436670", FUNC(j1) (10.0), 0.043472746168861436670L, DELTA1066, 0, 0); + + print_max_error ("j1", DELTAj1, 0); +} + +static void +jn_test (void) +{ + FLOAT s, c; + errno = 0; + FUNC (sincos) (0, &s, &c); + if (errno == ENOSYS) + /* Required function not implemented. */ + return; + FUNC(jn) (1, 1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + /* jn is the Bessel function of the first kind of order n. */ + init_max_error (); + + /* jn (0, x) == j0 (x) */ + check_float ("jn (0, NaN) == NaN", FUNC(jn) (0, nan_value), nan_value, 0, 0, 0); + check_float ("jn (0, inf) == 0", FUNC(jn) (0, plus_infty), 0, 0, 0, 0); + check_float ("jn (0, -1.0) == 0.76519768655796655145", FUNC(jn) (0, -1.0), 0.76519768655796655145L, 0, 0, 0); + check_float ("jn (0, 0.0) == 1.0", FUNC(jn) (0, 0.0), 1.0, 0, 0, 0); + check_float ("jn (0, 0.1) == 0.99750156206604003228", FUNC(jn) (0, 0.1L), 0.99750156206604003228L, 0, 0, 0); + check_float ("jn (0, 0.7) == 0.88120088860740528084", FUNC(jn) (0, 0.7L), 0.88120088860740528084L, 0, 0, 0); + check_float ("jn (0, 1.0) == 0.76519768655796655145", FUNC(jn) (0, 1.0), 0.76519768655796655145L, 0, 0, 0); + check_float ("jn (0, 1.5) == 0.51182767173591812875", FUNC(jn) (0, 1.5), 0.51182767173591812875L, 0, 0, 0); + check_float ("jn (0, 2.0) == 0.22389077914123566805", FUNC(jn) (0, 2.0), 0.22389077914123566805L, DELTA1075, 0, 0); + check_float ("jn (0, 8.0) == 0.17165080713755390609", FUNC(jn) (0, 8.0), 0.17165080713755390609L, DELTA1076, 0, 0); + check_float ("jn (0, 10.0) == -0.24593576445134833520", FUNC(jn) (0, 10.0), -0.24593576445134833520L, DELTA1077, 0, 0); + + /* jn (1, x) == j1 (x) */ + check_float ("jn (1, NaN) == NaN", FUNC(jn) (1, nan_value), nan_value, 0, 0, 0); + check_float ("jn (1, inf) == 0", FUNC(jn) (1, plus_infty), 0, 0, 0, 0); + + check_float ("jn (1, -1.0) == -0.44005058574493351596", FUNC(jn) (1, -1.0), -0.44005058574493351596L, 0, 0, 0); + check_float ("jn (1, 0.0) == 0.0", FUNC(jn) (1, 0.0), 0.0, 0, 0, 0); + check_float ("jn (1, 0.1) == 0.049937526036241997556", FUNC(jn) (1, 0.1L), 0.049937526036241997556L, 0, 0, 0); + check_float ("jn (1, 0.7) == 0.32899574154005894785", FUNC(jn) (1, 0.7L), 0.32899574154005894785L, 0, 0, 0); + check_float ("jn (1, 1.0) == 0.44005058574493351596", FUNC(jn) (1, 1.0), 0.44005058574493351596L, 0, 0, 0); + check_float ("jn (1, 1.5) == 0.55793650791009964199", FUNC(jn) (1, 1.5), 0.55793650791009964199L, 0, 0, 0); + check_float ("jn (1, 2.0) == 0.57672480775687338720", FUNC(jn) (1, 2.0), 0.57672480775687338720L, DELTA1086, 0, 0); + check_float ("jn (1, 8.0) == 0.23463634685391462438", FUNC(jn) (1, 8.0), 0.23463634685391462438L, DELTA1087, 0, 0); + check_float ("jn (1, 10.0) == 0.043472746168861436670", FUNC(jn) (1, 10.0), 0.043472746168861436670L, DELTA1088, 0, 0); + + /* jn (3, x) */ + check_float ("jn (3, NaN) == NaN", FUNC(jn) (3, nan_value), nan_value, 0, 0, 0); + check_float ("jn (3, inf) == 0", FUNC(jn) (3, plus_infty), 0, 0, 0, 0); + + check_float ("jn (3, -1.0) == -0.019563353982668405919", FUNC(jn) (3, -1.0), -0.019563353982668405919L, DELTA1091, 0, 0); + check_float ("jn (3, 0.0) == 0.0", FUNC(jn) (3, 0.0), 0.0, 0, 0, 0); + check_float ("jn (3, 0.1) == 0.000020820315754756261429", FUNC(jn) (3, 0.1L), 0.000020820315754756261429L, DELTA1093, 0, 0); + check_float ("jn (3, 0.7) == 0.0069296548267508408077", FUNC(jn) (3, 0.7L), 0.0069296548267508408077L, DELTA1094, 0, 0); + check_float ("jn (3, 1.0) == 0.019563353982668405919", FUNC(jn) (3, 1.0), 0.019563353982668405919L, DELTA1095, 0, 0); + check_float ("jn (3, 2.0) == 0.12894324947440205110", FUNC(jn) (3, 2.0), 0.12894324947440205110L, DELTA1096, 0, 0); + check_float ("jn (3, 10.0) == 0.058379379305186812343", FUNC(jn) (3, 10.0), 0.058379379305186812343L, DELTA1097, 0, 0); + + /* jn (10, x) */ + check_float ("jn (10, NaN) == NaN", FUNC(jn) (10, nan_value), nan_value, 0, 0, 0); + check_float ("jn (10, inf) == 0", FUNC(jn) (10, plus_infty), 0, 0, 0, 0); + + check_float ("jn (10, -1.0) == 0.26306151236874532070e-9", FUNC(jn) (10, -1.0), 0.26306151236874532070e-9L, DELTA1100, 0, 0); + check_float ("jn (10, 0.0) == 0.0", FUNC(jn) (10, 0.0), 0.0, 0, 0, 0); + check_float ("jn (10, 0.1) == 0.26905328954342155795e-19", FUNC(jn) (10, 0.1L), 0.26905328954342155795e-19L, DELTA1102, 0, 0); + check_float ("jn (10, 0.7) == 0.75175911502153953928e-11", FUNC(jn) (10, 0.7L), 0.75175911502153953928e-11L, DELTA1103, 0, 0); + check_float ("jn (10, 1.0) == 0.26306151236874532070e-9", FUNC(jn) (10, 1.0), 0.26306151236874532070e-9L, DELTA1104, 0, 0); + check_float ("jn (10, 2.0) == 0.25153862827167367096e-6", FUNC(jn) (10, 2.0), 0.25153862827167367096e-6L, DELTA1105, 0, 0); + check_float ("jn (10, 10.0) == 0.20748610663335885770", FUNC(jn) (10, 10.0), 0.20748610663335885770L, DELTA1106, 0, 0); + + print_max_error ("jn", DELTAjn, 0); +} + + +static void +ldexp_test (void) +{ + check_float ("ldexp (0, 0) == 0", FUNC(ldexp) (0, 0), 0, 0, 0, 0); + check_float ("ldexp (-0, 0) == -0", FUNC(ldexp) (minus_zero, 0), minus_zero, 0, 0, 0); + + check_float ("ldexp (inf, 1) == inf", FUNC(ldexp) (plus_infty, 1), plus_infty, 0, 0, 0); + check_float ("ldexp (-inf, 1) == -inf", FUNC(ldexp) (minus_infty, 1), minus_infty, 0, 0, 0); + check_float ("ldexp (NaN, 1) == NaN", FUNC(ldexp) (nan_value, 1), nan_value, 0, 0, 0); + + check_float ("ldexp (0.8, 4) == 12.8", FUNC(ldexp) (0.8L, 4), 12.8L, 0, 0, 0); + check_float ("ldexp (-0.854375, 5) == -27.34", FUNC(ldexp) (-0.854375L, 5), -27.34L, 0, 0, 0); + + /* ldexp (x, 0) == x. */ + check_float ("ldexp (1.0, 0) == 1.0", FUNC(ldexp) (1.0L, 0L), 1.0L, 0, 0, 0); +} + +static void +lgamma_test (void) +{ + errno = 0; + FUNC(lgamma) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + feclearexcept (FE_ALL_EXCEPT); + + init_max_error (); + + signgam = 0; + check_float ("lgamma (inf) == inf", FUNC(lgamma) (plus_infty), plus_infty, 0, 0, 0); + signgam = 0; + check_float ("lgamma (0) == inf plus division by zero exception", FUNC(lgamma) (0), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + signgam = 0; + check_float ("lgamma (NaN) == NaN", FUNC(lgamma) (nan_value), nan_value, 0, 0, 0); + + /* lgamma (x) == +inf plus divide by zero exception for integer x <= 0. */ + signgam = 0; + check_float ("lgamma (-3) == inf plus division by zero exception", FUNC(lgamma) (-3), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + signgam = 0; + check_float ("lgamma (-inf) == inf", FUNC(lgamma) (minus_infty), plus_infty, 0, 0, 0); + + signgam = 0; + check_float ("lgamma (1) == 0", FUNC(lgamma) (1), 0, 0, 0, 0); + check_int ("lgamma (1) sets signgam to 1", signgam, 1, 0, 0, 0); + + signgam = 0; + check_float ("lgamma (3) == M_LN2l", FUNC(lgamma) (3), M_LN2l, 0, 0, 0); + check_int ("lgamma (3) sets signgam to 1", signgam, 1, 0, 0, 0); + + signgam = 0; + check_float ("lgamma (0.5) == log(sqrt(pi))", FUNC(lgamma) (0.5), M_LOG_SQRT_PIl, 0, 0, 0); + check_int ("lgamma (0.5) sets signgam to 1", signgam, 1, 0, 0, 0); + signgam = 0; + check_float ("lgamma (-0.5) == log(2*sqrt(pi))", FUNC(lgamma) (-0.5), M_LOG_2_SQRT_PIl, DELTA1126, 0, 0); + check_int ("lgamma (-0.5) sets signgam to -1", signgam, -1, 0, 0, 0); + signgam = 0; + check_float ("lgamma (0.7) == 0.26086724653166651439", FUNC(lgamma) (0.7L), 0.26086724653166651439L, DELTA1128, 0, 0); + check_int ("lgamma (0.7) sets signgam to 1", signgam, 1, 0, 0, 0); + signgam = 0; + check_float ("lgamma (1.2) == -0.853740900033158497197e-1", FUNC(lgamma) (1.2L), -0.853740900033158497197e-1L, DELTA1130, 0, 0); + check_int ("lgamma (1.2) sets signgam to 1", signgam, 1, 0, 0, 0); + + print_max_error ("lgamma", DELTAlgamma, 0); +} + +static void +lrint_test (void) +{ + /* XXX this test is incomplete. We need to have a way to specifiy + the rounding method and test the critical cases. So far, only + unproblematic numbers are tested. */ + + init_max_error (); + + check_long ("lrint (0.0) == 0", FUNC(lrint) (0.0), 0, 0, 0, 0); + check_long ("lrint (-0) == 0", FUNC(lrint) (minus_zero), 0, 0, 0, 0); + check_long ("lrint (0.2) == 0", FUNC(lrint) (0.2L), 0, 0, 0, 0); + check_long ("lrint (-0.2) == 0", FUNC(lrint) (-0.2L), 0, 0, 0, 0); + + check_long ("lrint (1.4) == 1", FUNC(lrint) (1.4L), 1, 0, 0, 0); + check_long ("lrint (-1.4) == -1", FUNC(lrint) (-1.4L), -1, 0, 0, 0); + + check_long ("lrint (8388600.3) == 8388600", FUNC(lrint) (8388600.3L), 8388600, 0, 0, 0); + check_long ("lrint (-8388600.3) == -8388600", FUNC(lrint) (-8388600.3L), -8388600, 0, 0, 0); + + print_max_error ("lrint", 0, 0); +} + +static void +llrint_test (void) +{ + /* XXX this test is incomplete. We need to have a way to specifiy + the rounding method and test the critical cases. So far, only + unproblematic numbers are tested. */ + + init_max_error (); + + check_longlong ("llrint (0.0) == 0", FUNC(llrint) (0.0), 0, 0, 0, 0); + check_longlong ("llrint (-0) == 0", FUNC(llrint) (minus_zero), 0, 0, 0, 0); + check_longlong ("llrint (0.2) == 0", FUNC(llrint) (0.2L), 0, 0, 0, 0); + check_longlong ("llrint (-0.2) == 0", FUNC(llrint) (-0.2L), 0, 0, 0, 0); + + check_longlong ("llrint (1.4) == 1", FUNC(llrint) (1.4L), 1, 0, 0, 0); + check_longlong ("llrint (-1.4) == -1", FUNC(llrint) (-1.4L), -1, 0, 0, 0); + + check_longlong ("llrint (8388600.3) == 8388600", FUNC(llrint) (8388600.3L), 8388600, 0, 0, 0); + check_longlong ("llrint (-8388600.3) == -8388600", FUNC(llrint) (-8388600.3L), -8388600, 0, 0, 0); + + /* Test boundary conditions. */ + /* 0x1FFFFF */ + check_longlong ("llrint (2097151.0) == 2097151LL", FUNC(llrint) (2097151.0), 2097151LL, 0, 0, 0); + /* 0x800000 */ + check_longlong ("llrint (8388608.0) == 8388608LL", FUNC(llrint) (8388608.0), 8388608LL, 0, 0, 0); + /* 0x1000000 */ + check_longlong ("llrint (16777216.0) == 16777216LL", FUNC(llrint) (16777216.0), 16777216LL, 0, 0, 0); + /* 0x20000000000 */ + check_longlong ("llrint (2199023255552.0) == 2199023255552LL", FUNC(llrint) (2199023255552.0), 2199023255552LL, 0, 0, 0); + /* 0x40000000000 */ + check_longlong ("llrint (4398046511104.0) == 4398046511104LL", FUNC(llrint) (4398046511104.0), 4398046511104LL, 0, 0, 0); + /* 0x10000000000000 */ + check_longlong ("llrint (4503599627370496.0) == 4503599627370496LL", FUNC(llrint) (4503599627370496.0), 4503599627370496LL, 0, 0, 0); + /* 0x10000080000000 */ + check_longlong ("llrint (4503601774854144.0) == 4503601774854144LL", FUNC(llrint) (4503601774854144.0), 4503601774854144LL, 0, 0, 0); + /* 0x20000000000000 */ + check_longlong ("llrint (9007199254740992.0) == 9007199254740992LL", FUNC(llrint) (9007199254740992.0), 9007199254740992LL, 0, 0, 0); + /* 0x80000000000000 */ + check_longlong ("llrint (36028797018963968.0) == 36028797018963968LL", FUNC(llrint) (36028797018963968.0), 36028797018963968LL, 0, 0, 0); + /* 0x100000000000000 */ + check_longlong ("llrint (72057594037927936.0) == 72057594037927936LL", FUNC(llrint) (72057594037927936.0), 72057594037927936LL, 0, 0, 0); + + print_max_error ("llrint", 0, 0); +} + +static void +log_test (void) +{ + errno = 0; + FUNC(log) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + init_max_error (); + + check_float ("log (0) == -inf plus division by zero exception", FUNC(log) (0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("log (-0) == -inf plus division by zero exception", FUNC(log) (minus_zero), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_float ("log (1) == 0", FUNC(log) (1), 0, 0, 0, 0); + + check_float ("log (-1) == NaN plus invalid exception", FUNC(log) (-1), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("log (inf) == inf", FUNC(log) (plus_infty), plus_infty, 0, 0, 0); + + check_float ("log (e) == 1", FUNC(log) (M_El), 1, DELTA1163, 0, 0); + check_float ("log (1.0 / M_El) == -1", FUNC(log) (1.0 / M_El), -1, DELTA1164, 0, 0); + check_float ("log (2) == M_LN2l", FUNC(log) (2), M_LN2l, 0, 0, 0); + check_float ("log (10) == M_LN10l", FUNC(log) (10), M_LN10l, 0, 0, 0); + check_float ("log (0.7) == -0.35667494393873237891263871124118447", FUNC(log) (0.7L), -0.35667494393873237891263871124118447L, DELTA1167, 0, 0); + + print_max_error ("log", DELTAlog, 0); +} + + +static void +log10_test (void) +{ + errno = 0; + FUNC(log10) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("log10 (0) == -inf plus division by zero exception", FUNC(log10) (0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("log10 (-0) == -inf plus division by zero exception", FUNC(log10) (minus_zero), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_float ("log10 (1) == 0", FUNC(log10) (1), 0, 0, 0, 0); + + /* log10 (x) == NaN plus invalid exception if x < 0. */ + check_float ("log10 (-1) == NaN plus invalid exception", FUNC(log10) (-1), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("log10 (inf) == inf", FUNC(log10) (plus_infty), plus_infty, 0, 0, 0); + check_float ("log10 (NaN) == NaN", FUNC(log10) (nan_value), nan_value, 0, 0, 0); + + check_float ("log10 (0.1) == -1", FUNC(log10) (0.1L), -1, 0, 0, 0); + check_float ("log10 (10.0) == 1", FUNC(log10) (10.0), 1, 0, 0, 0); + check_float ("log10 (100.0) == 2", FUNC(log10) (100.0), 2, 0, 0, 0); + check_float ("log10 (10000.0) == 4", FUNC(log10) (10000.0), 4, 0, 0, 0); + check_float ("log10 (e) == log10(e)", FUNC(log10) (M_El), M_LOG10El, DELTA1178, 0, 0); + check_float ("log10 (0.7) == -0.15490195998574316929", FUNC(log10) (0.7L), -0.15490195998574316929L, DELTA1179, 0, 0); + + print_max_error ("log10", DELTAlog10, 0); +} + + +static void +log1p_test (void) +{ + errno = 0; + FUNC(log1p) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("log1p (0) == 0", FUNC(log1p) (0), 0, 0, 0, 0); + check_float ("log1p (-0) == -0", FUNC(log1p) (minus_zero), minus_zero, 0, 0, 0); + + check_float ("log1p (-1) == -inf plus division by zero exception", FUNC(log1p) (-1), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("log1p (-2) == NaN plus invalid exception", FUNC(log1p) (-2), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("log1p (inf) == inf", FUNC(log1p) (plus_infty), plus_infty, 0, 0, 0); + check_float ("log1p (NaN) == NaN", FUNC(log1p) (nan_value), nan_value, 0, 0, 0); + + check_float ("log1p (M_El - 1.0) == 1", FUNC(log1p) (M_El - 1.0), 1, DELTA1186, 0, 0); + + check_float ("log1p (-0.3) == -0.35667494393873237891263871124118447", FUNC(log1p) (-0.3L), -0.35667494393873237891263871124118447L, DELTA1187, 0, 0); + + print_max_error ("log1p", DELTAlog1p, 0); +} + + +static void +log2_test (void) +{ + errno = 0; + FUNC(log2) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("log2 (0) == -inf plus division by zero exception", FUNC(log2) (0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("log2 (-0) == -inf plus division by zero exception", FUNC(log2) (minus_zero), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_float ("log2 (1) == 0", FUNC(log2) (1), 0, 0, 0, 0); + + check_float ("log2 (-1) == NaN plus invalid exception", FUNC(log2) (-1), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("log2 (inf) == inf", FUNC(log2) (plus_infty), plus_infty, 0, 0, 0); + check_float ("log2 (NaN) == NaN", FUNC(log2) (nan_value), nan_value, 0, 0, 0); + + check_float ("log2 (e) == M_LOG2El", FUNC(log2) (M_El), M_LOG2El, 0, 0, 0); + check_float ("log2 (2.0) == 1", FUNC(log2) (2.0), 1, 0, 0, 0); + check_float ("log2 (16.0) == 4", FUNC(log2) (16.0), 4, 0, 0, 0); + check_float ("log2 (256.0) == 8", FUNC(log2) (256.0), 8, 0, 0, 0); + check_float ("log2 (0.7) == -0.51457317282975824043", FUNC(log2) (0.7L), -0.51457317282975824043L, DELTA1198, 0, 0); + + print_max_error ("log2", DELTAlog2, 0); +} + + +static void +logb_test (void) +{ + init_max_error (); + + check_float ("logb (inf) == inf", FUNC(logb) (plus_infty), plus_infty, 0, 0, 0); + check_float ("logb (-inf) == inf", FUNC(logb) (minus_infty), plus_infty, 0, 0, 0); + + check_float ("logb (0) == -inf plus division by zero exception", FUNC(logb) (0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_float ("logb (-0) == -inf plus division by zero exception", FUNC(logb) (minus_zero), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("logb (NaN) == NaN", FUNC(logb) (nan_value), nan_value, 0, 0, 0); + + check_float ("logb (1) == 0", FUNC(logb) (1), 0, 0, 0, 0); + check_float ("logb (e) == 1", FUNC(logb) (M_El), 1, 0, 0, 0); + check_float ("logb (1024) == 10", FUNC(logb) (1024), 10, 0, 0, 0); + check_float ("logb (-2000) == 10", FUNC(logb) (-2000), 10, 0, 0, 0); + + print_max_error ("logb", 0, 0); +} + +static void +lround_test (void) +{ + init_max_error (); + + check_long ("lround (0) == 0", FUNC(lround) (0), 0, 0, 0, 0); + check_long ("lround (-0) == 0", FUNC(lround) (minus_zero), 0, 0, 0, 0); + check_long ("lround (0.2) == 0.0", FUNC(lround) (0.2L), 0.0, 0, 0, 0); + check_long ("lround (-0.2) == 0", FUNC(lround) (-0.2L), 0, 0, 0, 0); + check_long ("lround (0.5) == 1", FUNC(lround) (0.5), 1, 0, 0, 0); + check_long ("lround (-0.5) == -1", FUNC(lround) (-0.5), -1, 0, 0, 0); + check_long ("lround (0.8) == 1", FUNC(lround) (0.8L), 1, 0, 0, 0); + check_long ("lround (-0.8) == -1", FUNC(lround) (-0.8L), -1, 0, 0, 0); + check_long ("lround (1.5) == 2", FUNC(lround) (1.5), 2, 0, 0, 0); + check_long ("lround (-1.5) == -2", FUNC(lround) (-1.5), -2, 0, 0, 0); + check_long ("lround (22514.5) == 22515", FUNC(lround) (22514.5), 22515, 0, 0, 0); + check_long ("lround (-22514.5) == -22515", FUNC(lround) (-22514.5), -22515, 0, 0, 0); +#ifndef TEST_FLOAT + check_long ("lround (2097152.5) == 2097153", FUNC(lround) (2097152.5), 2097153, 0, 0, 0); + check_long ("lround (-2097152.5) == -2097153", FUNC(lround) (-2097152.5), -2097153, 0, 0, 0); +#endif + print_max_error ("lround", 0, 0); +} + + +static void +llround_test (void) +{ + init_max_error (); + + check_longlong ("llround (0) == 0", FUNC(llround) (0), 0, 0, 0, 0); + check_longlong ("llround (-0) == 0", FUNC(llround) (minus_zero), 0, 0, 0, 0); + check_longlong ("llround (0.2) == 0.0", FUNC(llround) (0.2L), 0.0, 0, 0, 0); + check_longlong ("llround (-0.2) == 0", FUNC(llround) (-0.2L), 0, 0, 0, 0); + check_longlong ("llround (0.5) == 1", FUNC(llround) (0.5), 1, 0, 0, 0); + check_longlong ("llround (-0.5) == -1", FUNC(llround) (-0.5), -1, 0, 0, 0); + check_longlong ("llround (0.8) == 1", FUNC(llround) (0.8L), 1, 0, 0, 0); + check_longlong ("llround (-0.8) == -1", FUNC(llround) (-0.8L), -1, 0, 0, 0); + check_longlong ("llround (1.5) == 2", FUNC(llround) (1.5), 2, 0, 0, 0); + check_longlong ("llround (-1.5) == -2", FUNC(llround) (-1.5), -2, 0, 0, 0); + check_longlong ("llround (22514.5) == 22515", FUNC(llround) (22514.5), 22515, 0, 0, 0); + check_longlong ("llround (-22514.5) == -22515", FUNC(llround) (-22514.5), -22515, 0, 0, 0); +#ifndef TEST_FLOAT + check_longlong ("llround (2097152.5) == 2097153", FUNC(llround) (2097152.5), 2097153, 0, 0, 0); + check_longlong ("llround (-2097152.5) == -2097153", FUNC(llround) (-2097152.5), -2097153, 0, 0, 0); + check_longlong ("llround (34359738368.5) == 34359738369ll", FUNC(llround) (34359738368.5), 34359738369ll, 0, 0, 0); + check_longlong ("llround (-34359738368.5) == -34359738369ll", FUNC(llround) (-34359738368.5), -34359738369ll, 0, 0, 0); +#endif + + /* Test boundary conditions. */ + /* 0x1FFFFF */ + check_longlong ("llround (2097151.0) == 2097151LL", FUNC(llround) (2097151.0), 2097151LL, 0, 0, 0); + /* 0x800000 */ + check_longlong ("llround (8388608.0) == 8388608LL", FUNC(llround) (8388608.0), 8388608LL, 0, 0, 0); + /* 0x1000000 */ + check_longlong ("llround (16777216.0) == 16777216LL", FUNC(llround) (16777216.0), 16777216LL, 0, 0, 0); + /* 0x20000000000 */ + check_longlong ("llround (2199023255552.0) == 2199023255552LL", FUNC(llround) (2199023255552.0), 2199023255552LL, 0, 0, 0); + /* 0x40000000000 */ + check_longlong ("llround (4398046511104.0) == 4398046511104LL", FUNC(llround) (4398046511104.0), 4398046511104LL, 0, 0, 0); + /* 0x10000000000000 */ + check_longlong ("llround (4503599627370496.0) == 4503599627370496LL", FUNC(llround) (4503599627370496.0), 4503599627370496LL, 0, 0, 0); + /* 0x10000080000000 */ + check_longlong ("llrint (4503601774854144.0) == 4503601774854144LL", FUNC(llrint) (4503601774854144.0), 4503601774854144LL, 0, 0, 0); + /* 0x20000000000000 */ + check_longlong ("llround (9007199254740992.0) == 9007199254740992LL", FUNC(llround) (9007199254740992.0), 9007199254740992LL, 0, 0, 0); + /* 0x80000000000000 */ + check_longlong ("llround (36028797018963968.0) == 36028797018963968LL", FUNC(llround) (36028797018963968.0), 36028797018963968LL, 0, 0, 0); + /* 0x100000000000000 */ + check_longlong ("llround (72057594037927936.0) == 72057594037927936LL", FUNC(llround) (72057594037927936.0), 72057594037927936LL, 0, 0, 0); + +#ifndef TEST_FLOAT + /* 0x100000000 */ + check_longlong ("llround (4294967295.5) == 4294967296LL", FUNC(llround) (4294967295.5), 4294967296LL, 0, 0, 0); + /* 0x200000000 */ + check_longlong ("llround (8589934591.5) == 8589934592LL", FUNC(llround) (8589934591.5), 8589934592LL, 0, 0, 0); +#endif + + print_max_error ("llround", 0, 0); +} + +static void +modf_test (void) +{ + FLOAT x; + + init_max_error (); + + check_float ("modf (inf, &x) == 0", FUNC(modf) (plus_infty, &x), 0, 0, 0, 0); + check_float ("modf (inf, &x) sets x to plus_infty", x, plus_infty, 0, 0, 0); + check_float ("modf (-inf, &x) == -0", FUNC(modf) (minus_infty, &x), minus_zero, 0, 0, 0); + check_float ("modf (-inf, &x) sets x to minus_infty", x, minus_infty, 0, 0, 0); + check_float ("modf (NaN, &x) == NaN", FUNC(modf) (nan_value, &x), nan_value, 0, 0, 0); + check_float ("modf (NaN, &x) sets x to nan_value", x, nan_value, 0, 0, 0); + check_float ("modf (0, &x) == 0", FUNC(modf) (0, &x), 0, 0, 0, 0); + check_float ("modf (0, &x) sets x to 0", x, 0, 0, 0, 0); + check_float ("modf (1.5, &x) == 0.5", FUNC(modf) (1.5, &x), 0.5, 0, 0, 0); + check_float ("modf (1.5, &x) sets x to 1", x, 1, 0, 0, 0); + check_float ("modf (2.5, &x) == 0.5", FUNC(modf) (2.5, &x), 0.5, 0, 0, 0); + check_float ("modf (2.5, &x) sets x to 2", x, 2, 0, 0, 0); + check_float ("modf (-2.5, &x) == -0.5", FUNC(modf) (-2.5, &x), -0.5, 0, 0, 0); + check_float ("modf (-2.5, &x) sets x to -2", x, -2, 0, 0, 0); + check_float ("modf (20, &x) == 0", FUNC(modf) (20, &x), 0, 0, 0, 0); + check_float ("modf (20, &x) sets x to 20", x, 20, 0, 0, 0); + check_float ("modf (21, &x) == 0", FUNC(modf) (21, &x), 0, 0, 0, 0); + check_float ("modf (21, &x) sets x to 21", x, 21, 0, 0, 0); + check_float ("modf (89.5, &x) == 0.5", FUNC(modf) (89.5, &x), 0.5, 0, 0, 0); + check_float ("modf (89.5, &x) sets x to 89", x, 89, 0, 0, 0); + + print_max_error ("modf", 0, 0); +} + +static void +nearbyint_test (void) +{ + init_max_error (); + + check_float ("nearbyint (0.0) == 0.0", FUNC(nearbyint) (0.0), 0.0, 0, 0, 0); + check_float ("nearbyint (-0) == -0", FUNC(nearbyint) (minus_zero), minus_zero, 0, 0, 0); + check_float ("nearbyint (inf) == inf", FUNC(nearbyint) (plus_infty), plus_infty, 0, 0, 0); + check_float ("nearbyint (-inf) == -inf", FUNC(nearbyint) (minus_infty), minus_infty, 0, 0, 0); + check_float ("nearbyint (NaN) == NaN", FUNC(nearbyint) (nan_value), nan_value, 0, 0, 0); + + /* Default rounding mode is round to nearest. */ + check_float ("nearbyint (0.5) == 0.0", FUNC(nearbyint) (0.5), 0.0, 0, 0, 0); + check_float ("nearbyint (1.5) == 2.0", FUNC(nearbyint) (1.5), 2.0, 0, 0, 0); + check_float ("nearbyint (-0.5) == -0", FUNC(nearbyint) (-0.5), minus_zero, 0, 0, 0); + check_float ("nearbyint (-1.5) == -2.0", FUNC(nearbyint) (-1.5), -2.0, 0, 0, 0); + + print_max_error ("nearbyint", 0, 0); +} + +static void +nextafter_test (void) +{ + + init_max_error (); + + check_float ("nextafter (0, 0) == 0", FUNC(nextafter) (0, 0), 0, 0, 0, 0); + check_float ("nextafter (-0, 0) == 0", FUNC(nextafter) (minus_zero, 0), 0, 0, 0, 0); + check_float ("nextafter (0, -0) == -0", FUNC(nextafter) (0, minus_zero), minus_zero, 0, 0, 0); + check_float ("nextafter (-0, -0) == -0", FUNC(nextafter) (minus_zero, minus_zero), minus_zero, 0, 0, 0); + + check_float ("nextafter (9, 9) == 9", FUNC(nextafter) (9, 9), 9, 0, 0, 0); + check_float ("nextafter (-9, -9) == -9", FUNC(nextafter) (-9, -9), -9, 0, 0, 0); + check_float ("nextafter (inf, inf) == inf", FUNC(nextafter) (plus_infty, plus_infty), plus_infty, 0, 0, 0); + check_float ("nextafter (-inf, -inf) == -inf", FUNC(nextafter) (minus_infty, minus_infty), minus_infty, 0, 0, 0); + + check_float ("nextafter (NaN, 1.1) == NaN", FUNC(nextafter) (nan_value, 1.1L), nan_value, 0, 0, 0); + check_float ("nextafter (1.1, NaN) == NaN", FUNC(nextafter) (1.1L, nan_value), nan_value, 0, 0, 0); + check_float ("nextafter (NaN, NaN) == NaN", FUNC(nextafter) (nan_value, nan_value), nan_value, 0, 0, 0); + + /* XXX We need the hexadecimal FP number representation here for further + tests. */ + + print_max_error ("nextafter", 0, 0); +} + + +#if 0 /* XXX scp XXX */ +static void +nexttoward_test (void) +{ + init_max_error (); + check_float ("nexttoward (0, 0) == 0", FUNC(nexttoward) (0, 0), 0, 0, 0, 0); + check_float ("nexttoward (-0, 0) == 0", FUNC(nexttoward) (minus_zero, 0), 0, 0, 0, 0); + check_float ("nexttoward (0, -0) == -0", FUNC(nexttoward) (0, minus_zero), minus_zero, 0, 0, 0); + check_float ("nexttoward (-0, -0) == -0", FUNC(nexttoward) (minus_zero, minus_zero), minus_zero, 0, 0, 0); + + check_float ("nexttoward (9, 9) == 9", FUNC(nexttoward) (9, 9), 9, 0, 0, 0); + check_float ("nexttoward (-9, -9) == -9", FUNC(nexttoward) (-9, -9), -9, 0, 0, 0); + check_float ("nexttoward (inf, inf) == inf", FUNC(nexttoward) (plus_infty, plus_infty), plus_infty, 0, 0, 0); + check_float ("nexttoward (-inf, -inf) == -inf", FUNC(nexttoward) (minus_infty, minus_infty), minus_infty, 0, 0, 0); + + check_float ("nexttoward (NaN, 1.1) == NaN", FUNC(nexttoward) (nan_value, 1.1L), nan_value, 0, 0, 0); + check_float ("nexttoward (1.1, NaN) == NaN", FUNC(nexttoward) (1.1L, nan_value), nan_value, 0, 0, 0); + check_float ("nexttoward (NaN, NaN) == NaN", FUNC(nexttoward) (nan_value, nan_value), nan_value, 0, 0, 0); + + /* XXX We need the hexadecimal FP number representation here for further + tests. */ + + print_max_error ("nexttoward", 0, 0); +} +#endif + + +static void +pow_test (void) +{ + + errno = 0; + FUNC(pow) (0, 0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("pow (0, 0) == 1", FUNC(pow) (0, 0), 1, 0, 0, 0); + check_float ("pow (0, -0) == 1", FUNC(pow) (0, minus_zero), 1, 0, 0, 0); + check_float ("pow (-0, 0) == 1", FUNC(pow) (minus_zero, 0), 1, 0, 0, 0); + check_float ("pow (-0, -0) == 1", FUNC(pow) (minus_zero, minus_zero), 1, 0, 0, 0); + + check_float ("pow (10, 0) == 1", FUNC(pow) (10, 0), 1, 0, 0, 0); + check_float ("pow (10, -0) == 1", FUNC(pow) (10, minus_zero), 1, 0, 0, 0); + check_float ("pow (-10, 0) == 1", FUNC(pow) (-10, 0), 1, 0, 0, 0); + check_float ("pow (-10, -0) == 1", FUNC(pow) (-10, minus_zero), 1, 0, 0, 0); + + check_float ("pow (NaN, 0) == 1", FUNC(pow) (nan_value, 0), 1, 0, 0, 0); + check_float ("pow (NaN, -0) == 1", FUNC(pow) (nan_value, minus_zero), 1, 0, 0, 0); + + +#ifndef TEST_INLINE + check_float ("pow (1.1, inf) == inf", FUNC(pow) (1.1L, plus_infty), plus_infty, 0, 0, 0); + check_float ("pow (inf, inf) == inf", FUNC(pow) (plus_infty, plus_infty), plus_infty, 0, 0, 0); + check_float ("pow (-1.1, inf) == inf", FUNC(pow) (-1.1L, plus_infty), plus_infty, 0, 0, 0); + check_float ("pow (-inf, inf) == inf", FUNC(pow) (minus_infty, plus_infty), plus_infty, 0, 0, 0); + + check_float ("pow (0.9, inf) == 0", FUNC(pow) (0.9L, plus_infty), 0, 0, 0, 0); + check_float ("pow (1e-7, inf) == 0", FUNC(pow) (1e-7L, plus_infty), 0, 0, 0, 0); + check_float ("pow (-0.9, inf) == 0", FUNC(pow) (-0.9L, plus_infty), 0, 0, 0, 0); + check_float ("pow (-1e-7, inf) == 0", FUNC(pow) (-1e-7L, plus_infty), 0, 0, 0, 0); + + check_float ("pow (1.1, -inf) == 0", FUNC(pow) (1.1L, minus_infty), 0, 0, 0, 0); + check_float ("pow (inf, -inf) == 0", FUNC(pow) (plus_infty, minus_infty), 0, 0, 0, 0); + check_float ("pow (-1.1, -inf) == 0", FUNC(pow) (-1.1L, minus_infty), 0, 0, 0, 0); + check_float ("pow (-inf, -inf) == 0", FUNC(pow) (minus_infty, minus_infty), 0, 0, 0, 0); + + check_float ("pow (0.9, -inf) == inf", FUNC(pow) (0.9L, minus_infty), plus_infty, 0, 0, 0); + check_float ("pow (1e-7, -inf) == inf", FUNC(pow) (1e-7L, minus_infty), plus_infty, 0, 0, 0); + check_float ("pow (-0.9, -inf) == inf", FUNC(pow) (-0.9L, minus_infty), plus_infty, 0, 0, 0); + check_float ("pow (-1e-7, -inf) == inf", FUNC(pow) (-1e-7L, minus_infty), plus_infty, 0, 0, 0); + + check_float ("pow (inf, 1e-7) == inf", FUNC(pow) (plus_infty, 1e-7L), plus_infty, 0, 0, 0); + check_float ("pow (inf, 1) == inf", FUNC(pow) (plus_infty, 1), plus_infty, 0, 0, 0); + check_float ("pow (inf, 1e7) == inf", FUNC(pow) (plus_infty, 1e7L), plus_infty, 0, 0, 0); + + check_float ("pow (inf, -1e-7) == 0", FUNC(pow) (plus_infty, -1e-7L), 0, 0, 0, 0); + check_float ("pow (inf, -1) == 0", FUNC(pow) (plus_infty, -1), 0, 0, 0, 0); + check_float ("pow (inf, -1e7) == 0", FUNC(pow) (plus_infty, -1e7L), 0, 0, 0, 0); + + check_float ("pow (-inf, 1) == -inf", FUNC(pow) (minus_infty, 1), minus_infty, 0, 0, 0); + check_float ("pow (-inf, 11) == -inf", FUNC(pow) (minus_infty, 11), minus_infty, 0, 0, 0); + check_float ("pow (-inf, 1001) == -inf", FUNC(pow) (minus_infty, 1001), minus_infty, 0, 0, 0); + + check_float ("pow (-inf, 2) == inf", FUNC(pow) (minus_infty, 2), plus_infty, 0, 0, 0); + check_float ("pow (-inf, 12) == inf", FUNC(pow) (minus_infty, 12), plus_infty, 0, 0, 0); + check_float ("pow (-inf, 1002) == inf", FUNC(pow) (minus_infty, 1002), plus_infty, 0, 0, 0); + check_float ("pow (-inf, 0.1) == inf", FUNC(pow) (minus_infty, 0.1L), plus_infty, 0, 0, 0); + check_float ("pow (-inf, 1.1) == inf", FUNC(pow) (minus_infty, 1.1L), plus_infty, 0, 0, 0); + check_float ("pow (-inf, 11.1) == inf", FUNC(pow) (minus_infty, 11.1L), plus_infty, 0, 0, 0); + check_float ("pow (-inf, 1001.1) == inf", FUNC(pow) (minus_infty, 1001.1L), plus_infty, 0, 0, 0); + + check_float ("pow (-inf, -1) == -0", FUNC(pow) (minus_infty, -1), minus_zero, 0, 0, 0); + check_float ("pow (-inf, -11) == -0", FUNC(pow) (minus_infty, -11), minus_zero, 0, 0, 0); + check_float ("pow (-inf, -1001) == -0", FUNC(pow) (minus_infty, -1001), minus_zero, 0, 0, 0); + + check_float ("pow (-inf, -2) == 0", FUNC(pow) (minus_infty, -2), 0, 0, 0, 0); + check_float ("pow (-inf, -12) == 0", FUNC(pow) (minus_infty, -12), 0, 0, 0, 0); + check_float ("pow (-inf, -1002) == 0", FUNC(pow) (minus_infty, -1002), 0, 0, 0, 0); + check_float ("pow (-inf, -0.1) == 0", FUNC(pow) (minus_infty, -0.1L), 0, 0, 0, 0); + check_float ("pow (-inf, -1.1) == 0", FUNC(pow) (minus_infty, -1.1L), 0, 0, 0, 0); + check_float ("pow (-inf, -11.1) == 0", FUNC(pow) (minus_infty, -11.1L), 0, 0, 0, 0); + check_float ("pow (-inf, -1001.1) == 0", FUNC(pow) (minus_infty, -1001.1L), 0, 0, 0, 0); +#endif + + check_float ("pow (NaN, NaN) == NaN", FUNC(pow) (nan_value, nan_value), nan_value, 0, 0, 0); + check_float ("pow (0, NaN) == NaN", FUNC(pow) (0, nan_value), nan_value, 0, 0, 0); + check_float ("pow (1, NaN) == 1", FUNC(pow) (1, nan_value), 1, 0, 0, 0); + check_float ("pow (-1, NaN) == NaN", FUNC(pow) (-1, nan_value), nan_value, 0, 0, 0); + check_float ("pow (NaN, 1) == NaN", FUNC(pow) (nan_value, 1), nan_value, 0, 0, 0); + check_float ("pow (NaN, -1) == NaN", FUNC(pow) (nan_value, -1), nan_value, 0, 0, 0); + + /* pow (x, NaN) == NaN. */ + check_float ("pow (3.0, NaN) == NaN", FUNC(pow) (3.0, nan_value), nan_value, 0, 0, 0); + + check_float ("pow (1, inf) == 1", FUNC(pow) (1, plus_infty), 1, 0, 0, 0); + check_float ("pow (-1, inf) == 1", FUNC(pow) (-1, plus_infty), 1, 0, 0, 0); + check_float ("pow (1, -inf) == 1", FUNC(pow) (1, minus_infty), 1, 0, 0, 0); + check_float ("pow (-1, -inf) == 1", FUNC(pow) (-1, minus_infty), 1, 0, 0, 0); + + check_float ("pow (-0.1, 1.1) == NaN plus invalid exception", FUNC(pow) (-0.1L, 1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("pow (-0.1, -1.1) == NaN plus invalid exception", FUNC(pow) (-0.1L, -1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("pow (-10.1, 1.1) == NaN plus invalid exception", FUNC(pow) (-10.1L, 1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("pow (-10.1, -1.1) == NaN plus invalid exception", FUNC(pow) (-10.1L, -1.1L), nan_value, 0, 0, INVALID_EXCEPTION); + + check_float ("pow (0, -1) == inf plus division by zero exception", FUNC(pow) (0, -1), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("pow (0, -11) == inf plus division by zero exception", FUNC(pow) (0, -11), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("pow (-0, -1) == -inf plus division by zero exception", FUNC(pow) (minus_zero, -1), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("pow (-0, -11) == -inf plus division by zero exception", FUNC(pow) (minus_zero, -11), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + check_float ("pow (0, -2) == inf plus division by zero exception", FUNC(pow) (0, -2), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("pow (0, -11.1) == inf plus division by zero exception", FUNC(pow) (0, -11.1L), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("pow (-0, -2) == inf plus division by zero exception", FUNC(pow) (minus_zero, -2), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("pow (-0, -11.1) == inf plus division by zero exception", FUNC(pow) (minus_zero, -11.1L), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + + + check_float ("pow (0, 1) == 0", FUNC(pow) (0, 1), 0, 0, 0, 0); + check_float ("pow (0, 11) == 0", FUNC(pow) (0, 11), 0, 0, 0, 0); + + check_float ("pow (-0, 1) == -0", FUNC(pow) (minus_zero, 1), minus_zero, 0, 0, 0); + check_float ("pow (-0, 11) == -0", FUNC(pow) (minus_zero, 11), minus_zero, 0, 0, 0); + + + check_float ("pow (0, 2) == 0", FUNC(pow) (0, 2), 0, 0, 0, 0); + check_float ("pow (0, 11.1) == 0", FUNC(pow) (0, 11.1L), 0, 0, 0, 0); + + + check_float ("pow (-0, 2) == 0", FUNC(pow) (minus_zero, 2), 0, 0, 0, 0); + check_float ("pow (-0, 11.1) == 0", FUNC(pow) (minus_zero, 11.1L), 0, 0, 0, 0); + +#ifndef TEST_INLINE + /* pow (x, +inf) == +inf for |x| > 1. */ + check_float ("pow (1.5, inf) == inf", FUNC(pow) (1.5, plus_infty), plus_infty, 0, 0, 0); + + /* pow (x, +inf) == +0 for |x| < 1. */ + check_float ("pow (0.5, inf) == 0.0", FUNC(pow) (0.5, plus_infty), 0.0, 0, 0, 0); + + /* pow (x, -inf) == +0 for |x| > 1. */ + check_float ("pow (1.5, -inf) == 0.0", FUNC(pow) (1.5, minus_infty), 0.0, 0, 0, 0); + + /* pow (x, -inf) == +inf for |x| < 1. */ + check_float ("pow (0.5, -inf) == inf", FUNC(pow) (0.5, minus_infty), plus_infty, 0, 0, 0); +#endif + + /* pow (+inf, y) == +inf for y > 0. */ + check_float ("pow (inf, 2) == inf", FUNC(pow) (plus_infty, 2), plus_infty, 0, 0, 0); + + /* pow (+inf, y) == +0 for y < 0. */ + check_float ("pow (inf, -1) == 0.0", FUNC(pow) (plus_infty, -1), 0.0, 0, 0, 0); + + /* pow (-inf, y) == -inf for y an odd integer > 0. */ + check_float ("pow (-inf, 27) == -inf", FUNC(pow) (minus_infty, 27), minus_infty, 0, 0, 0); + + /* pow (-inf, y) == +inf for y > 0 and not an odd integer. */ + check_float ("pow (-inf, 28) == inf", FUNC(pow) (minus_infty, 28), plus_infty, 0, 0, 0); + + /* pow (-inf, y) == -0 for y an odd integer < 0. */ + check_float ("pow (-inf, -3) == -0", FUNC(pow) (minus_infty, -3), minus_zero, 0, 0, 0); + /* pow (-inf, y) == +0 for y < 0 and not an odd integer. */ + check_float ("pow (-inf, -2.0) == 0.0", FUNC(pow) (minus_infty, -2.0), 0.0, 0, 0, 0); + + /* pow (+0, y) == +0 for y an odd integer > 0. */ + check_float ("pow (0.0, 27) == 0.0", FUNC(pow) (0.0, 27), 0.0, 0, 0, 0); + + /* pow (-0, y) == -0 for y an odd integer > 0. */ + check_float ("pow (-0, 27) == -0", FUNC(pow) (minus_zero, 27), minus_zero, 0, 0, 0); + + /* pow (+0, y) == +0 for y > 0 and not an odd integer. */ + check_float ("pow (0.0, 4) == 0.0", FUNC(pow) (0.0, 4), 0.0, 0, 0, 0); + + /* pow (-0, y) == +0 for y > 0 and not an odd integer. */ + check_float ("pow (-0, 4) == 0.0", FUNC(pow) (minus_zero, 4), 0.0, 0, 0, 0); + + check_float ("pow (0.7, 1.2) == 0.65180494056638638188", FUNC(pow) (0.7L, 1.2L), 0.65180494056638638188L, DELTA1398, 0, 0); + +#if defined TEST_DOUBLE || defined TEST_LDOUBLE + check_float ("pow (-7.49321e+133, -9.80818e+16) == 0", FUNC(pow) (-7.49321e+133, -9.80818e+16), 0, 0, 0, 0); +#endif + + print_max_error ("pow", DELTApow, 0); +} + +static void +remainder_test (void) +{ + errno = 0; + FUNC(remainder) (1.625, 1.0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("remainder (1, 0) == NaN plus invalid exception", FUNC(remainder) (1, 0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remainder (1, -0) == NaN plus invalid exception", FUNC(remainder) (1, minus_zero), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remainder (inf, 1) == NaN plus invalid exception", FUNC(remainder) (plus_infty, 1), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remainder (-inf, 1) == NaN plus invalid exception", FUNC(remainder) (minus_infty, 1), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remainder (NaN, NaN) == NaN", FUNC(remainder) (nan_value, nan_value), nan_value, 0, 0, 0); + + check_float ("remainder (1.625, 1.0) == -0.375", FUNC(remainder) (1.625, 1.0), -0.375, 0, 0, 0); + check_float ("remainder (-1.625, 1.0) == 0.375", FUNC(remainder) (-1.625, 1.0), 0.375, 0, 0, 0); + check_float ("remainder (1.625, -1.0) == -0.375", FUNC(remainder) (1.625, -1.0), -0.375, 0, 0, 0); + check_float ("remainder (-1.625, -1.0) == 0.375", FUNC(remainder) (-1.625, -1.0), 0.375, 0, 0, 0); + check_float ("remainder (5.0, 2.0) == 1.0", FUNC(remainder) (5.0, 2.0), 1.0, 0, 0, 0); + check_float ("remainder (3.0, 2.0) == -1.0", FUNC(remainder) (3.0, 2.0), -1.0, 0, 0, 0); + + print_max_error ("remainder", 0, 0); +} + +static void +remquo_test (void) +{ + /* x is needed. */ + int x; + + errno = 0; + FUNC(remquo) (1.625, 1.0, &x); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("remquo (1, 0, &x) == NaN plus invalid exception", FUNC(remquo) (1, 0, &x), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remquo (1, -0, &x) == NaN plus invalid exception", FUNC(remquo) (1, minus_zero, &x), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remquo (inf, 1, &x) == NaN plus invalid exception", FUNC(remquo) (plus_infty, 1, &x), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remquo (-inf, 1, &x) == NaN plus invalid exception", FUNC(remquo) (minus_infty, 1, &x), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("remquo (NaN, NaN, &x) == NaN", FUNC(remquo) (nan_value, nan_value, &x), nan_value, 0, 0, 0); + + check_float ("remquo (1.625, 1.0, &x) == -0.375", FUNC(remquo) (1.625, 1.0, &x), -0.375, 0, 0, 0); + check_int ("remquo (1.625, 1.0, &x) sets x to 2", x, 2, 0, 0, 0); + check_float ("remquo (-1.625, 1.0, &x) == 0.375", FUNC(remquo) (-1.625, 1.0, &x), 0.375, 0, 0, 0); + check_int ("remquo (-1.625, 1.0, &x) sets x to -2", x, -2, 0, 0, 0); + check_float ("remquo (1.625, -1.0, &x) == -0.375", FUNC(remquo) (1.625, -1.0, &x), -0.375, 0, 0, 0); + check_int ("remquo (1.625, -1.0, &x) sets x to -2", x, -2, 0, 0, 0); + check_float ("remquo (-1.625, -1.0, &x) == 0.375", FUNC(remquo) (-1.625, -1.0, &x), 0.375, 0, 0, 0); + check_int ("remquo (-1.625, -1.0, &x) sets x to 2", x, 2, 0, 0, 0); + + check_float ("remquo (5, 2, &x) == 1", FUNC(remquo) (5, 2, &x), 1, 0, 0, 0); + check_int ("remquo (5, 2, &x) sets x to 2", x, 2, 0, 0, 0); + check_float ("remquo (3, 2, &x) == -1", FUNC(remquo) (3, 2, &x), -1, 0, 0, 0); + check_int ("remquo (3, 2, &x) sets x to 2", x, 2, 0, 0, 0); + + print_max_error ("remquo", 0, 0); +} + +static void +rint_test (void) +{ + init_max_error (); + + check_float ("rint (0.0) == 0.0", FUNC(rint) (0.0), 0.0, 0, 0, 0); + check_float ("rint (-0) == -0", FUNC(rint) (minus_zero), minus_zero, 0, 0, 0); + check_float ("rint (inf) == inf", FUNC(rint) (plus_infty), plus_infty, 0, 0, 0); + check_float ("rint (-inf) == -inf", FUNC(rint) (minus_infty), minus_infty, 0, 0, 0); + + /* Default rounding mode is round to even. */ + check_float ("rint (0.5) == 0.0", FUNC(rint) (0.5), 0.0, 0, 0, 0); + check_float ("rint (1.5) == 2.0", FUNC(rint) (1.5), 2.0, 0, 0, 0); + check_float ("rint (2.5) == 2.0", FUNC(rint) (2.5), 2.0, 0, 0, 0); + check_float ("rint (3.5) == 4.0", FUNC(rint) (3.5), 4.0, 0, 0, 0); + check_float ("rint (4.5) == 4.0", FUNC(rint) (4.5), 4.0, 0, 0, 0); + check_float ("rint (-0.5) == -0.0", FUNC(rint) (-0.5), -0.0, 0, 0, 0); + check_float ("rint (-1.5) == -2.0", FUNC(rint) (-1.5), -2.0, 0, 0, 0); + check_float ("rint (-2.5) == -2.0", FUNC(rint) (-2.5), -2.0, 0, 0, 0); + check_float ("rint (-3.5) == -4.0", FUNC(rint) (-3.5), -4.0, 0, 0, 0); + check_float ("rint (-4.5) == -4.0", FUNC(rint) (-4.5), -4.0, 0, 0, 0); + + print_max_error ("rint", 0, 0); +} + +static void +round_test (void) +{ + init_max_error (); + + check_float ("round (0) == 0", FUNC(round) (0), 0, 0, 0, 0); + check_float ("round (-0) == -0", FUNC(round) (minus_zero), minus_zero, 0, 0, 0); + check_float ("round (0.2) == 0.0", FUNC(round) (0.2L), 0.0, 0, 0, 0); + check_float ("round (-0.2) == -0", FUNC(round) (-0.2L), minus_zero, 0, 0, 0); + check_float ("round (0.5) == 1.0", FUNC(round) (0.5), 1.0, 0, 0, 0); + check_float ("round (-0.5) == -1.0", FUNC(round) (-0.5), -1.0, 0, 0, 0); + check_float ("round (0.8) == 1.0", FUNC(round) (0.8L), 1.0, 0, 0, 0); + check_float ("round (-0.8) == -1.0", FUNC(round) (-0.8L), -1.0, 0, 0, 0); + check_float ("round (1.5) == 2.0", FUNC(round) (1.5), 2.0, 0, 0, 0); + check_float ("round (-1.5) == -2.0", FUNC(round) (-1.5), -2.0, 0, 0, 0); + check_float ("round (2097152.5) == 2097153", FUNC(round) (2097152.5), 2097153, 0, 0, 0); + check_float ("round (-2097152.5) == -2097153", FUNC(round) (-2097152.5), -2097153, 0, 0, 0); + + print_max_error ("round", 0, 0); +} + + +static void +scalbn_test (void) +{ + + init_max_error (); + + check_float ("scalbn (0, 0) == 0", FUNC(scalbn) (0, 0), 0, 0, 0, 0); + check_float ("scalbn (-0, 0) == -0", FUNC(scalbn) (minus_zero, 0), minus_zero, 0, 0, 0); + + check_float ("scalbn (inf, 1) == inf", FUNC(scalbn) (plus_infty, 1), plus_infty, 0, 0, 0); + check_float ("scalbn (-inf, 1) == -inf", FUNC(scalbn) (minus_infty, 1), minus_infty, 0, 0, 0); + check_float ("scalbn (NaN, 1) == NaN", FUNC(scalbn) (nan_value, 1), nan_value, 0, 0, 0); + + check_float ("scalbn (0.8, 4) == 12.8", FUNC(scalbn) (0.8L, 4), 12.8L, 0, 0, 0); + check_float ("scalbn (-0.854375, 5) == -27.34", FUNC(scalbn) (-0.854375L, 5), -27.34L, 0, 0, 0); + + check_float ("scalbn (1, 0) == 1", FUNC(scalbn) (1, 0L), 1, 0, 0, 0); + + print_max_error ("scalbn", 0, 0); +} + +static void +scalbln_test (void) +{ + + init_max_error (); + + check_float ("scalbln (0, 0) == 0", FUNC(scalbln) (0, 0), 0, 0, 0, 0); + check_float ("scalbln (-0, 0) == -0", FUNC(scalbln) (minus_zero, 0), minus_zero, 0, 0, 0); + + check_float ("scalbln (inf, 1) == inf", FUNC(scalbln) (plus_infty, 1), plus_infty, 0, 0, 0); + check_float ("scalbln (-inf, 1) == -inf", FUNC(scalbln) (minus_infty, 1), minus_infty, 0, 0, 0); + check_float ("scalbln (NaN, 1) == NaN", FUNC(scalbln) (nan_value, 1), nan_value, 0, 0, 0); + + check_float ("scalbln (0.8, 4) == 12.8", FUNC(scalbln) (0.8L, 4), 12.8L, 0, 0, 0); + check_float ("scalbln (-0.854375, 5) == -27.34", FUNC(scalbln) (-0.854375L, 5), -27.34L, 0, 0, 0); + + check_float ("scalbln (1, 0) == 1", FUNC(scalbln) (1, 0L), 1, 0, 0, 0); + + print_max_error ("scalbn", 0, 0); +} + +static void +signbit_test (void) +{ + + init_max_error (); + + check_bool ("signbit (0) == false", signbit (0.0), 0, 0, 0, 0); + check_bool ("signbit (-0) == true", signbit (minus_zero), 1, 0, 0, 0); + check_bool ("signbit (inf) == false", signbit (plus_infty), 0, 0, 0, 0); + check_bool ("signbit (-inf) == true", signbit (minus_infty), 1, 0, 0, 0); + + /* signbit (x) != 0 for x < 0. */ + check_bool ("signbit (-1) == true", signbit (-1.0), 1, 0, 0, 0); + /* signbit (x) == 0 for x >= 0. */ + check_bool ("signbit (1) == false", signbit (1.0), 0, 0, 0, 0); + + print_max_error ("signbit", 0, 0); +} + +static void +sin_test (void) +{ + errno = 0; + FUNC(sin) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("sin (0) == 0", FUNC(sin) (0), 0, 0, 0, 0); + check_float ("sin (-0) == -0", FUNC(sin) (minus_zero), minus_zero, 0, 0, 0); + check_float ("sin (inf) == NaN plus invalid exception", FUNC(sin) (plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("sin (-inf) == NaN plus invalid exception", FUNC(sin) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("sin (NaN) == NaN", FUNC(sin) (nan_value), nan_value, 0, 0, 0); + + check_float ("sin (pi/6) == 0.5", FUNC(sin) (M_PI_6l), 0.5, 0, 0, 0); + check_float ("sin (-pi/6) == -0.5", FUNC(sin) (-M_PI_6l), -0.5, 0, 0, 0); + check_float ("sin (pi/2) == 1", FUNC(sin) (M_PI_2l), 1, 0, 0, 0); + check_float ("sin (-pi/2) == -1", FUNC(sin) (-M_PI_2l), -1, 0, 0, 0); + check_float ("sin (0.7) == 0.64421768723769105367261435139872014", FUNC(sin) (0.7L), 0.64421768723769105367261435139872014L, DELTA1524, 0, 0); + + print_max_error ("sin", DELTAsin, 0); + +} + + +static void +sincos_test (void) +{ + FLOAT sin_res, cos_res; + + errno = 0; + FUNC(sincos) (0, &sin_res, &cos_res); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + /* sincos is treated differently because it returns void. */ + FUNC (sincos) (0, &sin_res, &cos_res); + check_float ("sincos (0, &sin_res, &cos_res) puts 0 in sin_res", sin_res, 0, 0, 0, 0); + check_float ("sincos (0, &sin_res, &cos_res) puts 1 in cos_res", cos_res, 1, 0, 0, 0); + + FUNC (sincos) (minus_zero, &sin_res, &cos_res); + check_float ("sincos (-0, &sin_res, &cos_res) puts -0 in sin_res", sin_res, minus_zero, 0, 0, 0); + check_float ("sincos (-0, &sin_res, &cos_res) puts 1 in cos_res", cos_res, 1, 0, 0, 0); + FUNC (sincos) (plus_infty, &sin_res, &cos_res); + check_float ("sincos (inf, &sin_res, &cos_res) puts NaN in sin_res plus invalid exception", sin_res, nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("sincos (inf, &sin_res, &cos_res) puts NaN in cos_res", cos_res, nan_value, 0, 0, 0); + FUNC (sincos) (minus_infty, &sin_res, &cos_res); + check_float ("sincos (-inf, &sin_res, &cos_res) puts NaN in sin_res plus invalid exception", sin_res, nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("sincos (-inf, &sin_res, &cos_res) puts NaN in cos_res", cos_res, nan_value, 0, 0, 0); + FUNC (sincos) (nan_value, &sin_res, &cos_res); + check_float ("sincos (NaN, &sin_res, &cos_res) puts NaN in sin_res", sin_res, nan_value, 0, 0, 0); + check_float ("sincos (NaN, &sin_res, &cos_res) puts NaN in cos_res", cos_res, nan_value, 0, 0, 0); + + FUNC (sincos) (M_PI_2l, &sin_res, &cos_res); + check_float ("sincos (pi/2, &sin_res, &cos_res) puts 1 in sin_res", sin_res, 1, 0, 0, 0); + check_float ("sincos (pi/2, &sin_res, &cos_res) puts 0 in cos_res", cos_res, 0, DELTA1536, 0, 0); + FUNC (sincos) (M_PI_6l, &sin_res, &cos_res); + check_float ("sincos (pi/6, &sin_res, &cos_res) puts 0.5 in sin_res", sin_res, 0.5, 0, 0, 0); + check_float ("sincos (pi/6, &sin_res, &cos_res) puts 0.86602540378443864676372317075293616 in cos_res", cos_res, 0.86602540378443864676372317075293616L, 0, 0, 0); + FUNC (sincos) (M_PI_6l*2.0, &sin_res, &cos_res); + check_float ("sincos (M_PI_6l*2.0, &sin_res, &cos_res) puts 0.86602540378443864676372317075293616 in sin_res", sin_res, 0.86602540378443864676372317075293616L, DELTA1539, 0, 0); + check_float ("sincos (M_PI_6l*2.0, &sin_res, &cos_res) puts 0.5 in cos_res", cos_res, 0.5, DELTA1540, 0, 0); + FUNC (sincos) (0.7L, &sin_res, &cos_res); + check_float ("sincos (0.7, &sin_res, &cos_res) puts 0.64421768723769105367261435139872014 in sin_res", sin_res, 0.64421768723769105367261435139872014L, DELTA1541, 0, 0); + check_float ("sincos (0.7, &sin_res, &cos_res) puts 0.76484218728448842625585999019186495 in cos_res", cos_res, 0.76484218728448842625585999019186495L, DELTA1542, 0, 0); + + print_max_error ("sincos", DELTAsincos, 0); +} + +static void +sinh_test (void) +{ + errno = 0; + FUNC(sinh) (0.7L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + check_float ("sinh (0) == 0", FUNC(sinh) (0), 0, 0, 0, 0); + check_float ("sinh (-0) == -0", FUNC(sinh) (minus_zero), minus_zero, 0, 0, 0); + +#ifndef TEST_INLINE + check_float ("sinh (inf) == inf", FUNC(sinh) (plus_infty), plus_infty, 0, 0, 0); + check_float ("sinh (-inf) == -inf", FUNC(sinh) (minus_infty), minus_infty, 0, 0, 0); +#endif + check_float ("sinh (NaN) == NaN", FUNC(sinh) (nan_value), nan_value, 0, 0, 0); + + check_float ("sinh (0.7) == 0.75858370183953350346", FUNC(sinh) (0.7L), 0.75858370183953350346L, DELTA1548, 0, 0); +#if 0 /* XXX scp XXX */ + check_float ("sinh (0x8p-32) == 1.86264514923095703232705808926175479e-9", FUNC(sinh) (0x8p-32L), 1.86264514923095703232705808926175479e-9L, 0, 0, 0); +#endif + + print_max_error ("sinh", DELTAsinh, 0); +} + +static void +sqrt_test (void) +{ + errno = 0; + FUNC(sqrt) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("sqrt (0) == 0", FUNC(sqrt) (0), 0, 0, 0, 0); + check_float ("sqrt (NaN) == NaN", FUNC(sqrt) (nan_value), nan_value, 0, 0, 0); + check_float ("sqrt (inf) == inf", FUNC(sqrt) (plus_infty), plus_infty, 0, 0, 0); + + check_float ("sqrt (-0) == -0", FUNC(sqrt) (minus_zero), minus_zero, 0, 0, 0); + + /* sqrt (x) == NaN plus invalid exception for x < 0. */ + check_float ("sqrt (-1) == NaN plus invalid exception", FUNC(sqrt) (-1), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("sqrt (-inf) == NaN plus invalid exception", FUNC(sqrt) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("sqrt (NaN) == NaN", FUNC(sqrt) (nan_value), nan_value, 0, 0, 0); + + check_float ("sqrt (2209) == 47", FUNC(sqrt) (2209), 47, 0, 0, 0); + check_float ("sqrt (4) == 2", FUNC(sqrt) (4), 2, 0, 0, 0); + check_float ("sqrt (2) == M_SQRT2l", FUNC(sqrt) (2), M_SQRT2l, 0, 0, 0); + check_float ("sqrt (0.25) == 0.5", FUNC(sqrt) (0.25), 0.5, 0, 0, 0); + check_float ("sqrt (6642.25) == 81.5", FUNC(sqrt) (6642.25), 81.5, 0, 0, 0); + check_float ("sqrt (15239.9025) == 123.45", FUNC(sqrt) (15239.9025L), 123.45L, DELTA1562, 0, 0); + check_float ("sqrt (0.7) == 0.83666002653407554797817202578518747", FUNC(sqrt) (0.7L), 0.83666002653407554797817202578518747L, 0, 0, 0); + + print_max_error ("sqrt", DELTAsqrt, 0); +} + +static void +tan_test (void) +{ + errno = 0; + FUNC(tan) (0); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("tan (0) == 0", FUNC(tan) (0), 0, 0, 0, 0); + check_float ("tan (-0) == -0", FUNC(tan) (minus_zero), minus_zero, 0, 0, 0); + check_float ("tan (inf) == NaN plus invalid exception", FUNC(tan) (plus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("tan (-inf) == NaN plus invalid exception", FUNC(tan) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("tan (NaN) == NaN", FUNC(tan) (nan_value), nan_value, 0, 0, 0); + + check_float ("tan (pi/4) == 1", FUNC(tan) (M_PI_4l), 1, DELTA1569, 0, 0); + check_float ("tan (0.7) == 0.84228838046307944812813500221293775", FUNC(tan) (0.7L), 0.84228838046307944812813500221293775L, DELTA1570, 0, 0); + + print_max_error ("tan", DELTAtan, 0); +} + +static void +tanh_test (void) +{ + errno = 0; + FUNC(tanh) (0.7L); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + init_max_error (); + + check_float ("tanh (0) == 0", FUNC(tanh) (0), 0, 0, 0, 0); + check_float ("tanh (-0) == -0", FUNC(tanh) (minus_zero), minus_zero, 0, 0, 0); + +#ifndef TEST_INLINE + check_float ("tanh (inf) == 1", FUNC(tanh) (plus_infty), 1, 0, 0, 0); + check_float ("tanh (-inf) == -1", FUNC(tanh) (minus_infty), -1, 0, 0, 0); +#endif + check_float ("tanh (NaN) == NaN", FUNC(tanh) (nan_value), nan_value, 0, 0, 0); + + check_float ("tanh (0.7) == 0.60436777711716349631", FUNC(tanh) (0.7L), 0.60436777711716349631L, DELTA1576, 0, 0); + check_float ("tanh (-0.7) == -0.60436777711716349631", FUNC(tanh) (-0.7L), -0.60436777711716349631L, DELTA1577, 0, 0); + + check_float ("tanh (1.0) == 0.7615941559557648881194582826047935904", FUNC(tanh) (1.0L), 0.7615941559557648881194582826047935904L, 0, 0, 0); + check_float ("tanh (-1.0) == -0.7615941559557648881194582826047935904", FUNC(tanh) (-1.0L), -0.7615941559557648881194582826047935904L, 0, 0, 0); + + /* 2^-57 */ + check_float ("tanh (6.938893903907228377647697925567626953125e-18) == 6.938893903907228377647697925567626953125e-18", FUNC(tanh) (6.938893903907228377647697925567626953125e-18L), 6.938893903907228377647697925567626953125e-18L, 0, 0, 0); + + print_max_error ("tanh", DELTAtanh, 0); +} + +static void +tgamma_test (void) +{ + errno = 0; + FUNC(tgamma) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + feclearexcept (FE_ALL_EXCEPT); + + init_max_error (); + + check_float ("tgamma (inf) == inf", FUNC(tgamma) (plus_infty), plus_infty, 0, 0, 0); + check_float ("tgamma (0) == inf plus divide-by-zero", FUNC(tgamma) (0), plus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("tgamma (-0) == inf plus divide-by-zero", FUNC(tgamma) (minus_zero), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + /* tgamma (x) == NaN plus invalid exception for integer x <= 0. */ + check_float ("tgamma (-2) == NaN plus invalid exception", FUNC(tgamma) (-2), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("tgamma (-inf) == NaN plus invalid exception", FUNC(tgamma) (minus_infty), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("tgamma (NaN) == NaN", FUNC(tgamma) (nan_value), nan_value, 0, 0, 0); + + check_float ("tgamma (0.5) == sqrt (pi)", FUNC(tgamma) (0.5), M_SQRT_PIl, DELTA1587, 0, 0); + check_float ("tgamma (-0.5) == -2 sqrt (pi)", FUNC(tgamma) (-0.5), -M_2_SQRT_PIl, DELTA1588, 0, 0); + + check_float ("tgamma (1) == 1", FUNC(tgamma) (1), 1, 0, 0, 0); + check_float ("tgamma (4) == 6", FUNC(tgamma) (4), 6, DELTA1590, 0, 0); + + check_float ("tgamma (0.7) == 1.29805533264755778568", FUNC(tgamma) (0.7L), 1.29805533264755778568L, DELTA1591, 0, 0); + check_float ("tgamma (1.2) == 0.91816874239976061064", FUNC(tgamma) (1.2L), 0.91816874239976061064L, 0, 0, 0); + + print_max_error ("tgamma", DELTAtgamma, 0); +} + +static void +trunc_test (void) +{ + init_max_error (); + + check_float ("trunc (inf) == inf", FUNC(trunc) (plus_infty), plus_infty, 0, 0, 0); + check_float ("trunc (-inf) == -inf", FUNC(trunc) (minus_infty), minus_infty, 0, 0, 0); + check_float ("trunc (NaN) == NaN", FUNC(trunc) (nan_value), nan_value, 0, 0, 0); + + check_float ("trunc (0) == 0", FUNC(trunc) (0), 0, 0, 0, 0); + check_float ("trunc (-0) == -0", FUNC(trunc) (minus_zero), minus_zero, 0, 0, 0); + check_float ("trunc (0.625) == 0", FUNC(trunc) (0.625), 0, 0, 0, 0); + check_float ("trunc (-0.625) == -0", FUNC(trunc) (-0.625), minus_zero, 0, 0, 0); + check_float ("trunc (1) == 1", FUNC(trunc) (1), 1, 0, 0, 0); + check_float ("trunc (-1) == -1", FUNC(trunc) (-1), -1, 0, 0, 0); + check_float ("trunc (1.625) == 1", FUNC(trunc) (1.625), 1, 0, 0, 0); + check_float ("trunc (-1.625) == -1", FUNC(trunc) (-1.625), -1, 0, 0, 0); + + check_float ("trunc (1048580.625) == 1048580", FUNC(trunc) (1048580.625L), 1048580L, 0, 0, 0); + check_float ("trunc (-1048580.625) == -1048580", FUNC(trunc) (-1048580.625L), -1048580L, 0, 0, 0); + + check_float ("trunc (8388610.125) == 8388610.0", FUNC(trunc) (8388610.125L), 8388610.0L, 0, 0, 0); + check_float ("trunc (-8388610.125) == -8388610.0", FUNC(trunc) (-8388610.125L), -8388610.0L, 0, 0, 0); + + check_float ("trunc (4294967296.625) == 4294967296.0", FUNC(trunc) (4294967296.625L), 4294967296.0L, 0, 0, 0); + check_float ("trunc (-4294967296.625) == -4294967296.0", FUNC(trunc) (-4294967296.625L), -4294967296.0L, 0, 0, 0); + + + print_max_error ("trunc", 0, 0); +} + +static void +y0_test (void) +{ + FLOAT s, c; + errno = 0; + FUNC (sincos) (0, &s, &c); + if (errno == ENOSYS) + /* Required function not implemented. */ + return; + FUNC(y0) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + /* y0 is the Bessel function of the second kind of order 0 */ + init_max_error (); + + check_float ("y0 (-1.0) == NaN", FUNC(y0) (-1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("y0 (0.0) == -inf", FUNC(y0) (0.0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("y0 (NaN) == NaN", FUNC(y0) (nan_value), nan_value, 0, 0, 0); + check_float ("y0 (inf) == 0", FUNC(y0) (plus_infty), 0, 0, 0, 0); + + check_float ("y0 (0.1) == -1.5342386513503668441", FUNC(y0) (0.1L), -1.5342386513503668441L, DELTA1614, 0, 0); + check_float ("y0 (0.7) == -0.19066492933739506743", FUNC(y0) (0.7L), -0.19066492933739506743L, DELTA1615, 0, 0); + check_float ("y0 (1.0) == 0.088256964215676957983", FUNC(y0) (1.0), 0.088256964215676957983L, DELTA1616, 0, 0); + check_float ("y0 (1.5) == 0.38244892379775884396", FUNC(y0) (1.5), 0.38244892379775884396L, DELTA1617, 0, 0); + check_float ("y0 (2.0) == 0.51037567264974511960", FUNC(y0) (2.0), 0.51037567264974511960L, DELTA1618, 0, 0); + check_float ("y0 (8.0) == 0.22352148938756622053", FUNC(y0) (8.0), 0.22352148938756622053L, DELTA1619, 0, 0); + check_float ("y0 (10.0) == 0.055671167283599391424", FUNC(y0) (10.0), 0.055671167283599391424L, DELTA1620, 0, 0); + + print_max_error ("y0", DELTAy0, 0); +} + + +static void +y1_test (void) +{ + FLOAT s, c; + errno = 0; + FUNC (sincos) (0, &s, &c); + if (errno == ENOSYS) + /* Required function not implemented. */ + return; + FUNC(y1) (1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + /* y1 is the Bessel function of the second kind of order 1 */ + init_max_error (); + + check_float ("y1 (-1.0) == NaN", FUNC(y1) (-1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("y1 (0.0) == -inf", FUNC(y1) (0.0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("y1 (inf) == 0", FUNC(y1) (plus_infty), 0, 0, 0, 0); + check_float ("y1 (NaN) == NaN", FUNC(y1) (nan_value), nan_value, 0, 0, 0); + + check_float ("y1 (0.1) == -6.4589510947020269877", FUNC(y1) (0.1L), -6.4589510947020269877L, DELTA1625, 0, 0); + check_float ("y1 (0.7) == -1.1032498719076333697", FUNC(y1) (0.7L), -1.1032498719076333697L, DELTA1626, 0, 0); + check_float ("y1 (1.0) == -0.78121282130028871655", FUNC(y1) (1.0), -0.78121282130028871655L, DELTA1627, 0, 0); + check_float ("y1 (1.5) == -0.41230862697391129595", FUNC(y1) (1.5), -0.41230862697391129595L, DELTA1628, 0, 0); + check_float ("y1 (2.0) == -0.10703243154093754689", FUNC(y1) (2.0), -0.10703243154093754689L, DELTA1629, 0, 0); + check_float ("y1 (8.0) == -0.15806046173124749426", FUNC(y1) (8.0), -0.15806046173124749426L, DELTA1630, 0, 0); + check_float ("y1 (10.0) == 0.24901542420695388392", FUNC(y1) (10.0), 0.24901542420695388392L, DELTA1631, 0, 0); + + print_max_error ("y1", DELTAy1, 0); +} + +static void +yn_test (void) +{ + FLOAT s, c; + errno = 0; + FUNC (sincos) (0, &s, &c); + if (errno == ENOSYS) + /* Required function not implemented. */ + return; + FUNC(yn) (1, 1); + if (errno == ENOSYS) + /* Function not implemented. */ + return; + + /* yn is the Bessel function of the second kind of order n */ + init_max_error (); + + /* yn (0, x) == y0 (x) */ + check_float ("yn (0, -1.0) == NaN", FUNC(yn) (0, -1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("yn (0, 0.0) == -inf", FUNC(yn) (0, 0.0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("yn (0, NaN) == NaN", FUNC(yn) (0, nan_value), nan_value, 0, 0, 0); + check_float ("yn (0, inf) == 0", FUNC(yn) (0, plus_infty), 0, 0, 0, 0); + + check_float ("yn (0, 0.1) == -1.5342386513503668441", FUNC(yn) (0, 0.1L), -1.5342386513503668441L, DELTA1636, 0, 0); + check_float ("yn (0, 0.7) == -0.19066492933739506743", FUNC(yn) (0, 0.7L), -0.19066492933739506743L, DELTA1637, 0, 0); + check_float ("yn (0, 1.0) == 0.088256964215676957983", FUNC(yn) (0, 1.0), 0.088256964215676957983L, DELTA1638, 0, 0); + check_float ("yn (0, 1.5) == 0.38244892379775884396", FUNC(yn) (0, 1.5), 0.38244892379775884396L, DELTA1639, 0, 0); + check_float ("yn (0, 2.0) == 0.51037567264974511960", FUNC(yn) (0, 2.0), 0.51037567264974511960L, DELTA1640, 0, 0); + check_float ("yn (0, 8.0) == 0.22352148938756622053", FUNC(yn) (0, 8.0), 0.22352148938756622053L, DELTA1641, 0, 0); + check_float ("yn (0, 10.0) == 0.055671167283599391424", FUNC(yn) (0, 10.0), 0.055671167283599391424L, DELTA1642, 0, 0); + + /* yn (1, x) == y1 (x) */ + check_float ("yn (1, -1.0) == NaN", FUNC(yn) (1, -1.0), nan_value, 0, 0, INVALID_EXCEPTION); + check_float ("yn (1, 0.0) == -inf", FUNC(yn) (1, 0.0), minus_infty, 0, 0, DIVIDE_BY_ZERO_EXCEPTION); + check_float ("yn (1, inf) == 0", FUNC(yn) (1, plus_infty), 0, 0, 0, 0); + check_float ("yn (1, NaN) == NaN", FUNC(yn) (1, nan_value), nan_value, 0, 0, 0); + + check_float ("yn (1, 0.1) == -6.4589510947020269877", FUNC(yn) (1, 0.1L), -6.4589510947020269877L, DELTA1647, 0, 0); + check_float ("yn (1, 0.7) == -1.1032498719076333697", FUNC(yn) (1, 0.7L), -1.1032498719076333697L, DELTA1648, 0, 0); + check_float ("yn (1, 1.0) == -0.78121282130028871655", FUNC(yn) (1, 1.0), -0.78121282130028871655L, DELTA1649, 0, 0); + check_float ("yn (1, 1.5) == -0.41230862697391129595", FUNC(yn) (1, 1.5), -0.41230862697391129595L, DELTA1650, 0, 0); + check_float ("yn (1, 2.0) == -0.10703243154093754689", FUNC(yn) (1, 2.0), -0.10703243154093754689L, DELTA1651, 0, 0); + check_float ("yn (1, 8.0) == -0.15806046173124749426", FUNC(yn) (1, 8.0), -0.15806046173124749426L, DELTA1652, 0, 0); + check_float ("yn (1, 10.0) == 0.24901542420695388392", FUNC(yn) (1, 10.0), 0.24901542420695388392L, DELTA1653, 0, 0); + + /* yn (3, x) */ + check_float ("yn (3, inf) == 0", FUNC(yn) (3, plus_infty), 0, 0, 0, 0); + check_float ("yn (3, NaN) == NaN", FUNC(yn) (3, nan_value), nan_value, 0, 0, 0); + + check_float ("yn (3, 0.1) == -5099.3323786129048894", FUNC(yn) (3, 0.1L), -5099.3323786129048894L, DELTA1656, 0, 0); + check_float ("yn (3, 0.7) == -15.819479052819633505", FUNC(yn) (3, 0.7L), -15.819479052819633505L, DELTA1657, 0, 0); + check_float ("yn (3, 1.0) == -5.8215176059647288478", FUNC(yn) (3, 1.0), -5.8215176059647288478L, 0, 0, 0); + check_float ("yn (3, 2.0) == -1.1277837768404277861", FUNC(yn) (3, 2.0), -1.1277837768404277861L, DELTA1659, 0, 0); + check_float ("yn (3, 10.0) == -0.25136265718383732978", FUNC(yn) (3, 10.0), -0.25136265718383732978L, DELTA1660, 0, 0); + + /* yn (10, x) */ + check_float ("yn (10, inf) == 0", FUNC(yn) (10, plus_infty), 0, 0, 0, 0); + check_float ("yn (10, NaN) == NaN", FUNC(yn) (10, nan_value), nan_value, 0, 0, 0); + + check_float ("yn (10, 0.1) == -0.11831335132045197885e19", FUNC(yn) (10, 0.1L), -0.11831335132045197885e19L, DELTA1663, 0, 0); + check_float ("yn (10, 0.7) == -0.42447194260703866924e10", FUNC(yn) (10, 0.7L), -0.42447194260703866924e10L, DELTA1664, 0, 0); + check_float ("yn (10, 1.0) == -0.12161801427868918929e9", FUNC(yn) (10, 1.0), -0.12161801427868918929e9L, DELTA1665, 0, 0); + check_float ("yn (10, 2.0) == -129184.54220803928264", FUNC(yn) (10, 2.0), -129184.54220803928264L, DELTA1666, 0, 0); + check_float ("yn (10, 10.0) == -0.35981415218340272205", FUNC(yn) (10, 10.0), -0.35981415218340272205L, DELTA1667, 0, 0); + + print_max_error ("yn", DELTAyn, 0); + +} + + + +static void +initialize (void) +{ + plus_zero = 0.0; + nan_value = plus_zero / plus_zero; /* Suppress GCC warning */ + + minus_zero = FUNC(copysign) (0.0, -1.0); + plus_infty = CHOOSE (HUGE_VALL, HUGE_VAL, HUGE_VALF, + HUGE_VALL, HUGE_VAL, HUGE_VALF); + minus_infty = CHOOSE (-HUGE_VALL, -HUGE_VAL, -HUGE_VALF, + -HUGE_VALL, -HUGE_VAL, -HUGE_VALF); + + (void) &plus_zero; + (void) &nan_value; + (void) &minus_zero; + (void) &plus_infty; + (void) &minus_infty; + + /* Clear all exceptions. From now on we must not get random exceptions. */ + feclearexcept (FE_ALL_EXCEPT); +} + +#if 0 /* XXX scp XXX */ +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { "verbose", 'v', "NUMBER", 0, "Level of verbosity (0..3)"}, + { "ulps-file", 'u', NULL, 0, "Output ulps to file ULPs"}, + { "no-max-error", 'f', NULL, 0, + "Don't output maximal errors of functions"}, + { "no-points", 'p', NULL, 0, + "Don't output results of functions invocations"}, + { "ignore-max-ulp", 'i', "yes/no", 0, + "Ignore given maximal errors"}, + { NULL, 0, NULL, 0, NULL } +}; + +/* Short description of program. */ +static const char doc[] = "Math test suite: " TEST_MSG ; + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, NULL, doc, +}; + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'f': + output_max_error = 0; + break; + case 'i': + if (strcmp (arg, "yes") == 0) + ignore_max_ulp = 1; + else if (strcmp (arg, "no") == 0) + ignore_max_ulp = 0; + break; + case 'p': + output_points = 0; + break; + case 'u': + output_ulps = 1; + break; + case 'v': + if (optarg) + verbose = (unsigned int) strtoul (optarg, NULL, 0); + else + verbose = 3; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} +#endif + +#if 0 +/* function to check our ulp calculation. */ +void +check_ulp (void) +{ + int i; + + FLOAT u, diff, ulp; + /* This gives one ulp. */ + u = FUNC(nextafter) (10, 20); + check_equal (10.0, u, 1, &diff, &ulp); + printf ("One ulp: % .4" PRINTF_NEXPR "\n", ulp); + + /* This gives one more ulp. */ + u = FUNC(nextafter) (u, 20); + check_equal (10.0, u, 2, &diff, &ulp); + printf ("two ulp: % .4" PRINTF_NEXPR "\n", ulp); + + /* And now calculate 100 ulp. */ + for (i = 2; i < 100; i++) + u = FUNC(nextafter) (u, 20); + check_equal (10.0, u, 100, &diff, &ulp); + printf ("100 ulp: % .4" PRINTF_NEXPR "\n", ulp); +} +#endif + +int +main (int argc, char **argv) +{ +#if 0 /* XXX scp XXX */ + int remaining; +#endif + + verbose = 1; + output_ulps = 0; + output_max_error = 1; + output_points = 1; + /* XXX set to 0 for releases. */ + ignore_max_ulp = 0; + +#if 0 /* XXX scp XXX */ + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + if (remaining != argc) + { + fprintf (stderr, "wrong number of arguments"); + argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name); + exit (EXIT_FAILURE); + } +#endif + + if (output_ulps) + { + ulps_file = fopen ("ULPs", "a"); + if (ulps_file == NULL) + { + perror ("can't open file `ULPs' for writing: "); + exit (1); + } + } + + + initialize (); + printf (TEST_MSG); + +#if 0 + check_ulp (); +#endif + + /* Keep the tests a wee bit ordered (according to ISO C99). */ + /* Classification macros: */ + fpclassify_test (); + isfinite_test (); + isnormal_test (); + signbit_test (); + + /* Trigonometric functions: */ + acos_test (); + asin_test (); + atan_test (); + atan2_test (); + cos_test (); + sin_test (); + sincos_test (); + tan_test (); + + /* Hyperbolic functions: */ + acosh_test (); + asinh_test (); + atanh_test (); + cosh_test (); + sinh_test (); + tanh_test (); + + /* Exponential and logarithmic functions: */ + exp_test (); +#if 0 /* XXX scp XXX */ + exp10_test (); +#endif + exp2_test (); + expm1_test (); + frexp_test (); + ldexp_test (); + log_test (); + log10_test (); + log1p_test (); + log2_test (); + logb_test (); + modf_test (); + ilogb_test (); + scalbn_test (); + scalbln_test (); + + /* Power and absolute value functions: */ + cbrt_test (); + fabs_test (); + hypot_test (); + pow_test (); + sqrt_test (); + + /* Error and gamma functions: */ + erf_test (); + erfc_test (); + gamma_test (); + lgamma_test (); + tgamma_test (); + + /* Nearest integer functions: */ + ceil_test (); + floor_test (); + nearbyint_test (); + rint_test (); + lrint_test (); + llrint_test (); + round_test (); + lround_test (); + llround_test (); + trunc_test (); + + /* Remainder functions: */ + fmod_test (); + remainder_test (); + remquo_test (); + + /* Manipulation functions: */ + copysign_test (); + nextafter_test (); +#if 0 /* XXX scp XXX */ + nexttoward_test (); +#endif + + /* maximum, minimum and positive difference functions */ + fdim_test (); + fmax_test (); + fmin_test (); + + /* Multiply and add: */ + fma_test (); + +#if 0 /* XXX scp XXX */ + /* Complex functions: */ + cabs_test (); + cacos_test (); + cacosh_test (); + carg_test (); + casin_test (); + casinh_test (); + catan_test (); + catanh_test (); + ccos_test (); + ccosh_test (); + cexp_test (); + cimag_test (); + clog10_test (); + clog_test (); + conj_test (); + cpow_test (); + cproj_test (); + creal_test (); + csin_test (); + csinh_test (); + csqrt_test (); + ctan_test (); + ctanh_test (); +#endif + + /* Bessel functions: */ + j0_test (); + j1_test (); + jn_test (); + y0_test (); + y1_test (); + yn_test (); + + if (output_ulps) + fclose (ulps_file); + + printf ("\nTest suite completed:\n"); + printf (" %d test cases plus %d tests for exception flags executed.\n", + noTests, noExcTests); + if (noXFails) + printf (" %d expected failures occurred.\n", noXFails); + if (noXPasses) + printf (" %d unexpected passes occurred.\n", noXPasses); + if (noErrors) + { + printf (" %d errors occurred.\n", noErrors); + return 1; + } + printf (" All tests passed successfully.\n"); + + return 0; +} + +/* + * Local Variables: + * mode:c + * End: + */ diff --git a/openlibm/test/test-211.c b/openlibm/test/test-211.c new file mode 100644 index 0000000000..8bec4e9e62 --- /dev/null +++ b/openlibm/test/test-211.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int +main() +{ + float x = 0xd.65874p-4f; + float y = 4.0f; + float z = powf (x, y); + assert(z==0x1.f74424p-2); +} diff --git a/openlibm/test/test-double.c b/openlibm/test/test-double.c new file mode 100644 index 0000000000..4d239a71d2 --- /dev/null +++ b/openlibm/test/test-double.c @@ -0,0 +1,34 @@ +/* Copyright (C) 1997, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger , 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#define FUNC(function) function +#define FLOAT double +#define TEST_MSG "testing double (without inline functions)\n" +#define MATHCONST(x) x +#define CHOOSE(Clongdouble,Cdouble,Cfloat,Cinlinelongdouble,Cinlinedouble,Cinlinefloat) Cdouble +#define PRINTF_EXPR "e" +#define PRINTF_XEXPR "a" +#define PRINTF_NEXPR "f" +#define TEST_DOUBLE 1 + +#ifndef __NO_MATH_INLINES +# define __NO_MATH_INLINES +#endif + +#include "libm-test.c" diff --git a/openlibm/test/test-float.c b/openlibm/test/test-float.c new file mode 100644 index 0000000000..26a4213b47 --- /dev/null +++ b/openlibm/test/test-float.c @@ -0,0 +1,34 @@ +/* Copyright (C) 1997, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger , 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#define FUNC(function) function ## f +#define FLOAT float +#define TEST_MSG "testing float (without inline functions)\n" +#define MATHCONST(x) x +#define CHOOSE(Clongdouble,Cdouble,Cfloat,Cinlinelongdouble,Cinlinedouble,Cinlinefloat) Cfloat +#define PRINTF_EXPR "e" +#define PRINTF_XEXPR "a" +#define PRINTF_NEXPR "f" +#define TEST_FLOAT 1 + +#ifndef __NO_MATH_INLINES +# define __NO_MATH_INLINES +#endif + +#include "libm-test.c" diff --git a/openlibm/wasm32/Make.files b/openlibm/wasm32/Make.files new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openlibm/wasm32/assert.h b/openlibm/wasm32/assert.h new file mode 100644 index 0000000000..5c6205be28 --- /dev/null +++ b/openlibm/wasm32/assert.h @@ -0,0 +1 @@ +#define assert(x) ((void)0) diff --git a/openlibm/wasm32/float.h b/openlibm/wasm32/float.h new file mode 100644 index 0000000000..c82ecbdb92 --- /dev/null +++ b/openlibm/wasm32/float.h @@ -0,0 +1,33 @@ +#pragma once + +#define FLT_RADIX 2 + +#define FLT_TRUE_MIN 1.40129846432481707092e-45F +#define FLT_MIN 1.17549435082228750797e-38F +#define FLT_MAX 3.40282346638528859812e+38F +#define FLT_EPSILON 1.1920928955078125e-07F + +#define FLT_MANT_DIG 24 +#define FLT_MIN_EXP (-125) +#define FLT_MAX_EXP 128 +#define FLT_HAS_SUBNORM 1 + +#define FLT_DIG 6 +#define FLT_DECIMAL_DIG 9 +#define FLT_MIN_10_EXP (-37) +#define FLT_MAX_10_EXP 38 + +#define DBL_TRUE_MIN 4.94065645841246544177e-324 +#define DBL_MIN 2.22507385850720138309e-308 +#define DBL_MAX 1.79769313486231570815e+308 +#define DBL_EPSILON 2.22044604925031308085e-16 + +#define DBL_MANT_DIG 53 +#define DBL_MIN_EXP (-1021) +#define DBL_MAX_EXP 1024 +#define DBL_HAS_SUBNORM 1 + +#define DBL_DIG 15 +#define DBL_DECIMAL_DIG 17 +#define DBL_MIN_10_EXP (-307) +#define DBL_MAX_10_EXP 308 diff --git a/openlibm/wasm32/limits.h b/openlibm/wasm32/limits.h new file mode 100644 index 0000000000..1f8d1248ac --- /dev/null +++ b/openlibm/wasm32/limits.h @@ -0,0 +1,9 @@ +#include + +#define INT_MIN INT32_MIN +#define INT_MAX INT32_MAX +#define LONG_MIN INT32_MIN +#define LONG_MAX INT32_MAX +#define LLONG_MIN INT64_MIN +#define LLONG_MAX INT64_MAX + diff --git a/openlibm/wasm32/stdint.h b/openlibm/wasm32/stdint.h new file mode 100644 index 0000000000..3996e5b1d8 --- /dev/null +++ b/openlibm/wasm32/stdint.h @@ -0,0 +1,43 @@ +#pragma once + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef unsigned int uintptr_t; +typedef int intptr_t; + +_Static_assert(sizeof (uint8_t) == 1, "invalid size"); +_Static_assert(sizeof (uint16_t) == 2, "invalid size"); +_Static_assert(sizeof (uint32_t) == 4, "invalid size"); +_Static_assert(sizeof (uint64_t) == 8, "invalid size"); + +_Static_assert(sizeof (int8_t) == 1, "invalid size"); +_Static_assert(sizeof (int16_t) == 2, "invalid size"); +_Static_assert(sizeof (int32_t) == 4, "invalid size"); +_Static_assert(sizeof (int64_t) == 8, "invalid size"); + +_Static_assert(sizeof (uintptr_t) == sizeof (intptr_t), "invalid size"); +_Static_assert(sizeof (uintptr_t) == sizeof (void*), "invalid size"); +_Static_assert(sizeof (uintptr_t) == 4, "invalid size"); + +#define UINT8_MAX 0xFF +#define UINT16_MAX 0xFFFF +#define UINT32_MAX 0xFFFFFFFFUL +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL + +#define INT8_MAX 0x7F +#define INT16_MAX 0x7FFF +#define INT32_MAX 0x7FFFFFFF +#define INT64_MAX 0x7FFFFFFFFFFFFFFF + +#define INT8_MIN (-0x80) +#define INT16_MIN (-0x8000) +#define INT32_MIN (-0x80000000L) +#define INT64_MIN (-0x8000000000000000LL) diff --git a/redox-ioctl/Cargo.toml b/redox-ioctl/Cargo.toml new file mode 100644 index 0000000000..2acdaa8e7e --- /dev/null +++ b/redox-ioctl/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "redox-ioctl" +authors = ["bjorn3 "] +version = "0.1.0" +edition = "2024" +license = "MIT" +description = "Ioctl definitions and (de)serialization for Redox" + +[dependencies] +drm-sys = "0.8.0" +redox_syscall = "0.7" + +[features] diff --git a/redox-ioctl/src/drm.rs b/redox-ioctl/src/drm.rs new file mode 100644 index 0000000000..062407b103 --- /dev/null +++ b/redox-ioctl/src/drm.rs @@ -0,0 +1,319 @@ +use alloc::vec::Vec; +use core::{ + cmp, + ffi::{c_char, c_int, c_uint}, + iter, mem, slice, +}; +use drm_sys::drm_clip_rect; + +pub use drm_sys::{ + __kernel_size_t, DRM_PROP_NAME_LEN, drm_get_cap, drm_mode_card_res, + drm_mode_connector_set_property, drm_mode_create_dumb, drm_mode_crtc, + drm_mode_crtc_page_flip_target, drm_mode_cursor, drm_mode_cursor2, drm_mode_destroy_dumb, + drm_mode_fb_cmd, drm_mode_fb_cmd2, drm_mode_fb_dirty_cmd, drm_mode_get_blob, + drm_mode_get_connector, drm_mode_get_encoder, drm_mode_get_plane, drm_mode_get_plane_res, + drm_mode_get_property, drm_mode_map_dumb, drm_mode_modeinfo, drm_mode_obj_get_properties, + drm_mode_property_enum, drm_mode_set_plane, drm_set_client_cap, drm_version, +}; + +pub const VERSION: u64 = 0; +define_ioctl_data! { + struct drm_version, DrmVersion { + version_major: c_int, + version_minor: c_int, + version_patchlevel: c_int, + name_len: __kernel_size_t, + name: *mut c_char [array], + date_len: __kernel_size_t, + date: *mut c_char [array], + desc_len: __kernel_size_t, + desc: *mut c_char [array], + } +} + +pub const GET_CAP: u64 = 0x0C; +pub use drm_sys::DRM_CAP_DUMB_BUFFER; +define_ioctl_data! { + struct drm_get_cap, DrmGetCap { + capability: u64, + value: u64, + } +} + +pub const SET_CLIENT_CAP: u64 = 0x0D; +pub use drm_sys::DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT; +define_ioctl_data! { + struct drm_set_client_cap, DrmSetClientCap { + capability: u64, + value: u64, + } +} + +pub const MODE_CARD_RES: u64 = 0xA0; +define_ioctl_data! { + struct drm_mode_card_res, DrmModeCardRes { + fb_id_ptr: u64 [array], + crtc_id_ptr: u64 [array], + connector_id_ptr: u64 [array], + encoder_id_ptr: u64 [array], + count_fbs: u32, + count_crtcs: u32, + count_connectors: u32, + count_encoders: u32, + min_width: u32, + max_width: u32, + min_height: u32, + max_height: u32, + } +} + +pub const MODE_GET_CRTC: u64 = 0xA1; +pub const MODE_SET_CRTC: u64 = 0xA2; +define_ioctl_data! { + struct drm_mode_crtc, DrmModeCrtc { + set_connectors_ptr: u64 [array], + count_connectors: u32, + crtc_id: u32, + fb_id: u32, + x: u32, + y: u32, + gamma_size: u32, + mode_valid: u32, + mode: drm_mode_modeinfo, + } +} + +pub const MODE_CURSOR: u64 = 0xA3; +define_ioctl_data! { + struct drm_mode_cursor, DrmModeCursor { + flags: u32, + crtc_id: u32, + x: i32, + y:i32, + width:u32, + height:u32, + handle:u32, + } +} + +pub const MODE_GET_ENCODER: u64 = 0xA6; +define_ioctl_data! { + struct drm_mode_get_encoder, DrmModeGetEncoder { + encoder_id: u32, + encoder_type: u32, + crtc_id: u32, + possible_crtcs: u32, + possible_clones: u32, + } +} + +pub const MODE_GET_CONNECTOR: u64 = 0xA7; +define_ioctl_data! { + struct drm_mode_get_connector, DrmModeGetConnector { + encoders_ptr: u64 [array], + modes_ptr: u64 [array], + props_ptr: u64 [array], + prop_values_ptr: u64 [array], + count_modes: u32, + count_props: u32, + count_encoders: u32, + encoder_id: u32, + connector_id: u32, + connector_type: u32, + connector_type_id: u32, + connection: u32, + mm_width: u32, + mm_height: u32, + subpixel: u32, + pad: u32, + } +} + +pub const MODE_GET_PROPERTY: u64 = 0xAA; +define_ioctl_data! { + struct drm_mode_get_property, DrmModeGetProperty { + values_ptr: u64 [array], + enum_blob_ptr: u64 [array], + prop_id: u32, + flags: u32, + name: [c_char; DRM_PROP_NAME_LEN as usize], + count_values: u32, + count_enum_blobs: u32, + } +} + +pub const MODE_SET_PROPERTY: u64 = 0xAB; +define_ioctl_data! { + struct drm_mode_connector_set_property, DrmModeConnectorSetProperty { + value: u64, + prop_id: u32, + connector_id: u32, + } +} + +pub const MODE_GET_PROP_BLOB: u64 = 0xAC; +define_ioctl_data! { + struct drm_mode_get_blob, DrmModeGetBlob { + blob_id: u32, + length: u32, + data: u64 [array], + } +} + +pub const MODE_GET_FB: u64 = 0xAD; +pub const MODE_ADD_FB: u64 = 0xAE; +define_ioctl_data! { + struct drm_mode_fb_cmd, DrmModeFbCmd { + fb_id: u32, + width: u32, + height: u32, + pitch: u32, + bpp: u32, + depth: u32, + handle: u32, + } +} + +pub const MODE_RM_FB: u64 = 0xAF; +define_ioctl_data! { + struct standin_for_uint, StandinForUint { + inner: c_uint, + } +} + +#[repr(transparent)] +#[allow(non_camel_case_types)] +pub struct standin_for_uint { + pub inner: c_uint, +} + +pub const MODE_PAGE_FLIP: u64 = 0xB0; +define_ioctl_data! { + struct drm_mode_crtc_page_flip_target, DrmModeCrtcPageFlipTarget { + crtc_id: u32, + fb_id: u32, + flags: u32, + sequence: u32, + user_data: u64, + } +} + +pub const MODE_DIRTYFB: u64 = 0xB1; +define_ioctl_data! { + struct drm_mode_fb_dirty_cmd, DrmModeFbDirtyCmd { + fb_id: u32, + flags: u32, + color: u32, + num_clips: u32, + clips_ptr: u64 [array], + } +} + +pub const MODE_CREATE_DUMB: u64 = 0xB2; +define_ioctl_data! { + struct drm_mode_create_dumb, DrmModeCreateDumb { + height: u32, + width: u32, + bpp: u32, + flags: u32, + handle: u32, + pitch: u32, + size: u64, + } +} + +pub const MODE_MAP_DUMB: u64 = 0xB3; +define_ioctl_data! { + struct drm_mode_map_dumb, DrmModeMapDumb { + handle: u32, + pad: u32, + offset: u64, + } +} + +pub const MODE_DESTROY_DUMB: u64 = 0xB4; +define_ioctl_data! { + struct drm_mode_destroy_dumb, DrmModeDestroyDumb { + handle: u32, + } +} + +pub const MODE_GET_PLANE_RES: u64 = 0xB5; +define_ioctl_data! { + struct drm_mode_get_plane_res, DrmModeGetPlaneRes { + plane_id_ptr: u64 [array], + count_planes: u32, + } +} + +pub const MODE_GET_PLANE: u64 = 0xB6; +define_ioctl_data! { + struct drm_mode_get_plane, DrmModeGetPlane { + plane_id: u32, + crtc_id: u32, + fb_id: u32, + possible_crtcs: u32, + gamma_size: u32, + count_format_types: u32, + format_type_ptr: u64 [array], + } +} + +pub const MODE_SET_PLANE: u64 = 0xB7; +define_ioctl_data! { + struct drm_mode_set_plane, DrmModeSetPlane { + plane_id: u32, + crtc_id: u32, + fb_id: u32, + flags: u32, + crtc_x: i32, + crtc_y: i32, + crtc_w: u32, + crtc_h: u32, + src_x: u32, + src_y: u32, + src_h: u32, + src_w: u32, + } +} + +pub const MODE_OBJ_GET_PROPERTIES: u64 = 0xB9; +define_ioctl_data! { + struct drm_mode_obj_get_properties, DrmModeObjGetProperties { + props_ptr: u64 [array], + prop_values_ptr: u64 [array], + count_props: u32, + obj_id: u32, + obj_type: u32, + } +} + +pub const MODE_CURSOR2: u64 = 0xBB; +define_ioctl_data! { + struct drm_mode_cursor2, DrmModeCursor2 { + flags: u32, + crtc_id: u32, + x: i32, + y: i32, + width: u32, + height: u32, + handle: u32, + hot_x: i32, + hot_y: i32, + } +} + +pub const MODE_GET_FB2: u64 = 0xCE; +define_ioctl_data! { + struct drm_mode_fb_cmd2, DrmModeFbCmd2 { + fb_id: u32, + width: u32, + height: u32, + pixel_format: u32, + flags: u32, + handles: [u32; 4], + pitches: [u32; 4], + offsets: [u32; 4], + modifier: [u64; 4], + } +} diff --git a/redox-ioctl/src/ioctl_data.rs b/redox-ioctl/src/ioctl_data.rs new file mode 100644 index 0000000000..f9673f6d7d --- /dev/null +++ b/redox-ioctl/src/ioctl_data.rs @@ -0,0 +1,169 @@ +use alloc::vec::Vec; + +pub trait IoctlData { + unsafe fn write(&self) -> Vec; + unsafe fn read_from(&mut self, buf: &[u8]); +} + +macro_rules! define_ioctl_data { + (struct $ioctl_ty:ident, $mem_ty:ident { + $($rest:tt)* + }) => { + define_ioctl_data!( + struct $ioctl_ty, $mem_ty { $($rest)* } => (), (), () + ); + }; + (struct $ioctl_ty:ident, $mem_ty:ident { + $field:ident: $ty:ty, + $($rest:tt)* + } => + ($($ioctl_fields:tt)*), + ($($counted_fields:tt)*), + ($($noncounted_fields:tt)*) + ) => { + define_ioctl_data!( + struct $ioctl_ty, $mem_ty { $($rest)* } => + ($($ioctl_fields)* $field: $ty,), + ($($counted_fields)*), + ($($noncounted_fields)* $field: $ty,) + ); + }; + (struct $ioctl_ty:ident, $mem_ty:ident { + $field:ident: $ty:ty [array<$el:ty, $counted_by:ident>], + $($rest:tt)* + } => + ($($ioctl_fields:tt)*), + ($($counted_fields:tt)*), + ($($noncounted_fields:tt)*) + ) => { + define_ioctl_data!( + struct $ioctl_ty, $mem_ty { $($rest)* } => + ($($ioctl_fields)* $field: $ty,), + ($($counted_fields)* $field: $ty [array<$el, $counted_by>],), + ($($noncounted_fields)*) + ); + }; + (struct $ioctl_ty:ident, $mem_ty:ident {} => + ($($ioctl_field:ident: $ioctl_field_ty:ty,)*), + ($($counted_field:ident: $counted_ty:ty [array<$el:ty, $counted_by:ident>],)*), + ($($noncounted_field:ident: $noncounted_ty:ty,)*) + ) => { + // FIXME check ioctl_ty doesn't have padding + const _: $ioctl_ty = $ioctl_ty { + $($ioctl_field: unsafe { mem::zeroed::<$ioctl_field_ty>() },)* + }; + + #[repr(C)] + pub struct ${concat(__, $mem_ty, Noncounted)} { + $($noncounted_field: $noncounted_ty,)* + } + + pub struct $mem_ty<'a> { + noncounted_fields: &'a mut ${concat(__, $mem_ty, Noncounted)}, + $($counted_field: &'a mut [$el],)* + } + + impl $crate::ioctl_data::IoctlData for $ioctl_ty { + unsafe fn write(&self) -> Vec { + let noncounted_fields = ${concat(__, $mem_ty, Noncounted)} { + $($noncounted_field: self.$noncounted_field,)* + }; + // FIXME use Vec::with_capacity + let mut data = Vec::::new(); + data.extend_from_slice(&unsafe { + mem::transmute::< + ${concat(__, $mem_ty, Noncounted)}, + [u8; size_of::<${concat(__, $mem_ty, Noncounted)}>()], + >(noncounted_fields) + }); + $( + let size = self.$counted_by as usize * size_of::<$el>(); + if self.$counted_field as usize != 0 { + let $counted_field = unsafe { + slice::from_raw_parts(self.$counted_field as *const u8, size) + }; + data.extend_from_slice(&$counted_field); + } else { + data.extend(iter::repeat(0u8).take(size)); + }; + + )* + data + } + + unsafe fn read_from(&mut self, mut buf: &[u8]) { + // FIXME be robust against malicious scheme implementations by returning an error + // when the buf is the wrong size + let noncounted_fields = buf.split_off(..size_of::<${concat(__, $mem_ty, Noncounted)}>()).unwrap(); + + $( + let size = self.$counted_by as usize * size_of::<$el>(); + let $counted_field = buf.split_off(..size).unwrap(); + if self.$counted_field as usize != 0 { + unsafe { + slice::from_raw_parts_mut(self.$counted_field as *mut u8, size).copy_from_slice($counted_field); + } + } + )* + + assert!(buf.is_empty()); + + let noncounted_fields = unsafe { &*(noncounted_fields as *const _ as *const ${concat(__, $mem_ty, Noncounted)}) }; + $(self.$noncounted_field = noncounted_fields.$noncounted_field;)* + } + } + + impl<'a> $mem_ty<'a> { + pub fn with( + mut buf: &'a mut [u8], + f: impl FnOnce($mem_ty<'a>) -> syscall::Result, + ) -> syscall::Result { + let noncounted_fields = buf.split_off_mut(..size_of::<${concat(__, $mem_ty, Noncounted)}>()) + .ok_or(syscall::Error::new(syscall::EINVAL))?; + let noncounted_fields = unsafe { &mut *(noncounted_fields as *mut _ as *mut ${concat(__, $mem_ty, Noncounted)}) }; + + $( + let $counted_field = buf.split_off_mut(..noncounted_fields.$counted_by as usize * size_of::<$el>()) + .ok_or(syscall::Error::new(syscall::EINVAL))?; + let $counted_field = unsafe { + slice::from_raw_parts_mut($counted_field as *mut _ as *mut $el, noncounted_fields.$counted_by as usize) + }; + )* + + if !buf.is_empty() { + return Err(syscall::Error::new(syscall::EINVAL)); + } + + + + Ok( f($mem_ty { + noncounted_fields, + $($counted_field,)* + })?) + } + + $( + pub fn $noncounted_field(&self) -> $noncounted_ty { + self.noncounted_fields.$noncounted_field + } + + /// Should not be called for fields used as array length + pub fn ${concat(set_, $noncounted_field)}(&mut self, data: $noncounted_ty) { + self.noncounted_fields.$noncounted_field = data; + } + )* + + $( + pub fn $counted_field(&self) -> &[$el] { + self.$counted_field + } + + pub fn ${concat(set_, $counted_field)}(&mut self, data: &[$el]) { + let copied_count = cmp::min(data.len(), self.$counted_field.len()); + self.$counted_field[..copied_count].copy_from_slice(&data[..copied_count]); + self.noncounted_fields.$counted_by = data.len() as _; + } + )* + } + }; +} diff --git a/redox-ioctl/src/lib.rs b/redox-ioctl/src/lib.rs new file mode 100644 index 0000000000..0746aba78a --- /dev/null +++ b/redox-ioctl/src/lib.rs @@ -0,0 +1,11 @@ +#![feature(macro_metavar_expr_concat)] +#![deny(unsafe_op_in_unsafe_fn)] +#![no_std] + +extern crate alloc; + +#[macro_use] +mod ioctl_data; +pub use ioctl_data::IoctlData; + +pub mod drm; diff --git a/redox-rt/Cargo.toml b/redox-rt/Cargo.toml new file mode 100644 index 0000000000..773af3bbef --- /dev/null +++ b/redox-rt/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "redox-rt" +authors = ["4lDO2 <4lDO2@protonmail.com>"] +version = "0.1.0" +edition = "2024" +license = "MIT" +description = "Libc-independent runtime for Redox" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags.workspace = true +goblin = { version = "0.10", default-features = false, features = ["elf32", "elf64", "endian_fd"] } +plain.workspace = true +ioslice.workspace = true +redox_syscall.workspace = true +redox-path.workspace = true +redox_protocols.workspace = true + +generic-rt = { path = "../generic-rt" } + +[features] +proc = [] +default = ["proc"] + +[lints] +workspace = true diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs new file mode 100644 index 0000000000..0724c9bb5b --- /dev/null +++ b/redox-rt/src/arch/aarch64.rs @@ -0,0 +1,485 @@ +use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull}; + +use syscall::{data::*, error::*}; + +use crate::{ + Tcb, + proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner}, + signal::{PosixStackt, RtSigarea, SigStack, inner_c}, +}; +use redox_protocols::protocol::{ProcCall, RtSigInfo}; + +use super::ForkScratchpad; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub const STACK_TOP: usize = 1 << 47; +pub const STACK_SIZE: usize = 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub altstack_top: usize, + pub altstack_bottom: usize, + pub tmp_x1_x2: [usize; 2], + pub tmp_x3_x4: [usize; 2], + pub tmp_x5_x6: [usize; 2], + pub tmp_x7_x8: [usize; 2], + pub tmp_sp: usize, + pub onstack: u64, + pub disable_signals_depth: u64, + pub pctl: usize, // TODO: remove + pub last_sig_was_restart: bool, + pub last_sigstack: Option>, + pub tmp_rt_inf: RtSigInfo, + pub tmp_id_inf: u64, +} +#[repr(C)] +#[derive(Debug, Default)] +pub struct ArchIntRegs { + pub x30: usize, + pub x29: usize, + pub x28: usize, + pub x27: usize, + pub x26: usize, + pub x25: usize, + pub x24: usize, + pub x23: usize, + pub x22: usize, + pub x21: usize, + pub x20: usize, + pub x19: usize, + pub x18: usize, + pub x17: usize, + pub x16: usize, + pub x15: usize, + pub x14: usize, + pub x13: usize, + pub x12: usize, + pub x11: usize, + pub x10: usize, + pub x9: usize, + pub x8: usize, + pub x7: usize, + pub x6: usize, + pub x5: usize, + pub x4: usize, + pub x3: usize, + pub x2: usize, + pub x1: usize, + + pub sp: usize, + pub nzcv: usize, // user-accessible PSTATE bits + + pub pc: usize, + pub x0: usize, +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = open_via_dup.dup(b"regs/env")?; + + env.tpidr_el0 = 0; + + file.write(&mut env)?; + Ok(()) +} + +unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) +} + +unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) { + //let _ = syscall::write(1, alloc::format!("CUR{cur_filetable_fd}PROC{new_proc_fd}THR{new_thr_fd}\n").as_bytes()); + let _ = syscall::close(scratchpad.cur_filetable_fd); + unsafe { + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), + new_proc_fd: if scratchpad.new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(scratchpad.new_proc_fd)) + }, + }) + }; +} + +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" + stp x29, x30, [sp, #-16]! + stp x27, x28, [sp, #-16]! + stp x25, x26, [sp, #-16]! + stp x23, x24, [sp, #-16]! + stp x21, x22, [sp, #-16]! + stp x19, x20, [sp, #-16]! + + //TODO: store floating point regs + + // x0: &ForkArgs + mov x1, sp + bl {fork_impl} + + ldp x19, x20, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x29, x30, [sp], #16 + ret +"] <= [fork_impl = sym fork_impl]); + +asmfunction!(__relibc_internal_fork_ret: [" + # scratchpad is in x1, move to x0 for child_hook + mov x0, x1 + + bl {child_hook} + + //TODO: load floating point regs + + mov x0, xzr + + ldp x19, x20, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x29, x30, [sp], #16 + + ret +"] <= [child_hook = sym child_hook]); + +// https://devblogs.microsoft.com/oldnewthing/20220811-00/?p=106963 +asmfunction!(__relibc_internal_sigentry: [" + // Clear any active reservation. + clrex + + // The old pc and x0 are saved in the sigcontrol struct. + mrs x0, tpidr_el0 // ABI ptr + ldr x0, [x0] // TCB ptr + + // Save x1-x6 and sp + stp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] + stp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] + stp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] + stp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}] + mov x1, sp + str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}] + + ldr x6, [x0, #{tcb_sa_off} + {sa_pctl}] +1: + // Load x1 with the thread's bits + add x5, x0, #{tcb_sc_off} + {sc_word} + ldaxr x1, [x5] + + // First check if there are standard thread signals, + and x4, x1, x1, lsr #32 // x4 := x1 & (x1 >> 32) + cbnz x4, 3f // jump if x4 != 0 + clrex + + // and if not, load process pending bitset. + add x5, x6, #{pctl_pending} + ldaxr x2, [x5] + + // Check if there are standard proc signals: + lsr x3, x1, #32 // mask + and w3, w2, w3 // pending unblocked proc + cbz w3, 4f // skip 'fetch_andn' step if zero + + // If there was one, find which one, and try clearing the bit (last value in x3, addr in x6) + // this picks the MSB rather than the LSB, unlike x86. POSIX does not require any specific + // ordering though. + clz w3, w3 + mov w4, #31 + sub w3, w4, w3 + // x3 now contains the sig_idx + + mov x4, #1 + lsl x4, x4, x3 // bit to remove + + sub x4, x2, x4 // bit was certainly set, so sub is allowed + // x4 is now the new mask to be set + add x5, x6, #{pctl_pending} + + add x2, x5, #{pctl_sender_infos} + add x2, x2, w3, uxtb 3 + ldar x2, [x2] + + // Try clearing the bit, retrying on failure. + stxr w1, x4, [x5] // try setting pending set to x4, set w1 := 0 on success + cbnz w1, 1b // retry everything if this fails + mov x1, x3 + b 2f +4: + // Check for realtime signals, thread/proc. + clrex + + // Load the pending set again. TODO: optimize this? + // Process pending - realtime + add x1, x6, #{pctl_pending} + ldaxr x2, [x1] + lsr x2, x2, #32 + + // Thread pending - realtime and allowset + add x5, x0, #{tcb_sc_off} + {sc_word} + 8 + ldar x1, [x5] + + orr x2, x1, x2 // combine proc and thread pending + and x2, x2, x2, lsr #32 // AND pending with allowset + cbz x2, 7f // spurious signal if realtime is clear + + rbit x2, x2 + clz x2, x2 + // x2 now contains sig_idx - 32 + + // If realtime signal was directed at thread, handle it as an idempotent signal. + lsr x3, x1, x2 // x3 := x1 >> x2; x1 is thread pending + tbnz x3, #0, 5f // jump if bit is nonzero + + // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base | (flags << 8)) + // x8 x0 x1 x2 x3 x4 + + mov x5, x0 // save TCB pointer + mov x6, x2 + ldr x8, ={SYS_CALL} + adrp x0, {proc_fd} + ldr x0, [x0, #:lo12:{proc_fd}] + add x1, x5, #{tcb_sa_off} + {sa_tmp_rt_inf} + str x6, [x1] + mov x2, #{RTINF_SIZE} + adrp x4, {proc_call} + add x4, x4, :lo12:{proc_call} + mov x3, #1 + svc 0 + mov x3, x0 + mov x0, x5 // restore TCB pointer + mov x2, x6 // restore signal number - 32 + add x1, x2, #32 // signal number + cbnz x3, 1b + b 2f +5: + // A realtime signal was sent to this thread, try clearing its bit. + // x3 contains last rt signal word, x2 contains rt_idx + clrex + + // Calculate the absolute sig_idx + add x1, x3, 32 + + // Load si_pid and si_uid + add x2, x0, #{tcb_sc_off} + {sc_sender_infos} + add x2, x2, w1, uxtb #3 + ldar x2, [x2] + + add x3, x0, #{tcb_sc_off} + {sc_word} + 8 + ldxr x2, [x3] + + // Calculate new mask + mov x4, #1 + lsl x4, x4, x2 + sub x2, x2, x4 // remove bit + + stxr w5, x2, [x3] + cbnz w5, 1b + str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}] + b 2f +3: + // A standard signal was sent to this thread, try clearing its bit. + clz w1, w1 + mov x2, #31 + sub x1, x2, x1 + + // Load si_pid and si_uid + add x2, x0, #{tcb_sc_off} + {sc_sender_infos} + add x2, x2, w1, uxtb #3 + ldar x2, [x2] + + // Clear bit from mask + mov x3, #1 + lsl x3, x3, x1 + sub x4, x4, x3 + + // Try updating the mask + stxr w3, x1, [x5] + cbnz w3, 1b + + str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}] +2: + ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}] + add x3, x3, {pctl_actions} + add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action + // TODO: NOT ATOMIC (tearing allowed between regs)! + ldxp x2, x3, [x2] + clrex + + // Calculate new sp wrt redzone and alignment + mov x4, sp + sub x4, x4, {REDZONE_SIZE} + and x4, x4, -{STACK_ALIGN} + mov sp, x4 + + // skip sigaltstack step if SA_ONSTACK is clear + // tbz x2, #{SA_ONSTACK_BIT}, 2f + + ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}] + ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}] + stp x2, x3, [sp, #-16]! + + ldr x2, [x0, #{tcb_sa_off} + {sa_tmp_sp}] + mrs x3, nzcv + stp x2, x3, [sp, #-16]! + + ldp x2, x3, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] + stp x2, x3, [sp, #-16]! + ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] + stp x4, x3, [sp, #-16]! + ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] + stp x6, x5, [sp, #-16]! + ldp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}] + stp x8, x7, [sp, #-16]! + + stp x10, x9, [sp, #-16]! + stp x12, x11, [sp, #-16]! + stp x14, x13, [sp, #-16]! + stp x16, x15, [sp, #-16]! + stp x18, x17, [sp, #-16]! + stp x20, x19, [sp, #-16]! + stp x22, x21, [sp, #-16]! + stp x24, x23, [sp, #-16]! + stp x26, x25, [sp, #-16]! + stp x28, x27, [sp, #-16]! + stp x30, x29, [sp, #-16]! + + str w1, [sp, #-4] + sub sp, sp, #64 + + mov x0, sp + bl {inner} + + add sp, sp, #64 + + ldp x30, x29, [sp], #16 + ldp x28, x27, [sp], #16 + ldp x26, x25, [sp], #16 + ldp x24, x23, [sp], #16 + ldp x22, x21, [sp], #16 + ldp x20, x19, [sp], #16 + ldp x18, x17, [sp], #16 + ldp x16, x15, [sp], #16 + ldp x14, x13, [sp], #16 + ldp x12, x11, [sp], #16 + ldp x10, x9, [sp], #16 + ldp x8, x7, [sp], #16 + ldp x6, x5, [sp], #16 + ldp x4, x3, [sp], #16 + ldp x2, x1, [sp], #16 + + ldr x0, [sp, #8] + msr nzcv, x0 + +8: + // x18 is reserved by ABI as 'platform register', so clobbering it should be safe. + mov x18, sp + ldr x0, [x18] + mov sp, x0 + + ldp x18, x0, [x18, #16] + br x18 +7: + // Spurious signal, i.e. all bitsets were 0 at the time they were checked + clrex + + ldr x1, [x0, #{tcb_sc_off} + {sc_flags}] + and x1, x1, ~1 + str x1, [x0, #{tcb_sc_off} + {sc_flags}] + + ldp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] + ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] + ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}] + ldr x18, [x0, #{tcb_sc_off} + {sc_saved_pc}] + ldr x0, [x0, #{tcb_sc_off} + {sc_saved_x0}] + br x18 +"] <= [ + pctl_pending = const (offset_of!(SigProcControl, pending)), + pctl_actions = const (offset_of!(SigProcControl, actions)), + pctl_sender_infos = const (offset_of!(SigProcControl, sender_infos)), + tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)), + tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)), + sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2), + sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4), + sa_tmp_x5_x6 = const offset_of!(SigArea, tmp_x5_x6), + sa_tmp_x7_x8 = const offset_of!(SigArea, tmp_x7_x8), + sa_tmp_sp = const offset_of!(SigArea, tmp_sp), + sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), + sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), + sa_pctl = const offset_of!(SigArea, pctl), + sc_saved_pc = const offset_of!(Sigcontrol, saved_ip), + sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), + sc_word = const offset_of!(Sigcontrol, word), + sc_flags = const offset_of!(Sigcontrol, control_flags), + proc_fd = sym PROC_FD, + inner = sym inner_c, + proc_call = sym PROC_CALL, + + SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 + + SYS_CALL = const syscall::SYS_CALL, + + STACK_ALIGN = const 16, + REDZONE_SIZE = const 128, + RTINF_SIZE = const size_of::(), +]); + +asmfunction!(__relibc_internal_rlct_clone_ret: [" + # Load registers + ldp x8, x0, [sp], #16 + ldp x1, x2, [sp], #16 + ldp x3, x4, [sp], #16 + + # Call entry point + blr x8 + + ret +"] <= []); + +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!("mov {}, sp", out(reg) sp); + } + sp +} + +pub unsafe fn manually_enter_trampoline() { + let ctl = unsafe { &Tcb::current().unwrap().os_specific.control }; + + ctl.saved_archdep_reg.set(0); + let ip_location = &ctl.saved_ip as *const _ as usize; + + unsafe { + core::arch::asm!(" + bl 2f + b 3f + 2: + str lr, [x0] + b __relibc_internal_sigentry + 3: + ", + inout("x0") ip_location => _, out("lr") _); + } +} + +pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) -> PosixStackt { + PosixStackt { + sp: core::ptr::null_mut(), // TODO + size: 0, // TODO + flags: 0, // TODO + } +} +pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) { + let orig_pc = core::mem::replace(&mut stack.regs.pc, __relibc_internal_sigentry as usize); + control.saved_ip.set(orig_pc); + control.saved_archdep_reg.set(stack.regs.x0); +} +pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); +static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize]; diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs new file mode 100644 index 0000000000..6461fe5f87 --- /dev/null +++ b/redox-rt/src/arch/i686.rs @@ -0,0 +1,411 @@ +use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::Ordering}; + +use syscall::*; + +use crate::{ + proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner}, + signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, inner_fastcall}, +}; +use redox_protocols::protocol::{ProcCall, RtSigInfo}; + +use super::ForkScratchpad; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub const STACK_TOP: usize = 1 << 31; +pub const STACK_SIZE: usize = 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub altstack_top: usize, + pub altstack_bottom: usize, + pub tmp_eip: usize, + pub tmp_esp: usize, + pub tmp_eax: usize, + pub tmp_ebx: usize, + pub tmp_ecx: usize, + pub tmp_edx: usize, + pub tmp_edi: usize, + pub tmp_esi: usize, + pub tmp_rt_inf: RtSigInfo, + pub tmp_signo: usize, + pub tmp_id_inf: u64, + pub tmp_mm0: u64, + pub disable_signals_depth: u64, + pub last_sig_was_restart: bool, + pub last_sigstack: Option>, +} +#[derive(Debug, Default)] +#[repr(C, align(16))] +pub struct ArchIntRegs { + pub fxsave: [u16; 29], + + // ensure fxsave region is 16 byte aligned + pub _pad: [usize; 2], // fxsave "available" +0 + + pub ebp: usize, // fxsave "available" +8 + pub esi: usize, // avail +12 + pub edi: usize, // avail +16 + pub ebx: usize, // avail +20 + pub eax: usize, // avail +24 + pub ecx: usize, // avail +28 + pub edx: usize, // avail +32 + + pub eflags: usize, // avail +36 + pub eip: usize, // avail +40 + pub esp: usize, // avail +44 +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = open_via_dup.dup(b"regs/env")?; + + env.fsbase = 0; + env.gsbase = 0; + + file.write(&mut env)?; + Ok(()) +} + +unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) +} + +// TODO: duplicate code with x86_64 +unsafe extern "cdecl" fn child_hook(scratchpad: ForkScratchpad) { + let _ = syscall::close(scratchpad.cur_filetable_fd); + unsafe { + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), + new_proc_fd: if scratchpad.new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(scratchpad.new_proc_fd)) + }, + }) + }; +} + +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" + mov ecx, [esp+4] + + push ebp + mov ebp, esp + + // Push preserved registers + push ebx + push edi + push esi + push ebp + + sub esp, 32 + + //TODO stmxcsr [esp+16] + fnstcw [esp+24] + + mov edx, esp + call {fork_impl} + + jmp 2f +"] <= [fork_impl = sym fork_impl]); + +asmfunction!(__relibc_internal_fork_ret: [" + // Arguments already on the stack + call {child_hook} + + //TODO ldmxcsr [esp+16] + fldcw [esp+24] + + xor eax, eax + + .p2align 4 +2: + add esp, 32 + + // Pop preserved registers + pop ebp + pop esi + pop edi + pop ebx + + pop ebp + + ret +"] <= [child_hook = sym child_hook]); +asmfunction!(__relibc_internal_sigentry: [" + // Save some registers + mov gs:[{tcb_sa_off} + {sa_tmp_esp}], esp + mov gs:[{tcb_sa_off} + {sa_tmp_eax}], eax + mov gs:[{tcb_sa_off} + {sa_tmp_edx}], edx + mov gs:[{tcb_sa_off} + {sa_tmp_ecx}], ecx + mov gs:[{tcb_sa_off} + {sa_tmp_ebx}], ebx + mov gs:[{tcb_sa_off} + {sa_tmp_edi}], edi + mov gs:[{tcb_sa_off} + {sa_tmp_esi}], esi +1: + // Read standard signal word - first for this thread + mov edx, gs:[{tcb_sc_off} + {sc_word} + 4] + mov eax, gs:[{tcb_sc_off} + {sc_word}] + and eax, edx + bsf eax, eax + jnz 9f + + // Read standard signal word - for the process + lea ecx, [{pctl}] + mov eax, [ecx + {pctl_pending}] + and eax, edx + bsf eax, eax + jz 3f + + // Read si_pid and si_uid, atomically. + movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0 + movq mm0, [ecx + {pctl_sender_infos} + eax * 8] + movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0 + movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}] + + // Try clearing the pending bit, otherwise retry if another thread did that first + lock btr [ecx + {pctl_pending}], eax + jnc 1b + jmp 2f +3: + // Read realtime thread and process signal word together + mov edx, [ecx + {pctl_pending} + 4] + mov eax, gs:[{tcb_sc_off} + {sc_word} + 8] + or eax, edx + and eax, gs:[{tcb_sc_off} + {sc_word} + 12] + bsf eax, eax + jz 7f // spurious signal + + // If thread rather than process was specifically targeted, send the signal to it first. + bt edx, eax + jnc 8f + + // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base) + // eax ebx ecx edx esi edi + + mov ebx, [{proc_fd}] + mov ecx, gs:[0] + add ecx, {tcb_sa_off} + {sa_tmp_rt_inf} + mov [ecx], eax + mov gs:[{tcb_sa_off} + {sa_tmp_signo}], eax + mov edx, {RTINF_SIZE} + mov esi, 1 + lea edi, [{proc_call}] + mov eax, {SYS_CALL} + int 0x80 + test eax, eax + jnz 1b + + mov eax, gs:[{tcb_sa_off} + {sa_tmp_signo}] + add eax, 32 + jmp 2f +8: + add eax, 32 +9: + // Read si_pid and si_uid, atomically. + movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0 + movq mm0, gs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8] + movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0 + movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}] + mov edx, eax + shr edx, 5 + mov ecx, eax + and ecx, 31 + lock btr gs:[{tcb_sc_off} + {sc_word} + edx * 8], ecx + + add eax, 64 +2: + and esp, -{STACK_ALIGN} + + mov edx, eax + add edx, edx + bt dword ptr [{pctl} + {pctl_actions} + edx * 8 + 4], 28 + jnc 4f + + mov edx, gs:[{tcb_sa_off} + {sa_altstack_top}] + cmp esp, edx + ja 3f + + cmp esp, gs:[{tcb_sa_off} + {sa_altstack_bottom}] + jnbe 4f +3: + mov esp, edx +4: + // Now that we have a stack, we can finally start populating the signal stack. + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esp}] + push dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}] + push dword ptr gs:[{tcb_sc_off} + {sc_saved_eflags}] + + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}] + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ecx}] + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}] + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ebx}] + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edi}] + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esi}] + push ebp + + sub esp, 2 * 4 + 29 * 16 + fxsave [esp] + + mov [esp - 4], eax + sub esp, 48 + + mov ecx, esp + call {inner} + + fxrstor [esp + 48] + add esp, 48 + 29 * 16 + 2 * 4 + + pop ebp + pop esi + pop edi + pop ebx + pop eax + pop ecx + pop edx + + popfd + pop dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}] + + .globl __relibc_internal_sigentry_crit_first +__relibc_internal_sigentry_crit_first: + pop esp + + .globl __relibc_internal_sigentry_crit_second +__relibc_internal_sigentry_crit_second: + jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}] +7: + mov eax, gs:[0] + lea esp, [eax + {tcb_sc_off} + {sc_saved_eflags}] + popfd + + mov esp, gs:[{tcb_sa_off} + {sa_tmp_esp}] + + mov eax, gs:[{tcb_sc_off} + {sc_saved_eip}] + mov gs:[{tcb_sa_off} + {sa_tmp_eip}], eax + + mov eax, gs:[{tcb_sa_off} + {sa_tmp_eax}] + mov ebx, gs:[{tcb_sa_off} + {sa_tmp_ebx}] + mov ecx, gs:[{tcb_sa_off} + {sa_tmp_ecx}] + mov edx, gs:[{tcb_sa_off} + {sa_tmp_edx}] + mov edi, gs:[{tcb_sa_off} + {sa_tmp_edi}] + mov esi, gs:[{tcb_sa_off} + {sa_tmp_esi}] + + and dword ptr gs:[{tcb_sc_off} + {sc_control}], ~1 + .globl __relibc_internal_sigentry_crit_third +__relibc_internal_sigentry_crit_third: + jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}] +"] <= [ + inner = sym inner_fastcall, + sa_tmp_eip = const offset_of!(SigArea, tmp_eip), + sa_tmp_esp = const offset_of!(SigArea, tmp_esp), + sa_tmp_eax = const offset_of!(SigArea, tmp_eax), + sa_tmp_ebx = const offset_of!(SigArea, tmp_ebx), + sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx), + sa_tmp_edx = const offset_of!(SigArea, tmp_edx), + sa_tmp_edi = const offset_of!(SigArea, tmp_edi), + sa_tmp_esi = const offset_of!(SigArea, tmp_esi), + sa_tmp_mm0 = const offset_of!(SigArea, tmp_mm0), + sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), + sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), + sa_tmp_signo = const offset_of!(SigArea, tmp_signo), + sa_altstack_top = const offset_of!(SigArea, altstack_top), + sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), + sc_control = const offset_of!(Sigcontrol, control_flags), + sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_saved_eip = const offset_of!(Sigcontrol, saved_ip), + sc_word = const offset_of!(Sigcontrol, word), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), + tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + pctl_actions = const offset_of!(SigProcControl, actions), + pctl_sender_infos = const offset_of!(SigProcControl, sender_infos), + pctl_pending = const offset_of!(SigProcControl, pending), + pctl = sym PROC_CONTROL_STRUCT, + proc_fd = sym PROC_FD, + proc_call = sym PROC_CALL, + STACK_ALIGN = const 16, + SYS_CALL = const syscall::SYS_CALL, + RTINF_SIZE = const size_of::(), +]); + +asmfunction!(__relibc_internal_rlct_clone_ret -> usize: [" + # Load registers + pop eax + + sub esp, 8 + + mov DWORD PTR [esp], 0x00001F80 + # TODO: ldmxcsr [esp] + mov WORD PTR [esp], 0x037F + fldcw [esp] + + add esp, 8 + + # Call entry point + call eax + + ret +"] <= []); +unsafe extern "C" { + fn __relibc_internal_sigentry_crit_first(); + fn __relibc_internal_sigentry_crit_second(); + fn __relibc_internal_sigentry_crit_third(); +} +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { + if stack.regs.eip == __relibc_internal_sigentry_crit_first as usize { + let stack_ptr = stack.regs.esp as *const usize; + stack.regs.esp = unsafe { stack_ptr.read() }; + stack.regs.eip = unsafe { stack_ptr.sub(1).read() }; + } else if stack.regs.eip == __relibc_internal_sigentry_crit_second as usize + || stack.regs.eip == __relibc_internal_sigentry_crit_third as usize + { + stack.regs.eip = area.tmp_eip; + } + PosixStackt { + sp: stack.regs.esp as *mut (), + size: 0, // TODO + flags: 0, // TODO + } +} +pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) { + let orig_eip = core::mem::replace(&mut stack.regs.eip, __relibc_internal_sigentry as usize); + control.saved_ip.set(orig_eip); + control.saved_archdep_reg.set(stack.regs.eflags); +} +#[unsafe(no_mangle)] +pub unsafe fn manually_enter_trampoline() { + let c = unsafe { &crate::Tcb::current().unwrap().os_specific.control }; + c.control_flags.store( + c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86? + + unsafe { + core::arch::asm!(" + call 2f + jmp 3f + 2: + pop dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}] + jmp __relibc_internal_sigentry + 3: + ", + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + sc_saved_eip = const offset_of!(Sigcontrol, saved_ip), + ); + } +} +/// Get current stack pointer, weak granularity guarantees. +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!("mov {}, esp", out(reg) sp); + } + sp +} + +pub static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); +static PROC_CALL: u64 = ProcCall::Sigdeq as u64; diff --git a/redox-rt/src/arch/mod.rs b/redox-rt/src/arch/mod.rs new file mode 100644 index 0000000000..282f28ed5d --- /dev/null +++ b/redox-rt/src/arch/mod.rs @@ -0,0 +1,27 @@ +#[cfg(target_arch = "aarch64")] +pub use self::aarch64::*; +#[cfg(target_arch = "aarch64")] +pub mod aarch64; + +#[cfg(target_arch = "x86")] +pub use self::i686::*; +#[cfg(target_arch = "x86")] +pub mod i686; + +#[cfg(target_arch = "x86_64")] +pub use self::x86_64::*; +#[cfg(target_arch = "x86_64")] +pub mod x86_64; + +#[cfg(target_arch = "riscv64")] +pub use self::riscv64::*; +#[cfg(target_arch = "riscv64")] +pub mod riscv64; + +#[derive(Debug)] +#[repr(C)] +pub struct ForkScratchpad { + pub cur_filetable_fd: usize, + pub new_proc_fd: usize, + pub new_thr_fd: usize, +} diff --git a/redox-rt/src/arch/riscv64.rs b/redox-rt/src/arch/riscv64.rs new file mode 100644 index 0000000000..a72cc32913 --- /dev/null +++ b/redox-rt/src/arch/riscv64.rs @@ -0,0 +1,654 @@ +use core::cell::SyncUnsafeCell; + +use crate::{ + Tcb, + proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner}, + signal::{PosixStackt, RtSigarea, SigStack, get_sigaltstack, inner_c}, +}; +use core::{mem::offset_of, ptr::NonNull, sync::atomic::Ordering}; +use redox_protocols::protocol::{ProcCall, RtSigInfo}; +use syscall::{data::*, error::*}; + +use super::ForkScratchpad; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub const STACK_TOP: usize = 1 << 38; +pub const STACK_SIZE: usize = 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub tmp_sp: u64, + pub tmp_t1: u64, + pub tmp_t2: u64, + pub tmp_t3: u64, + pub tmp_t4: u64, + pub tmp_a0: u64, + pub tmp_a1: u64, + pub tmp_a2: u64, + pub tmp_a3: u64, + pub tmp_a4: u64, + pub tmp_a7: u64, + + pub pctl: usize, // TODO: remove + pub tmp_ip: u64, + pub tmp_rt_inf: RtSigInfo, + pub tmp_id_inf: u64, + pub altstack_top: usize, + pub altstack_bottom: usize, + pub disable_signals_depth: u64, + pub last_sig_was_restart: bool, + pub last_sigstack: Option>, +} +#[repr(C)] +#[derive(Debug, Default)] +pub struct ArchIntRegs { + pub int_regs: [u64; 31], + pub pc: u64, + pub fp_regs: [u64; 32], + pub fcsr: u32, + _pad: u32, +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = open_via_dup.dup(b"regs/env")?; + + env.tp = 0; + + file.write(&mut env)?; + Ok(()) +} + +unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) +} + +unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) { + let _ = syscall::close(scratchpad.cur_filetable_fd); + unsafe { + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), + new_proc_fd: if scratchpad.new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(scratchpad.new_proc_fd)) + }, + }) + }; +} + +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" + .attribute arch, \"rv64gc\" # rust bug 80608 + addi sp, sp, -200 + sd s0, 0(sp) + sd s1, 8(sp) + sd s2, 16(sp) + sd s3, 24(sp) + sd s4, 32(sp) + sd s5, 40(sp) + sd s6, 48(sp) + sd s7, 56(sp) + sd s8, 64(sp) + sd s9, 72(sp) + sd s10, 80(sp) + sd s11, 88(sp) + sd ra, 96(sp) + + fsd fs0, 104(sp) + fsd fs1, 112(sp) + fsd fs2, 120(sp) + fsd fs3, 128(sp) + fsd fs4, 136(sp) + fsd fs5, 144(sp) + fsd fs6, 152(sp) + fsd fs7, 160(sp) + fsd fs8, 168(sp) + fsd fs9, 176(sp) + fsd fs10, 184(sp) + fsd fs11, 192(sp) + + // a0 is forwarded from this function + mv a1, sp + jal {fork_impl} + + ld s0, 0(sp) + ld s1, 8(sp) + ld s2, 16(sp) + ld s3, 24(sp) + ld s4, 32(sp) + ld s5, 40(sp) + ld s6, 48(sp) + ld s7, 56(sp) + ld s8, 64(sp) + ld s9, 72(sp) + ld s10, 80(sp) + ld s11, 88(sp) + ld ra, 96(sp) + + fld fs0, 104(sp) + fld fs1, 112(sp) + fld fs2, 120(sp) + fld fs3, 128(sp) + fld fs4, 136(sp) + fld fs5, 144(sp) + fld fs6, 152(sp) + fld fs7, 160(sp) + fld fs8, 168(sp) + fld fs9, 176(sp) + fld fs10, 184(sp) + fld fs11, 192(sp) + + addi sp, sp, 200 + ret +"] <= [fork_impl = sym fork_impl]); + +asmfunction!(__relibc_internal_fork_ret: [" + .attribute arch, \"rv64gc\" # rust bug 80608 + + # scratchpad is in a1, move to a0 for child_hook + mv a0, a1 + + jal {child_hook} + + mv a0, zero + + ld s0, 0(sp) + ld s1, 8(sp) + ld s2, 16(sp) + ld s3, 24(sp) + ld s4, 32(sp) + ld s5, 40(sp) + ld s6, 48(sp) + ld s7, 56(sp) + ld s8, 64(sp) + ld s9, 72(sp) + ld s10, 80(sp) + ld s11, 88(sp) + ld ra, 96(sp) + + fld fs0, 104(sp) + fld fs1, 112(sp) + fld fs2, 120(sp) + fld fs3, 128(sp) + fld fs4, 136(sp) + fld fs5, 144(sp) + fld fs6, 152(sp) + fld fs7, 160(sp) + fld fs8, 168(sp) + fld fs9, 176(sp) + fld fs10, 184(sp) + fld fs11, 192(sp) + + addi sp, sp, 200 + ret +"] <= [child_hook = sym child_hook]); + +asmfunction!(__relibc_internal_sigentry: [" + .attribute arch, \"rv64gc\" # rust bug 80608 + // Save some registers + ld t0, -8(tp) // Tcb + sd sp, ({tcb_sa_off} + {sa_tmp_sp})(t0) + sd t1, ({tcb_sa_off} + {sa_tmp_t1})(t0) + sd t2, ({tcb_sa_off} + {sa_tmp_t2})(t0) + sd t3, ({tcb_sa_off} + {sa_tmp_t3})(t0) + sd t4, ({tcb_sa_off} + {sa_tmp_t4})(t0) + ld t4, ({tcb_sa_off} + {sa_off_pctl})(t0) + + // First, select signal, always pick first available bit +99: + // Read first signal word + ld t1, ({tcb_sc_off} + {sc_word})(t0) + srli t2, t1, 32 // bitset to low word + and t1, t1, t2 // masked bitset in low word + beqz t1, 3f + + // Found in first thread signal word + mv t3, x0 +2: andi t2, t1, 1 + bnez t2, 10f + addi t3, t3, 1 + srli t1, t1, 1 + j 2b + + // If no unblocked thread signal was found, check for process. + // This is competitive; we need to atomically check if *we* cleared the process-wide pending + // bit, otherwise restart. +3: lw t1, {pctl_off_pending}(t4) + and t1, t1, t2 + beqz t1, 3f + // Found in first process signal word + li t3, -1 +2: andi t2, t1, 1 + addi t3, t3, 1 + srli t1, t1, 1 + beqz t2, 2b + slli t1, t3, 3 // * 8 == size_of SenderInfo + add t1, t1, t4 + ld t1, {pctl_off_sender_infos}(t1) + sd t1, ({tcb_sa_off} + {sa_tmp_id_inf})(t0) + li t1, 1 + sll t1, t1, t3 + not t1, t1 + addi t2, t4, {pctl_off_pending} + amoand.w.aq t2, t1, (t2) + and t1, t1, t2 + bne t1, t2, 9f + +3: + // Read second signal word - both process and thread simultaneously. + // This must be done since POSIX requires low realtime signals to be picked first. + ld t1, ({tcb_sc_off} + {sc_word} + 8)(t0) + lw t2, ({pctl_off_pending} + 4)(t4) + or t4, t1, t2 + srli t2, t1, 32 + and t4, t2, t4 + beqz t4, 7f + li t3, -1 +2: andi t2, t4, 1 + addi t3, t3, 1 + srli t4, t4, 1 + beqz t2, 2b + li t2, 1 + sll t2, t2, t3 + and t1, t1, t2 + addi t3, t3, 32 + bnez t1, 10f // thread signal + + // otherwise, try (competitively) dequeueing realtime signal + + // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base) + // a7 a0 a1 a2 a3 a4 + + // TODO: This SYS_CALL invocation has not yet been tested due to toolchain issues. + + sd a0, ({tcb_sa_off} + {sa_tmp_a0})(t0) + sd a1, ({tcb_sa_off} + {sa_tmp_a1})(t0) + sd a2, ({tcb_sa_off} + {sa_tmp_a2})(t0) + sd a3, ({tcb_sa_off} + {sa_tmp_a3})(t0) + sd a4, ({tcb_sa_off} + {sa_tmp_a4})(t0) + sd a7, ({tcb_sa_off} + {sa_tmp_a7})(t0) + li a7, {SYS_CALL} + addi a2, t3, -32 + add a1, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig + sd a2, (a1) + li a2, {RTINF_SIZE} + li a3, 1 +1337: + auipc a4, %pcrel_hi({proc_fd}) + addi a4, a4, %pcrel_lo(1337b) + ecall + ld a3, ({tcb_sa_off} + {sa_tmp_a3})(t0) + ld a4, ({tcb_sa_off} + {sa_tmp_a4})(t0) + bnez a0, 99b // assumes error can only be EAGAIN + j 9f + +10: // thread signal. t3 holds signal number + srli t1, t3, 5 + bnez t1, 2f // FIXME senderinfo? + sll t2, t3, 3 // * 8 == size_of SenderInfo + add t2, t2, t0 + ld t2, ({tcb_sc_off} + {sc_sender_infos})(t2) + sd t2, ({tcb_sa_off} + {sa_tmp_id_inf})(t0) +2: andi t4, t3, 31 + li t2, 1 + sll t2, t2, t4 + not t2, t2 + sll t1, t1, 3 + add t1, t1, t0 + addi t1, t1, {tcb_sc_off} + {sc_word} + amoand.w.aq x0, t2, (t1) + addi t3, t3, 64 // indicate signal was targeted at thread + +9: // process signal t3 holds signal number + + // By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or + // not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then + // skip the sigaltstack logic. + ld t4, ({tcb_sa_off} + {sa_off_pctl})(t0) + andi t1, t3, 63 + slli t1, t1, 4 // * 16 == size_of RawAction + add t1, t1, t4 + ld t1, {pctl_off_actions}(t1) + slli t1, t1, 63-58 // SA_ONSTACK in sign bit + bgez t1, 3f + + // If current RSP is above altstack region, switch to altstack + ld t1, ({tcb_sa_off} + {sa_altstack_top})(t0) + bgtu sp, t1, 2f + ld t2, ({tcb_sa_off} + {sa_altstack_bottom})(t0) + bgtu sp, t3, 3f +2: mv sp, t1 +3: + // form mcontext on stack + addi sp, sp, -33 * 8 + fsd f0, (0 * 8)(sp) + fsd f1, (1 * 8)(sp) + fsd f2, (2 * 8)(sp) + fsd f3, (3 * 8)(sp) + fsd f4, (4 * 8)(sp) + fsd f5, (5 * 8)(sp) + fsd f6, (6 * 8)(sp) + fsd f7, (7 * 8)(sp) + fsd f8, (8 * 8)(sp) + fsd f9, (9 * 8)(sp) + fsd f10, (10 * 8)(sp) + fsd f11, (11 * 8)(sp) + fsd f12, (12 * 8)(sp) + fsd f13, (13 * 8)(sp) + fsd f14, (14 * 8)(sp) + fsd f15, (15 * 8)(sp) + fsd f16, (16 * 8)(sp) + fsd f17, (17 * 8)(sp) + fsd f18, (18 * 8)(sp) + fsd f19, (19 * 8)(sp) + fsd f20, (20 * 8)(sp) + fsd f21, (21 * 8)(sp) + fsd f22, (22 * 8)(sp) + fsd f23, (23 * 8)(sp) + fsd f24, (24 * 8)(sp) + fsd f25, (25 * 8)(sp) + fsd f26, (26 * 8)(sp) + fsd f27, (27 * 8)(sp) + fsd f28, (28 * 8)(sp) + fsd f29, (29 * 8)(sp) + fsd f30, (30 * 8)(sp) + fsd f31, (31 * 8)(sp) + csrr t1, fcsr + sw t1, (32 * 8)(sp) + + addi sp, sp, -32 * 8 + sd x1, 0(sp) + ld t1, ({tcb_sa_off} + {sa_tmp_sp})(t0) + sd t1, (1 * 8)(sp) // x2 is sp + sd x3, (2 * 8)(sp) + sd x4, (3 * 8)(sp) + ld t1, ({tcb_sc_off} + {sc_saved_t0})(t0) + sd t1, (4 * 8)(sp) // x5 is t0 + ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t0) + sd t1, (5 * 8)(sp) // x6 is t1 + ld t1, ({tcb_sa_off} + {sa_tmp_t2})(t0) + sd t1, (6 * 8)(sp) // x7 is t2 + sd x8, (7 * 8)(sp) + sd x9, (8 * 8)(sp) + sd x10, (9 * 8)(sp) + sd x11, (10 * 8)(sp) + sd x12, (11 * 8)(sp) + sd x13, (12 * 8)(sp) + sd x14, (13 * 8)(sp) + sd x15, (14 * 8)(sp) + sd x16, (15 * 8)(sp) + sd x17, (16 * 8)(sp) + sd x18, (17 * 8)(sp) + sd x19, (18 * 8)(sp) + sd x20, (19 * 8)(sp) + sd x21, (20 * 8)(sp) + sd x22, (21 * 8)(sp) + sd x23, (22 * 8)(sp) + sd x24, (23 * 8)(sp) + sd x25, (24 * 8)(sp) + sd x26, (25 * 8)(sp) + sd x27, (26 * 8)(sp) + ld t1, ({tcb_sa_off} + {sa_tmp_t3})(t0) + sd t1, (27 * 8)(sp) // t3 is x28 + ld t1, ({tcb_sa_off} + {sa_tmp_t4})(t0) + sd t1, (28 * 8)(sp) // t4 is x29 + sd x30, (29 * 8)(sp) + sd x31, (30 * 8)(sp) + ld t1, ({tcb_sc_off} + {sc_saved_ip})(t0) + sd t1, (31 * 8)(sp) + + // form ucontext + addi sp, sp, -64 + sw t3, 60(sp) + + mv t0, sp + jal {inner} + + addi sp, sp, 64 + + addi t0, sp, 32 * 8 + fld f0, (0 * 8)(t0) + fld f1, (1 * 8)(t0) + fld f2, (2 * 8)(t0) + fld f3, (3 * 8)(t0) + fld f4, (4 * 8)(t0) + fld f5, (5 * 8)(t0) + fld f6, (6 * 8)(t0) + fld f7, (7 * 8)(t0) + fld f8, (8 * 8)(t0) + fld f9, (9 * 8)(t0) + fld f10, (10 * 8)(t0) + fld f11, (11 * 8)(t0) + fld f12, (12 * 8)(t0) + fld f13, (13 * 8)(t0) + fld f14, (14 * 8)(t0) + fld f15, (15 * 8)(t0) + fld f16, (16 * 8)(t0) + fld f17, (17 * 8)(t0) + fld f18, (18 * 8)(t0) + fld f19, (19 * 8)(t0) + fld f20, (20 * 8)(t0) + fld f21, (21 * 8)(t0) + fld f22, (22 * 8)(t0) + fld f23, (23 * 8)(t0) + fld f24, (24 * 8)(t0) + fld f25, (25 * 8)(t0) + fld f26, (26 * 8)(t0) + fld f27, (27 * 8)(t0) + fld f28, (28 * 8)(t0) + fld f29, (29 * 8)(t0) + fld f30, (30 * 8)(t0) + fld f31, (31 * 8)(t0) + lw t1, (32 * 8)(t0) + csrw fcsr, t1 + + ld x1, 0(sp) + // skip sp + // skip gp + ld x4, (3 * 8)(sp) + ld x5, (4 * 8)(sp) + ld x6, (5 * 8)(sp) + ld x7, (6 * 8)(sp) + ld x8, (7 * 8)(sp) + ld x9, (8 * 8)(sp) + ld x10, (9 * 8)(sp) + ld x11, (10 * 8)(sp) + ld x12, (11 * 8)(sp) + ld x13, (12 * 8)(sp) + ld x14, (13 * 8)(sp) + ld x15, (14 * 8)(sp) + ld x16, (15 * 8)(sp) + ld x17, (16 * 8)(sp) + ld x18, (17 * 8)(sp) + ld x19, (18 * 8)(sp) + ld x20, (19 * 8)(sp) + ld x21, (20 * 8)(sp) + ld x22, (21 * 8)(sp) + ld x23, (22 * 8)(sp) + ld x24, (23 * 8)(sp) + ld x25, (24 * 8)(sp) + ld x26, (25 * 8)(sp) + ld x27, (26 * 8)(sp) + ld x28, (27 * 8)(sp) + ld x29, (28 * 8)(sp) + ld x30, (29 * 8)(sp) + ld x31, (30 * 8)(sp) + ld gp, (31 * 8)(sp) // new IP; this clobbers register x3/gp which is ABI reserved + .global __relibc_internal_sigentry_crit_first +__relibc_internal_sigentry_crit_first: + ld sp, (1 * 8)(sp) + .global __relibc_internal_sigentry_crit_second +__relibc_internal_sigentry_crit_second: + jr gp +7: + // A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled. + + // restore stack + ld sp, ({tcb_sa_off} + {sa_tmp_sp})(t0) + + // move saved IP away from control, allowing arch_pre to save us if interrupted. + ld t1, ({tcb_sc_off} + {sc_saved_ip})(t0) + sd t1, ({tcb_sa_off} + {sa_tmp_ip})(t0) + + // restore regs + ld t2, ({tcb_sa_off} + {sa_tmp_t2})(t0) + ld t3, ({tcb_sa_off} + {sa_tmp_t3})(t0) + ld t4, ({tcb_sa_off} + {sa_tmp_t4})(t0) + + // move saved t0 away from control as well + mv t1, t0 + ld t0, ({tcb_sc_off} + {sc_saved_t0})(t0) + + // Re-enable signals. This code can be interrupted after this signal, so we need to define + // 'crit_third'. + ld gp, ({tcb_sc_off} + {sc_control})(t1) + andi gp, gp, ~1 + sd gp, ({tcb_sc_off} + {sc_control})(t1) + + .globl __relibc_internal_sigentry_crit_third +__relibc_internal_sigentry_crit_third: + ld gp, ({tcb_sa_off} + {sa_tmp_ip})(t1) + .globl __relibc_internal_sigentry_crit_fourth +__relibc_internal_sigentry_crit_fourth: + ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1) + .globl __relibc_internal_sigentry_crit_fifth +__relibc_internal_sigentry_crit_fifth: + jr gp + "] <= [ + tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)), + sc_word = const offset_of!(Sigcontrol, word), + sc_saved_t0 = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_saved_ip = const offset_of!(Sigcontrol, saved_ip), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), + sc_control = const offset_of!(Sigcontrol, control_flags), + + tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)), + sa_off_pctl = const offset_of!(SigArea, pctl), + sa_tmp_sp = const offset_of!(SigArea, tmp_sp), + sa_tmp_t1 = const offset_of!(SigArea, tmp_t1), + sa_tmp_t2 = const offset_of!(SigArea, tmp_t2), + sa_tmp_t3 = const offset_of!(SigArea, tmp_t3), + sa_tmp_t4 = const offset_of!(SigArea, tmp_t4), + sa_tmp_a0 = const offset_of!(SigArea, tmp_a0), + sa_tmp_a1 = const offset_of!(SigArea, tmp_a1), + sa_tmp_a2 = const offset_of!(SigArea, tmp_a2), + sa_tmp_a3 = const offset_of!(SigArea, tmp_a3), + sa_tmp_a4 = const offset_of!(SigArea, tmp_a4), + sa_tmp_a7 = const offset_of!(SigArea, tmp_a7), + sa_tmp_ip = const offset_of!(SigArea, tmp_ip), + sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), + sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), + sa_altstack_top = const offset_of!(SigArea, altstack_top), + sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), + + pctl_off_actions = const offset_of!(SigProcControl, actions), + inner = sym inner_c, + pctl_off_pending = const offset_of!(SigProcControl, pending), + pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos), + SYS_CALL = const syscall::SYS_CALL, + RTINF_SIZE = const size_of::(), + proc_fd = sym PROC_FD, +]); + +asmfunction!(__relibc_internal_rlct_clone_ret: [" + ld t0, 0(sp) + ld a0, 8(sp) + ld a1, 16(sp) + ld a2, 24(sp) + ld a3, 32(sp) + ld a4, 40(sp) + ld a5, 48(sp) + addi sp, sp, 56 + + jalr t0 + ret +"] <= []); + +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!( + "mv {}, sp", + out(reg) sp, + options(nomem)); + } + sp +} + +pub unsafe fn manually_enter_trampoline() { + let ctl = unsafe { &Tcb::current().unwrap().os_specific.control }; + + ctl.control_flags.store( + ctl.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + ctl.saved_archdep_reg.set(0); + let ip_location = &ctl.saved_ip as *const _ as usize; + + unsafe { + core::arch::asm!(" + jal 2f + j 3f + 2: + sd ra, 0(t0) + la t0, __relibc_internal_sigentry + jalr x0, t0 + 3: + ", + inout("t0") ip_location => _, out("ra") _); + } +} + +unsafe extern "C" { + fn __relibc_internal_sigentry_crit_first(); + fn __relibc_internal_sigentry_crit_second(); + fn __relibc_internal_sigentry_crit_third(); + fn __relibc_internal_sigentry_crit_fourth(); + fn __relibc_internal_sigentry_crit_fifth(); +} +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { + // It is impossible to update SP and PC atomically. Instead, we abuse the fact that + // signals are disabled in the prologue of the signal trampoline, which allows us to emulate + // atomicity inside the critical section, consisting of one instruction at 'crit_first', and + // one at 'crit_second', see asm. + + if stack.regs.pc == __relibc_internal_sigentry_crit_first as u64 { + // Reexecute 'ld sp, (1 * 8)(sp)' + let stack_ptr = stack.regs.int_regs[1] as *const u64; // x2 + stack.regs.int_regs[1] = unsafe { stack_ptr.add(1).read() }; + // and 'jr gp' steps. + stack.regs.pc = stack.regs.int_regs[2]; + } else if stack.regs.pc == __relibc_internal_sigentry_crit_second as u64 + || stack.regs.pc == __relibc_internal_sigentry_crit_fifth as u64 + { + // just reexecute the jump + stack.regs.pc = stack.regs.int_regs[2]; + } else if stack.regs.pc == __relibc_internal_sigentry_crit_third as u64 { + // ld gp, ({tcb_sa_off} + {sa_tmp_ip})(t1) + stack.regs.int_regs[2] = area.tmp_ip; + // ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1) + stack.regs.int_regs[5] = area.tmp_t1; + // j gp + stack.regs.pc = stack.regs.int_regs[2]; + } else if stack.regs.pc == __relibc_internal_sigentry_crit_fourth as u64 { + // ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1) + stack.regs.int_regs[5] = area.tmp_t1; + // jr gp + stack.regs.pc = stack.regs.int_regs[2]; + } + + get_sigaltstack(area, stack.regs.int_regs[1] as usize).into() +} +pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) { + let orig_pc = core::mem::replace(&mut stack.regs.pc, __relibc_internal_sigentry as u64); + control.saved_ip.set(orig_pc as usize); + control + .saved_archdep_reg + .set(stack.regs.int_regs[4] as usize); // t0 +} +pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); +static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize]; diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs new file mode 100644 index 0000000000..dc0c9ad1d0 --- /dev/null +++ b/redox-rt/src/arch/x86_64.rs @@ -0,0 +1,528 @@ +use core::{ + cell::SyncUnsafeCell, + mem::offset_of, + ptr::NonNull, + sync::atomic::{AtomicU8, Ordering}, +}; + +use syscall::{ + data::{SigProcControl, Sigcontrol}, + error::*, +}; + +use crate::{ + Tcb, + proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner}, + signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, get_sigaltstack, inner_c}, +}; +use redox_protocols::protocol::{ProcCall, RtSigInfo}; + +use super::ForkScratchpad; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub const STACK_TOP: usize = 1 << 47; +pub const STACK_SIZE: usize = 8 * 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub tmp_rip: usize, + pub tmp_rsp: usize, + pub tmp_rax: usize, + pub tmp_rdx: usize, + pub tmp_rdi: usize, + pub tmp_rsi: usize, + pub tmp_r8: usize, + pub tmp_r10: usize, + pub tmp_r12: usize, + pub tmp_rt_inf: RtSigInfo, + pub tmp_id_inf: u64, + + pub altstack_top: usize, + pub altstack_bottom: usize, + pub disable_signals_depth: u64, + pub last_sig_was_restart: bool, + pub last_sigstack: Option>, +} + +#[repr(C, align(16))] +#[derive(Debug, Default)] +pub struct ArchIntRegs { + pub ymm_upper: [u128; 16], + pub fxsave: [u128; 29], + pub r15: usize, // fxsave "available" +0 + pub r14: usize, // available +8 + pub r13: usize, // available +16 + pub r12: usize, // available +24 + pub rbp: usize, // available +32 + pub rbx: usize, // available +40 + pub r11: usize, // outside fxsave, and so on + pub r10: usize, + pub r9: usize, + pub r8: usize, + pub rax: usize, + pub rcx: usize, + pub rdx: usize, + pub rsi: usize, + pub rdi: usize, + pub rflags: usize, + pub rip: usize, + pub rsp: usize, +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = open_via_dup.dup(b"regs/env")?; + + env.fsbase = 0; + env.gsbase = 0; + + file.write(&mut env)?; + Ok(()) +} + +unsafe extern "sysv64" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp, args)) +} + +#[allow(unsafe_op_in_unsafe_fn)] +unsafe extern "sysv64" fn child_hook(scratchpad: &ForkScratchpad) { + let _ = syscall::close(scratchpad.cur_filetable_fd); + crate::child_hook_common(crate::ChildHookCommonArgs { + new_thr_fd: FdGuard::new(scratchpad.new_thr_fd), + new_proc_fd: if scratchpad.new_proc_fd == usize::MAX { + None + } else { + Some(FdGuard::new(scratchpad.new_proc_fd)) + }, + }); +} + +asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: [" + push rbp + mov rbp, rsp + + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + + sub rsp, 16 + + stmxcsr [rsp+16] + fnstcw [rsp+8] + + // rdi: &ForkArgs + // rsi: initial_rsp + mov rsi, rsp + call {fork_impl} + + add rsp, 64 + + pop rbp + ret + +"] <= [fork_impl = sym fork_impl]); +asmfunction!(__relibc_internal_fork_ret: [" + # scratchpad is in rsi, move to rdi for child_hook + mov rdi, rsi + + call {child_hook} + + ldmxcsr [rsp + 16] + mov rcx, [rsp + 8] + + xor rax, rax + + add rsp, 16 + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + + pop rbp + ret +"] <= [child_hook = sym child_hook]); +asmfunction!(__relibc_internal_rlct_clone_ret: [" + # Load registers + pop rax + pop rdi + pop rsi + pop rdx + pop rcx + pop r8 + pop r9 + + mov DWORD PTR [rsp - 8], 0x00001F80 + ldmxcsr [rsp - 8] + mov WORD PTR [rsp - 8], 0x037F + fldcw [rsp - 8] + + # Call entry point + call rax + + ret +"] <= []); + +asmfunction!(__relibc_internal_sigentry: [" + // Save some registers + mov fs:[{tcb_sa_off} + {sa_tmp_rsp}], rsp + mov fs:[{tcb_sa_off} + {sa_tmp_rax}], rax + mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx + mov fs:[{tcb_sa_off} + {sa_tmp_rdi}], rdi + mov fs:[{tcb_sa_off} + {sa_tmp_rsi}], rsi + mov fs:[{tcb_sa_off} + {sa_tmp_r8}], r8 + mov fs:[{tcb_sa_off} + {sa_tmp_r10}], r10 + mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r12 + + // First, select signal, always pick first available bit +1: + // Read standard signal word - first targeting this thread + mov rax, fs:[{tcb_sc_off} + {sc_word}] + mov rdx, rax + shr rdx, 32 + and eax, edx + bsf eax, eax + jnz 2f + + // If no unblocked thread signal was found, check for process. + // This is competitive; we need to atomically check if *we* cleared the process-wide pending + // bit, otherwise restart. + mov eax, [rip + {pctl} + {pctl_off_pending}] + and eax, edx + bsf eax, eax + jz 8f + lea rdi, [rip + {pctl} + {pctl_off_sender_infos}] + mov rdi, [rdi + rax * 8] + lock btr [rip + {pctl} + {pctl_off_pending}], eax + mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi + jc 9f +8: + // Read second signal word - both process and thread simultaneously. + // This must be done since POSIX requires low realtime signals to be picked first. + mov edx, fs:[{tcb_sc_off} + {sc_word} + 8] + mov eax, [rip + {pctl} + {pctl_off_pending} + 4] + or eax, edx + and eax, fs:[{tcb_sc_off} + {sc_word} + 12] + bsf eax, eax + jz 7f + + bt edx, eax // check if signal was sent to thread specifically + jnc 81f + // if so, continue as usual + add eax, 32 + jmp 2f +81: + // otherwise, try (competitively) dequeueing realtime signal + + // SYS_CALL(fd, payload_base, payload_len, metadata_len | (flags << 8), metadata_base) + // rax rdi rsi rdx r10 r8 + + mov r12d, eax + mov rsi, fs:[0] + mov rdi, [rip+{proc_fd}] + add rsi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig + mov rdx, {RTINF_SIZE} + mov [rsi], eax + lea r8, [rip + {proc_call_sigdeq}] + mov r10, 1 + mov eax, {SYS_CALL} + syscall + test eax, eax + jnz 1b // assumes error can only be EAGAIN + lea eax, [r12d + 32] + jmp 9f +2: + mov edx, eax + shr edx, 5 + mov rdi, fs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8] + lock btr fs:[{tcb_sc_off} + {sc_word} + edx * 4], eax + mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi + add eax, 64 // indicate signal was targeted at thread +9: + sub rsp, {REDZONE_SIZE} + and rsp, -{STACK_ALIGN} + + // By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or + // not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then + // skip the sigaltstack logic. + lea rdx, [rip + {pctl} + {pctl_off_actions}] + + mov ecx, eax + and ecx, 63 + + // LEA doesn't support 16x, so just do two x8s. + lea rdx, [rdx + 8 * rcx] + lea rdx, [rdx + 8 * rcx] + + bt qword ptr [rdx], {SA_ONSTACK_BIT} + jnc 4f + + // Otherwise, the altstack is already active. The sigaltstack being disabled, is equivalent + // to setting 'top' to usize::MAX and 'bottom' to 0. + + // If current RSP is above altstack region, switch to altstack + mov rdx, fs:[{tcb_sa_off} + {sa_altstack_top}] + cmp rsp, rdx + cmova rsp, rdx + + // If current RSP is below altstack region, also switch to altstack + cmp rsp, fs:[{tcb_sa_off} + {sa_altstack_bottom}] + cmovbe rsp, rdx + + .p2align 4 +4: + // Now that we have a stack, we can finally start initializing the signal stack! + + push fs:[{tcb_sa_off} + {sa_tmp_rsp}] + push fs:[{tcb_sc_off} + {sc_saved_rip}] + push fs:[{tcb_sc_off} + {sc_saved_rflags}] + + push fs:[{tcb_sa_off} + {sa_tmp_rdi}] + push fs:[{tcb_sa_off} + {sa_tmp_rsi}] + push fs:[{tcb_sa_off} + {sa_tmp_rdx}] + push rcx + push fs:[{tcb_sa_off} + {sa_tmp_rax}] + push fs:[{tcb_sa_off} + {sa_tmp_r8}] + push r9 + push fs:[{tcb_sa_off} + {sa_tmp_r10}] + push r11 + push rbx + push rbp + push fs:[{tcb_sa_off} + {sa_tmp_r12}] + push r13 + push r14 + push r15 + sub rsp, (29 + 16) * 16 // fxsave region minus available bytes + fxsave64 [rsp + 16 * 16] + + // TODO: self-modifying? + cmp byte ptr [rip + {supports_avx}], 0 + je 5f + + // Prefer vextractf128 over vextracti128 since the former only requires AVX version 1. + vextractf128 [rsp + 15 * 16], ymm0, 1 + vextractf128 [rsp + 14 * 16], ymm1, 1 + vextractf128 [rsp + 13 * 16], ymm2, 1 + vextractf128 [rsp + 12 * 16], ymm3, 1 + vextractf128 [rsp + 11 * 16], ymm4, 1 + vextractf128 [rsp + 10 * 16], ymm5, 1 + vextractf128 [rsp + 9 * 16], ymm6, 1 + vextractf128 [rsp + 8 * 16], ymm7, 1 + vextractf128 [rsp + 7 * 16], ymm8, 1 + vextractf128 [rsp + 6 * 16], ymm9, 1 + vextractf128 [rsp + 5 * 16], ymm10, 1 + vextractf128 [rsp + 4 * 16], ymm11, 1 + vextractf128 [rsp + 3 * 16], ymm12, 1 + vextractf128 [rsp + 2 * 16], ymm13, 1 + vextractf128 [rsp + 16], ymm14, 1 + vextractf128 [rsp], ymm15, 1 +5: + mov [rsp - 4], eax + sub rsp, 64 // alloc space for ucontext fields + + mov rdi, rsp + call {inner} + + add rsp, 64 + + fxrstor64 [rsp + 16 * 16] + + cmp byte ptr [rip + {supports_avx}], 0 + je 6f + + vinsertf128 ymm0, ymm0, [rsp + 15 * 16], 1 + vinsertf128 ymm1, ymm1, [rsp + 14 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 13 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 12 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 11 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 10 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 9 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 8 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 7 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 6 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 5 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 4 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 3 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 2 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 16], 1 + vinsertf128 ymm2, ymm2, [rsp], 1 +6: + add rsp, (29 + 16) * 16 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + pop r11 + pop r10 + pop r9 + pop r8 + pop rax + pop rcx + pop rdx + pop rsi + pop rdi + + popfq + pop qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}] + + // x86 lacks atomic instructions for setting both the stack and instruction pointer + // simultaneously, except the slow microcoded IRETQ instruction. Thus, we let the arch_pre + // function emulate atomicity between the pop rsp and indirect jump. + + .globl __relibc_internal_sigentry_crit_first +__relibc_internal_sigentry_crit_first: + + pop rsp + + .globl __relibc_internal_sigentry_crit_second +__relibc_internal_sigentry_crit_second: + jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}] +7: + // A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled. + + // restore flags + mov rax, fs:[0] // load FS base + // TODO: Use lahf/sahf rather than pushfq/popfq? + lea rsp, [rax + {tcb_sc_off} + {sc_saved_rflags}] + popfq + + // restore stack + mov rsp, fs:[{tcb_sa_off} + {sa_tmp_rsp}] + + // move saved RIP away from control, allowing arch_pre to save us if interrupted. + mov rax, fs:[{tcb_sc_off} + {sc_saved_rip}] + mov fs:[{tcb_sa_off} + {sa_tmp_rip}], rax + + // restore regs + mov rax, fs:[{tcb_sa_off} + {sa_tmp_rax}] + mov rdx, fs:[{tcb_sa_off} + {sa_tmp_rdx}] + + // Re-enable signals. This code can be interrupted after this signal, so we need to define + // 'crit_third'. + and qword ptr fs:[{tcb_sc_off} + {sc_control}], ~1 + + .globl __relibc_internal_sigentry_crit_third +__relibc_internal_sigentry_crit_third: + jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}] +"] <= [ + inner = sym inner_c, + sa_tmp_rip = const offset_of!(SigArea, tmp_rip), + sa_tmp_rsp = const offset_of!(SigArea, tmp_rsp), + sa_tmp_rax = const offset_of!(SigArea, tmp_rax), + sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx), + sa_tmp_rdi = const offset_of!(SigArea, tmp_rdi), + sa_tmp_rsi = const offset_of!(SigArea, tmp_rsi), + sa_tmp_r8 = const offset_of!(SigArea, tmp_r8), + sa_tmp_r10 = const offset_of!(SigArea, tmp_r10), + sa_tmp_r12 = const offset_of!(SigArea, tmp_r12), + sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf), + sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf), + sa_altstack_top = const offset_of!(SigArea, altstack_top), + sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), + sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_saved_rip = const offset_of!(Sigcontrol, saved_ip), + sc_word = const offset_of!(Sigcontrol, word), + sc_sender_infos = const offset_of!(Sigcontrol, sender_infos), + sc_control = const offset_of!(Sigcontrol, control_flags), + tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + pctl_off_actions = const offset_of!(SigProcControl, actions), + pctl_off_pending = const offset_of!(SigProcControl, pending), + pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos), + pctl = sym PROC_CONTROL_STRUCT, + supports_avx = sym SUPPORTS_AVX, + REDZONE_SIZE = const 128, + STACK_ALIGN = const 16, + SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 + SYS_CALL = const syscall::SYS_CALL, + proc_call_sigdeq = sym PROC_CALL_SIGDEQ, + RTINF_SIZE = const size_of::(), + proc_fd = sym PROC_FD, +]); + +unsafe extern "C" { + fn __relibc_internal_sigentry_crit_first(); + fn __relibc_internal_sigentry_crit_second(); + fn __relibc_internal_sigentry_crit_third(); +} +/// Fixes some edge cases, and calculates the value for uc_stack. +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt { + // It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is + // almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that + // signals are disabled in the prologue of the signal trampoline, which allows us to emulate + // atomicity inside the critical section, consisting of one instruction at 'crit_first', one at + // 'crit_second', and one at 'crit_third', see asm. + + if stack.regs.rip == __relibc_internal_sigentry_crit_first as usize { + // Reexecute pop rsp and jump steps. This case needs to be different from the one below, + // since rsp has not been overwritten with the previous context's stack, just yet. At this + // point, we know [rsp+0] contains the saved RSP, and [rsp-8] contains the saved RIP. + let stack_ptr = stack.regs.rsp as *const usize; + stack.regs.rsp = unsafe { stack_ptr.read() }; + stack.regs.rip = unsafe { stack_ptr.sub(1).read() }; + } else if stack.regs.rip == __relibc_internal_sigentry_crit_second as usize + || stack.regs.rip == __relibc_internal_sigentry_crit_third as usize + { + // Almost finished, just reexecute the jump before tmp_rip is overwritten by this + // deeper-level signal. + stack.regs.rip = area.tmp_rip; + } + + get_sigaltstack(area, stack.regs.rsp).into() +} + +/// Rearrange the restore-stack and sigarea in a way that makes it look like a new signal was +/// immediately delivered after restoring the allowset to what it was prior to the original signal delivery. +pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) { + let orig_rip = core::mem::replace(&mut stack.regs.rip, __relibc_internal_sigentry as usize); + control.saved_ip.set(orig_rip); + control.saved_archdep_reg.set(stack.regs.rflags); +} + +pub(crate) static SUPPORTS_AVX: AtomicU8 = AtomicU8::new(0); + +// __relibc will be prepended to the name, so no_mangle is fine +#[allow(unsafe_op_in_unsafe_fn)] +#[unsafe(no_mangle)] +pub unsafe fn manually_enter_trampoline() { + let c = &Tcb::current().unwrap().os_specific.control; + c.control_flags.store( + c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86? + + core::arch::asm!(" + lea rax, [rip + 2f] + mov fs:[{tcb_sc_off} + {sc_saved_rip}], rax + jmp __relibc_internal_sigentry + 2: + ", + out("rax") _, + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + sc_saved_rip = const offset_of!(Sigcontrol, saved_ip), + ); +} + +/// Get current stack pointer, weak granularity guarantees. +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!("mov {}, rsp", out(reg) sp); + } + sp +} + +static PROC_CALL_SIGDEQ: u64 = ProcCall::Sigdeq as u64; +pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX); diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs new file mode 100644 index 0000000000..12835a6a7a --- /dev/null +++ b/redox-rt/src/lib.rs @@ -0,0 +1,313 @@ +#![no_std] +#![allow(internal_features)] +#![deny(unsafe_op_in_unsafe_fn)] +#![feature(core_intrinsics, int_roundings, slice_ptr_get, sync_unsafe_cell)] +#![forbid(unreachable_patterns)] + +use core::cell::UnsafeCell; + +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] +use generic_rt::ExpectTlsFree; // not used on aarch64 or riscv64 +use generic_rt::GenericTcb; +use redox_protocols::protocol::ProcMeta; +use syscall::Sigcontrol; + +use self::{ + proc::{FdGuard, FdGuardUpper, STATIC_PROC_INFO}, + sync::Mutex, +}; + +extern crate alloc; + +#[macro_export] +macro_rules! asmfunction( + ($name:ident $(($($arg:ty),*))? $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => { + ::core::arch::global_asm!(concat!(" + .p2align 4 + .section .text.", stringify!($name), ", \"ax\", @progbits + .globl ", stringify!($name), " + .type ", stringify!($name), ", @function + ", stringify!($name), ": + ", $($asmstmt, "\n",)* " + .size ", stringify!($name), ", . - ", stringify!($name), " + "), $($decl = $(sym $symname)?$(const $constval)?),*); + + unsafe extern "C" { + pub fn $name($($(_: $arg),*)?) $(-> $ret)?; + } + } +); + +pub mod arch; +pub mod proc; + +// TODO: Replace auxvs with a non-stack-based interface, but keep getauxval for compatibility +#[path = "../../src/platform/auxv_defs.rs"] +pub mod auxv_defs; + +pub mod signal; +pub mod sync; +pub mod sys; +pub mod thread; + +#[derive(Debug, Default)] +pub struct RtTcb { + pub control: Sigcontrol, + pub arch: UnsafeCell, + pub thr_fd: UnsafeCell>, +} +impl RtTcb { + pub fn current() -> &'static Self { + unsafe { &Tcb::current().unwrap().os_specific } + } + pub fn thread_fd(&self) -> &FdGuardUpper { + unsafe { (&*self.thr_fd.get()).as_ref().unwrap() } + } +} + +pub type Tcb = GenericTcb; + +/// OS and architecture specific code to activate TLS - Redox aarch64 +#[allow(unsafe_op_in_unsafe_fn)] +#[cfg(target_arch = "aarch64")] +pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) { + // Uses ABI page + let abi_ptr = tls_end - tls_len - 16; + core::ptr::write(abi_ptr as *mut usize, tls_end); + core::arch::asm!( + "msr tpidr_el0, {}", + in(reg) abi_ptr, + ); +} + +/// OS and architecture specific code to activate TLS - Redox x86 +#[allow(unsafe_op_in_unsafe_fn)] +#[cfg(target_arch = "x86")] +pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end: usize, _tls_len: usize) { + let mut env = syscall::EnvRegisters::default(); + + let file = tcb + .thread_fd() + .dup(b"regs/env") + .expect_notls("failed to open handle for process registers"); + + file.read(&mut env).expect_notls("failed to read gsbase"); + + env.gsbase = tls_end as u32; + + file.write(&env).expect_notls("failed to write gsbase"); +} + +/// OS and architecture specific code to activate TLS - Redox x86_64 +#[allow(unsafe_op_in_unsafe_fn)] +#[cfg(target_arch = "x86_64")] +pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end_and_tcb_start: usize, _tls_len: usize) { + let mut env = syscall::EnvRegisters::default(); + + let file = tcb + .thread_fd() + .dup(b"regs/env") + .expect_notls("failed to open handle for process registers"); + + file.read(&mut env).expect_notls("failed to read fsbase"); + + env.fsbase = tls_end_and_tcb_start as u64; + + file.write(&env).expect_notls("failed to write fsbase"); +} + +/// OS and architecture specific code to activate TLS - Redox riscv64 +#[allow(unsafe_op_in_unsafe_fn)] +#[cfg(target_arch = "riscv64")] +pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) { + // tp points to static tls block + // FIXME limited to a single initial master + let tls_start = tls_end - tls_len; + let abi_ptr = tls_start - 8; + core::ptr::write(abi_ptr as *mut usize, tls_end); + core::arch::asm!( + "mv tp, {}", + in(reg) tls_start + ); +} + +/// Initialize redox-rt in situations where relibc is not used +#[allow(unsafe_op_in_unsafe_fn)] +#[cfg(not(feature = "proc"))] +pub unsafe fn initialize_freestanding(this_thr_fd: FdGuardUpper) -> &'static FdGuardUpper { + // TODO: This code is a hack! Integrate the ld_so TCB code into generic-rt, and then use that + // (this function will need pointers to the ELF structs normally passed in auxvs), so the TCB + // is initialized properly. + + // TODO: TLS + let page = { + &mut *(syscall::fmap( + !0, + &syscall::Map { + offset: 0, + size: syscall::PAGE_SIZE, + flags: syscall::MapFlags::PROT_READ + | syscall::MapFlags::PROT_WRITE + | syscall::MapFlags::MAP_PRIVATE, + address: 0, + }, + ) + .unwrap() as *mut Tcb) + }; + page.tcb_ptr = page; + page.tcb_len = syscall::PAGE_SIZE; + page.tls_end = (page as *mut Tcb).cast(); + + // Make sure to use ptr::write to prevent dropping the existing FdGuard + page.os_specific.thr_fd.get().write(Some(this_thr_fd)); + + #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + unsafe { + let tcb_addr = page as *mut Tcb as usize; + tcb_activate(&page.os_specific, tcb_addr, 0) + } + #[cfg(target_arch = "aarch64")] + unsafe { + let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr); + core::arch::asm!("msr tpidr_el0, {}", in(reg) abi_ptr); + } + #[cfg(target_arch = "riscv64")] + unsafe { + let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr) as usize; + core::arch::asm!("mv tp, {}", in(reg) (abi_ptr + 8)); + } + initialize(); + + (*page.os_specific.thr_fd.get()).as_ref().unwrap() +} +pub(crate) fn read_proc_meta(proc: &FdGuardUpper) -> syscall::Result { + let mut bytes = [0_u8; size_of::()]; + proc.read(&mut bytes)?; + Ok(*plain::from_bytes::(&bytes).unwrap()) +} +pub unsafe fn initialize( + #[cfg(feature = "proc")] proc_fd: FdGuardUpper, + #[cfg(feature = "proc")] ns_fd: Option, +) { + #[cfg(feature = "proc")] + let metadata = read_proc_meta(&proc_fd).unwrap(); + + #[cfg(not(feature = "proc"))] + // Bootstrap mode, don't associate proc fds with PIDs + let metadata = ProcMeta::default(); + + #[cfg(feature = "proc")] + { + unsafe { crate::arch::PROC_FD.get().write(proc_fd.as_raw_fd()) }; + } + + unsafe { + STATIC_PROC_INFO.get().write(StaticProcInfo { + pid: metadata.pid, + + #[cfg(feature = "proc")] + proc_fd: Some(proc_fd), + + #[cfg(not(feature = "proc"))] + proc_fd: None, + }) + }; + + #[cfg(feature = "proc")] + { + *DYNAMIC_PROC_INFO.lock() = DynamicProcInfo { + pgid: metadata.pgid, + ruid: metadata.ruid, + euid: metadata.euid, + suid: metadata.suid, + egid: metadata.egid, + rgid: metadata.rgid, + sgid: metadata.sgid, + ns_fd, + }; + } +} + +pub(crate) struct StaticProcInfo { + pid: u32, + proc_fd: Option, +} +pub struct DynamicProcInfo { + pub pgid: u32, + pub euid: u32, + pub suid: u32, + pub ruid: u32, + pub egid: u32, + pub rgid: u32, + pub sgid: u32, + pub ns_fd: Option, +} + +static DYNAMIC_PROC_INFO: Mutex = Mutex::new(DynamicProcInfo { + pgid: u32::MAX, + ruid: u32::MAX, + euid: u32::MAX, + suid: u32::MAX, + rgid: u32::MAX, + egid: u32::MAX, + sgid: u32::MAX, + ns_fd: None, +}); + +#[inline] +pub(crate) fn static_proc_info() -> &'static StaticProcInfo { + unsafe { &*STATIC_PROC_INFO.get() } +} +#[inline] +pub fn current_proc_fd() -> &'static FdGuardUpper { + let info = static_proc_info(); + info.proc_fd.as_ref().unwrap() +} +#[inline] +pub fn current_namespace_fd() -> syscall::Result { + DYNAMIC_PROC_INFO + .lock() + .ns_fd + .as_ref() + .map(|g| g.as_raw_fd()) + .ok_or(syscall::Error::new(syscall::ENOENT)) +} +struct ChildHookCommonArgs { + new_thr_fd: FdGuard, + new_proc_fd: Option, +} + +unsafe fn child_hook_common(args: ChildHookCommonArgs) { + let new_thr_fd = args.new_thr_fd.to_upper().unwrap(); + let new_proc_fd = args.new_proc_fd.map(|x| x.to_upper().unwrap()); + + // TODO: just pass PID to child rather than obtaining it via IPC? + #[cfg(feature = "proc")] + let metadata = read_proc_meta( + new_proc_fd + .as_ref() + .expect("must be present with proc feature"), + ) + .unwrap(); + + #[cfg(not(feature = "proc"))] + let metadata = ProcMeta::default(); + + if let Some(proc_fd) = &new_proc_fd { + unsafe { crate::arch::PROC_FD.get().write(proc_fd.as_raw_fd()) }; + } + + let old_proc_fd = unsafe { + STATIC_PROC_INFO + .get() + .replace(StaticProcInfo { + pid: metadata.pid, + proc_fd: new_proc_fd, + }) + .proc_fd + }; + drop(old_proc_fd); + + let old_thr_fd = unsafe { RtTcb::current().thr_fd.get().replace(Some(new_thr_fd)) }; + drop(old_thr_fd); +} diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs new file mode 100644 index 0000000000..48cce3453d --- /dev/null +++ b/redox-rt/src/proc.rs @@ -0,0 +1,1190 @@ +use core::{cell::SyncUnsafeCell, cmp, fmt::Debug, ops::Range}; + +use crate::{ + DYNAMIC_PROC_INFO, RtTcb, StaticProcInfo, + arch::*, + auxv_defs::*, + read_proc_meta, + sys::{fstat, open, proc_call, thread_call}, +}; +use redox_protocols::protocol::{ProcCall, ThreadCall}; + +use alloc::{boxed::Box, vec}; + +use goblin::elf::header::ET_DYN; +//TODO: allow use of either 32-bit or 64-bit programs +#[cfg(target_pointer_width = "32")] +use goblin::elf32::{ + header::Header, + program_header::program_header32::{PF_R, PF_W, PF_X, PT_INTERP, PT_LOAD, ProgramHeader}, +}; +#[cfg(target_pointer_width = "64")] +use goblin::elf64::{ + header::Header, + program_header::program_header64::{PF_R, PF_W, PF_X, PT_INTERP, PT_LOAD, ProgramHeader}, +}; + +use syscall::{ + CallFlags, F_GETFD, GrantDesc, GrantFlags, MAP_FIXED_NOREPLACE, MAP_SHARED, Map, O_CLOEXEC, + PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE, SetSighandlerData, + error::*, + flag::{MapFlags, SEEK_SET}, +}; + +pub enum FexecResult { + Interp { + path: Box<[u8]>, + interp_override: InterpOverride, + }, +} +pub struct InterpOverride { + phdrs_vaddr: usize, + at_entry: usize, + at_phnum: usize, + at_phent: usize, + name: Box<[u8]>, + min_mmap_addr: usize, + grants_fd: usize, +} + +pub struct ExtraInfo<'a> { + pub cwd: Option<&'a [u8]>, + // POSIX states that while sigactions are reset, ignored sigactions will remain ignored. + pub sigignmask: u64, + // POSIX also states that the sigprocmask must be preserved across execs. + pub sigprocmask: u64, + /// File mode creation mask (POSIX) + pub umask: u32, + /// Thread handle + pub thr_fd: usize, + /// Process handle + pub proc_fd: usize, + /// Namespace handle + pub ns_fd: Option, + /// CWD handle + pub cwd_fd: Option, +} + +pub fn fexec_impl( + image_file: FdGuardUpper, + thread_fd: &FdGuardUpper, + proc_fd: &FdGuardUpper, + path: &[u8], + args: &[&[u8]], + envs: &[&[u8]], + extrainfo: &ExtraInfo, + interp_override: Option, +) -> Result { + // Here, we do the minimum part of loading an application, which is what the kernel used to do. + // We load the executable into memory (albeit at different offsets in this executable), fix + // some misalignments, and then switch address space. + + let mut header_bytes = [0_u8; size_of::
()]; + pread_all(&image_file, 0, &mut header_bytes)?; + let header = Header::from_bytes(&header_bytes); + + let grants_fd = if let Some(interp) = interp_override.as_ref() { + FdGuard::new(interp.grants_fd).to_upper()? + } else { + let current_addrspace_fd = thread_fd.dup(b"addrspace")?; + current_addrspace_fd.dup(b"empty")?.to_upper()? + }; + + // Never allow more than 1 MiB of program headers. + const MAX_PH_SIZE: usize = 1024 * 1024; + let phentsize = u64::from(header.e_phentsize) as usize; + let phnum = u64::from(header.e_phnum) as usize; + let pheaders_size = phentsize + .saturating_mul(phnum) + .saturating_add(size_of::
()); + + if pheaders_size > MAX_PH_SIZE { + return Err(Error::new(E2BIG)); + } + let mut phs_raw = vec![0_u8; pheaders_size]; + phs_raw[..size_of::
()].copy_from_slice(&header_bytes); + let phs = &mut phs_raw[size_of::
()..]; + + // TODO: Remove clone, but this would require more as_refs and as_muts + let mut min_mmap_addr = interp_override + .as_ref() + .map(|interp| interp.min_mmap_addr) + .unwrap_or(PAGE_SIZE); + let mut update_min_mmap_addr = |addr: usize, size: usize| { + min_mmap_addr = cmp::max(min_mmap_addr, (addr + size).next_multiple_of(PAGE_SIZE)); + }; + + pread_all(&image_file, u64::from(header.e_phoff), phs).map_err(|_| Error::new(EIO))?; + + let mut span: Option> = None; + for ph_idx in 0..phnum { + let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize]; + let segment: &ProgramHeader = + plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?; + if segment.p_type != PT_LOAD { + continue; + } + + let voff = segment.p_vaddr as usize % PAGE_SIZE; + let vaddr = segment.p_vaddr as usize - voff; + let vsize = (segment.p_memsz as usize + voff).next_multiple_of(segment.p_align as usize); + let b = vaddr..vaddr + vsize; + + span = Some(if let Some(a) = span { + a.start.min(b.start)..a.end.max(b.end) + } else { + b + }); + } + let span = span.expect("ELF executables must contain at least one `PT_LOAD` segment"); + let span_size = (span.end - span.start).next_multiple_of(PAGE_SIZE); + + let base_addr = if header.e_type == ET_DYN { + // PIE + let addr = mmap_anon_remote(&grants_fd, 0, 0, span_size, MapFlags::PROT_NONE)?; + update_min_mmap_addr(addr, span_size); + addr + } else { + mmap_anon_remote( + &grants_fd, + 0, + span.start, + span_size, + MapFlags::MAP_FIXED_NOREPLACE, + )?; + update_min_mmap_addr(span.start, span_size); + 0 + }; + + let mut phdrs_vaddr = 0; + let mut interpreter = None; + + for ph_idx in 0..phnum { + let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize]; + let segment: &ProgramHeader = + plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?; + let mut flags = if segment.p_flags & PF_R == PF_R { + syscall::PROT_READ + } else { + syscall::PROT_NONE + }; + + // W ^ X. If it is executable, do not allow it to be writable, even if requested + if segment.p_flags & PF_X == PF_X { + flags |= syscall::PROT_EXEC; + } else if segment.p_flags & PF_W == PF_W { + flags |= syscall::PROT_WRITE; + } + + match segment.p_type { + // PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice. + PT_INTERP => { + let mut interp = vec![0_u8; segment.p_filesz as usize]; + pread_all(&image_file, u64::from(segment.p_offset), &mut interp)?; + + interpreter = Some(interp.into_boxed_slice()); + } + PT_LOAD => { + let voff = segment.p_vaddr as usize % PAGE_SIZE; + let vaddr = segment.p_vaddr as usize - voff; + let filesz = segment.p_filesz as usize; + + let total_page_count = (segment.p_memsz as usize + voff).div_ceil(PAGE_SIZE); + + // The case where segments overlap so that they share one page, is not handled. + // TODO: Should it be? + + if segment.p_filesz > segment.p_memsz { + return Err(Error::new(ENOEXEC)); + } + + mmap_anon_remote( + &grants_fd, + 0, + base_addr + vaddr, + total_page_count * PAGE_SIZE, + flags | MapFlags::MAP_FIXED, + )?; + + if segment.p_offset <= header.e_phoff + && header.e_phoff < segment.p_offset + segment.p_filesz + { + phdrs_vaddr = + (header.e_phoff - segment.p_offset + segment.p_vaddr) as usize + base_addr; + } + + // TODO: Attempt to mmap with MAP_PRIVATE directly from the image file instead. + + if filesz > 0 { + let (_guard, dst_memory) = unsafe { + MmapGuard::map_mut_anywhere( + &grants_fd, + base_addr + vaddr, // offset + (voff + filesz).next_multiple_of(PAGE_SIZE), // size + )? + }; + pread_all( + &image_file, + u64::from(segment.p_offset), + &mut dst_memory[voff..voff + filesz], + )?; + } + } + _ => continue, + } + } + + if let Some(interpreter_path) = interpreter { + return Ok(FexecResult::Interp { + path: interpreter_path, + interp_override: InterpOverride { + at_entry: base_addr + header.e_entry as usize, + at_phnum: phnum, + at_phent: phentsize, + phdrs_vaddr, + name: path.into(), + min_mmap_addr, + grants_fd: grants_fd.take(), + }, + }); + } + + mmap_anon_remote( + &grants_fd, + 0, + STACK_TOP - STACK_SIZE, + STACK_SIZE, + MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_FIXED_NOREPLACE, + )?; + + let mut sp = STACK_TOP; + let mut stack_page = Option::::None; + + let mut push = |word: usize| -> Result<()> { + let old_page_no = sp / PAGE_SIZE; + sp -= size_of::(); + let new_page_no = sp / PAGE_SIZE; + let new_page_off = sp % PAGE_SIZE; + + let page = if let Some(ref mut page) = stack_page + && old_page_no == new_page_no + { + page + } else if let Some(ref mut stack_page) = stack_page { + stack_page.remap(new_page_no * PAGE_SIZE, PROT_READ | PROT_WRITE)?; + stack_page + } else { + let new = MmapGuard::map( + &grants_fd, + &Map { + offset: new_page_no * PAGE_SIZE, + size: PAGE_SIZE, + flags: PROT_READ | PROT_WRITE, + address: 0, // let kernel decide + }, + )?; + + stack_page.insert(new) + }; + + unsafe { + page.as_mut_ptr_slice() + .as_mut_ptr() + .add(new_page_off) + .cast::() + .write(word); + } + + Ok(()) + }; + + push(0)?; + push(AT_NULL)?; + if let Some(ref r#override) = interp_override { + push(r#override.at_entry)?; + push(AT_ENTRY)?; + push(base_addr)?; + push(AT_BASE)?; + push(r#override.phdrs_vaddr)?; + push(AT_PHDR)?; + } else { + push(base_addr + header.e_entry as usize)?; + push(AT_ENTRY)?; + push(phdrs_vaddr)?; + push(AT_PHDR)?; + } + push( + interp_override + .as_ref() + .map_or(header.e_phnum as usize, |o| o.at_phnum), + )?; + push(AT_PHNUM)?; + push( + interp_override + .as_ref() + .map_or(header.e_phentsize as usize, |o| o.at_phent), + )?; + push(AT_PHENT)?; + + let total_args_envs_auxvpointee_size = args.iter().map(|arg| arg.len() + 1).sum::() + + envs.iter().map(|env| env.len() + 1).sum::() + + extrainfo.cwd.map_or(0, |s| s.len() + 1); + let args_envs_size_aligned = total_args_envs_auxvpointee_size.next_multiple_of(PAGE_SIZE); + let target_args_env_address = mmap_anon_remote( + &grants_fd, + 0, + 0, + args_envs_size_aligned, + MapFlags::PROT_READ | MapFlags::PROT_WRITE, + )?; + update_min_mmap_addr(target_args_env_address, args_envs_size_aligned); + + let mut offset = 0; + + let mut argc = 0; + + { + let mut append = |source_slice: &[u8]| -> Result { + // TODO + let address = target_args_env_address + offset; + + if !source_slice.is_empty() { + let containing_page = address.div_floor(PAGE_SIZE) * PAGE_SIZE; + let displacement = address - containing_page; + let size = source_slice.len() + displacement; + let aligned_size = size.next_multiple_of(PAGE_SIZE); + + let (_guard, memory) = unsafe { + MmapGuard::map_mut_anywhere(&grants_fd, containing_page, aligned_size)? + }; + memory[displacement..][..source_slice.len()].copy_from_slice(source_slice); + } + + offset += source_slice.len() + 1; + Ok(address) + }; + + if let Some(cwd) = extrainfo.cwd { + push(append(cwd)?)?; + push(AT_REDOX_INITIAL_CWD_PTR)?; + push(cwd.len())?; + push(AT_REDOX_INITIAL_CWD_LEN)?; + } + + #[cfg(target_pointer_width = "32")] + { + push((extrainfo.sigignmask >> 32) as usize)?; + push(AT_REDOX_INHERITED_SIGIGNMASK_HI)?; + } + push(extrainfo.sigignmask as usize)?; + push(AT_REDOX_INHERITED_SIGIGNMASK)?; + #[cfg(target_pointer_width = "32")] + { + push((extrainfo.sigprocmask >> 32) as usize)?; + push(AT_REDOX_INHERITED_SIGPROCMASK_HI)?; + } + push(extrainfo.sigprocmask as usize)?; + push(AT_REDOX_INHERITED_SIGPROCMASK)?; + + push(extrainfo.umask as usize)?; + push(AT_REDOX_UMASK)?; + + push(extrainfo.thr_fd as usize)?; + push(AT_REDOX_THR_FD)?; + push(extrainfo.proc_fd as usize)?; + push(AT_REDOX_PROC_FD)?; + push(extrainfo.ns_fd.unwrap_or(usize::MAX))?; + push(AT_REDOX_NS_FD)?; + push(extrainfo.cwd_fd.unwrap_or(usize::MAX))?; + push(AT_REDOX_CWD_FD)?; + + push(0)?; + + for env in envs.iter().rev() { + push(append(env)?)?; + } + + push(0)?; + + for arg in args.iter().rev() { + push(append(arg)?)?; + argc += 1; + } + } + + push(argc)?; + + if let Ok(sighandler_fd) = thread_fd.dup(b"sighandler") { + let _ = sighandler_fd.write(&SetSighandlerData { + user_handler: 0, + excp_handler: 0, + thread_control_addr: 0, + proc_control_addr: 0, + }); + // TODO: sync with procmgr + } + + // TODO: Restore old name if exec failed? + { + let mut buf = [0; 32]; + let new_name = interp_override.as_ref().map_or(path, |o| &o.name); + let len = new_name.len().min(32); + buf[..len].copy_from_slice(&new_name[..len]); + // XXX: takes &mut [] since it can mutate, but we could unsafe{}ly pass it directly + // otherwise + let _ = proc_call( + proc_fd.as_raw_fd(), + &mut buf, + CallFlags::empty(), + &[ProcCall::Rename as u64], + ); + } + + // TODO: Error handling + let _ = proc_call( + proc_fd.as_raw_fd(), + &mut [], + CallFlags::empty(), + &[ProcCall::DisableSetpgid as u64], + ); + + if interp_override.is_some() { + let mmap_min_fd = grants_fd.dup(b"mmap-min-addr")?; + let _ = mmap_min_fd.write(&usize::to_ne_bytes(min_mmap_addr)); + } + + let addrspace_selection_fd = thread_fd.dup(b"current-addrspace")?.to_upper()?; + + let _ = addrspace_selection_fd.write(&create_set_addr_space_buf( + grants_fd.as_raw_fd(), + base_addr + header.e_entry as usize, + sp, + )); + + // Close all O_CLOEXEC file descriptors. TODO: close_range? + { + // NOTE: This approach of implementing O_CLOEXEC will not work in multithreaded + // scenarios. While execve() is undefined according to POSIX if there exist sibling + // threads, it could still be allowed by keeping certain file descriptors and instead + // set the active file table. + let files_fd = syscall::dup(thread_fd.as_raw_fd(), b"filetable-binary")?; + let mut files_reader = FileBufReader::from_fd(files_fd); + loop { + let fd = match files_reader.read_le_u64()? { + None => break, + Some(fd) => fd, + }; + let fd = usize::try_from(fd).unwrap(); + + if fd == addrspace_selection_fd.as_raw_fd() || fd == files_fd { + continue; // Will be closed below + } + + let flags = syscall::fcntl(fd, F_GETFD, 0)?; + + if flags & O_CLOEXEC == O_CLOEXEC { + let _ = syscall::close(fd); + } + } + } + + unsafe { + deactivate_tcb(&thread_fd)?; + } + + // Dropping this FD will cause the address space switch. + drop(addrspace_selection_fd); + + unreachable!(); +} +fn write_usizes(fd: &FdGuardUpper, usizes: [usize; N]) -> Result { + fd.write(unsafe { plain::as_bytes(&usizes) }) +} +pub fn mmap_remote( + addrspace_fd: &FdGuardUpper, + fd: &FdGuardUpper, + offset: usize, + dst_addr: usize, + len: usize, + flags: MapFlags, +) -> Result { + write_usizes( + addrspace_fd, + [ + // op + syscall::flag::ADDRSPACE_OP_MMAP, + // fd + fd.as_raw_fd(), + // "offset" + offset, + // address + dst_addr, + // size + len, + // flags + flags.bits(), + ], + ) +} +pub fn mmap_anon_remote( + addrspace_fd: &FdGuardUpper, + offset: usize, + dst_addr: usize, + len: usize, + flags: MapFlags, +) -> Result { + write_usizes( + addrspace_fd, + [ + // op + syscall::flag::ADDRSPACE_OP_MMAP, + // fd + !0, + // "offset" + offset, + // address + dst_addr, + // size + len, + // flags + flags.bits(), + ], + ) +} +pub fn mprotect_remote( + addrspace_fd: &FdGuardUpper, + addr: usize, + len: usize, + flags: MapFlags, +) -> Result<()> { + write_usizes( + addrspace_fd, + [ + // op + syscall::flag::ADDRSPACE_OP_MPROTECT, + // address + addr, + // size + len, + // flags + flags.bits(), + ], + )?; + Ok(()) +} +pub fn munmap_remote(addrspace_fd: &FdGuardUpper, addr: usize, len: usize) -> Result<()> { + write_usizes( + addrspace_fd, + [ + // op + syscall::flag::ADDRSPACE_OP_MUNMAP, + // address + addr, + // size + len, + ], + )?; + Ok(()) +} +pub fn munmap_transfer( + src: &FdGuardUpper, + dst: &FdGuardUpper, + src_addr: usize, + dst_addr: usize, + len: usize, + flags: MapFlags, +) -> Result<()> { + write_usizes( + dst, + [ + // op + syscall::flag::ADDRSPACE_OP_TRANSFER, + // fd + src.as_raw_fd(), + // "offset" (source address) + src_addr, + // address + dst_addr, + // size + len, + // flags + (flags | MapFlags::MAP_FIXED_NOREPLACE).bits(), + ], + )?; + Ok(()) +} +fn pread_all(fd: &FdGuardUpper, offset: u64, buf: &mut [u8]) -> Result<()> { + fd.lseek(offset as isize, SEEK_SET)?; + + let mut total_bytes_read = 0; + + while total_bytes_read < buf.len() { + total_bytes_read += match fd.read(&mut buf[total_bytes_read..])? { + 0 => return Err(Error::new(ENOEXEC)), + bytes_read => bytes_read, + } + } + Ok(()) +} + +pub struct MmapGuard<'a> { + fd: &'a FdGuardUpper, + base: usize, + size: usize, +} +impl<'a> MmapGuard<'a> { + pub fn map(fd: &'a FdGuardUpper, map: &Map) -> Result { + let base = unsafe { syscall::fmap(fd.as_raw_fd(), map)? }; + Ok(Self { + fd, + size: map.size, + base, + }) + } + pub fn remap(&mut self, offset: usize, mut flags: MapFlags) -> Result<()> { + flags.remove(MapFlags::MAP_FIXED_NOREPLACE); + flags.insert(MapFlags::MAP_FIXED); + + let _new_base = unsafe { + syscall::fmap( + self.fd.as_raw_fd(), + &Map { + offset, + size: self.size, + flags, + address: self.base, + }, + )? + }; + + Ok(()) + } + pub unsafe fn map_mut_anywhere( + fd: &'a FdGuardUpper, + offset: usize, + size: usize, + ) -> Result<(Self, &'a mut [u8])> { + let mut this = Self::map( + fd, + &Map { + size, + offset, + address: 0, + flags: PROT_READ | PROT_WRITE, + }, + )?; + let slice = unsafe { &mut *this.as_mut_ptr_slice() }; + + Ok((this, slice)) + } + pub fn addr(&self) -> usize { + self.base + } + pub fn len(&self) -> usize { + self.size + } + pub fn as_mut_ptr_slice(&mut self) -> *mut [u8] { + core::ptr::slice_from_raw_parts_mut(self.base as *mut u8, self.size) + } + pub fn take(mut self) { + self.size = 0; + } +} +impl<'a> Drop for MmapGuard<'a> { + fn drop(&mut self) { + if self.size != 0 { + let _ = unsafe { syscall::funmap(self.base, self.size) }; + } + } +} + +struct FileBufReader { + fd: usize, + buf: [u8; 8192], + pos: usize, + cap: usize, +} + +impl FileBufReader { + pub fn from_fd(fd: usize) -> FileBufReader { + FileBufReader { + fd, + buf: [0; 8192], + pos: 0, + cap: 0, + } + } +} + +impl FileBufReader { + fn read_le_u64(&mut self) -> Result> { + if self.pos >= self.cap { + debug_assert!(self.pos == self.cap); + self.cap = crate::sys::posix_read(self.fd, &mut self.buf)?; + self.pos = 0; + } + + if self.cap == 0 { + return Ok(None); + } + + if self.cap - self.pos < 8 { + unreachable!(); + } + + let num = u64::from_le_bytes(self.buf[self.pos..self.pos + 8].try_into().unwrap()); + + self.pos += 8; + + Ok(Some(num)) + } +} + +#[repr(transparent)] +pub struct FdGuard { + fd: usize, +} +pub type FdGuardUpper = FdGuard; +impl FdGuard { + #[inline] + pub fn new(fd: usize) -> Self { + Self { fd } + } + + #[inline] + pub fn open>(path: T, flags: usize) -> Result { + open(path, flags).map(Self::new) + } + + #[inline] + pub fn to_upper(self) -> Result { + // Move to upper table if necessary + let fd = if self.fd & syscall::UPPER_FDTBL_TAG == 0 { + //TODO: use F_DUPFD_CLOEXEC? + let fd = syscall::fcntl(self.fd, syscall::F_DUPFD, syscall::UPPER_FDTBL_TAG)?; + drop(self); + fd + } else { + self.take() + }; + Ok(FdGuard:: { fd }) + } + + // Not implemented for UPPER to prevent misuse + #[inline] + pub fn as_c_fd(&self) -> Option { + i32::try_from(self.fd).ok() + } +} +impl FdGuard { + #[inline] + pub fn openat>( + &self, + path: T, + flags: usize, + fcntl_flags: usize, + ) -> Result> { + syscall::openat(self.fd, path, flags, fcntl_flags).map(FdGuard::new) + } + #[inline] + pub fn dup(&self, buf: &[u8]) -> Result> { + syscall::dup(self.fd, buf).map(FdGuard::new) + } + + #[inline] + pub fn fcntl(&self, cmd: usize, arg: usize) -> Result { + syscall::fcntl(self.fd, cmd, arg) + } + + #[inline] + pub fn fstat(&self, stat: &mut syscall::Stat) -> Result { + fstat(self.fd, stat) + } + + #[inline] + pub fn lseek(&self, offset: isize, whence: usize) -> Result { + syscall::lseek(self.fd, offset, whence) + } + + #[inline] + pub fn read(&self, buf: &mut [u8]) -> Result { + syscall::read(self.fd, buf) + } + + #[inline] + pub fn write(&self, buf: &[u8]) -> Result { + syscall::write(self.fd, buf) + } + + #[inline] + pub fn call_ro(&self, payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { + syscall::call_ro(self.fd, payload, flags, metadata) + } + + #[inline] + pub fn call_wo(&self, payload: &[u8], flags: CallFlags, metadata: &[u64]) -> Result { + syscall::call_wo(self.fd, payload, flags, metadata) + } + + #[inline] + pub fn call_rw(&self, payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { + syscall::call_rw(self.fd, payload, flags, metadata) + } + + #[inline] + pub fn as_raw_fd(&self) -> usize { + self.fd + } + + #[inline] + pub fn take(self) -> usize { + let fd = self.fd; + core::mem::forget(self); + fd + } +} +impl Drop for FdGuard { + #[inline] + fn drop(&mut self) { + let _ = syscall::close(self.fd); + } +} +impl Debug for FdGuard { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "[fd {}]", self.fd) + } +} +impl Debug for FdGuardUpper { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "[fd upper {}]", self.fd & !syscall::UPPER_FDTBL_TAG) + } +} +pub fn create_set_addr_space_buf( + space: usize, + ip: usize, + sp: usize, +) -> [u8; size_of::() * 3] { + let mut buf = [0u8; size_of::() * 3]; + buf.copy_from_slice([space, sp, ip].map(usize::to_ne_bytes).as_flattened()); + buf +} +pub fn create_set_addr_space_buf_for_fork( + space: usize, + ip: usize, + sp: usize, + arg1: usize, +) -> [u8; size_of::() * 4] { + let mut buf = [0u8; size_of::() * 4]; + buf.copy_from_slice([space, sp, ip, arg1].map(usize::to_ne_bytes).as_flattened()); + buf +} + +/// Spawns a new context which will not share the same address space as the current one. File +/// descriptors from other schemes are reobtained with `dup`, and grants referencing such file +/// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. +pub fn fork_impl(args: &ForkArgs<'_>) -> Result { + let old_mask = crate::signal::get_sigmask()?; + let pid = unsafe { + Error::demux(__relibc_internal_fork_wrapper( + args as *const ForkArgs as usize, + ))? + }; + + if pid == 0 { + crate::signal::set_sigmask(Some(old_mask), None)?; + } + Ok(pid) +} + +pub enum ForkArgs<'a> { + Init { + this_thr_fd: &'a FdGuardUpper, + auth: &'a FdGuard, + }, + Managed, +} + +pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result { + let (cur_filetable_fd, new_proc_fd, new_thr_fd, new_pid); + + { + let cur_thr_fd = match args { + ForkArgs::Init { this_thr_fd, .. } => this_thr_fd, + ForkArgs::Managed => RtTcb::current().thread_fd(), + }; + let NewChildProc { + proc_fd, + thr_fd, + pid, + } = new_child_process(args)?; + new_proc_fd = proc_fd; + new_thr_fd = thr_fd; + new_pid = pid; + + // Copy existing files into new file table, but do not reuse the same file table (i.e. new + // parent FDs will not show up for the child). + let scratchpad = { + cur_filetable_fd = cur_thr_fd.dup(b"filetable")?; + + // This must be done before the address space is copied. + let proc_fd = new_proc_fd.as_ref().map_or(usize::MAX, |p| p.as_raw_fd()); + //let _ = syscall::write(1, alloc::format!("FDTBL{}PROC{}THR{}\n", *cur_filetable_fd, proc_fd, *new_thr_fd).as_bytes()); + + ForkScratchpad { + cur_filetable_fd: cur_filetable_fd.as_raw_fd(), + new_proc_fd: proc_fd, + new_thr_fd: new_thr_fd.as_raw_fd(), + } + }; + #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "riscv64" + ))] + let arg1 = { + let scratchpad_ptr: *const ForkScratchpad = &scratchpad; + scratchpad_ptr as usize + }; + #[cfg(target_arch = "x86")] + unsafe { + let scratchpad_ptr = initial_rsp as *mut ForkScratchpad; + scratchpad_ptr.write(scratchpad); + } + + // CoW-duplicate address space. + { + let new_addr_space_sel_fd = new_thr_fd.dup(b"current-addrspace")?; + + let cur_addr_space_fd = cur_thr_fd.dup(b"addrspace")?.to_upper()?; + let new_addr_space_fd = cur_addr_space_fd.dup(b"exclusive")?.to_upper()?; + + let mut grant_desc_buf = [GrantDesc::default(); 16]; + loop { + let bytes_read = { + let buf = unsafe { + core::slice::from_raw_parts_mut( + grant_desc_buf.as_mut_ptr().cast(), + grant_desc_buf.len() * size_of::(), + ) + }; + cur_addr_space_fd.read(buf)? + }; + if bytes_read == 0 { + break; + } + + let grants = &grant_desc_buf[..bytes_read / size_of::()]; + + for grant in grants { + if !grant.flags.contains(GrantFlags::GRANT_SCHEME) + || !grant.flags.contains(GrantFlags::GRANT_SHARED) + { + continue; + } + + let buf; + + // TODO: write! using some #![no_std] Cursor type (tracking the length)? + #[cfg(target_pointer_width = "64")] + { + //buf = *b"grant-fd-AAAABBBBCCCCDDDD"; + //write!(&mut buf, "grant-fd-{:>016x}", grant.base).unwrap(); + buf = alloc::format!("grant-fd-{:>016x}", grant.base).into_bytes(); + } + + #[cfg(target_pointer_width = "32")] + { + //buf = *b"grant-fd-AAAABBBB"; + //write!(&mut buf[..], "grant-fd-{:>08x}", grant.base).unwrap(); + buf = alloc::format!("grant-fd-{:>08x}", grant.base).into_bytes(); + } + + let grant_fd = cur_addr_space_fd.dup(&buf)?.to_upper()?; + + let mut flags = MAP_SHARED | MAP_FIXED_NOREPLACE; + + flags.set(PROT_READ, grant.flags.contains(GrantFlags::GRANT_READ)); + flags.set(PROT_WRITE, grant.flags.contains(GrantFlags::GRANT_WRITE)); + flags.set(PROT_EXEC, grant.flags.contains(GrantFlags::GRANT_EXEC)); + + mmap_remote( + &new_addr_space_fd, + &grant_fd, + grant.offset as usize, + grant.base, + grant.size, + flags, + )?; + } + } + #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "riscv64" + ))] + let buf = create_set_addr_space_buf_for_fork( + new_addr_space_fd.as_raw_fd(), + __relibc_internal_fork_ret as usize, + initial_rsp as usize, + arg1, + ); + #[cfg(target_arch = "x86")] + let buf = create_set_addr_space_buf( + new_addr_space_fd.as_raw_fd(), + __relibc_internal_fork_ret as usize, + initial_rsp as usize, + ); + new_addr_space_sel_fd.write(&buf)?; + } + { + // Reuse the same sigaltstack and signal entry (all memory will be re-mapped CoW later). + // + // Do this after the address space is cloned, since the kernel will get a shared + // reference to the TCB and whatever pages stores the signal proc control struct. + { + let new_sighandler_fd = new_thr_fd.dup(b"sighandler")?; + new_sighandler_fd.write(&crate::signal::current_setsighandler_struct())?; + } + if let Some(ref proc_fd) = new_proc_fd { + proc_call( + proc_fd.as_raw_fd(), + &mut [], + CallFlags::empty(), + &[ProcCall::SyncSigPctl as u64], + )?; + thread_call( + new_thr_fd.as_raw_fd(), + &mut [], + CallFlags::empty(), + &[ThreadCall::SyncSigTctl as u64], + )?; + } + } + { + // Copy environment registers. + let cur_env_regs_fd = cur_thr_fd.dup(b"regs/env")?; + let new_env_regs_fd = new_thr_fd.dup(b"regs/env")?; + + let mut env_regs = syscall::EnvRegisters::default(); + cur_env_regs_fd.read(&mut env_regs)?; + new_env_regs_fd.write(&env_regs)?; + } + } + // Copy the file table. We do this last to ensure that all previously used file descriptors are + // closed. The only exception -- the filetable selection fd and the current filetable fd -- + // will be closed by the child process. + { + // TODO: Use file descriptor forwarding or something similar to avoid copying the file + // table in the kernel. + let new_filetable_fd = cur_filetable_fd.dup(b"copy")?; + let new_filetable_sel_fd = new_thr_fd.dup(b"current-filetable")?; + new_filetable_sel_fd.write(&usize::to_ne_bytes(new_filetable_fd.as_raw_fd()))?; + } + let start_fd = new_thr_fd.dup(b"start")?; + start_fd.write(&[0])?; + Ok(new_pid) +} + +pub struct NewChildProc { + proc_fd: Option, + + thr_fd: FdGuardUpper, + pid: usize, +} + +pub fn new_child_process(args: &ForkArgs<'_>) -> Result { + match *args { + ForkArgs::Managed => { + let proc_info = crate::static_proc_info(); + let this_proc_fd = proc_info + .proc_fd + .as_ref() + .expect("cannot use ForkArgs::Managed without an existing proc info"); + let child_proc_fd = this_proc_fd.dup(b"fork")?.to_upper()?; + let only_thread_fd = child_proc_fd.dup(b"thread-0")?.to_upper()?; + let meta = read_proc_meta(&child_proc_fd)?; + Ok(NewChildProc { + proc_fd: Some(child_proc_fd), + thr_fd: only_thread_fd, + pid: meta.pid as usize, + }) + } + #[cfg(feature = "proc")] + ForkArgs::Init { .. } => unreachable!(), + + #[cfg(not(feature = "proc"))] + ForkArgs::Init { this_thr_fd, auth } => { + let thr_fd = auth.dup(b"new-context")?.to_upper()?; + let buf = syscall::ProcSchemeAttrs { + pid: 0, + euid: 0, + egid: 0, + prio: !0, // Value is overwritten later + debug_name: { + let mut buf = [0; 32]; + let src = b"[init]"; + buf[..src.len()].copy_from_slice(src); + buf + }, + }; + let attr_fd = + thr_fd.dup(alloc::format!("auth-{}-attrs", auth.as_raw_fd()).as_bytes())?; + attr_fd.write(&buf)?; + Ok(NewChildProc { + thr_fd, + pid: 1, // dummy fd to distinguish child from parent + proc_fd: None, + }) + } + } +} + +pub unsafe fn make_init(proc_cap: usize) -> (&'static FdGuardUpper, &'static FdGuardUpper) { + let proc_fd = FdGuard::new( + syscall::openat(proc_cap, "init", syscall::O_CLOEXEC, 0).expect("failed to create init"), + ) + .to_upper() + .unwrap(); + + syscall::sendfd( + proc_fd.as_raw_fd(), + RtTcb::current().thread_fd().dup(&[]).unwrap().take(), + 0, + 0, + ) + .expect("failed to assign current thread to init process"); + + let managed_thr_fd = proc_fd + .dup(b"thread-0") + .expect("failed to get managed thread for init") + .to_upper() + .unwrap(); + + let managed_thr_fd = unsafe { (*RtTcb::current().thr_fd.get()).insert(managed_thr_fd) }; + + unsafe { + STATIC_PROC_INFO.get().write(crate::StaticProcInfo { + pid: 1, + proc_fd: Some(proc_fd), + }) + }; + *DYNAMIC_PROC_INFO.lock() = crate::DynamicProcInfo { + pgid: 1, + ruid: 0, + euid: 0, + suid: 0, + rgid: 0, + egid: 0, + sgid: 0, + ns_fd: None, + }; + ( + unsafe { (*STATIC_PROC_INFO.get()).proc_fd.as_ref().unwrap() }, + managed_thr_fd, + ) +} +pub(crate) static STATIC_PROC_INFO: SyncUnsafeCell = + SyncUnsafeCell::new(StaticProcInfo { + pid: 0, + proc_fd: None, + }); diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs new file mode 100644 index 0000000000..022f8737d1 --- /dev/null +++ b/redox-rt/src/signal.rs @@ -0,0 +1,1015 @@ +use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering}; + +use syscall::{ + CallFlags, EAGAIN, EINTR, EINVAL, ENOMEM, EPERM, Error, RawAction, Result, SenderInfo, + SetSighandlerData, SigProcControl, Sigcontrol, SigcontrolFlags, TimeSpec, data::AtomicU64, +}; + +use crate::{ + RtTcb, Tcb, + arch::*, + current_proc_fd, static_proc_info, + sync::Mutex, + sys::{proc_call, this_thread_call}, +}; + +use redox_protocols::protocol::{ + ProcCall, RtSigInfo, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, + SIGWINCH, ThreadCall, +}; + +#[cfg(target_arch = "x86_64")] +static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); + +pub fn sighandler_function() -> usize { + // TODO: HWCAP? + + __relibc_internal_sigentry as usize +} + +/// ucontext_t representation +#[repr(C)] +pub struct SigStack { + #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "riscv64" + ))] + _pad: [usize; 1], // pad from 7*8 to 64 + + #[cfg(target_arch = "x86")] + _pad: [usize; 3], // pad from 9*4 to 12*4 + + pub link: *mut SigStack, + + pub old_stack: PosixStackt, + pub old_mask: u64, + pub(crate) sival: usize, + pub(crate) sig_code: u32, + pub(crate) sig_num: u32, + + // x86_64: 864 bytes + // i686: 512 bytes + // aarch64: 272 bytes (SIMD TODO) + // riscv64: 520 bytes (vector extensions TODO) + pub regs: ArchIntRegs, +} +#[repr(C)] +pub struct PosixStackt { + pub sp: *mut (), + pub flags: i32, + pub size: usize, +} + +pub const SS_ONSTACK: usize = 1; +pub const SS_DISABLE: usize = 2; + +impl From for PosixStackt { + fn from(value: Sigaltstack) -> Self { + match value { + Sigaltstack::Disabled => PosixStackt { + sp: core::ptr::null_mut(), + size: 0, + flags: SS_DISABLE.try_into().unwrap(), + }, + Sigaltstack::Enabled { + onstack, + base, + size, + } => PosixStackt { + sp: base.cast(), + size, + flags: if onstack { + SS_ONSTACK.try_into().unwrap() + } else { + 0 + }, + }, + } + } +} + +#[repr(C)] +// TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining +// it here. Alternatively, check at compile time that the structs are equivalent. +pub struct SiginfoAbi { + pub si_signo: i32, + pub si_errno: i32, + pub si_code: i32, + pub si_pid: i32, // pid_t + pub si_uid: i32, // uid_t + pub si_addr: *mut (), // *mut c_void + pub si_status: i32, + pub si_value: usize, // sigval +} + +#[inline(always)] +unsafe fn inner(stack: &mut SigStack) { + let os = unsafe { &Tcb::current().unwrap().os_specific }; + + let stack_ptr = NonNull::from(&mut *stack); + stack.link = core::mem::replace( + unsafe { &mut (*os.arch.get()).last_sigstack }, + Some(stack_ptr), + ) + .map_or_else(core::ptr::null_mut, |x| x.as_ptr()); + + let signals_were_disabled = unsafe { (*os.arch.get()).disable_signals_depth > 0 }; + + let targeted_thread_not_process = stack.sig_num >= 64; + stack.sig_num %= 64; + + // asm counts from 0 + stack.sig_num += 1; + + let (sender_pid, sender_uid) = { + let area = unsafe { &mut *os.arch.get() }; + + // Undefined if the signal was not realtime + stack.sival = area.tmp_rt_inf.arg; + + stack.old_stack = unsafe { arch_pre(stack, area) }; + + if (stack.sig_num - 1) / 32 == 1 && !targeted_thread_not_process { + stack.sig_code = area.tmp_rt_inf.code as u32; + (area.tmp_rt_inf.pid, area.tmp_rt_inf.uid) + } else { + stack.sig_code = 0; // TODO: SI_USER constant? + // TODO: Handle SIGCHLD. Maybe that should always be queued though? + let inf = SenderInfo::from_raw(area.tmp_id_inf); + (inf.pid, inf.ruid) + } + }; + + let sigaction = { + let guard = SIGACTIONS_LOCK.lock(); + let action = convert_old(&PROC_CONTROL_STRUCT.actions[stack.sig_num as usize - 1]); + if action.flags.contains(SigactionFlags::RESETHAND) { + drop(guard); + // TODO: handle error? + let _ = sigaction( + stack.sig_num as u8, + Some(&Sigaction { + kind: SigactionKind::Default, + mask: 0, + flags: SigactionFlags::empty(), + }), + None, + ); + } + action + }; + let shall_restart = sigaction.flags.contains(SigactionFlags::RESTART); + let sig = (stack.sig_num & 0x3f) as u8; + + let handler = match sigaction.kind { + // TODO: Since sigaction may be called while procmgr is checking the IGNORED bit, it is + // likely possible there can be a race condition resulting in the signal trampoline running + // and reaching this code. If so, we do already know whether the signal is IGNORED *now*, + // and so we should return early ideally without even temporarily touching the signal mask. + SigactionKind::Ignore => { + panic!("ctl {:#x?} signal {}", os.control, stack.sig_num) + } + // this case should be treated equally as the one above + // + // _ if sigaction.flags.contains(SigactionFlags::IGNORED) => { + // panic!("ctl2 {:#x?} signal {}", os.control, stack.sig_num) + // } + SigactionKind::Default if usize::from(sig) == SIGCONT => SignalHandler { handler: None }, + SigactionKind::Default => { + let _ = proc_call( + current_proc_fd().as_raw_fd(), + &mut [], + CallFlags::empty(), + &[ProcCall::Exit as u64, u64::from(sig) << 8], + ); + panic!() + } + SigactionKind::Handled { handler } => handler, + }; + + // Set sigmask to sa_mask and unmark the signal as pending. + let prev_sigallow = get_allowset_raw(&os.control.word); + + let mut sigallow_inside = !sigaction.mask & prev_sigallow; + if !sigaction.flags.contains(SigactionFlags::NODEFER) { + sigallow_inside &= !sig_bit(stack.sig_num); + } + + let _pending_when_sa_mask = set_allowset_raw(&os.control.word, prev_sigallow, sigallow_inside); + + // TODO: If sa_mask caused signals to be unblocked, deliver one or all of those first? + + // Re-enable signals again. + let control_flags = &os.control.control_flags; + control_flags.store( + control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + core::sync::atomic::compiler_fence(Ordering::Acquire); + + stack.old_mask = prev_sigallow; + + // Call handler, either sa_handler or sa_siginfo depending on flag. + if sigaction.flags.contains(SigactionFlags::SIGINFO) + && let Some(sigaction) = unsafe { handler.sigaction } + { + let info = SiginfoAbi { + si_signo: stack.sig_num as c_int, + si_addr: core::ptr::null_mut(), + si_code: stack.sig_code as i32, + si_errno: 0, + si_pid: sender_pid as i32, + si_status: 0, + si_uid: sender_uid as i32, + si_value: stack.sival, + }; + unsafe { + sigaction( + stack.sig_num as c_int, + core::ptr::addr_of!(info).cast(), + stack as *mut SigStack as *mut (), + ) + }; + } else if let Some(handler) = unsafe { handler.handler } { + handler(stack.sig_num as c_int); + } + + // Disable signals while we modify the sigmask again + control_flags.store( + control_flags.load(Ordering::Relaxed) | SigcontrolFlags::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + core::sync::atomic::compiler_fence(Ordering::Acquire); + + // Update allowset again, and obtain the set of pending unblocked signals with this new + // allowset. If this is nonempty, we must deliver the signal here rather than wait for it + // asynchronously, as shown e.g. in https://gitlab.redox-os.org/redox-os/relibc/-/issues/239. + // We do this even if signals_were_disabled beforehand (where the caller hence explicitly + // must have jumped to this trampoline). + + let new_mask = stack.old_mask; + let old_mask = get_allowset_raw(&os.control.word); + + let pending_unblocked = { + let thread_pending = set_allowset_raw(&os.control.word, old_mask, new_mask); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Relaxed); + (thread_pending | proc_pending) & new_mask + }; + + unsafe { (*os.arch.get()).last_sig_was_restart = shall_restart }; + + // TODO: Support setting uc_link to jump back to a different context? + unsafe { (*os.arch.get()).last_sigstack = NonNull::new(stack.link) }; + + // TODO: Support restoring uc_stack? + + if pending_unblocked > 0 { + // If there were signals visible at the time we checked (otherwise it can be considered + // "asynchronous" and will be delivered the next time an EINTR is seen, or after the + // post-preemption check in the kernel), we delay clearing the INHIBIT_DELIVERY bit, and + // rearrange the saved instr ptr field on the stack and the sc_saved_rip field, so that it + // returns to the start of the trampoline in a way that makes it look like a new signal was + // immediately delivered. There's no need to worry about the __crit_* sections since + // INHIBIT_DELIVERY is still set. + arch_ret_to_sig(stack, &os.control); + } else if !signals_were_disabled { + // Re-enable signals again, except in the case where they were disabled and the trampoline + // was explicitly jumped to when handling EINTR. + core::sync::atomic::compiler_fence(Ordering::Release); + control_flags.store( + control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(), + Ordering::Relaxed, + ); + } +} +#[cfg(not(target_arch = "x86"))] +pub(crate) unsafe extern "C" fn inner_c(stack: usize) { + unsafe { inner(&mut *(stack as *mut SigStack)) } +} +#[cfg(target_arch = "x86")] +pub(crate) unsafe extern "fastcall" fn inner_fastcall(stack: usize) { + unsafe { inner(&mut *(stack as *mut SigStack)) } +} + +pub fn get_sigmask() -> Result { + let mut mask = 0; + modify_sigmask(Some(&mut mask), Option:: u64>::None)?; + Ok(mask) +} +pub fn set_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { + modify_sigmask(old, new.map(move |newmask| move |_| newmask)) +} +pub fn or_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { + // Parsing nightmare... :) + modify_sigmask( + old, + new.map(move |newmask| move |oldmask| oldmask | newmask), + ) +} +pub fn andn_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { + modify_sigmask( + old, + new.map(move |newmask| move |oldmask| oldmask & !newmask), + ) +} +fn get_allowset_raw(words: &[AtomicU64; 2]) -> u64 { + (words[0].load(Ordering::Relaxed) >> 32) | ((words[1].load(Ordering::Relaxed) >> 32) << 32) +} +/// Sets mask from old to new, returning what was pending at the time. +fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new_raw: u64) -> u64 { + // TODO: should these bits always be set, or never be set? + let new = new_raw | ALLOWSET_ALWAYS; + + // This assumes *only this thread* can change the allowset. If this rule is broken, the use of + // fetch_add will corrupt the words entirely. fetch_add is very efficient on x86, being + // generated as LOCK XADD which is the fastest RMW instruction AFAIK. + let prev_w0 = words[0].fetch_add( + ((new & 0xffff_ffff) << 32).wrapping_sub((old & 0xffff_ffff) << 32), + Ordering::Relaxed, + ) & 0xffff_ffff; + let prev_w1 = words[1].fetch_add( + ((new >> 32) << 32).wrapping_sub((old >> 32) << 32), + Ordering::Relaxed, + ) & 0xffff_ffff; + + prev_w0 | (prev_w1 << 32) +} +const ALLOWSET_ALWAYS: u64 = sig_bit(SIGSTOP as u32) | sig_bit(SIGKILL as u32); +fn modify_sigmask(old: Option<&mut u64>, op: Option u64>) -> Result<()> { + let _guard = tmp_disable_signals(); + let ctl = current_sigctl(); + + let prev = get_allowset_raw(&ctl.word); + + if let Some(old) = old { + *old = !prev; + } + let Some(op) = op else { + return Ok(()); + }; + + let next = !op(!prev); + + let thread_pending = set_allowset_raw(&ctl.word, prev, next); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Relaxed); + + // POSIX requires that at least one pending unblocked signal be delivered before + // pthread_sigmask returns, if there is one. + if (thread_pending | proc_pending) & next != 0 { + unsafe { + manually_enter_trampoline(); + } + } + + Ok(()) +} + +#[derive(Clone, Copy, Default)] +pub enum SigactionKind { + #[default] + Default, + + Ignore, + Handled { + handler: SignalHandler, + }, +} + +#[derive(Clone, Copy, Default)] +pub struct Sigaction { + pub kind: SigactionKind, + pub mask: u64, + pub flags: SigactionFlags, +} + +impl Sigaction { + fn ip(&self) -> usize { + unsafe { + match self.kind { + SigactionKind::Handled { handler } => { + if self.flags.contains(SigactionFlags::SIGINFO) { + handler.sigaction.map_or(0, |a| a as usize) + } else { + handler.handler.map_or(0, |a| a as usize) + } + } + _ => 0, + } + } + } +} + +const MASK_DONTCARE: u64 = !0; + +fn convert_old(action: &RawAction) -> Sigaction { + let old_first = action.first.load(Ordering::Relaxed); + let old_mask = action.user_data.load(Ordering::Relaxed); + + let handler = (old_first & !(u64::from(STORED_FLAGS) << 32)) as usize; + let flags = SigactionFlags::from_bits_retain(((old_first >> 32) as u32) & STORED_FLAGS); + + let kind = if handler == default_handler as usize { + SigactionKind::Default + } else if flags.contains(SigactionFlags::IGNORED) { + SigactionKind::Ignore + } else { + SigactionKind::Handled { + handler: unsafe { core::mem::transmute(handler as usize) }, + } + }; + + Sigaction { + mask: old_mask, + flags, + kind, + } +} + +pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction>) -> Result<()> { + let _sigguard = tmp_disable_signals(); + let ctl = current_sigctl(); + + let _guard = SIGACTIONS_LOCK.lock(); + sigaction_inner(ctl, signal, new, old) +} +fn sigaction_inner( + ctl: &Sigcontrol, + signal: u8, + new: Option<&Sigaction>, + old: Option<&mut Sigaction>, +) -> Result<()> { + // TODO: Now that the goal of keeping logic out of the IPC backend, no longer holds when + // procmgr has taken over signal handling from the kernel, it would probably make sense to make + // parts of this function an IPC call, for synchronization purposes. Apart from SA_RESETHAND + // logic which may need to be fast, regular sigaction is typically in the 'configuration' + // category, allowed to be slower. + + if matches!(usize::from(signal), 0 | 32 | 65..) { + return Err(Error::new(EINVAL)); + } + if matches!(usize::from(signal), SIGKILL | SIGSTOP) { + if new.is_some() { + return Err(Error::new(EINVAL)); + } + if let Some(old) = old { + // TODO: Is this the correct value to set it to? + *old = Sigaction { + kind: SigactionKind::Default, + mask: 0, + flags: SigactionFlags::empty(), + }; + } + return Ok(()); + } + + let action = &PROC_CONTROL_STRUCT.actions[usize::from(signal) - 1]; + + if let Some(old) = old { + *old = convert_old(action); + } + + let Some(new) = new else { + return Ok(()); + }; + + let explicit_handler = new.ip(); + + let sig_group = (signal - 1) / 32; + let sig_idx = (signal - 1) % 32; + + let (mask, flags, handler) = match (usize::from(signal), new.kind) { + (_, SigactionKind::Ignore) | (SIGURG | SIGWINCH, SigactionKind::Default) => { + // TODO: relibc and procmgr have access to all threads, redox_rt doesn't currently. + // Do this for all threads! + ctl.word[usize::from(sig_group)].fetch_and(!(1 << sig_idx), Ordering::Relaxed); + PROC_CONTROL_STRUCT + .pending + .fetch_and(!sig_bit(signal.into()), Ordering::Relaxed); + + // TODO: handle tmp_disable_signals + ( + MASK_DONTCARE, + SigactionFlags::IGNORED, + if matches!(new.kind, SigactionKind::Default) { + default_handler as usize + } else { + 0 + }, + ) + } + // TODO: Handle pending signals before these flags are set. + (SIGTSTP | SIGTTOU | SIGTTIN, SigactionKind::Default) => ( + MASK_DONTCARE, + SigactionFlags::SIG_SPECIFIC, + default_handler as usize, + ), + (SIGCHLD, SigactionKind::Default) => { + let nocldstop_bit = new.flags & SigactionFlags::SIG_SPECIFIC; + + // Default action is to ignore. Hence all pending SIGCHLD signals should be discarded. + + // TODO: relibc and procmgr have access to all threads, redox_rt doesn't currently. + // Do this for all threads! + ctl.word[usize::from(sig_group)].fetch_and(!(1 << sig_idx), Ordering::Relaxed); + PROC_CONTROL_STRUCT + .pending + .fetch_and(!sig_bit(signal.into()), Ordering::Relaxed); + + ( + MASK_DONTCARE, + SigactionFlags::IGNORED | nocldstop_bit, + default_handler as usize, + ) + } + + (_, SigactionKind::Default) => (new.mask, new.flags, default_handler as usize), + (_, SigactionKind::Handled { .. }) => (new.mask, new.flags, explicit_handler), + }; + let new_first = (handler as u64) | (u64::from(flags.bits() & STORED_FLAGS) << 32); + action.first.store(new_first, Ordering::Relaxed); + action.user_data.store(mask, Ordering::Relaxed); + + Ok(()) +} + +fn current_sigctl() -> &'static Sigcontrol { + &unsafe { Tcb::current() }.unwrap().os_specific.control +} + +pub struct TmpDisableSignalsGuard { + _inner: (), +} + +pub fn tmp_disable_signals() -> TmpDisableSignalsGuard { + unsafe { + let ctl = ¤t_sigctl().control_flags; + ctl.store( + ctl.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + core::sync::atomic::compiler_fence(Ordering::Acquire); + + // TODO: fence? + (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth += 1; + } + + TmpDisableSignalsGuard { _inner: () } +} +impl Drop for TmpDisableSignalsGuard { + fn drop(&mut self) { + unsafe { + let depth = + &mut (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth; + *depth -= 1; + + if *depth == 0 { + let ctl = ¤t_sigctl().control_flags; + ctl.store( + ctl.load(Ordering::Relaxed) & !syscall::flag::INHIBIT_DELIVERY.bits(), + Ordering::Release, + ); + core::sync::atomic::compiler_fence(Ordering::Acquire); + } + } + } +} + +bitflags::bitflags! { + // Some flags are ignored by the rt, but they match relibc's 1:1 to simplify conversion. + #[derive(Clone, Copy, Default)] + pub struct SigactionFlags: u32 { + const NOCLDWAIT = 2; + const RESTORER = 4; + const SIGINFO = 0x0200_0000; + const ONSTACK = 0x0400_0000; + const RESTART = 0x0800_0000; + const NODEFER = 0x1000_0000; + const RESETHAND = 0x2000_0000; + const SIG_SPECIFIC = 0x4000_0000; + const IGNORED = 0x8000_0000; + } +} + +const STORED_FLAGS: u32 = 0xfe00_0000; + +fn default_handler(_sig: c_int) { + unreachable!(); +} + +#[derive(Clone, Copy)] +pub union SignalHandler { + pub handler: Option, + pub sigaction: Option, +} + +static SIGACTIONS_LOCK: Mutex<()> = Mutex::new(()); + +pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl { + pending: AtomicU64::new(0), + actions: [const { + RawAction { + first: AtomicU64::new(0), + user_data: AtomicU64::new(0), + } + }; 64], + sender_infos: [const { AtomicU64::new(0) }; 32], +}; + +const fn sig_bit(sig: u32) -> u64 { + //assert_ne!(sig, 32); + //assert_ne!(sig, 0); + 1 << (sig - 1) +} + +pub fn setup_sighandler(tcb: &RtTcb, first_thread: bool) { + if first_thread { + let _guard = SIGACTIONS_LOCK.lock(); + for (sig_idx, action) in PROC_CONTROL_STRUCT.actions.iter().enumerate() { + let sig = sig_idx + 1; + let bits = if matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU) { + SigactionFlags::SIG_SPECIFIC + } else if matches!(sig, SIGCHLD | SIGURG | SIGWINCH) { + SigactionFlags::IGNORED + } else { + SigactionFlags::empty() + }; + action.first.store( + (u64::from(bits.bits()) << 32) | default_handler as u64, + Ordering::Relaxed, + ); + } + } + let arch = unsafe { &mut *tcb.arch.get() }; + { + // The asm decides whether to use the altstack, based on whether the saved stack pointer + // was already on that stack. Thus, setting the altstack to the entire address space, is + // equivalent to not using any altstack at all (the default). + arch.altstack_top = usize::MAX; + arch.altstack_bottom = 0; + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + { + arch.pctl = core::ptr::addr_of!(PROC_CONTROL_STRUCT) as usize; + } + } + + #[cfg(target_arch = "x86_64")] + { + let cpuid_eax1_ecx = unsafe { core::arch::x86_64::__cpuid(1) }.ecx; + CPUID_EAX1_ECX.store(cpuid_eax1_ecx, core::sync::atomic::Ordering::Relaxed); + SUPPORTS_AVX.store(u8::from(cpuid_eax1_ecx & 1 << 28 != 0), Ordering::Relaxed); + } + + let data = current_setsighandler_struct(); + + let fd = tcb + .thread_fd() + .dup(b"sighandler") + .expect("failed to open sighandler fd"); + fd.write(&data).expect("failed to write to sighandler fd"); + this_thread_call( + &mut [], + CallFlags::empty(), + &[ThreadCall::SyncSigTctl as u64], + ) + .expect("failed to sync signal tctl"); + + // TODO: Inherited set of ignored signals + // TODO: handle error + let _ = set_sigmask(Some(0), None); +} +pub type RtSigarea = RtTcb; // TODO +pub fn current_setsighandler_struct() -> SetSighandlerData { + SetSighandlerData { + user_handler: sighandler_function(), + excp_handler: 0, // TODO + thread_control_addr: core::ptr::addr_of!( + unsafe { Tcb::current() }.unwrap().os_specific.control + ) as usize, + proc_control_addr: &PROC_CONTROL_STRUCT as *const SigProcControl as usize, + } +} + +#[derive(Clone, Copy, Default, PartialEq)] +pub enum Sigaltstack { + #[default] + Disabled, + + Enabled { + onstack: bool, + base: *mut (), + size: usize, + }, +} + +pub(crate) fn get_sigaltstack(tcb: &SigArea, sp: usize) -> Sigaltstack { + if tcb.altstack_bottom == 0 && tcb.altstack_top == usize::MAX { + Sigaltstack::Disabled + } else { + Sigaltstack::Enabled { + base: tcb.altstack_bottom as *mut (), + size: tcb.altstack_top - tcb.altstack_bottom, + onstack: (tcb.altstack_bottom..=tcb.altstack_top).contains(&sp), + } + } +} + +pub unsafe fn sigaltstack( + new: Option<&Sigaltstack>, + old_out: Option<&mut Sigaltstack>, +) -> Result<()> { + let _g = tmp_disable_signals(); + let tcb = unsafe { &mut *Tcb::current().unwrap().os_specific.arch.get() }; + + let old = get_sigaltstack(tcb, crate::arch::current_sp()); + + if matches!(old, Sigaltstack::Enabled { onstack: true, .. }) + && new.is_some_and(|new| *new != old) + { + return Err(Error::new(EPERM)); + } + + if let Some(old_out) = old_out { + *old_out = old; + } + if let Some(new) = new { + match *new { + Sigaltstack::Disabled => { + tcb.altstack_bottom = 0; + tcb.altstack_top = usize::MAX; + } + Sigaltstack::Enabled { onstack: true, .. } => return Err(Error::new(EINVAL)), + Sigaltstack::Enabled { + base, + size, + onstack: false, + } => { + if size < MIN_SIGALTSTACK_SIZE { + return Err(Error::new(ENOMEM)); + } + + tcb.altstack_bottom = base as usize; + tcb.altstack_top = base as usize + size; + } + } + } + Ok(()) +} + +pub const MIN_SIGALTSTACK_SIZE: usize = 2048; + +pub fn currently_pending_blocked() -> u64 { + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + let w0 = control.word[0].load(Ordering::Relaxed); + let w1 = control.word[1].load(Ordering::Relaxed); + let allow = (w0 >> 32) | ((w1 >> 32) << 32); + let thread_pending = (w0 & 0xffff_ffff) | ((w1 & 0xffff_ffff) << 32); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Relaxed); + + core::sync::atomic::fence(Ordering::Acquire); // TODO: Correct ordering? + + (thread_pending | proc_pending) & !allow +} +pub enum Unreachable {} + +pub fn await_signal_async(inner_allowset: u64) -> Result { + let _guard = tmp_disable_signals(); + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + + let old_allowset = get_allowset_raw(&control.word); + set_allowset_raw(&control.word, old_allowset, inner_allowset); + + let res = syscall::nanosleep( + &TimeSpec { + tv_sec: i64::MAX, + tv_nsec: 0, + }, + &mut TimeSpec::default(), + ); + + if res == Err(Error::new(EINTR)) { + unsafe { + manually_enter_trampoline(); + } + } + // POSIX says it shall restore the mask to what it was prior to the call, which is interpreted + // as allowing any changes to sigprocmask inside the signal handler, to be discarded. + set_allowset_raw(&control.word, inner_allowset, old_allowset); + + res?; + unreachable!() +} + +/// Run a callback with the specified signal mask, atomically +pub fn callback_or_signal_async Result>( + inner_allowset: u64, + callback: F, +) -> Result { + let _guard = tmp_disable_signals(); + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + + let old_allowset = get_allowset_raw(&control.word); + set_allowset_raw(&control.word, old_allowset, inner_allowset); + let res = callback(); + if let Err(err) = &res { + if err.errno == EINTR { + // Run trampoline if EINTR returned + unsafe { + manually_enter_trampoline(); + } + } + } + + // POSIX says it shall restore the mask to what it was prior to the call, which is interpreted + // as allowing any changes to sigprocmask inside the signal handler, to be discarded. + set_allowset_raw(&control.word, inner_allowset, old_allowset); + + res +} + +/*#[unsafe(no_mangle)] +pub extern "C" fn __redox_rt_debug_sigctl() { + let tcb = &RtTcb::current().control; + let _ = syscall::write(1, alloc::format!("SIGCTL: {tcb:#x?}\n").as_bytes()); +}*/ + +// TODO: deadline-based API +pub fn await_signal_sync(inner_allowset: u64, timeout: Option<&TimeSpec>) -> Result { + let _guard = tmp_disable_signals(); + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + + let old_allowset = get_allowset_raw(&control.word); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire); + let thread_pending = set_allowset_raw(&control.word, old_allowset, inner_allowset); + + // Check if there are already signals matching the requested set, before waiting. + if let Some(info) = try_claim_multiple(proc_pending, thread_pending, inner_allowset, control) { + // TODO: RAII + set_allowset_raw(&control.word, inner_allowset, old_allowset); + return Ok(info); + } + + let res = match timeout { + Some(t) => syscall::nanosleep(&t, &mut TimeSpec::default()), + None => syscall::nanosleep( + &TimeSpec { + tv_sec: i64::MAX, + tv_nsec: 0, + }, + &mut TimeSpec::default(), + ), + }; + + let thread_pending = set_allowset_raw(&control.word, inner_allowset, old_allowset); + let proc_pending = PROC_CONTROL_STRUCT.pending.load(Ordering::Acquire); + + if let Err(error) = res + && error.errno != EINTR + { + return Err(error); + } + + // Then check if there were any signals left after waiting. + try_claim_multiple(proc_pending, thread_pending, inner_allowset, control) + // Normally ETIMEDOUT but not for sigtimedwait. + .ok_or(Error::new(EAGAIN)) +} + +fn try_claim_multiple( + mut proc_pending: u64, + mut thread_pending: u64, + allowset: u64, + control: &Sigcontrol, +) -> Option { + while (proc_pending | thread_pending) & allowset != 0 { + let sig_idx = ((proc_pending | thread_pending) & allowset).trailing_zeros(); + if thread_pending & allowset & (1 << sig_idx) != 0 + && let Some(res) = try_claim_single(sig_idx, Some(control)) + { + return Some(res); + } + thread_pending &= !(1 << sig_idx); + if proc_pending & allowset & (1 << sig_idx) != 0 + && let Some(res) = try_claim_single(sig_idx, None) + { + return Some(res); + } + proc_pending &= !(1 << sig_idx); + } + None +} +fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option { + let sig_group = sig_idx / 32; + + if sig_group == 1 && thread_control.is_none() { + // Queued (realtime) signal + let rt_inf: RtSigInfo = unsafe { + let mut buf = [0_u8; size_of::()]; + buf[..4].copy_from_slice(&(sig_idx - 32).to_ne_bytes()); + proc_call( + static_proc_info().proc_fd.as_ref().unwrap().as_raw_fd(), + &mut buf, + CallFlags::empty(), + &[ProcCall::Sigdeq as u64], + ) + .ok()?; + core::mem::transmute(buf) + }; + Some(SiginfoAbi { + si_signo: sig_idx as i32 + 1, + si_errno: 0, + si_code: rt_inf.code, + si_pid: rt_inf.pid as i32, + si_uid: rt_inf.uid as i32, + si_status: 0, + si_value: rt_inf.arg, + si_addr: core::ptr::null_mut(), + }) + } else { + // Idempotent (standard or thread realtime) signal + let info = SenderInfo::from_raw(match thread_control { + Some(ctl) => { + // Only this thread can clear pending bits, so this will always succeed. + let info = match ctl.sender_infos.get(sig_idx as usize) { + Some(i) => i.load(Ordering::Relaxed), + // TODO: Protocol will need to be extended in order to allow passing si_uid and + // si_pid for per-thread (non-queued) realtime signals. + None => 0, + }; + // TODO: Ordering? + ctl.word[sig_group as usize].fetch_and(!(1 << (sig_idx % 32)), Ordering::Release); + info + } + None => { + // Must have sig_group == 0 here due to the above if stmt + let info = + PROC_CONTROL_STRUCT.sender_infos[sig_idx as usize].load(Ordering::Acquire); + if PROC_CONTROL_STRUCT + .pending + .fetch_and(!(1 << sig_idx), Ordering::Release) + & (1 << sig_idx) + == 0 + { + // already claimed + return None; + } + info + } + }); + Some(SiginfoAbi { + si_signo: sig_idx as i32 + 1, + si_errno: 0, + si_code: 0, // TODO: SI_USER const? + si_pid: info.pid as i32, + si_uid: info.ruid as i32, + si_status: 0, + si_value: 0, // undefined + si_addr: core::ptr::null_mut(), + }) + } +} +pub fn apply_inherited_sigignmask(inherited: u64) { + let _sig_guard = tmp_disable_signals(); + let _guard = SIGACTIONS_LOCK.lock(); + let ctl = current_sigctl(); + + // Set all signals in the inherited set that have explicitly been set to SIG_IGN. Those whose + // (default) effective action is to ignore but are set to SIG_DFL, are not in this set, and the + // initial SIG_DFL state would be "Ignore" anyway but still return SIG_DFL when asked, as + // usual. + for bit in (0..64).filter(|b| inherited & (1 << b) != 0) { + let sig = u8::try_from(bit + 1).unwrap(); + let _ = sigaction_inner( + ctl, + sig, + Some(&Sigaction { + // TODO: correct fields? + flags: SigactionFlags::IGNORED, + kind: SigactionKind::Ignore, + mask: 0, + }), + None, + ); + } +} +pub fn get_sigignmask_to_inherit() -> u64 { + let _sig_guard = tmp_disable_signals(); + let _guard = SIGACTIONS_LOCK.lock(); + let ctl = current_sigctl(); + + let mut mask = 0_u64; + // Fill the mask with the set of the inherited signals that have explicitly been set to + // SIG_IGN. Again this excludes signals that would have returned SIG_DFL from sigaction, + // despite their default action being "Ignore". + + for bit in 0..64 { + let sig = u8::try_from(bit + 1).unwrap(); + let mut old = Sigaction::default(); + let _ = sigaction_inner(ctl, sig, None, Some(&mut old)); + if matches!(old.kind, SigactionKind::Ignore) { + mask |= 1 << bit; + } + } + + mask +} diff --git a/redox-rt/src/sync.rs b/redox-rt/src/sync.rs new file mode 100644 index 0000000000..beae40aa05 --- /dev/null +++ b/redox-rt/src/sync.rs @@ -0,0 +1,68 @@ +// TODO: Share code for simple futex-based mutex between relibc's Mutex<()> and this. + +use core::{ + cell::UnsafeCell, + ops::{Deref, DerefMut}, + sync::atomic::{AtomicU32, Ordering}, +}; + +pub struct Mutex { + pub lockword: AtomicU32, + pub inner: UnsafeCell, +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +impl Mutex { + /// Represents an unlocked [Mutex]. + pub const UNLOCKED: u32 = 0; + /// Represents a locked [Mutex]. + pub const LOCKED: u32 = 1; + /// Represents a waiting [Mutex]. + pub const WAITING: u32 = 2; + + pub const fn new(t: T) -> Self { + Self { + lockword: AtomicU32::new(0), + inner: UnsafeCell::new(t), + } + } + pub fn lock(&self) -> MutexGuard<'_, T> { + while self + .lockword + .compare_exchange( + Self::UNLOCKED, + Self::LOCKED, + Ordering::Acquire, + Ordering::Relaxed, + ) + .is_err() + { + core::hint::spin_loop(); + } + MutexGuard { lock: self } + } +} +pub struct MutexGuard<'l, T> { + lock: &'l Mutex, +} +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.inner.get() } + } +} +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.inner.get() } + } +} +impl Drop for MutexGuard<'_, T> { + fn drop(&mut self) { + self.lock + .lockword + .store(Mutex::::UNLOCKED, Ordering::Release); + } +} diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs new file mode 100644 index 0000000000..f0363a39a9 --- /dev/null +++ b/redox-rt/src/sys.rs @@ -0,0 +1,563 @@ +use core::{ + mem::{replace, size_of}, + ptr::addr_of, + sync::atomic::{AtomicU32, Ordering}, +}; + +use ioslice::IoSlice; +use syscall::{ + CallFlags, EINVAL, ERESTART, StdFsCallKind, TimeSpec, + data::StdFsCallMeta, + error::{self, EINTR, ENODEV, ESRCH, Error, Result}, +}; + +use crate::{ + DYNAMIC_PROC_INFO, DynamicProcInfo, RtTcb, Tcb, + arch::manually_enter_trampoline, + proc::{FdGuard, FdGuardUpper}, + read_proc_meta, + signal::tmp_disable_signals, +}; +use alloc::vec::Vec; +use redox_protocols::protocol::{ + NsDup, ProcCall, ProcKillTarget, RtSigInfo, ThreadCall, WaitFlags, +}; + +#[inline] +fn wrapper(restart: bool, erestart: bool, mut f: impl FnMut() -> Result) -> Result { + loop { + let _guard = tmp_disable_signals(); + let rt_sigarea = unsafe { &Tcb::current().unwrap().os_specific }; + let res = f(); + let code = if erestart { ERESTART } else { EINTR }; + + if let Err(err) = res + && err == Error::new(code) + { + unsafe { + manually_enter_trampoline(); + } + if restart && unsafe { (*rt_sigarea.arch.get()).last_sig_was_restart } { + continue; + } + } + + return res; + } +} +// TODO: uninitialized memory? +#[inline] +pub fn posix_read(fd: usize, buf: &mut [u8]) -> Result { + wrapper(true, false, || syscall::read(fd, buf)) +} +#[inline] +pub fn posix_write(fd: usize, buf: &[u8]) -> Result { + wrapper(true, false, || syscall::write(fd, buf)) +} +#[inline] +pub fn posix_kill(target: ProcKillTarget, sig: usize) -> Result<()> { + if sig > 64 { + return Err(Error::new(EINVAL)); + } + + match wrapper(false, true, || { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Kill as u64, target.raw() as u64, sig as u64], + ) + }) { + Ok(_) | Err(Error { errno: ERESTART }) => Ok(()), + Err(error) => Err(error), + } +} +#[inline] +pub fn posix_sigqueue(pid: usize, sig: usize, arg: usize) -> Result<()> { + let target = ProcKillTarget::from_raw(pid); + if !matches!(target, ProcKillTarget::SingleProc(_)) { + return Err(Error::new(ESRCH)); + } + if sig <= 32 { + return posix_kill(target, sig); + } + let mut siginf = RtSigInfo { + arg, + code: -1, // TODO: SI_QUEUE constant + uid: 0, // TODO + pid: posix_getpid(), + }; + match wrapper(false, true, || { + this_proc_call( + unsafe { plain::as_mut_bytes(&mut siginf) }, + CallFlags::empty(), + &[ProcCall::Sigq as u64, pid as u64, sig as u64], + ) + }) { + Ok(_) + | Err(Error { + errno: error::ERESTART, + }) => Ok(()), + Err(error) => Err(error), + } +} +#[inline] +pub fn posix_getpid() -> u32 { + // SAFETY: read-only except during program/fork child initialization + unsafe { addr_of!((*crate::STATIC_PROC_INFO.get()).pid).read() } +} +#[inline] +pub fn posix_getppid() -> u32 { + this_proc_call(&mut [], CallFlags::empty(), &[ProcCall::Getppid as u64]).expect("cannot fail") + as u32 +} + +#[inline] +pub fn posix_setpriority(which: i32, who: u32, prio: u32) -> Result<(), syscall::Error> { + if which != 0 { + return Err(syscall::Error::new(syscall::EINVAL)); // TODO: Add support for PRIO_PGRP and PRIO_PROCESS + } + + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::SetProcPriority as u64, who as u64, prio as u64], + )?; + + Ok(()) +} + +#[inline] +pub fn posix_getpriority(which: i32, who: u32) -> Result { + if which != 0 { + return Err(syscall::Error::new(syscall::EINVAL)); + } + + let res = this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::GetProcPriority as u64, who as u64], + )?; + + Ok(res as u32) +} + +#[inline] +pub unsafe fn sys_futex_wait(addr: *mut u32, val: u32, deadline: Option<&TimeSpec>) -> Result<()> { + wrapper(true, false, || { + unsafe { + syscall::syscall5( + syscall::SYS_FUTEX, + addr as usize, + syscall::FUTEX_WAIT, + val as usize, + deadline.map_or(0, |d| d as *const _ as usize), + 0, + ) + } + .map(|_| ()) + }) +} +#[inline] +pub unsafe fn sys_futex_wake(addr: *mut u32, num: u32) -> Result { + unsafe { + syscall::syscall5( + syscall::SYS_FUTEX, + addr as usize, + syscall::FUTEX_WAKE, + num as usize, + 0, + 0, + ) + } + .map(|awoken| awoken as u32) +} +unsafe fn raw_sys_call( + fd: usize, + payload_ptr: *const u8, + len: usize, + flags: CallFlags, + metadata: &[u64], +) -> Result { + unsafe { + syscall::syscall5( + syscall::SYS_CALL, + fd, + payload_ptr as usize, + len, + metadata.len() | flags.bits(), + metadata.as_ptr() as usize, + ) + } +} +pub fn sys_call_ro( + fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + unsafe { + raw_sys_call( + fd, + payload.as_mut_ptr(), + payload.len(), + flags | CallFlags::READ, + metadata, + ) + } +} +pub fn sys_call_wo(fd: usize, payload: &[u8], flags: CallFlags, metadata: &[u64]) -> Result { + unsafe { + raw_sys_call( + fd, + payload.as_ptr(), + payload.len(), + flags | CallFlags::WRITE, + metadata, + ) + } +} +pub fn sys_call_rw( + fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + unsafe { + raw_sys_call( + fd, + payload.as_mut_ptr(), + payload.len(), + flags | CallFlags::READ | CallFlags::WRITE, + metadata, + ) + } +} +pub fn sys_call( + fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + unsafe { raw_sys_call(fd, payload.as_mut_ptr(), payload.len(), flags, metadata) } +} +pub fn this_proc_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { + proc_call( + crate::current_proc_fd().as_raw_fd(), + payload, + flags, + metadata, + ) +} +pub fn proc_call( + proc_fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + sys_call(proc_fd, payload, flags, metadata) +} +pub fn thread_call( + thread_fd: usize, + payload: &mut [u8], + flags: CallFlags, + metadata: &[u64], +) -> Result { + sys_call(thread_fd, payload, flags, metadata) +} +pub fn this_thread_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result { + thread_call( + RtTcb::current().thread_fd().as_raw_fd(), + payload, + flags, + metadata, + ) +} + +#[derive(Clone, Copy, Debug)] +pub enum WaitpidTarget { + AnyChild, + AnyGroupMember, + SingleProc { pid: usize }, + ProcGroup { pgid: usize }, +} +impl WaitpidTarget { + pub fn from_posix_arg(raw: isize) -> Self { + match raw { + 0 => Self::AnyGroupMember, + -1 => Self::AnyChild, + 1.. => Self::SingleProc { pid: raw as usize }, + ..-1 => Self::ProcGroup { + pgid: -raw as usize, + }, + } + } +} + +pub fn sys_waitpid(target: WaitpidTarget, status: &mut usize, flags: WaitFlags) -> Result { + let (call, pid) = match target { + WaitpidTarget::AnyChild => (ProcCall::Waitpid, 0), + WaitpidTarget::SingleProc { pid } => (ProcCall::Waitpid, pid), + WaitpidTarget::AnyGroupMember => (ProcCall::Waitpgid, 0), + WaitpidTarget::ProcGroup { pgid } => (ProcCall::Waitpgid, pgid), + }; + wrapper(true, false, || { + this_proc_call( + unsafe { plain::as_mut_bytes(status) }, + CallFlags::empty(), + &[call as u64, pid as u64, flags.bits() as u64], + ) + }) +} +pub fn posix_kill_thread(thread_fd: usize, signal: u32) -> Result<()> { + // TODO: don't hardcode? + if signal > 64 { + return Err(Error::new(EINVAL)); + } + + match wrapper(false, true, || { + thread_call( + thread_fd, + &mut [], + CallFlags::empty(), + &[ThreadCall::SignalThread as u64, signal.into()], + ) + }) { + Ok(_) | Err(Error { errno: ERESTART }) => Ok(()), + Err(error) => Err(error), + } +} + +static UMASK: AtomicU32 = AtomicU32::new(0o022); + +/// Controls the set of bits removed from the `mode` mask when new file descriptors are created. +/// +/// Must be validated by the caller +// +// TODO: validate here? +#[inline] +pub fn swap_umask(mask: u32) -> u32 { + UMASK.swap(mask, Ordering::AcqRel) +} + +#[inline] +pub fn get_umask() -> u32 { + UMASK.load(Ordering::Acquire) +} + +/// Real/Effective/Set-User/Group ID +pub struct Resugid { + pub ruid: T, + pub euid: T, + pub suid: T, + pub rgid: T, + pub egid: T, + pub sgid: T, +} + +/// Sets [res][ug]id, fields that are None will be unchanged. +pub fn posix_setresugid(ids: &Resugid>) -> Result<()> { + // TODO: not sure how "tmp" an IPC call is? + let _sig_guard = tmp_disable_signals(); + let mut guard = DYNAMIC_PROC_INFO.lock(); + + let mut buf = [0_u8; size_of::() * 6]; + plain::slice_from_mut_bytes(&mut buf) + .unwrap() + .copy_from_slice(&[ + ids.ruid.unwrap_or(u32::MAX), + ids.euid.unwrap_or(u32::MAX), + ids.suid.unwrap_or(u32::MAX), + ids.rgid.unwrap_or(u32::MAX), + ids.egid.unwrap_or(u32::MAX), + ids.sgid.unwrap_or(u32::MAX), + ]); + + this_proc_call(&mut buf, CallFlags::empty(), &[ProcCall::SetResugid as u64])?; + + if let Some(ruid) = ids.ruid { + guard.ruid = ruid; + } + if let Some(euid) = ids.euid { + guard.euid = euid; + } + if let Some(suid) = ids.suid { + guard.suid = suid; + } + if let Some(rgid) = ids.rgid { + guard.rgid = rgid; + } + if let Some(egid) = ids.egid { + guard.egid = egid; + } + if let Some(sgid) = ids.sgid { + guard.sgid = sgid; + } + + Ok(()) +} +pub fn posix_getresugid() -> Resugid { + let _sig_guard = tmp_disable_signals(); + let DynamicProcInfo { + ruid, + euid, + suid, + rgid, + egid, + sgid, + .. + } = *DYNAMIC_PROC_INFO.lock(); + Resugid { + ruid, + euid, + suid, + rgid, + egid, + sgid, + } +} +pub fn getens() -> Result { + read_proc_meta(crate::current_proc_fd()).map(|meta| meta.ens as usize) +} +pub fn get_proc_credentials(cap_fd: usize, target_pid: usize, buf: &mut [u8]) -> Result { + if buf.len() < size_of::() { + return Err(Error::new(EINVAL)); + } + proc_call( + cap_fd, + buf, + CallFlags::empty(), + &[ProcCall::GetProcCredentials as u64, target_pid as u64], + ) +} +pub fn posix_exit(status: i32) -> ! { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Exit as u64, (status & 0xFF) as u64], + ) + .expect("failed to call proc mgr with Exit"); + let _ = syscall::write(1, b"redox-rt: ProcCall::Exit FAILED, abort()ing!\n"); + core::intrinsics::abort(); +} +pub fn posix_getpgid(pid: usize) -> Result { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Setpgid as u64, pid as u64, u64::wrapping_neg(1)], + ) +} +pub fn posix_setpgid(pid: usize, pgid: usize) -> Result<()> { + if pgid == usize::wrapping_neg(1) { + return Err(Error::new(EINVAL)); + } + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Setpgid as u64, pid as u64, pgid as u64], + )?; + Ok(()) +} +pub fn posix_getsid(pid: usize) -> Result { + this_proc_call( + &mut [], + CallFlags::empty(), + &[ProcCall::Getsid as u64, pid as u64], + ) +} +pub fn posix_setsid() -> Result { + this_proc_call(&mut [], CallFlags::empty(), &[ProcCall::Setsid as u64])?; + Ok(posix_getpid()) +} +pub fn posix_nanosleep(rqtp: &TimeSpec, rmtp: &mut TimeSpec) -> Result<()> { + wrapper(false, false, || syscall::nanosleep(rqtp, rmtp))?; + Ok(()) +} +pub fn setns(fd: usize) -> Option { + let mut info = DYNAMIC_PROC_INFO.lock(); + let new_fd_guard = FdGuard::new(fd).to_upper().unwrap(); + let old_fd_guard = replace(&mut info.ns_fd, Some(new_fd_guard)); + old_fd_guard +} +pub fn getns() -> Result { + let cur_ns = crate::current_namespace_fd()?; + if cur_ns == usize::MAX { + Err(Error::new(ENODEV)) + } else { + Ok(cur_ns) + } +} +pub fn open>(path: T, flags: usize) -> Result { + let path = path.as_ref(); + let fcntl_flags = flags & syscall::O_FCNTL_MASK; + unsafe { + syscall::syscall5( + syscall::SYS_OPENAT, + crate::current_namespace_fd()?, + path.as_ptr() as usize, + path.len(), + flags, + fcntl_flags, + ) + } +} +pub fn openat>( + fd: usize, + path: T, + flags: usize, + fcntl_flags: usize, +) -> Result { + let path = path.as_ref(); + unsafe { + syscall::syscall5( + syscall::SYS_OPENAT, + fd, + path.as_ptr() as usize, + path.len(), + flags, + fcntl_flags, + ) + } +} +pub fn unlink>(path: T, flags: usize) -> Result { + let path = path.as_ref(); + unsafe { + syscall::syscall4( + syscall::SYS_UNLINKAT, + crate::current_namespace_fd()?, + path.as_ptr() as usize, + path.len(), + flags, + ) + } +} +pub fn mkns(names: &[IoSlice]) -> Result { + let mut buf = Vec::from((NsDup::ForkNs as usize).to_ne_bytes()); + for name in names { + let name_bytes = name.as_slice(); + let len = name_bytes.len(); + let _scheme_name = core::str::from_utf8(name_bytes).map_err(|_| Error::new(EINVAL))?; + buf.extend_from_slice(&len.to_ne_bytes()); + buf.extend_from_slice(name_bytes); + } + FdGuard::new(syscall::dup(crate::current_namespace_fd()?, &buf)?).to_upper() +} +pub fn register_scheme_to_ns(ns_fd: usize, name: &str, cap_fd: usize) -> Result<()> { + let mut buf = alloc::vec::Vec::from((NsDup::IssueRegister as usize).to_ne_bytes()); + buf.extend_from_slice(name.as_bytes()); + let ns_this_scheme = FdGuard::new(syscall::dup(ns_fd, &buf)?); + let cap_bytes = cap_fd.to_ne_bytes(); + ns_this_scheme.call_wo(&cap_bytes, CallFlags::FD, &[])?; + Ok(()) +} +pub fn std_fs_call_ro(fd: usize, payload: &mut [u8], metadata: &StdFsCallMeta) -> Result { + sys_call_ro(fd, payload, CallFlags::STD_FS, metadata) +} +pub fn std_fs_call_wo(fd: usize, payload: &[u8], metadata: &StdFsCallMeta) -> Result { + sys_call_wo(fd, payload, CallFlags::STD_FS, metadata) +} +pub fn std_fs_call_rw(fd: usize, payload: &mut [u8], metadata: &StdFsCallMeta) -> Result { + sys_call_rw(fd, payload, CallFlags::STD_FS, metadata) +} +pub fn fstat(fd: usize, stat: &mut syscall::Stat) -> Result { + std_fs_call_ro(fd, stat, &StdFsCallMeta::new(StdFsCallKind::Fstat, 0, 0)) +} diff --git a/redox-rt/src/thread.rs b/redox-rt/src/thread.rs new file mode 100644 index 0000000000..9ad1d4615a --- /dev/null +++ b/redox-rt/src/thread.rs @@ -0,0 +1,70 @@ +use core::mem::size_of; + +use syscall::Result; + +use crate::{RtTcb, arch::*, proc::*, signal::tmp_disable_signals, static_proc_info}; + +/// Spawns a new context sharing the same address space as the current one (i.e. a new thread). +pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result { + let proc_info = static_proc_info(); + let cur_proc_fd = proc_info.proc_fd.as_ref().unwrap(); + + let cur_thr_fd = RtTcb::current().thread_fd(); + let new_thr_fd = cur_proc_fd.dup(b"new-thread")?.to_upper().unwrap(); + + // Inherit existing address space + { + let cur_addr_space_fd = cur_thr_fd.dup(b"addrspace")?; + let new_addr_space_sel_fd = new_thr_fd.dup(b"current-addrspace")?; + + let buf = create_set_addr_space_buf( + cur_addr_space_fd.as_raw_fd(), + __relibc_internal_rlct_clone_ret as *const () as usize, + stack as usize, + ); + new_addr_space_sel_fd.write(&buf)?; + } + + // Inherit reference to file table + { + let cur_filetable_fd = cur_thr_fd.dup(b"filetable")?; + let new_filetable_sel_fd = new_thr_fd.dup(b"current-filetable")?; + + new_filetable_sel_fd.write(&usize::to_ne_bytes(cur_filetable_fd.as_raw_fd()))?; + } + + // Since the signal handler is not yet initialized, signals specifically targeting the thread + // (relibc is only required to implement thread-specific signals that already originate from + // the same process) will be discarded. Process-specific signals will ignore this new thread, + // until it has initialized its own signal handler. + + let start_fd = new_thr_fd.dup(b"start")?; + + let fd = new_thr_fd.as_raw_fd(); + unsafe { + tcb.thr_fd.get().write(Some(new_thr_fd)); + } + + // Unblock context. + start_fd.write(&[0])?; + + Ok(fd) +} + +pub unsafe fn exit_this_thread(stack_base: *mut (), stack_size: usize) -> ! { + let _guard = tmp_disable_signals(); + + let tcb = RtTcb::current(); + // TODO: modify interface so it writes directly to the thread fd? + let status_fd = tcb.thread_fd().dup(b"status").unwrap(); + + let _ = unsafe { syscall::funmap(tcb as *const RtTcb as usize, syscall::PAGE_SIZE) }; + + let mut buf = [0; size_of::() * 3]; + plain::slice_from_mut_bytes(&mut buf) + .unwrap() + .copy_from_slice(&[usize::MAX, stack_base as usize, stack_size]); + // TODO: SYS_CALL w/CONSUME + status_fd.write(&buf).unwrap(); + unreachable!() +} diff --git a/renamesyms.sh b/renamesyms.sh new file mode 100755 index 0000000000..4ab73f8d54 --- /dev/null +++ b/renamesyms.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +set -e + +target=$1 +deps_dir=$2 + +if [ -z "$target" ] || [ -z "$deps_dir" ]; then + echo "Usage:\n\t./renamesyms.sh TARGET DEPS_DIR" + exit 1 +fi + +if [ ! -f "$target" ]; then + echo "Target file '$target' does not exist" + exit 1 +fi +if [ ! -d "$deps_dir" ] ; then + echo "Deps dir '$deps_dir' does not exist or not a directory" + exit 1 +fi + +symbols_file=`mktemp` +special_syms=( + __rdl_oom + __rg_alloc + __rg_alloc_zeroed + __rg_dealloc + __rg_oom + __rg_realloc + __rust_alloc + __rust_alloc_error_handler + __rust_alloc_error_handler_should_panic + __rust_alloc_zeroed + __rust_dealloc + __rust_no_alloc_shim_is_unstable + __rust_realloc +) + +for dep in `find $deps_dir -type f -name "*.rlib"`; do + "${NM}" --format=posix -g "$dep" 2>/dev/null | sed 's/.*:.*//g' | awk '{if ($2 == "T") print $1}' | sed 's/^\(.*\)$/\1 __relibc_\1/g' >> $symbols_file +done + +for special_sym in "${special_syms[@]}"; do + echo "$special_sym __relibc_$special_sym" >> $symbols_file +done + +mangled_alloc_syms=$("${NM}" --format=posix -g "$target" 2>/dev/null | awk '{print $1}' | grep "7___rustc" || true) +for sym in $mangled_alloc_syms; do + echo "$sym __relibc_$sym" >> $symbols_file +done + +sorted_file=`mktemp` +sort -u "$symbols_file" > "$sorted_file" +rm -f "$symbols_file" + +"${OBJCOPY}" --redefine-syms="$sorted_file" "$target" + +rm -f "$sorted_file" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..b3394ccad7 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2025-11-15" +components = ["rust-src"] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000..3a0fc9168d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,21 @@ +blank_lines_lower_bound = 0 +blank_lines_upper_bound = 1 +brace_style = "SameLineWhere" +disable_all_formatting = false +edition = "2024" +empty_item_single_line = true +fn_single_line = false +force_explicit_abi = true +format_strings = false +hard_tabs = false +imports_granularity = "Crate" +imports_indent = "Block" +imports_layout = "Mixed" +indent_style = "Block" +max_width = 100 +newline_style = "Unix" +show_parse_errors = true +skip_children = false +tab_spaces = 4 +trailing_comma = "Vertical" +where_single_line = false diff --git a/src/c/stdlib.c b/src/c/stdlib.c new file mode 100644 index 0000000000..62e98108bb --- /dev/null +++ b/src/c/stdlib.c @@ -0,0 +1,13 @@ +double strtod(const char *nptr, char **endptr); + +long double strtold(const char *nptr, char **endptr) { + return (long double)strtod(nptr, endptr); +} + +double relibc_ldtod(const long double* val) { + return (double)(*val); +} + +void relibc_dtold(double val, long double* out) { + *out = (long double)val; +} diff --git a/src/c_str.rs b/src/c_str.rs new file mode 100644 index 0000000000..37880314a7 --- /dev/null +++ b/src/c_str.rs @@ -0,0 +1,322 @@ +//! Nul-terminated byte strings. + +use core::{marker::PhantomData, ptr::NonNull, str::Utf8Error}; + +use alloc::{borrow::Cow, string::String}; + +use crate::platform::types::{c_char, wchar_t}; + +mod private { + pub trait Sealed {} +} +#[derive(Clone, Copy, Debug)] +pub enum Thin {} + +#[derive(Clone, Copy, Debug)] +pub enum Wide {} + +impl private::Sealed for Thin {} +impl private::Sealed for Wide {} + +pub trait Kind: private::Sealed + Copy + 'static { + /// c_char or wchar_t + type C: Copy + 'static; + // u8 or u32 + type Char: Copy + From + Into + PartialEq + 'static; + + const NUL: Self::Char; + + const IS_THIN_NOT_WIDE: bool; + + fn r2c(c: Self::Char) -> Self::C; + fn c2r(c: Self::C) -> Self::Char; + + fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]>; + fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]>; + + unsafe fn strlen(s: *const Self::C) -> usize; + unsafe fn strchr(s: *const Self::C, c: Self::C) -> *const Self::C; + unsafe fn strchrnul(s: *const Self::C, c: Self::C) -> *const Self::C; +} +impl Kind for Thin { + type C = c_char; + type Char = u8; + + const NUL: Self::Char = 0; + const IS_THIN_NOT_WIDE: bool = true; + + unsafe fn strlen(s: *const c_char) -> usize { + unsafe { crate::header::string::strlen(s) } + } + unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char { + unsafe { crate::header::string::strchr(s, c.into()) } + } + unsafe fn strchrnul(s: *const c_char, c: c_char) -> *const c_char { + unsafe { crate::header::string::strchrnul(s, c.into()) } + } + fn r2c(c: u8) -> c_char { + c as _ + } + fn c2r(c: c_char) -> u8 { + c as _ + } + fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]> { + Some(b) + } + fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]> { + Some(c) + } +} +impl Kind for Wide { + type C = wchar_t; + type Char = u32; + + const NUL: Self::Char = 0; + const IS_THIN_NOT_WIDE: bool = false; + + unsafe fn strlen(s: *const Self::C) -> usize { + unsafe { crate::header::wchar::wcslen(s) } + } + unsafe fn strchr(s: *const Self::C, c: Self::C) -> *const Self::C { + unsafe { crate::header::wchar::wcschr(s, c) } + } + unsafe fn strchrnul(mut s: *const Self::C, c: Self::C) -> *const Self::C { + // TODO: optimized function + while unsafe { s.read() } != c && unsafe { s.read() } != 0 { + s = unsafe { s.add(1) }; + } + s + } + fn r2c(c: Self::Char) -> Self::C { + c as _ + } + fn c2r(c: Self::C) -> Self::Char { + c as _ + } + fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]> { + None + } + fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]> { + None + } +} + +/// Safe wrapper for immutable borrowed C strings, guaranteed to be the same layout as `*const u8`. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct NulStr<'a, T: Kind> { + ptr: NonNull, + _marker: PhantomData<&'a [u8]>, +} +pub type CStr<'a> = NulStr<'a, Thin>; +pub type WStr<'a> = NulStr<'a, Wide>; + +impl<'a, T: Kind> NulStr<'a, T> { + /// Safety + /// + /// The ptr must be valid up to and including the first NUL byte from the base ptr. + pub const unsafe fn from_ptr(ptr: *const T::C) -> Self { + Self { + ptr: unsafe { NonNull::new_unchecked(ptr.cast_mut()) }, + _marker: PhantomData, + } + } + pub unsafe fn from_nullable_ptr(ptr: *const T::C) -> Option { + if ptr.is_null() { + None + } else { + Some(unsafe { Self::from_ptr(ptr) }) + } + } + /// Look for the closest occurence of `c`, and if found, split the string into a slice up to + /// that byte and a `CStr` starting at that byte. + #[inline] + #[doc(alias = "strchrnul")] + pub fn find_get_subslice_or_all( + self, + c: impl Into, + ) -> Result<(&'a [T::Char], Self), (&'a [T::Char], Self)> { + let c = c.into(); + + // SAFETY: strchrnul expects self.as_ptr() to be valid up to and including its last NUL + // byte + let found = unsafe { T::strchrnul(self.as_ptr(), T::r2c(c)) }; + + // SAFETY: the pointer returned from strchrnul is always a substring of this string, and + // hence always valid as a CStr. + let found = unsafe { Self::from_ptr(found) }; + let until = unsafe { self.slice_until_substr(found) }; + + if found.first() == T::NUL { + // The character was not found, and we got the end of the string instead. + Err((until, found)) + } else { + Ok((until, found)) + } + } + /// # Safety + /// + /// `substr` must be contained within `self` + #[inline] + pub unsafe fn slice_until_substr(self, substr: NulStr<'_, T>) -> &'a [T::Char] { + let index = unsafe { + // SAFETY: the sub-pointer as returned by strchr must be derived from the same + // allocation + substr.as_ptr().offset_from(self.as_ptr()) as usize + }; + unsafe { core::slice::from_raw_parts(self.as_ptr().cast::(), index) } + } + /// Look for the closest occurence of `c`, and if found, split the string into a slice up to + /// that byte and a `CStr` starting at that byte. + #[inline] + pub fn find_get_subslice(self, c: T::Char) -> Option<(&'a [T::Char], Self)> { + let rest = self.find(c)?; + + // SAFETY: the output of strchr is obviously a substring if it doesn't return NULL + Some((unsafe { self.slice_until_substr(rest) }, rest)) + } + /// Look for the closest occurence of `c`, and return a new string starting at that byte if + /// found. + #[doc(alias = "strchr")] + #[doc(alias = "wcschr")] + #[inline] + pub fn find(self, c: T::Char) -> Option { + unsafe { + // SAFETY: the only requirement is for self.as_ptr() to be valid up to and including + // the nearest NUL byte, which this type requires + let ret = T::strchr(self.as_ptr(), T::r2c(c)); + // SAFETY: strchr must either return NULL (not found) or a substring of self, which can + // never exceed the nearest NUL byte of self + Self::from_nullable_ptr(ret) + } + } + // TODO: strrchr, strchrnul wrappers + + #[inline] + pub fn contains(self, c: T::Char) -> bool { + self.find(c).is_some() + } + #[inline] + pub fn first(self) -> T::Char { + unsafe { + // SAFETY: Self must be valid up to and including its nearest NUL byte, which certainly + // implies its readable length is nonzero (string is empty if this first byte is 0). + T::c2r(self.ptr.read()) + } + } + #[inline] + pub fn first_char(self) -> Option { + char::from_u32(self.first().into()) + } + /// Same as `split_first` except also requires that the first char be convertible into `char` + #[inline] + pub fn split_first_char(self) -> Option<(char, Self)> { + self.split_first() + .and_then(|(c, r)| Some((char::from_u32(c.into())?, r))) + } + /// Split this string into `Some((first_byte, string_after_that))` or `None` if empty. + #[inline] + pub fn split_first(self) -> Option<(T::Char, Self)> { + if self.first() == T::NUL { + return None; + } + Some((self.first(), unsafe { + Self::from_ptr(self.as_ptr().add(1)) + })) + } + pub fn to_chars_with_nul(self) -> &'a [T::Char] { + unsafe { + // SAFETY: The string must be valid at least until (and including) the NUL byte. + let len = T::strlen(self.ptr.as_ptr()); + core::slice::from_raw_parts(self.ptr.as_ptr().cast(), len + 1) + } + } + pub fn to_chars(self) -> &'a [T::Char] { + let s = self.to_chars_with_nul(); + &s[..s.len() - 1] + } + pub const fn as_ptr(self) -> *const T::C { + self.ptr.as_ptr() + } + pub const unsafe fn from_chars_with_nul_unchecked(chars: &'a [T::Char]) -> Self { + unsafe { Self::from_ptr(chars.as_ptr().cast()) } + } + pub fn from_chars_with_nul(chars: &'a [T::Char]) -> Result { + if chars.last() != Some(&T::NUL) || chars[..chars.len() - 1].contains(&T::NUL) { + return Err(FromCharsWithNulError); + } + + Ok(unsafe { Self::from_chars_with_nul_unchecked(chars) }) + } + pub fn from_chars_until_nul(chars: &'a [T::Char]) -> Result { + if !chars.contains(&T::NUL) { + return Err(FromCharsUntilNulError); + } + + Ok(unsafe { Self::from_chars_with_nul_unchecked(chars) }) + } + /// Scan the string to get its length. + #[doc(alias = "strlen")] + #[doc(alias = "wcslen")] + pub fn len(self) -> usize { + self.to_chars().len() + } + #[inline] + pub fn is_empty(&self) -> bool { + self.first() == T::NUL + } +} +impl<'a> CStr<'a> { + pub fn to_owned_cstring(self) -> CString { + CString::from(unsafe { core::ffi::CStr::from_ptr(self.ptr.as_ptr()) }) + } + pub fn borrow(string: &'a CString) -> Self { + unsafe { Self::from_ptr(string.as_ptr()) } + } + #[inline] + pub fn to_bytes(self) -> &'a [u8] { + self.to_chars() + } + #[inline] + pub fn to_bytes_with_nul(self) -> &'a [u8] { + self.to_chars_with_nul() + } + pub fn to_str(self) -> Result<&'a str, Utf8Error> { + core::str::from_utf8(self.to_bytes()) + } + pub fn to_string_lossy(self) -> Cow<'a, str> { + String::from_utf8_lossy(self.to_bytes()) + } + #[inline] + pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &'a [u8]) -> Self { + unsafe { Self::from_chars_with_nul_unchecked(bytes) } + } + #[inline] + pub fn from_bytes_with_nul(bytes: &'a [u8]) -> Result { + Self::from_chars_with_nul(bytes) + } + #[inline] + pub fn from_bytes_until_nul(bytes: &'a [u8]) -> Result { + Self::from_chars_until_nul(bytes) + } +} + +unsafe impl Send for NulStr<'_, T> {} +unsafe impl Sync for NulStr<'_, T> {} + +impl From<&core::ffi::CStr> for CStr<'_> { + fn from(s: &core::ffi::CStr) -> Self { + // SAFETY: + // * We can assume that `s` is valid because the caller should have upheld its + // safety concerns when constructing it. + unsafe { Self::from_ptr(s.as_ptr()) } + } +} + +#[derive(Debug)] +pub struct FromCharsWithNulError; + +#[derive(Debug)] +pub struct FromCharsUntilNulError; + +pub use alloc::ffi::CString; diff --git a/src/c_vec.rs b/src/c_vec.rs new file mode 100644 index 0000000000..e5e35af527 --- /dev/null +++ b/src/c_vec.rs @@ -0,0 +1,294 @@ +//! Equivalent of Rust's `Vec`, but using relibc's own allocator. + +use crate::{ + io::{self, Write}, + platform::{self, WriteByte, types::*}, +}; +use core::{ + cmp, fmt, + iter::IntoIterator, + mem, + ops::{Deref, DerefMut}, + ptr::{self, NonNull}, + slice, +}; + +/// Error that occurs when an allocation fails +#[derive(Debug, Default, Hash, PartialEq, Eq, Clone, Copy)] +pub struct AllocError; + +/// A normal vector allocated in Rust needs to be dropped from Rust +/// too, in order to avoid UB. This CVec is an abstraction that works +/// using only C allocations functions and can therefore be dropped +/// from C. Just like the Rust Vec, this does bounds checks to assure +/// you never reach isize::MAX. Unless you need to drop something from +/// C, prefer Rust's builtin Vec. +pub struct CVec { + ptr: NonNull, + len: usize, + cap: usize, +} +impl CVec { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + ptr: NonNull::dangling(), + len: 0, + cap: 0, + } + } + fn check_bounds(i: usize) -> Result { + if i > isize::MAX as usize { + Err(AllocError) + } else { + Ok(i) + } + } + fn check_mul(x: usize, y: usize) -> Result { + x.checked_mul(y) + .ok_or(AllocError) + .and_then(Self::check_bounds) + } + pub fn with_capacity(cap: usize) -> Result { + if cap == 0 { + return Ok(Self::new()); + } + let size = Self::check_mul(cap, mem::size_of::())?; + let ptr = NonNull::new(unsafe { platform::alloc(size).cast::() }).ok_or(AllocError)?; + Ok(Self { ptr, len: 0, cap }) + } + unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> { + let size = Self::check_mul(cap, mem::size_of::())?; + let ptr = if cap == 0 { + NonNull::dangling() + } else if self.cap > 0 { + NonNull::new( + unsafe { platform::realloc(self.ptr.as_ptr().cast::(), size) }.cast::(), + ) + .ok_or(AllocError)? + } else { + NonNull::new((unsafe { platform::alloc(size) }).cast::()).ok_or(AllocError)? + }; + self.ptr = ptr; + self.cap = cap; + Ok(()) + } + unsafe fn drop_range(&mut self, start: usize, end: usize) { + let mut start = unsafe { self.ptr.as_ptr().add(start) }; + let end = unsafe { self.ptr.as_ptr().add(end) }; + while start < end { + unsafe { ptr::drop_in_place(start) }; + start = unsafe { start.add(1) }; + } + } + + // Push stuff + + pub fn reserve(&mut self, required: usize) -> Result<(), AllocError> { + let required_len = self + .len + .checked_add(required) + .ok_or(AllocError) + .and_then(Self::check_bounds)?; + if required_len > self.cap { + let new_cap = cmp::min(required_len.next_power_of_two(), isize::MAX as usize); + unsafe { + self.resize(new_cap)?; + } + } + Ok(()) + } + pub fn push(&mut self, elem: T) -> Result<(), AllocError> { + self.reserve(1)?; + unsafe { + ptr::write(self.ptr.as_ptr().add(self.len), elem); + } + self.len += 1; // no need to bounds check, as new len <= cap + Ok(()) + } + pub fn extend_from_slice(&mut self, elems: &[T]) -> Result<(), AllocError> + where + T: Copy, + { + self.reserve(elems.len())?; + unsafe { + ptr::copy_nonoverlapping(elems.as_ptr(), self.ptr.as_ptr().add(self.len), elems.len()); + } + self.len += elems.len(); // no need to bounds check, as new len <= cap + Ok(()) + } + pub fn append(&mut self, other: &mut Self) -> Result<(), AllocError> { + let len = other.len; + other.len = 0; // move + self.reserve(len)?; + unsafe { + ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), len); + } + self.len += other.len(); // no need to bounds check, as new len <= cap + Ok(()) + } + + // Pop stuff + + pub fn truncate(&mut self, len: usize) { + if len < self.len { + unsafe { + let old_len = self.len; + self.drop_range(len, old_len); + } + self.len = len; + } + } + pub fn shrink_to_fit(&mut self) -> Result<(), AllocError> { + if self.len < self.cap { + unsafe { + let new_cap = self.len; + self.resize(new_cap)?; + } + } + Ok(()) + } + pub fn pop(&mut self) -> Option { + if self.is_empty() { + None + } else { + let elem = unsafe { ptr::read(self.as_ptr().add(self.len - 1)) }; + self.len -= 1; + Some(elem) + } + } + + // Misc stuff + + pub fn capacity(&self) -> usize { + self.cap + } + pub fn as_ptr(&self) -> *const T { + self.ptr.as_ptr() + } + pub fn as_mut_ptr(&mut self) -> *mut T { + self.ptr.as_ptr() + } + /// Leaks the inner data. This is safe to drop from C! + pub fn leak(mut self) -> *mut T { + let ptr = self.as_mut_ptr(); + mem::forget(self); + ptr + } +} +impl Deref for CVec { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + } +} +impl DerefMut for CVec { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } + } +} +impl Drop for CVec { + fn drop(&mut self) { + unsafe { + let len = self.len; + self.drop_range(0, len); + } + } +} +impl<'a, T> IntoIterator for &'a CVec { + type Item = <&'a [T] as IntoIterator>::Item; + type IntoIter = <&'a [T] as IntoIterator>::IntoIter; + fn into_iter(self) -> Self::IntoIter { + <&[T]>::into_iter(&*self) + } +} +impl<'a, T> IntoIterator for &'a mut CVec { + type Item = <&'a mut [T] as IntoIterator>::Item; + type IntoIter = <&'a mut [T] as IntoIterator>::IntoIter; + fn into_iter(self) -> Self::IntoIter { + <&mut [T]>::into_iter(&mut *self) + } +} + +impl Write for CVec { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend_from_slice(buf).map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + "AllocStringWriter::write failed to allocate", + ) + })?; + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} +impl fmt::Write for CVec { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write(s.as_bytes()).map_err(|_| fmt::Error)?; + Ok(()) + } +} +impl WriteByte for CVec { + fn write_u8(&mut self, byte: u8) -> fmt::Result { + self.write(&[byte]).map_err(|_| fmt::Error)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::CVec; + + #[test] + fn push_pop() { + let mut vec = CVec::new(); + vec.push(1).unwrap(); + vec.push(2).unwrap(); + vec.push(3).unwrap(); + assert_eq!(&vec[..], &[1, 2, 3]); + assert_eq!(vec.pop().unwrap(), 3); + assert_eq!(&vec[..], &[1, 2]); + } + #[test] + fn extend_from_slice() { + use crate::io::Write; + + let mut vec = CVec::new(); + vec.extend_from_slice(&[1, 2, 3]).unwrap(); + vec.extend_from_slice(&[4, 5, 6]).unwrap(); + assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6]); + assert_eq!(vec.write(&[7, 8, 9]).unwrap(), 3); + assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6, 7, 8, 9]); + } + #[test] + fn dropped() { + use alloc::rc::Rc; + + let counter = Rc::new(()); + let mut vec = CVec::with_capacity(3).unwrap(); + vec.push(Rc::clone(&counter)).unwrap(); + vec.push(Rc::clone(&counter)).unwrap(); + vec.push(Rc::clone(&counter)).unwrap(); + assert_eq!(Rc::strong_count(&counter), 4); + + let popped = vec.pop().unwrap(); + assert_eq!(Rc::strong_count(&counter), 4); + drop(popped); + assert_eq!(Rc::strong_count(&counter), 3); + + vec.push(Rc::clone(&counter)).unwrap(); + vec.push(Rc::clone(&counter)).unwrap(); + vec.push(Rc::clone(&counter)).unwrap(); + + assert_eq!(vec.len(), 5); + assert_eq!(Rc::strong_count(&counter), 6); + vec.truncate(1); + assert_eq!(Rc::strong_count(&counter), 2); + + drop(vec); + assert_eq!(Rc::strong_count(&counter), 1); + } +} diff --git a/src/crt0/Cargo.toml b/src/crt0/Cargo.toml new file mode 100644 index 0000000000..3ca5057316 --- /dev/null +++ b/src/crt0/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "crt0" +version = "0.1.0" +authors = ["Jeremy Soller "] +edition = "2024" + +[lib] +name = "crt0" +crate-type = ["staticlib"] + +[lints] +workspace = true diff --git a/src/crt0/src/lib.rs b/src/crt0/src/lib.rs new file mode 100644 index 0000000000..fef86f62f4 --- /dev/null +++ b/src/crt0/src/lib.rs @@ -0,0 +1,105 @@ +//! crt0 + +#![no_std] +#![feature(linkage)] + +use core::{ + arch::global_asm, + ffi::{c_char, c_int}, +}; + +#[cfg(target_arch = "aarch64")] +global_asm!( + " + .globl _start +_start: + mov x0, sp + and sp, x0, #0xfffffffffffffff0 //align sp + bl relibc_crt0 +" +); + +#[cfg(target_arch = "x86")] +global_asm!( + " + .globl _start + .type _start, @function +_start: + sub esp, 8 + + mov DWORD PTR [esp], 0x00001F80 + # ldmxcsr [esp] + mov WORD PTR [esp], 0x037F + fldcw [esp] + + add esp, 8 + + push esp + call relibc_crt0 + .size _start, . - _start +" +); + +#[cfg(target_arch = "x86_64")] +global_asm!( + " + .globl _start + .type _start, @function +_start: + mov rdi, rsp + and rsp, 0xFFFFFFFFFFFFFFF0 + + sub rsp, 8 + + mov DWORD PTR [rsp], 0x00001F80 + ldmxcsr [rsp] + mov WORD PTR [rsp], 0x037F + fldcw [rsp] + + add rsp, 8 + + call relibc_crt0 + .size _start, . - _start +" +); + +#[cfg(target_arch = "riscv64")] +global_asm!( + " + .globl _start +_start: + mv a0, sp + la t0, relibc_crt0 + jalr ra, t0 +" +); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn relibc_crt0(sp: usize) -> ! { + // This wrapper ensures a dynamic libc.so can access a hidden main function + //TODO: common definition of types + unsafe extern "C" { + fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int; + fn relibc_start_v1( + sp: usize, + main: unsafe extern "C" fn( + argc: isize, + argv: *mut *mut c_char, + envp: *mut *mut c_char, + ) -> c_int, + ) -> !; + } + unsafe { relibc_start_v1(sp, main) } +} + +#[linkage = "weak"] +#[unsafe(no_mangle)] +pub extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! { + loop {} +} + +#[panic_handler] +#[linkage = "weak"] +pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! { + relibc_panic(pi) +} diff --git a/src/crti/Cargo.toml b/src/crti/Cargo.toml new file mode 100644 index 0000000000..be768bed71 --- /dev/null +++ b/src/crti/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "crti" +version = "0.1.0" +authors = ["jD91mZM2 "] +edition = "2024" + +[lints] +workspace = true diff --git a/src/crti/src/lib.rs b/src/crti/src/lib.rs new file mode 100644 index 0000000000..c0ca4291cf --- /dev/null +++ b/src/crti/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +// we don't support _init/_fini functions, only init/fini arrays diff --git a/src/crtn/Cargo.toml b/src/crtn/Cargo.toml new file mode 100644 index 0000000000..a32f11631b --- /dev/null +++ b/src/crtn/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "crtn" +version = "0.1.0" +authors = ["jD91mZM2 "] +edition = "2024" + +[lints] +workspace = true diff --git a/src/crtn/src/lib.rs b/src/crtn/src/lib.rs new file mode 100644 index 0000000000..c0ca4291cf --- /dev/null +++ b/src/crtn/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +// we don't support _init/_fini functions, only init/fini arrays diff --git a/src/cxa.rs b/src/cxa.rs new file mode 100644 index 0000000000..cd75de7440 --- /dev/null +++ b/src/cxa.rs @@ -0,0 +1,103 @@ +use crate::platform::types::{c_int, c_void}; +use alloc::vec::Vec; +use core::cell::RefCell; +use spin::Mutex; + +#[derive(Clone, Copy)] +struct CxaAtExitFunc { + func: extern "C" fn(*mut c_void), + arg: usize, + dso: usize, +} + +#[derive(Clone, Copy)] +struct CxaThreadAtExitFunc { + func: extern "C" fn(*mut c_void), + obj: *mut c_void, + dso: *mut c_void, +} + +static CXA_ATEXIT_FUNCS: Mutex>> = Mutex::new(Vec::new()); +#[thread_local] +static DTORS: RefCell> = RefCell::new(Vec::new()); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __cxa_atexit( + func: Option, + arg: *mut c_void, + dso: *mut c_void, +) -> c_int { + let Some(func) = func else { + return 0; + }; + + let entry = CxaAtExitFunc { + func, + arg: arg as usize, + dso: dso as usize, + }; + + let mut funcs = CXA_ATEXIT_FUNCS.lock(); + + for slot in funcs.iter_mut() { + if slot.is_none() { + *slot = Some(entry); + return 0; + } + } + + // No empty slots + funcs.push(Some(entry)); + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __cxa_finalize(dso: *mut c_void) { + let mut funcs = CXA_ATEXIT_FUNCS.lock(); + + let dso_usize = dso as usize; + + for slot in funcs.iter_mut().rev() { + if let Some(entry) = slot.as_ref() + && (dso.is_null() || entry.dso == dso_usize) + && let Some(entry_to_run) = slot.take() + { + (entry_to_run.func)(entry_to_run.arg as *mut c_void); + } + } + + // clean up remaining list + if dso.is_null() { + funcs.clear(); + } else { + funcs.retain(|opt| opt.is_some()); + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __cxa_thread_atexit_impl( + func: extern "C" fn(*mut c_void), + obj: *mut c_void, + dso: *mut c_void, +) { + let entry = CxaThreadAtExitFunc { func, obj, dso }; + DTORS.borrow_mut().push(entry); +} + +// called internally +pub unsafe fn __cxa_thread_finalize() { + let mut dtors = DTORS.borrow_mut(); + while let Some(entry) = dtors.pop() { + (entry.func)(entry.obj); + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn _ITM_deregisterTMCloneTable(_ptr: *mut c_void) { + // No-op +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn _ITM_registerTMCloneTable(_ptr: *mut c_void, _len: usize) { + // No-op +} diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000000..777fbba8d8 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,51 @@ +use alloc::{string::String, vec::Vec}; + +use crate::{ + c_str::CStr, + fs::File, + header::fcntl, + io::{self, BufRead, BufReader}, +}; + +pub enum Separator { + Character(char), + Whitespace, +} + +pub struct Db { + reader: R, + separator: Separator, +} + +impl Db { + pub fn new(reader: R, separator: Separator) -> Self { + Db { reader, separator } + } + + pub fn read(&mut self) -> io::Result>> { + let mut line = String::new(); + if self.reader.read_line(&mut line)? == 0 { + return Ok(None); + } + + let vec = if let Some(not_comment) = line.trim().split('#').next() { + match self.separator { + Separator::Character(c) => not_comment.split(c).map(String::from).collect(), + Separator::Whitespace => not_comment.split_whitespace().map(String::from).collect(), + } + } else { + Vec::new() + }; + + Ok(Some(vec)) + } +} + +pub type FileDb = Db>; + +impl FileDb { + pub fn open(path: CStr, separator: Separator) -> io::Result { + let file = File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC)?; + Ok(Db::new(BufReader::new(file), separator)) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000..21d470b3b8 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,90 @@ +use alloc::boxed::Box; + +use crate::{header::errno::STR_ERROR, platform::types::c_int}; + +/// Positive error codes (EINVAL, not -EINVAL). +#[derive(Debug, Eq, PartialEq)] +// TODO: Move to a more generic place. +pub struct Errno(pub c_int); + +impl Errno { + pub fn sync(self) -> Self { + crate::platform::ERRNO.set(self.0); + self + } +} + +pub type Result = core::result::Result; + +#[cfg(target_os = "redox")] +impl From for Errno { + #[inline] + fn from(value: syscall::Error) -> Self { + Errno(value.errno) + } +} +#[cfg(target_os = "redox")] +impl From for syscall::Error { + #[inline] + fn from(value: Errno) -> Self { + syscall::Error::new(value.0) + } +} + +impl From for crate::io::Error { + #[inline] + fn from(Errno(errno): Errno) -> Self { + Self::from_raw_os_error(errno) + } +} + +// TODO: core::error::Error + +impl core::fmt::Display for Errno { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match usize::try_from(self.0).ok().and_then(|i| STR_ERROR.get(i)) { + Some(desc) => write!(f, "{desc}"), + None => write!(f, "unknown error ({})", self.0), + } + } +} + +pub trait ResultExt { + fn or_minus_one_errno(self) -> T; +} +impl> ResultExt for Result { + fn or_minus_one_errno(self) -> T { + match self { + Self::Ok(v) => v, + Self::Err(Errno(errno)) => { + crate::platform::ERRNO.set(errno); + T::from(-1) + } + } + } +} +pub trait ResultExtPtrMut { + fn or_errno_null_mut(self) -> *mut T; +} +impl ResultExtPtrMut for Result<*mut T, Errno> { + fn or_errno_null_mut(self) -> *mut T { + match self { + Self::Ok(ptr) => ptr, + Self::Err(Errno(errno)) => { + crate::platform::ERRNO.set(errno); + core::ptr::null_mut() + } + } + } +} +impl ResultExtPtrMut for Result, Errno> { + fn or_errno_null_mut(self) -> *mut T { + match self { + Self::Ok(ptr) => Box::into_raw(ptr), + Self::Err(Errno(errno)) => { + crate::platform::ERRNO.set(errno); + core::ptr::null_mut() + } + } + } +} diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000000..3f2b4265b0 --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,156 @@ +use crate::{ + c_str::CStr, + error::{Errno, ResultExt}, + header::{ + fcntl::O_CREAT, + sys_stat::stat, + unistd::{SEEK_CUR, SEEK_END, SEEK_SET}, + }, + io, + out::Out, + platform::{Pal, Sys, types::*}, +}; +use core::ops::Deref; + +pub struct File { + pub fd: c_int, + /// To avoid self referential FILE struct that needs both a reader and a writer, + /// make "reference" files that share fd but don't close on drop. + pub reference: bool, +} + +impl File { + pub fn new(fd: c_int) -> Self { + Self { + fd, + reference: false, + } + } + + pub fn open(path: CStr, oflag: c_int) -> Result { + Sys::open(path, oflag, 0) + .map(Self::new) + .map_err(Errno::sync) + } + + pub fn openat(dirfd: c_int, path: CStr, oflag: c_int) -> Result { + Sys::openat(dirfd, path, oflag, 0) + .map(Self::new) + .map_err(Errno::sync) + } + + pub fn create(path: CStr, oflag: c_int, mode: mode_t) -> Result { + Sys::open(path, oflag | O_CREAT, mode) + .map(Self::new) + .map_err(Errno::sync) + } + + pub fn createat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result { + Sys::openat(dirfd, path, oflag | O_CREAT, mode) + .map(Self::new) + .map_err(Errno::sync) + } + + pub fn sync_all(&self) -> Result<(), Errno> { + Sys::fsync(self.fd).map_err(Errno::sync) + } + + pub fn set_len(&self, size: u64) -> Result<(), Errno> { + Sys::ftruncate(self.fd, size as off_t).map_err(Errno::sync) + } + + pub fn fstat(&self) -> Result { + let mut file_st = stat::default(); + Sys::fstat(self.fd, Out::from_mut(&mut file_st)).map_err(Errno::sync)?; + Ok(file_st) + } + + pub fn try_clone(&self) -> io::Result { + Ok(Self::new(Sys::dup(self.fd)?)) + } + + /// Create a new file pointing to the same underlying descriptor. This file + /// will know it's a "reference" and won't close the fd. It will, however, + /// not prevent the original file from closing the fd. + pub unsafe fn get_ref(&self) -> Self { + Self { + fd: self.fd, + reference: true, + } + } +} + +impl io::Read for &File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match Sys::read(self.fd, buf).map(|read| read as ssize_t).or_minus_one_errno() /* TODO */ { + -1 => Err(io::last_os_error()), + ok => Ok(ok as usize), + } + } +} + +impl io::Write for &File { + fn write(&mut self, buf: &[u8]) -> io::Result { + match Sys::write(self.fd, buf) + .map(|read| read as ssize_t) + .or_minus_one_errno() + { + -1 => Err(io::last_os_error()), + ok => Ok(ok as usize), + } + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl io::Seek for &File { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + let (offset, whence) = match pos { + io::SeekFrom::Start(start) => (start as off_t, SEEK_SET), + io::SeekFrom::Current(current) => (current as off_t, SEEK_CUR), + io::SeekFrom::End(end) => (end as off_t, SEEK_END), + }; + + Ok(Sys::lseek(self.fd, offset, whence)? as u64) + } +} + +impl io::Read for File { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&mut &*self).read(buf) + } +} + +impl io::Write for File { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&mut &*self).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + (&mut &*self).flush() + } +} + +impl io::Seek for File { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + (&mut &*self).seek(pos) + } +} + +impl Deref for File { + type Target = c_int; + + fn deref(&self) -> &Self::Target { + &self.fd + } +} + +impl Drop for File { + fn drop(&mut self) { + if !self.reference { + let _ = Sys::close(self.fd); + } + } +} diff --git a/src/header/_aio/cbindgen.toml b/src/header/_aio/cbindgen.toml new file mode 100644 index 0000000000..fd387cb50e --- /dev/null +++ b/src/header/_aio/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = [] +include_guard = "_RELIBC_AIO_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/_aio/mod.rs b/src/header/_aio/mod.rs new file mode 100644 index 0000000000..b75ba38f78 --- /dev/null +++ b/src/header/_aio/mod.rs @@ -0,0 +1,75 @@ +//! `aio.h` implementation. +//! +//! See . + +use crate::{ + header::{bits_timespec::timespec, signal::sigevent}, + platform::types::{c_int, c_void}, +}; + +/// See . +pub struct aiocb { + pub aio_fildes: c_int, + pub aio_lio_opcode: c_int, + pub aio_reqprio: c_int, + pub aio_buf: *mut c_void, + pub aio_nbytes: usize, + pub aio_sigevent: sigevent, +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_read(aiocbp: *mut aiocb) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_write(aiocbp: *mut aiocb) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn lio_listio( + mode: c_int, + list: *const *const aiocb, + nent: c_int, + sig: *mut sigevent, +) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_error(aiocbp: *const aiocb) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_return(aiocbp: *mut aiocb) -> usize { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_cancel(fildes: c_int, aiocbp: *mut aiocb) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_suspend( + list: *const *const aiocb, + nent: c_int, + timeout: *const timespec, +) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn aio_fsync(operation: c_int, aiocbp: *mut aiocb) -> c_int { + unimplemented!(); +} diff --git a/src/header/_fenv/cbindgen.toml b/src/header/_fenv/cbindgen.toml new file mode 100644 index 0000000000..e8ca9b12ad --- /dev/null +++ b/src/header/_fenv/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["stdint.h", "sys/types.h"] +include_guard = "_RELIBC_FENV_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/_fenv/mod.rs b/src/header/_fenv/mod.rs new file mode 100644 index 0000000000..54dd24062c --- /dev/null +++ b/src/header/_fenv/mod.rs @@ -0,0 +1,85 @@ +//! `fenv.h` implementation. +//! +//! See . + +use crate::platform::types::c_int; + +/// See . +pub const FE_ALL_EXCEPT: c_int = 0; +/// See . +pub const FE_TONEAREST: c_int = 0; + +/// See . +pub type fexcept_t = u64; + +/// See . +#[repr(C)] +pub struct fenv_t { + pub cw: u64, +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fegetexceptflag(flagp: *mut fexcept_t, excepts: c_int) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fegetround() -> c_int { + FE_TONEAREST +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn feraiseexcept(except: c_int) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fesetexceptflag(flagp: *const fexcept_t, excepts: c_int) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fesetround(round: c_int) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int { + unimplemented!(); +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int { + unimplemented!(); +} diff --git a/src/header/_paths/cbindgen.toml b/src/header/_paths/cbindgen.toml new file mode 100644 index 0000000000..885a98f571 --- /dev/null +++ b/src/header/_paths/cbindgen.toml @@ -0,0 +1,5 @@ +include_guard = "_RELIBC_PATHS_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true diff --git a/src/header/_paths/linux.rs b/src/header/_paths/linux.rs new file mode 100644 index 0000000000..34cf290f7a --- /dev/null +++ b/src/header/_paths/linux.rs @@ -0,0 +1,29 @@ +//! Linux specific constants for paths.h. +//! Entirely borrowed from musl as there isn't a list of what to include. + +pub const _PATH_DEFPATH: &str = "/usr/local/bin:/bin:/usr/bin"; +pub const _PATH_STDPATH: &str = "/bin:/usr/bin:/sbin:/usr/sbin"; + +pub const _PATH_BSHELL: &str = "/bin/sh"; +pub const _PATH_CONSOLE: &str = "/dev/console"; +pub const _PATH_DEVNULL: &str = "/dev/null"; +pub const _PATH_KLOG: &str = "/proc/kmsg"; +pub const _PATH_LASTLOG: &str = "/var/log/lastlog"; +pub const _PATH_MAILDIR: &str = "/var/mail"; +pub const _PATH_MAN: &str = "/usr/share/man"; +pub const _PATH_MNTTAB: &str = "/etc/fstab"; +pub const _PATH_NOLOGIN: &str = "/etc/nologin"; +pub const _PATH_SENDMAIL: &str = "/usr/sbin/sendmail"; +pub const _PATH_SHADOW: &str = "/etc/shadow"; +pub const _PATH_SHELLS: &str = "/etc/shells"; +pub const _PATH_TTY: &str = "/dev/tty"; +pub const _PATH_UTMP: &str = "/var/run/utmp"; +pub const _PATH_WTMP: &str = "/var/log/wtmp"; +pub const _PATH_VI: &str = "/usr/bin/vi"; + +// Trailing backslash intentional as these are dir paths. +pub const _PATH_DEV: &str = "/dev/"; +pub const _PATH_TMP: &str = "/tmp/"; +pub const _PATH_VARDB: &str = "/var/lib/misc/"; +pub const _PATH_VARRUN: &str = "/var/run/"; +pub const _PATH_VARTMP: &str = "/var/tmp/"; diff --git a/src/header/_paths/mod.rs b/src/header/_paths/mod.rs new file mode 100644 index 0000000000..7c391b437c --- /dev/null +++ b/src/header/_paths/mod.rs @@ -0,0 +1,11 @@ +//! Implementation specific, non-standard path aliases + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +mod sys; + +pub use sys::*; diff --git a/src/header/_paths/redox.rs b/src/header/_paths/redox.rs new file mode 100644 index 0000000000..a2a0ed0274 --- /dev/null +++ b/src/header/_paths/redox.rs @@ -0,0 +1,13 @@ +//! Redox specific constants for paths.h. + +// NOTE: Cross check that new entries correspond to files on Redox before adding. + +pub const _PATH_BSHELL: &str = "/bin/sh"; +pub const _PATH_DEVNULL: &str = "/dev/null"; +pub const _PATH_MAN: &str = "/usr/share/man"; +pub const _PATH_TTY: &str = "/dev/tty"; + +// Trailing backslash intentional as these are dir paths. +pub const _PATH_DEV: &str = "/dev/"; +pub const _PATH_TMP: &str = "/tmp/"; +pub const _PATH_VARTMP: &str = "/var/tmp/"; diff --git a/src/header/_template/cbindgen.toml b/src/header/_template/cbindgen.toml new file mode 100644 index 0000000000..fbdebd78fd --- /dev/null +++ b/src/header/_template/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = [] +include_guard = "_RELIBC_TEMPLATE_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/_template/mod.rs b/src/header/_template/mod.rs new file mode 100644 index 0000000000..3696d1e784 --- /dev/null +++ b/src/header/_template/mod.rs @@ -0,0 +1,10 @@ +//! template implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/template.h.html + +use platform::types::*; + +/* +#[unsafe(no_mangle)] +pub extern "C" fn func(args) -> c_int { + unimplemented!(); +} +*/ diff --git a/src/header/arch_aarch64_user/cbindgen.toml b/src/header/arch_aarch64_user/cbindgen.toml new file mode 100644 index 0000000000..2000868e87 --- /dev/null +++ b/src/header/arch_aarch64_user/cbindgen.toml @@ -0,0 +1,7 @@ +sys_includes = [] +include_guard = "_AARCH64_USER_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/header/arch_aarch64_user/mod.rs b/src/header/arch_aarch64_user/mod.rs new file mode 100644 index 0000000000..5586c38bac --- /dev/null +++ b/src/header/arch_aarch64_user/mod.rs @@ -0,0 +1,30 @@ +use crate::platform::types::{c_double, c_uint, c_ulong, c_ulonglong}; + +#[repr(C)] +pub struct user_regs_struct { + pub regs: [c_ulonglong; 31], + pub sp: c_ulonglong, + pub pc: c_ulonglong, + pub pstate: c_ulonglong, +} + +#[repr(C)] +pub struct user_fpsimd_struct { + pub vregs: [c_double; 32], // BUG: rust doesn't have f128 which is equivalent for long double + pub fpsr: c_uint, + pub fpcr: c_uint, +} + +pub type elf_greg_t = c_ulong; +pub type elf_gregset_t = *mut [c_ulong; 34]; +pub type elf_fpregset_t = user_fpsimd_struct; + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_aarch64_user( + a: user_regs_struct, + b: user_fpsimd_struct, + c: elf_gregset_t, + d: elf_greg_t, + e: elf_fpregset_t, +) { +} diff --git a/src/header/arch_riscv64_user/cbindgen.toml b/src/header/arch_riscv64_user/cbindgen.toml new file mode 100644 index 0000000000..369bf8c1d6 --- /dev/null +++ b/src/header/arch_riscv64_user/cbindgen.toml @@ -0,0 +1,7 @@ +sys_includes = [] +include_guard = "_RISCV64_USER_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/header/arch_riscv64_user/mod.rs b/src/header/arch_riscv64_user/mod.rs new file mode 100644 index 0000000000..e0e9794f15 --- /dev/null +++ b/src/header/arch_riscv64_user/mod.rs @@ -0,0 +1,39 @@ +use crate::platform::types::{c_double, c_float, c_uint, c_ulong}; + +#[repr(C)] +pub struct user_regs_struct { + pub regs: [c_ulong; 31], // x1-x31 + pub pc: c_ulong, +} + +#[repr(C)] +pub struct user_fpregs_f_struct { + pub fpregs: [c_float; 32], + pub fcsr: c_uint, +} + +#[repr(C)] +pub struct user_fpregs_g_struct { + pub fpregs: [c_double; 32], + pub fcsr: c_uint, +} + +#[repr(C)] +pub struct user_fpregs_struct { + pub f_regs: user_fpregs_f_struct, + pub g_regs: user_fpregs_g_struct, +} + +pub type elf_greg_t = c_ulong; +pub type elf_gregset_t = user_regs_struct; +pub type elf_fpregset_t = user_fpregs_struct; + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_riscv64_user( + a: user_regs_struct, + b: user_fpregs_struct, + c: elf_gregset_t, + d: elf_greg_t, + e: elf_fpregset_t, +) { +} diff --git a/src/header/arch_x64_user/cbindgen.toml b/src/header/arch_x64_user/cbindgen.toml new file mode 100644 index 0000000000..04a2ccea4e --- /dev/null +++ b/src/header/arch_x64_user/cbindgen.toml @@ -0,0 +1,7 @@ +sys_includes = [] +include_guard = "_X64_USER_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/header/arch_x64_user/mod.rs b/src/header/arch_x64_user/mod.rs new file mode 100644 index 0000000000..116b990282 --- /dev/null +++ b/src/header/arch_x64_user/mod.rs @@ -0,0 +1,83 @@ +//! A part of the ptrace compatibility for Redox OS + +use crate::platform::types::{c_char, c_int, c_long, c_ulong}; + +#[repr(C)] +pub struct user_fpregs_struct { + pub cwd: u16, + pub swd: u16, + pub ftw: u16, + pub fop: u16, + pub rip: u64, + pub rdp: u64, + pub mxcsr: u32, + pub mxcr_mask: u32, + pub st_space: [u32; 32], + pub xmm_space: [u32; 64], + pub padding: [u32; 24], +} + +#[repr(C)] +pub struct user_regs_struct { + pub r15: c_ulong, + pub r14: c_ulong, + pub r13: c_ulong, + pub r12: c_ulong, + pub rbp: c_ulong, + pub rbx: c_ulong, + pub r11: c_ulong, + pub r10: c_ulong, + pub r9: c_ulong, + pub r8: c_ulong, + pub rax: c_ulong, + pub rcx: c_ulong, + pub rdx: c_ulong, + pub rsi: c_ulong, + pub rdi: c_ulong, + pub orig_rax: c_ulong, + pub rip: c_ulong, + pub cs: c_ulong, + pub eflags: c_ulong, + pub rsp: c_ulong, + pub ss: c_ulong, + pub fs_base: c_ulong, + pub gs_base: c_ulong, + pub ds: c_ulong, + pub es: c_ulong, + pub fs: c_ulong, + pub gs: c_ulong, +} + +pub type elf_greg_t = c_ulong; + +pub type elf_gregset_t = *mut [c_ulong; 27]; +pub type elf_fpregset_t = user_fpregs_struct; +#[repr(C)] +pub struct user { + pub regs: user_regs_struct, + pub u_fpvalid: c_int, + pub i387: user_fpregs_struct, + pub u_tsize: c_ulong, + pub u_dsize: c_ulong, + pub u_ssize: c_ulong, + pub start_code: c_ulong, + pub start_stack: c_ulong, + pub signal: c_long, + pub reserved: c_int, + pub u_ar0: *mut user_regs_struct, + pub u_fpstate: *mut user_fpregs_struct, + pub magic: c_ulong, + pub u_comm: [c_char; 32], + pub u_debugreg: [c_ulong; 8], +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_x86_user( + a: user_fpregs_struct, + b: user_regs_struct, + c: user, + d: elf_gregset_t, + e: elf_greg_t, + f: elf_fpregset_t, +) { +} diff --git a/src/header/arpa_inet/cbindgen.toml b/src/header/arpa_inet/cbindgen.toml new file mode 100644 index 0000000000..e6205e3805 --- /dev/null +++ b/src/header/arpa_inet/cbindgen.toml @@ -0,0 +1,31 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the in_port_t and in_addr_t types as described in and the socklen_t type as defined in ." +# - "The header shall define the in_addr structure as described in ." +# - "The header shall define the INET_ADDRSTRLEN and INET6_ADDRSTRLEN macros as described in ." +# - "The header shall define the uint32_t and uint16_t types as described in ." +# - "Inclusion of the header may also make visible all symbols from and ." +# +# Possible cycle between arpa/inet.h and netinet/in.h solved by: +# - including netinet/in.h in arpa/inet.h +# - splitting out functions (htonl, htons, ntohl, ntohs) into bits/arpainet.h +# +# netinet/in.h brings in sys/types.h which brings in features.h (needed for #[deprecated] annotation) +# bits/arpainet.h brings in inttypes.h +sys_includes = ["netinet/in.h"] +include_guard = "_ARPA_INET_H" +after_includes = """ +#include // for htonl, htons, ntohl, ntohs +#include // for socklen_t +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"in_addr" = "struct in_addr" diff --git a/src/header/arpa_inet/mod.rs b/src/header/arpa_inet/mod.rs new file mode 100644 index 0000000000..56806d4a04 --- /dev/null +++ b/src/header/arpa_inet/mod.rs @@ -0,0 +1,447 @@ +//! `arpa/inet.h` implementation. +//! +//! See . + +use alloc::{string::String, vec::Vec}; +use core::{ + ptr, slice, + str::{self, FromStr}, +}; + +use crate::{ + c_str::CStr, + header::{ + bits_arpainet::ntohl, + bits_socklen_t::socklen_t, + errno::{EAFNOSUPPORT, ENOSPC}, + netinet_in::{INADDR_NONE, INET6_ADDRSTRLEN, in6_addr, in_addr, in_addr_t}, + sys_socket::constants::{AF_INET, AF_INET6}, + }, + platform::{ + self, + types::{c_char, c_int, c_void}, + }, + raw_cell::RawCell, +}; + +/// See . +/// +/// # Deprecated +/// The `inet_addr()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn inet_addr(cp: *const c_char) -> in_addr_t { + let mut val: in_addr = in_addr { s_addr: 0 }; + + if unsafe { inet_aton(cp, &raw mut val) } > 0 { + val.s_addr + } else { + INADDR_NONE + } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int { + let cp_cstr = unsafe { CStr::from_ptr(cp) }; + let mut four_parts_decimal_only = false; + if cp_cstr.contains(b'.') { + // 2, 3 or 4 part address + let parts = unsafe { str::from_utf8_unchecked(cp_cstr.to_bytes()).split('.') }; + let mut count = 0; + for part in parts { + if let Some(hex_or_oct) = part.strip_prefix('0') + && part.len() > 1 + { + match hex_or_oct.bytes().next() { + Some(b'x' | b'X') => todo_skip!(0, "parsing hex values unimplemented"), + // TODO: C2Y accept `0o` or `0O` as octal prefixes, C23 and below only use `0` + _ => todo_skip!(0, "parsing octal values unimplemented"), + } + } else { + count += 1; + } + } + if count == 4 { + four_parts_decimal_only = true; + } + } else if cp_cstr.len() == 4 { + // 1 part address (32 bit value to be stored directly into address without byte rearrangement) + let s_addr_bytes: [u8; 4] = cp_cstr.to_bytes().try_into().expect("guaranteed 4 bytes"); + unsafe { + (*inp.cast::()).s_addr = in_addr_t::from_ne_bytes(s_addr_bytes); + } + return 1; // successful + } + if four_parts_decimal_only { + unsafe { inet_pton(AF_INET, cp, inp.cast::()) } + } else { + todo_skip!(0, "parsing 2 or more non-decimal values unimplemented"); + // TODO convert octal and hexadecimal parts into decimal and feed into `inet_pton` + 0 // indicates `cp` is an invalid string + } +} + +/// See . +/// +/// # Deprecation +/// The `inet_lnaof()` function was specified in Networking Services Issue 5, +/// but not in the Open Group Base Specifications Issue 6 and later. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn inet_lnaof(r#in: in_addr) -> in_addr_t { + if r#in.s_addr >> 24 < 128 { + r#in.s_addr & 0xff_ffff + } else if r#in.s_addr >> 24 < 192 { + r#in.s_addr & 0xffff + } else { + r#in.s_addr & 0xff + } +} + +/// See . +/// +/// # Deprecation +/// The `inet_makeaddr()` function was specified in Networking Services Issue +/// 5, but not in the Open Group Base Specifications Issue 6 and later. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn inet_makeaddr(net: in_addr_t, lna: in_addr_t) -> in_addr { + let mut output: in_addr = in_addr { s_addr: 0 }; + + if net < 256 { + output.s_addr = lna | net << 24; + } else if net < 65536 { + output.s_addr = lna | net << 16; + } else { + output.s_addr = lna | net << 8; + } + + output +} + +/// See . +/// +/// # Deprecation +/// The `inet_netof()` function was specified in Networking Services Issue 5, +/// but not in the Open Group Base Specifications Issue 6 and later. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn inet_netof(r#in: in_addr) -> in_addr_t { + if r#in.s_addr >> 24 < 128 { + r#in.s_addr & 0xff_ffff + } else if r#in.s_addr >> 24 < 192 { + r#in.s_addr & 0xffff + } else { + r#in.s_addr & 0xff + } +} + +/// See . +/// +/// # Deprecation +/// The `inet_network()` function was specified in Networking Services Issue 5, +/// but not in the Open Group Base Specifications Issue 6 and later. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn inet_network(cp: *const c_char) -> in_addr_t { + ntohl(unsafe { + #[allow(deprecated)] + inet_addr(cp) + }) +} + +/// See . +/// +/// # Deprecation +/// The `inet_ntoa()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn inet_ntoa(r#in: in_addr) -> *mut c_char { + static NTOA_ADDR: RawCell<[c_char; 16]> = RawCell::new([0; 16]); + + unsafe { + let ptr = inet_ntop( + AF_INET, + ptr::from_ref::(&r#in).cast::(), + NTOA_ADDR.unsafe_mut().as_mut_ptr(), + NTOA_ADDR.unsafe_ref().len() as socklen_t, + ); + // Mutable pointer is required, inet_ntop returns destination as const pointer + ptr.cast_mut() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn inet_ntop( + af: c_int, + src: *const c_void, + dst: *mut c_char, + size: socklen_t, +) -> *const c_char { + if af == AF_INET6 { + if size < INET6_ADDRSTRLEN as socklen_t { + platform::ERRNO.set(ENOSPC); + return ptr::null(); + } + let s6_addr = unsafe { &(*(src.cast::())).s6_addr }; + let output = inet_ntop6(s6_addr); + let bytes = output.as_bytes(); + unsafe { + ptr::copy(bytes.as_ptr().cast::(), dst, bytes.len()); + *dst.add(bytes.len()) = 0; + } + dst + } else if af == AF_INET { + if size < 16 { + platform::ERRNO.set(ENOSPC); + ptr::null() + } else { + let s_addr = unsafe { + slice::from_raw_parts( + ptr::from_ref(&(*(src.cast::())).s_addr).cast::(), + 4, + ) + }; + let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]); + unsafe { + ptr::copy(addr.as_ptr().cast::(), dst, addr.len()); + } + dst + } + } else { + platform::ERRNO.set(EAFNOSUPPORT); + ptr::null() + } +} + +fn inet_ntop6(addr: &[u8; 16]) -> String { + let groups: [u16; 8] = core::array::from_fn(|i| { + u16::from_be_bytes([addr[i * 2], addr[i * 2 + 1]]) + }); + + let mut best_start = 8usize; + let mut best_len = 1usize; + let mut cur_start = 8usize; + let mut cur_len = 0usize; + + for i in 0..8 { + if groups[i] == 0 { + if cur_len == 0 { + cur_start = i; + } + cur_len += 1; + } else { + if cur_len > best_len { + best_start = cur_start; + best_len = cur_len; + } + cur_len = 0; + } + } + if cur_len > best_len { + best_start = cur_start; + best_len = cur_len; + } + + let mut parts = Vec::new(); + let mut i = 0usize; + while i < 8 { + if i == best_start && best_len > 1 { + if i == 0 { + parts.push(String::new()); + } + if i + best_len == 8 { + parts.push(String::new()); + } + i += best_len; + } else { + parts.push(format!("{:x}", groups[i])); + i += 1; + } + } + + if best_len == 8 { + return String::from("::"); + } + + parts.join(":") +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_void) -> c_int { + if af == AF_INET6 { + let src_cstr = unsafe { CStr::from_ptr(src) }; + let src_str = match src_cstr.to_str() { + Ok(s) => s, + Err(_) => return 0, + }; + let out = unsafe { &mut *(dst.cast::()) }; + if inet_pton6(src_str, &mut out.s6_addr) { + 1 + } else { + 0 + } + } else if af == AF_INET { + let s_addr = unsafe { + slice::from_raw_parts_mut( + ptr::from_mut(&mut (*dst.cast::()).s_addr).cast::(), + 4, + ) + }; + let src_cstr = unsafe { CStr::from_ptr(src) }; + let mut octets = unsafe { str::from_utf8_unchecked(src_cstr.to_bytes()).split('.') }; + for part in s_addr.iter_mut().take(4) { + if let Some(n) = octets + .next() + .filter(|x| !x.len() > 3) + .and_then(|x| u8::from_str(x).ok()) + { + *part = n; + } else { + return 0; + } + } + if octets.next().is_none() { + 1 // Success + } else { + 0 + } + } else { + platform::ERRNO.set(EAFNOSUPPORT); + -1 + } +} + +fn inet_pton6(src: &str, dst: &mut [u8; 16]) -> bool { + dst.fill(0); + + let double_colon_pos = src.find("::"); + let second_double = if let Some(pos) = double_colon_pos { + src[pos + 2..].find("::").map(|p| p + pos + 2) + } else { + None + }; + if second_double.is_some() { + return false; + } + + let (left_str, right_str) = match double_colon_pos { + Some(pos) => (&src[..pos], &src[pos + 2..]), + None => (src, ""), + }; + + let left_groups: Vec<&str> = if left_str.is_empty() { + Vec::new() + } else { + left_str.split(':').collect() + }; + let right_groups: Vec<&str> = if right_str.is_empty() { + Vec::new() + } else { + right_str.split(':').collect() + }; + + let right_has_ipv4 = right_groups.last().is_some_and(|g| g.contains('.')); + let mut left_count = left_groups.len(); + let mut right_count = right_groups.len(); + if right_has_ipv4 { + right_count -= 1; + left_count += 1; + } + + let gap = 8 - left_count - right_count; + if double_colon_pos.is_none() && gap != 0 { + return false; + } + if double_colon_pos.is_some() && gap < 0 { + return false; + } + if double_colon_pos.is_none() && left_groups.len() + right_groups.len() != 8 { + return false; + } + + let mut idx = 0usize; + + for group in &left_groups { + if idx >= 16 { + return false; + } + let val = match parse_hex_group(group) { + Some(v) => v, + None => return false, + }; + dst[idx] = (val >> 8) as u8; + dst[idx + 1] = val as u8; + idx += 2; + } + + if double_colon_pos.is_some() { + for _ in 0..gap { + if idx >= 16 { + return false; + } + dst[idx] = 0; + dst[idx + 1] = 0; + idx += 2; + } + } + + let right_hex_count = if right_has_ipv4 { + right_groups.len().saturating_sub(1) + } else { + right_groups.len() + }; + + for group in &right_groups[..right_hex_count] { + if idx >= 16 { + return false; + } + let val = match parse_hex_group(group) { + Some(v) => v, + None => return false, + }; + dst[idx] = (val >> 8) as u8; + dst[idx + 1] = val as u8; + idx += 2; + } + + if right_has_ipv4 { + if idx != 12 { + return false; + } + let ipv4_str = right_groups[right_groups.len() - 1]; + let parts: Vec<&str> = ipv4_str.split('.').collect(); + if parts.len() != 4 { + return false; + } + for (i, part) in parts.iter().enumerate() { + match u8::from_str(part) { + Ok(v) => dst[12 + i] = v, + Err(_) => return false, + } + } + idx += 4; + } + + idx == 16 +} + +fn parse_hex_group(s: &str) -> Option { + if s.is_empty() || s.len() > 4 { + return None; + } + let mut val: u16 = 0; + for c in s.chars() { + val = val.checked_mul(16)?; + match c.to_digit(16) { + Some(d) => val = val.checked_add(d as u16)?, + None => return None, + } + } + Some(val) +} diff --git a/src/header/assert/cbindgen.toml b/src/header/assert/cbindgen.toml new file mode 100644 index 0000000000..eea531eabd --- /dev/null +++ b/src/header/assert/cbindgen.toml @@ -0,0 +1,28 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/assert.h.html +# +# There are no spec quotations relating to includes +# +# features.h included for Rust never type (no return) +sys_includes = ["features.h"] +include_guard = "_RELIBC_ASSERT_H" +# trailer is placed outside include_guard +trailer = """ +// Do not use include guard, to ensure assert is always defined +#ifdef assert +#undef assert +#endif + +#ifdef NDEBUG +# define assert(cond) (void) 0 +#else +# define assert(cond) \ + ((void)((cond) || (__assert_fail(__func__, __FILE__, __LINE__, #cond), 0))) +#endif +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/assert/mod.rs b/src/header/assert/mod.rs new file mode 100644 index 0000000000..208d9019ae --- /dev/null +++ b/src/header/assert/mod.rs @@ -0,0 +1,24 @@ +//! `assert.h` implementation. +//! +//! See . + +use crate::{ + c_str::CStr, + platform::types::{c_char, c_int}, +}; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __assert_fail( + func: *const c_char, + file: *const c_char, + line: c_int, + cond: *const c_char, +) -> ! { + let func = unsafe { CStr::from_ptr(func) }.to_string_lossy(); + let file = unsafe { CStr::from_ptr(file) }.to_string_lossy(); + let cond = unsafe { CStr::from_ptr(cond) }.to_string_lossy(); + + eprintln!("{}: {}:{}: Assertion `{}` failed.", func, file, line, cond); + + core::intrinsics::abort(); +} diff --git a/src/header/bits_arpainet/cbindgen.toml b/src/header/bits_arpainet/cbindgen.toml new file mode 100644 index 0000000000..2db885b93e --- /dev/null +++ b/src/header/bits_arpainet/cbindgen.toml @@ -0,0 +1,19 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html +# +# The arpa/inet header should define the following functions: +# - htonl +# - htons +# - ntohl +# - ntohs +# +# They are also meant to be available in the netinet/in header. +# The arpa/inet and netinet/in headers both say that they may include each other which creates a cycle. +# To break the cycle we include netinet/in in the arpa/inet header and split out the above functions. +# +# include inttypes.h to get uint16_t and uint32_t +sys_includes = ["inttypes.h"] +include_guard = "_RELIBC_BITS_ARPAINET_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true diff --git a/src/header/bits_arpainet/mod.rs b/src/header/bits_arpainet/mod.rs new file mode 100644 index 0000000000..176b4d7958 --- /dev/null +++ b/src/header/bits_arpainet/mod.rs @@ -0,0 +1,25 @@ +use crate::platform::types::{uint16_t, uint32_t}; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htonl(hostlong: uint32_t) -> uint32_t { + hostlong.to_be() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htons(hostshort: uint16_t) -> uint16_t { + hostshort.to_be() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ntohl(netlong: uint32_t) -> uint32_t { + u32::from_be(netlong) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ntohs(netshort: uint16_t) -> uint16_t { + u16::from_be(netshort) +} diff --git a/src/header/bits_iovec/cbindgen.toml b/src/header/bits_iovec/cbindgen.toml new file mode 100644 index 0000000000..c9c51fbb10 --- /dev/null +++ b/src/header/bits_iovec/cbindgen.toml @@ -0,0 +1,18 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_uio.h.html +# +# Split out to avoid including all of sys/uio.h in sys/socket.h. +# +# POSIX headers that require iovec: +# - sys/socket.h +# - sys/uio.h (where it should be defined) +# +# sys/types.h included for c_void and size_t +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_BITS_IOVEC_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/bits_iovec/mod.rs b/src/header/bits_iovec/mod.rs new file mode 100644 index 0000000000..2136ad90e1 --- /dev/null +++ b/src/header/bits_iovec/mod.rs @@ -0,0 +1,38 @@ +use alloc::vec::Vec; +use core::slice; + +use crate::platform::types::{c_void, size_t}; + +/// See . +#[repr(C)] +#[derive(Debug, CheckVsLibcCrate)] +pub struct iovec { + pub iov_base: *mut c_void, + pub iov_len: size_t, +} + +impl iovec { + unsafe fn to_slice(&self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.iov_base.cast::(), self.iov_len) } + } +} + +pub unsafe fn gather(iovs: &[iovec]) -> Vec { + let mut vec = Vec::new(); + for iov in iovs.iter() { + vec.extend_from_slice(unsafe { iov.to_slice() }); + } + vec +} + +pub unsafe fn scatter(iovs: &[iovec], vec: Vec) { + let mut i = 0; + for iov in iovs.iter() { + let slice = unsafe { iov.to_slice() }; + slice.copy_from_slice(&vec[i..][..slice.len()]); + i += slice.len(); + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_alias_iovec(_: iovec) {} diff --git a/src/header/bits_locale-t/cbindgen.toml b/src/header/bits_locale-t/cbindgen.toml new file mode 100644 index 0000000000..bd79a605d1 --- /dev/null +++ b/src/header/bits_locale-t/cbindgen.toml @@ -0,0 +1,29 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html +# +# The locale header should define this type but multiple headers make use of it. +# This type is split out from the locale header to avoid including all of locale in the other headers. +# +# POSIX headers that require locale_t: +# - ctype.h +# - langinfo.h +# - libintl.h (Redox does not have this header, it seems to be supplied externally) +# - monetary.h (TODO note exists, not currently imported) +# - string.h +# - strings.h (TODO note exists, not currently imported) +# - time.h (TODO note exists, not currently imported) +# - wchar.h (required by *_l functions, no TODO note present) +# - wctype.h (required by *_l functions, TODO note exists, not currently imported) +sys_includes = [] +include_guard = "_RELIBC_BITS_LOCALE_T_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true + +[export] +include = [ + "locale_t" +] + +[enum] +prefix_with_name = true diff --git a/src/header/bits_locale-t/mod.rs b/src/header/bits_locale-t/mod.rs new file mode 100644 index 0000000000..0e1c4c3611 --- /dev/null +++ b/src/header/bits_locale-t/mod.rs @@ -0,0 +1 @@ +pub type locale_t = *mut core::ffi::c_void; diff --git a/src/header/bits_pthread/cbindgen.toml b/src/header/bits_pthread/cbindgen.toml new file mode 100644 index 0000000000..ea30c3892b --- /dev/null +++ b/src/header/bits_pthread/cbindgen.toml @@ -0,0 +1,53 @@ +sys_includes = [] +include_guard = "_RELIBC_BITS_PTHREAD_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true +# TODO: Any better way to implement pthread_cleanup_push/pthread_cleanup_pop? +after_includes = """ +#define PTHREAD_COND_INITIALIZER ((pthread_cond_t){0}) +#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t){0}) +#define PTHREAD_ONCE_INIT ((pthread_once_t){0}) +#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t){0}) + +#define pthread_cleanup_push(ROUTINE, ARG) do { \\ + struct { \\ + void (*routine)(void *); \\ + void *arg; \\ + void *prev; \\ + } __relibc_internal_pthread_ll_entry = { \\ + .routine = (void (*)(void *))(ROUTINE), \\ + .arg = (void *)(ARG), \\ + }; \\ + __relibc_internal_pthread_cleanup_push(&__relibc_internal_pthread_ll_entry); + +#define pthread_cleanup_pop(EXECUTE) \\ + __relibc_internal_pthread_cleanup_pop((EXECUTE)); \\ +} while(0) + +""" + +[export.rename] +"AtomicInt" = "int" +"AtomicUint" = "unsigned" + +[export] +include = [ + "pthread_attr_t", + "pthread_rwlockattr_t", + "pthread_rwlock_t", + "pthread_barrier_t", + "pthread_barrierattr_t", + "pthread_mutex_t", + "pthread_mutexattr_t", + "pthread_condattr_t", + "pthread_cond_t", + "pthread_spinlock_t", + "pthread_once_t", + "pthread_t", + "pthread_key_t", +] + +[enum] +prefix_with_name = true diff --git a/src/header/bits_pthread/mod.rs b/src/header/bits_pthread/mod.rs new file mode 100644 index 0000000000..ce21b6a3fa --- /dev/null +++ b/src/header/bits_pthread/mod.rs @@ -0,0 +1,123 @@ +#![allow(non_camel_case_types)] + +use crate::platform::types::{c_int, c_uchar, c_ulong, c_void, size_t}; + +// XXX: https://github.com/eqrion/cbindgen/issues/685 +// +// We need to write the opaque types ourselves, and apparently cbindgen doesn't even support +// expanding macros! Instead, we rely on checking that the lengths are correct, when these headers +// are parsed in the regular compilation phase. + +/// The `pthread_attr_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_attr_t { + __relibc_internal_size: [c_uchar; 32], + __relibc_internal_align: size_t, +} +/// The `pthread_rwlockattr_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_rwlockattr_t { + __relibc_internal_size: [c_uchar; 1], + __relibc_internal_align: c_uchar, +} +/// The `pthread_rwlock_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_rwlock_t { + __relibc_internal_size: [c_uchar; 4], + __relibc_internal_align: c_int, +} +/// The `pthread_barrier_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_barrier_t { + __relibc_internal_size: [c_uchar; 24], + __relibc_internal_align: c_int, +} +/// The `pthread_barrierattr_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_barrierattr_t { + __relibc_internal_size: [c_uchar; 4], + __relibc_internal_align: c_int, +} +/// The `pthread_mutex_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_mutex_t { + __relibc_internal_size: [c_uchar; 12], + __relibc_internal_align: c_int, +} +/// The `pthread_mutexattr_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_mutexattr_t { + __relibc_internal_size: [c_uchar; 20], + __relibc_internal_align: c_int, +} +/// The `pthread_cond_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_cond_t { + __relibc_internal_size: [c_uchar; 8], + __relibc_internal_align: c_int, +} +/// The `pthread_condattr_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_condattr_t { + __relibc_internal_size: [c_uchar; 8], + __relibc_internal_align: c_int, +} +/// The `pthread_spinlock_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_spinlock_t { + __relibc_internal_size: [c_uchar; 4], + __relibc_internal_align: c_int, +} +/// The `pthread_once_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[repr(C)] +pub union pthread_once_t { + __relibc_internal_size: [c_uchar; 4], + __relibc_internal_align: c_int, +} + +macro_rules! assert_equal_size( + ($export:ident, $wrapped:ident) => { + const _: () = unsafe { + type Wrapped = crate::header::pthread::$wrapped; + + // Fail at compile-time if sizes differ. + + // TODO: Is this UB? + let export = $export { __relibc_internal_align: 0 }; + let _: Wrapped = core::mem::transmute(export.__relibc_internal_size); + + // Fail at compile-time if alignments differ. + let a = [0_u8; core::mem::align_of::<$export>()]; + #[allow(clippy::useless_transmute)] + let b: [u8; core::mem::align_of::()] = core::mem::transmute(a); + }; + // TODO: Turn into a macro? + #[cfg(all(target_os = "redox", feature = "check_against_libc_crate"))] + const _: () = unsafe { + use ::__libc_only_for_layout_checks as libc; + + let export = $export { __relibc_internal_align: 0 }; + let _: libc::$export = core::mem::transmute(export.__relibc_internal_size); + + let a = [0_u8; core::mem::align_of::<$export>()]; + let b: [u8; core::mem::align_of::()] = core::mem::transmute(a); + + }; + } +); +assert_equal_size!(pthread_attr_t, RlctAttr); +assert_equal_size!(pthread_rwlock_t, RlctRwlock); +assert_equal_size!(pthread_rwlockattr_t, RlctRwlockAttr); +assert_equal_size!(pthread_barrier_t, RlctBarrier); +assert_equal_size!(pthread_barrierattr_t, RlctBarrierAttr); +assert_equal_size!(pthread_mutex_t, RlctMutex); +assert_equal_size!(pthread_mutexattr_t, RlctMutexAttr); +assert_equal_size!(pthread_cond_t, RlctCond); +assert_equal_size!(pthread_condattr_t, RlctCondAttr); +assert_equal_size!(pthread_spinlock_t, RlctSpinlock); +assert_equal_size!(pthread_once_t, RlctOnce); + +/// The `pthread_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type pthread_t = *mut c_void; +/// The `pthread_key_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type pthread_key_t = c_ulong; diff --git a/src/header/bits_safamily-t/cbindgen.toml b/src/header/bits_safamily-t/cbindgen.toml new file mode 100644 index 0000000000..9b70509927 --- /dev/null +++ b/src/header/bits_safamily-t/cbindgen.toml @@ -0,0 +1,21 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html +# +# The sys/socket header should define this type. +# It is split out to avoid other headers from including all of sys/socket. +# +# POSIX headers that require sa_family_t: +# - netinet/in.h +# - sys/socket.h +# - sys/un.h +sys_includes = [] +include_guard = "_RELIBC_BITS_SA_FAMILY_T_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true + +[export] +include = ["sa_family_t"] + +[enum] +prefix_with_name = true diff --git a/src/header/bits_safamily-t/mod.rs b/src/header/bits_safamily-t/mod.rs new file mode 100644 index 0000000000..706ee0a78e --- /dev/null +++ b/src/header/bits_safamily-t/mod.rs @@ -0,0 +1,3 @@ +use crate::platform::types::c_ushort; + +pub type sa_family_t = c_ushort; diff --git a/src/header/bits_sigset-t/cbindgen.toml b/src/header/bits_sigset-t/cbindgen.toml new file mode 100644 index 0000000000..5cd5ba7970 --- /dev/null +++ b/src/header/bits_sigset-t/cbindgen.toml @@ -0,0 +1,18 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html +# +# This type is split out to prevent importing all of signal.h into other headers. +# +# POSIX headers that require sigset_t: +# - poll.h +# - signal.h (where it should be defined) +# - sys/select.h +sys_includes = [] +include_guard = "_RELIBC_BITS_SIGSET_T_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true +[export] +include = ["sigset_t"] +[enum] +prefix_with_name = true diff --git a/src/header/bits_sigset-t/mod.rs b/src/header/bits_sigset-t/mod.rs new file mode 100644 index 0000000000..fea419ee95 --- /dev/null +++ b/src/header/bits_sigset-t/mod.rs @@ -0,0 +1,4 @@ +use crate::platform::types::c_ulonglong; + +#[allow(non_camel_case_types)] +pub type sigset_t = c_ulonglong; diff --git a/src/header/bits_socklen-t/cbindgen.toml b/src/header/bits_socklen-t/cbindgen.toml new file mode 100644 index 0000000000..94a53c8954 --- /dev/null +++ b/src/header/bits_socklen-t/cbindgen.toml @@ -0,0 +1,12 @@ +sys_includes = [] +include_guard = "_RELIBC_BITS_SOCKLEN_T_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true +[export] +include = [ + "socklen_t" +] +[enum] +prefix_with_name = true diff --git a/src/header/bits_socklen-t/mod.rs b/src/header/bits_socklen-t/mod.rs new file mode 100644 index 0000000000..74de941e6c --- /dev/null +++ b/src/header/bits_socklen-t/mod.rs @@ -0,0 +1 @@ +pub type socklen_t = u32; diff --git a/src/header/bits_timespec/cbindgen.toml b/src/header/bits_timespec/cbindgen.toml new file mode 100644 index 0000000000..817170d32f --- /dev/null +++ b/src/header/bits_timespec/cbindgen.toml @@ -0,0 +1,25 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html +# +# The time header should define timespec but multiple headers make use of it. +# This type is split out from the time header to avoid including all of time in the other headers. +# +# POSIX headers that require timespec: +# - aio.h (all functions currently unimplemented but timespec imported) +# - poll.h +# - sched.h +# - semaphore.h +# - signal.h +# - sys/select.h (not currently imported, needed for pselect, no TODO) +# - sys/stat.h +# - sys/time.h +# +# include sys/types.h to get time_t for timespec +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_BITS_TIME_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/bits_timespec/mod.rs b/src/header/bits_timespec/mod.rs new file mode 100644 index 0000000000..7c3e031ec9 --- /dev/null +++ b/src/header/bits_timespec/mod.rs @@ -0,0 +1,64 @@ +use crate::{ + header::time::NANOSECONDS, + platform::types::{c_long, time_t}, +}; + +/// See . +#[repr(C)] +#[derive(Clone, Default, Debug)] +pub struct timespec { + pub tv_sec: time_t, + pub tv_nsec: c_long, +} + +impl timespec { + // TODO: Write test + + /// similar logic with timeradd + #[allow(clippy::should_implement_trait)] // not to confuse std::ops::Add + pub fn add(base: timespec, interval: timespec) -> Option { + let delta_sec = base.tv_sec.checked_add(interval.tv_sec)?; + let delta_nsec = base.tv_nsec.checked_add(interval.tv_nsec)?; + + if delta_sec < 0 || delta_nsec < 0 { + return None; + } + + Some(Self { + tv_sec: delta_sec + (delta_nsec / NANOSECONDS) as time_t, + tv_nsec: delta_nsec % NANOSECONDS, + }) + } + /// similar logic with timersub + pub fn subtract(later: timespec, earlier: timespec) -> Option { + let delta_sec = later.tv_sec.checked_sub(earlier.tv_sec)?; + let delta_nsec = later.tv_nsec.checked_sub(earlier.tv_nsec)?; + + let time = if delta_nsec < 0 { + let roundup_sec = -delta_nsec / NANOSECONDS + 1; + timespec { + tv_sec: delta_sec - (roundup_sec as time_t), + tv_nsec: roundup_sec * NANOSECONDS - delta_nsec, + } + } else { + timespec { + tv_sec: delta_sec + (delta_nsec / NANOSECONDS) as time_t, + tv_nsec: delta_nsec % NANOSECONDS, + } + }; + + if time.tv_sec < 0 { + // https://man7.org/linux/man-pages/man2/settimeofday.2.html + // caller should return EINVAL + return None; + } + + Some(time) + } + pub fn is_default(&self) -> bool { + self.tv_nsec == 0 && self.tv_sec == 0 + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_alias_timespec(_: timespec) {} diff --git a/src/header/cpio/cbindgen.toml b/src/header/cpio/cbindgen.toml new file mode 100644 index 0000000000..c578d9b65e --- /dev/null +++ b/src/header/cpio/cbindgen.toml @@ -0,0 +1,16 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/cpio.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +include_guard = "_RELIBC_CPIO_H" +# cbindgen still can't handle exporting CStr +after_includes = """ +#define MAGIC "070707" +""" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/cpio/mod.rs b/src/header/cpio/mod.rs new file mode 100644 index 0000000000..a53ea2c0ab --- /dev/null +++ b/src/header/cpio/mod.rs @@ -0,0 +1,48 @@ +//! `cpio.h` implementation. +//! +//! See . + +use crate::platform::types::c_int; + +/// Read by owner. +pub const C_IRUSR: c_int = 0o0000400; +/// Write by owner. +pub const C_IWUSR: c_int = 0o0000200; +/// Execute by owner. +pub const C_IXUSR: c_int = 0o0000100; +/// Read by group. +pub const C_IRGRP: c_int = 0o0000040; +/// Write by group. +pub const C_IWGRP: c_int = 0o0000020; +/// Execute by group. +pub const C_IXGRP: c_int = 0o0000010; +/// Read by others. +pub const C_IROTH: c_int = 0o0000004; +/// Write by others. +pub const C_IWOTH: c_int = 0o0000002; +/// Execute by others. +pub const C_IXOTH: c_int = 0o0000001; + +/// Set user ID. +pub const C_ISUID: c_int = 0o0004000; +/// Set group ID. +pub const C_ISGID: c_int = 0o0002000; +/// On directories, restricted deletion flag. +pub const C_ISVTX: c_int = 0o0001000; + +/// Directory. +pub const C_ISDIR: c_int = 0o0040000; +/// FIFO. +pub const C_ISFIFO: c_int = 0o0010000; +/// Regular file. +pub const C_ISREG: c_int = 0o0100000; +/// Block special. +pub const C_ISBLK: c_int = 0o0060000; +/// Character special. +pub const C_ISCHR: c_int = 0o0020000; +/// Reserved. +pub const C_ISCTG: c_int = 0o0110000; +/// Symbolic link. +pub const C_ISLNK: c_int = 0o0120000; +/// Socket. +pub const C_ISSOCK: c_int = 0o0140000; diff --git a/src/header/crypt/argon2.rs b/src/header/crypt/argon2.rs new file mode 100644 index 0000000000..99b7e57e2d --- /dev/null +++ b/src/header/crypt/argon2.rs @@ -0,0 +1,16 @@ +use alloc::string::{String, ToString}; +use argon2::{ + Argon2, + password_hash::{PasswordHash, PasswordVerifier}, +}; + +pub fn crypt_argon2(key: &str, setting: &str) -> Option { + let hash = PasswordHash::new(setting).ok()?; + let argon2 = Argon2::default(); + + if argon2.verify_password(key.as_bytes(), &hash).is_ok() { + Some(setting.to_string()) + } else { + None + } +} diff --git a/src/header/crypt/blowfish.rs b/src/header/crypt/blowfish.rs new file mode 100644 index 0000000000..54e9fe1082 --- /dev/null +++ b/src/header/crypt/blowfish.rs @@ -0,0 +1,90 @@ +use crate::platform::types::{c_uchar, c_uint}; +use alloc::{string::String, vec::Vec}; +use base64ct::{Base64Bcrypt, Encoding}; +use bcrypt_pbkdf::bcrypt_pbkdf; +use core::str; + +const MIN_COST: u32 = 4; +const MAX_COST: u32 = 31; +const BHASH_WORDS: usize = 8; +const BHASH_OUTPUT_SIZE: usize = BHASH_WORDS * 4; + +/// Inspired by https://github.com/Keats/rust-bcrypt/blob/87fc59e917bcb6cf3f3752fc7f2b4c659d415597/src/lib.rs#L135 +fn split_with_prefix(hash: &str) -> Option<(&str, &str, c_uint)> { + let valid_prefixes = ["2y", "2b", "2a", "2x"]; + + // Should be [prefix, cost, hash] + let raw_parts: Vec<_> = hash.split('$').skip(1).collect(); + if raw_parts.len() != 3 { + return None; + } + + let prefix = raw_parts[0]; + let setting = raw_parts[2]; + + if !valid_prefixes.contains(&prefix) { + return None; + } + + raw_parts[1] + .parse::() + .ok() + .map(|cost| (prefix, setting, cost)) +} + +/// Performs Blowfish key derivation on a given password with a specific setting. +/// +/// # Parameters +/// * `passw`: The password to be hashed. It must be a string slice (`&str`). +/// * `setting`: The settings for the Blowfish key derivation. It must be a string slice (`&str`) +/// and should follow the format `$$$`, where `` is a string that +/// indicates the type of the hash (e.g., "$2a$"), `` is a decimal number representing +/// the cost factor for the Blowfish operation, and `` is a base64-encoded string +/// representing the salt to be used for the Blowfish function. +/// +/// # Returns +/// * `Option`: Returns `Some(String)` if the Blowfish operation was successful, where the +/// returned string is the result of the Blowfish operation formatted according to the Modular +/// Crypt Format (MCF). If the Blowfish operation failed, it returns `None`. +/// +/// # Errors +/// * If the cost factor is outside the range `[MIN_COST, MAX_COST]`. +/// +/// # Example +/// ``` +/// let password = "correctbatteryhorsestapler"; +/// let setting = "$2y$12$L6Bc/AlTQHyd9liGgGEZyO"; +/// let result = crypt_blowfish(password, setting); +/// assert!(result.is_some()); +///``` +/// +/// # Note +/// The `crypt_blowfish` function uses the Blowfish block cipher for hashing. +/// The output of the Blowfish operation is base64-encoded using the BCrypt variant of base64. +pub fn crypt_blowfish(passw: &str, setting: &str) -> Option { + if let Some((prefix, setting, cost)) = split_with_prefix(setting) { + if !(MIN_COST..=MAX_COST).contains(&cost) { + return None; + } + // Passwords need to be null terminated + let mut vec = Vec::with_capacity(passw.len() + 1); + vec.extend_from_slice(passw.as_bytes()); + vec.push(0); + + // We only consider the first 72 chars; truncate if necessary. + let passw_t = if vec.len() > 72 { &vec[..72] } else { &vec }; + let passw: &[c_uchar] = passw_t; + let setting = setting.as_bytes(); + let mut output = vec![0; BHASH_OUTPUT_SIZE + 1]; + + bcrypt_pbkdf(passw, setting, cost, &mut output).ok()?; + Some(format!( + "${}${}${}", + prefix, + cost, + Base64Bcrypt::encode_string(&output), + )) + } else { + None + } +} diff --git a/src/header/crypt/cbindgen.toml b/src/header/crypt/cbindgen.toml new file mode 100644 index 0000000000..bdcf5ebda1 --- /dev/null +++ b/src/header/crypt/cbindgen.toml @@ -0,0 +1,9 @@ +include_guard = "_RELIBC_CRYPT_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + + +[enum] +prefix_with_name = true \ No newline at end of file diff --git a/src/header/crypt/md5.rs b/src/header/crypt/md5.rs new file mode 100644 index 0000000000..25cfacff37 --- /dev/null +++ b/src/header/crypt/md5.rs @@ -0,0 +1,146 @@ +use crate::platform::types::c_uchar; +use alloc::string::String; +use base64ct::{Base64ShaCrypt, Encoding}; +use core::str; +use md5_crypto::{Digest, Md5}; + +// Block size for MD5 +const BLOCK_SIZE: usize = 16; +// PWD part length of the password string +const PW_SIZE_MD5: usize = 22; +// Maximum length of a setting +const SALT_MAX: usize = 8; +// Inverse encoding map for MD5. +const MAP_MD5: [c_uchar; BLOCK_SIZE] = [12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11]; + +const KEY_MAX: usize = 30000; + +fn encode_md5(source: &[c_uchar]) -> Option<[c_uchar; PW_SIZE_MD5]> { + let mut transposed = [0; BLOCK_SIZE]; + for (i, &ti) in MAP_MD5.iter().enumerate() { + transposed[i] = source[ti as usize]; + } + let mut buf = [0; PW_SIZE_MD5]; + Base64ShaCrypt::encode(&transposed, &mut buf).ok()?; + Some(buf) +} + +/// Function taken from PR: https://github.com/RustCrypto/password-hashes/pull/351 +/// This won't be needed once the PR is merged +fn inner_md5(passw: &str, setting: &str) -> Option { + let mut digest_b = Md5::default(); + digest_b.update(passw); + digest_b.update(setting); + digest_b.update(passw); + let hash_b = digest_b.finalize(); + + let mut digest_a = Md5::default(); + digest_a.update(passw); + digest_a.update("$1$"); + digest_a.update(setting); + + let mut pw_len = passw.len(); + let rounds = pw_len / BLOCK_SIZE; + for _ in 0..rounds { + digest_a.update(hash_b); + } + + // leftover passw + digest_a.update(&hash_b[..(pw_len - rounds * BLOCK_SIZE)]); + + while pw_len > 0 { + match pw_len & 1 { + 0 => digest_a.update(&passw[..1]), + 1 => digest_a.update([0u8]), + _ => unreachable!(), + } + pw_len >>= 1; + } + + let mut hash_a = digest_a.finalize(); + + // Repeatedly run the collected hash value through MD5 to burn + // CPU cycles + for i in 0..1000_usize { + // new hasher + let mut hasher = Md5::default(); + + // Add key or last result + if (i & 1) != 0 { + hasher.update(passw); + } else { + hasher.update(hash_a); + } + + // Add setting for numbers not divisible by 3 + if i % 3 != 0 { + hasher.update(setting); + } + + // Add key for numbers not divisible by 7 + if i % 7 != 0 { + hasher.update(passw); + } + + // Add key or last result + if (i & 1) != 0 { + hasher.update(hash_a); + } else { + hasher.update(passw); + } + + // digest_c.clone_from_slice(&hasher.finalize()); + hash_a = hasher.finalize(); + } + encode_md5(hash_a.as_slice()) + .map(|encstr| format!("$1${}${}", setting, str::from_utf8(&encstr).unwrap())) +} + +/// Performs MD5 hashing on a given password with a specific setting. +/// +/// # Parameters +/// * `passw`: The password to be hashed. It must be a string slice (`&str`). +/// * `setting`: The settings for the MD5 hashing. It must be a string slice (`&str`) +/// and should start with "$1$". The rest of the string should represent the salt +/// to be used for the MD5 hashing. +/// +/// # Returns +/// * `Option`: Returns `Some(String)` if the MD5 operation was successful, where the +/// returned string is the result of the MD5 operation formatted according to the Modular +/// Crypt Format (MCF). If the MD5 operation failed, it returns `None`. +/// +/// # Errors +/// * If the `passw` length exceeds `KEY_MAX`. +/// * If the `setting` does not start with "$1$". +/// +/// # Example +/// ``` +/// let password = "my_password"; +/// let setting = "$1$saltstring"; +/// let result = crypt_md5(password, setting); +/// assert!(result.is_some()); +/// ``` +/// +/// # Note +/// The `crypt_md5` function uses the MD5 hashing algorithm for hashing. +/// The output of the MD5 operation is base64-encoded using the BCrypt variant of base64. +pub fn crypt_md5(passw: &str, setting: &str) -> Option { + /* reject large keys */ + if passw.len() > KEY_MAX { + return None; + } + + if &setting[0..3] != "$1$" { + return None; + } + + let cursor = 3; + let slen = cursor + + setting[cursor..cursor + SALT_MAX] + .chars() + .take_while(|c| *c != '$') + .count(); + let setting = &setting[cursor..slen]; + + inner_md5(passw, setting) +} diff --git a/src/header/crypt/mod.rs b/src/header/crypt/mod.rs new file mode 100644 index 0000000000..4a3edd6872 --- /dev/null +++ b/src/header/crypt/mod.rs @@ -0,0 +1,121 @@ +//! `crypt.h` implementation. +//! +//! Non-POSIX, see . + +use ::scrypt::password_hash::{Salt, SaltString}; +use alloc::{ + ffi::CString, + string::{String, ToString}, +}; +use core::ptr; +use rand::{Rng, SeedableRng, rngs::SmallRng}; + +use crate::{ + c_str::CStr, + header::{errno::EINVAL, stdlib::rand}, + platform::{ + self, + types::{c_char, c_int}, + }, +}; + +mod argon2; +mod blowfish; +mod md5; +mod pbkdf2; +mod scrypt; +mod sha; + +use self::{ + argon2::crypt_argon2, + blowfish::crypt_blowfish, + md5::crypt_md5, + pbkdf2::crypt_pbkdf2, + scrypt::crypt_scrypt, + sha::{ + ShaType::{Sha256, Sha512}, + crypt_sha, + }, +}; + +/// See . +#[repr(C)] +pub struct crypt_data { + initialized: c_int, + buff: [c_char; 256], +} + +impl crypt_data { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + crypt_data { + initialized: 1, + buff: [0; 256], + } + } +} + +fn gen_salt() -> Option { + let mut rng = SmallRng::seed_from_u64(unsafe { rand() as u64 }); + let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH]; + rng.fill_bytes(&mut bytes); + Some(SaltString::encode_b64(&bytes).ok()?.as_str().to_string()) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn crypt_r( + key: *const c_char, + setting: *const c_char, + data: *mut crypt_data, +) -> *mut c_char { + if unsafe { (*data).initialized } == 0 { + unsafe { *data = crypt_data::new() }; + } + + let key = unsafe { CStr::from_ptr(key) } + .to_str() + .expect("key must be utf-8"); + let setting = unsafe { CStr::from_ptr(setting) } + .to_str() + .expect("setting must be utf-8"); + + let encoded = if setting.starts_with('$') { + if setting.starts_with("$1$") { + crypt_md5(key, setting) + } else if setting.starts_with("$2") && setting.as_bytes().get(3) == Some(&b'$') { + crypt_blowfish(key, setting) + } else if setting.starts_with("$5$") { + crypt_sha(key, setting, Sha256) + } else if setting.starts_with("$6$") { + crypt_sha(key, setting, Sha512) + } else if setting.starts_with("$7$") { + crypt_scrypt(key, setting) + } else if setting.starts_with("$8$") { + crypt_pbkdf2(key, setting) + } else if setting.starts_with("$argon2") { + crypt_argon2(key, setting) + } else { + platform::ERRNO.set(EINVAL); + return ptr::null_mut(); + } + } else { + None + }; + + if let Some(inner) = encoded { + let len = inner.len(); + if let Ok(ret) = CString::new(inner) { + let ret_ptr = ret.into_raw(); + unsafe { + let dst = (*data).buff.as_mut_ptr(); + ptr::copy_nonoverlapping(ret_ptr, dst.cast(), len); + } + ret_ptr.cast() + } else { + ptr::null_mut() + } + } else { + ptr::null_mut() + } +} diff --git a/src/header/crypt/pbkdf2.rs b/src/header/crypt/pbkdf2.rs new file mode 100644 index 0000000000..9a614873e6 --- /dev/null +++ b/src/header/crypt/pbkdf2.rs @@ -0,0 +1,64 @@ +use super::gen_salt; +use alloc::string::{String, ToString}; +use base64ct::{Base64Bcrypt, Encoding}; +use core::str; +use pbkdf2::pbkdf2_hmac; +use sha2::Sha256; + +/// Performs PBKDF2 key derivation on a given password with a specific setting. +/// +/// # Parameters +/// * `passw`: The password to be hashed. It must be a string slice (`&str`). +/// * `setting`: The settings for the PBKDF2 key derivation. It must be a string slice (`&str`) +/// and should follow the format `$$`. The `` part should be a hexadecimal +/// number representing the iteration count for the PBKDF2 function. The `` part should +/// be a base64-encoded string representing the salt to be used for the PBKDF2 function. +/// +/// # Returns +/// * `Option`: Returns `Some(String)` if the PBKDF2 operation was successful, where the +/// returned string is the result of the PBKDF2 operation formatted according to the Modular +/// Crypt Format (MCF). If the PBKDF2 operation failed, it returns `None`. +/// +/// # Errors +/// * If the `setting` does not contain a '$' character. +/// * If the `setting` contains another '$' character after the first one. +/// * If the `` part of the `setting` is empty. +/// * If the `` part of the `setting` cannot be converted into a `u32` integer. +/// +/// # Example +/// ``` +/// let password = "my_password"; +/// let setting = "$8$3e8$salt"; +/// let result = crypt_pbkdf2(password, setting); +/// assert!(result.is_some()); +/// ``` +/// +/// # Note +/// The `crypt_pbkdf2` function uses the SHA256 hashing algorithm for the PBKDF2 operation. +/// The output of the PBKDF2 operation is base64-encoded using the BCrypt variant of base64. +pub fn crypt_pbkdf2(passw: &str, setting: &str) -> Option { + if let Some((iter_str, salt)) = &setting[3..].split_once('$') { + if salt.contains('$') { + return None; + } + + let actual_salt = if !salt.is_empty() { + salt.to_string() + } else { + gen_salt()? + }; + + let iter = u32::from_str_radix(iter_str, 16).ok()?; + let mut buffer = [0u8; 32]; + pbkdf2_hmac::(passw.as_bytes(), actual_salt.as_bytes(), iter, &mut buffer); + + Some(format!( + "$8${}${}${}", + iter_str, + salt, + Base64Bcrypt::encode_string(&buffer) + )) + } else { + None + } +} diff --git a/src/header/crypt/scrypt.rs b/src/header/crypt/scrypt.rs new file mode 100644 index 0000000000..334d9dfdb4 --- /dev/null +++ b/src/header/crypt/scrypt.rs @@ -0,0 +1,113 @@ +use super::gen_salt; +use crate::platform::types::{c_uchar, c_uint}; +use alloc::string::{String, ToString}; +use base64ct::{Base64Bcrypt, Encoding}; +use core::str; +use scrypt::{Params, scrypt}; + +/// Map for encoding and decoding +#[inline(always)] +fn to_digit(c: char, radix: u32) -> Option { + match c { + '.' => Some(0), + '/' => Some(1), + _ => c.to_digit(radix).map(|d| d + 2), + } +} + +/// Decodes a 5 character lengt str value to c_uint +/// +/// # Arguments +/// +/// * `value` - A string slice that represents a u32 value in base64 +/// +/// # Returns +/// +/// * `Option` - Returns the decoded c_uint value if successful, otherwise None +fn dencode_uint(value: &str) -> Option { + if value.len() != 5 { + return None; + } + + value + .chars() + .enumerate() + .try_fold(0 as c_uint, |acc, (i, c)| { + acc.checked_add((to_digit(c, 30)? as c_uint) << (i * 6)) + }) +} + +/// Reads settings for password encryption +/// +/// # Arguments +/// +/// * `setting` - A string slice that represents the settings +/// +/// # Returns +/// +/// * `Option<(c_uchar, c_uint, c_uint, String)>` - Returns a tuple containing the settings if successful, otherwise None +fn read_setting(setting: &str) -> Option<(c_uchar, c_uint, c_uint, String)> { + let nlog2 = to_digit(setting.chars().next()?, 30)? as c_uchar; + let r = dencode_uint(&setting[1..6])?; + let p = dencode_uint(&setting[6..11])?; + + let salt = &setting[11..]; + let actual_salt = if !salt.is_empty() { + salt.to_string() + } else { + gen_salt()? + }; + + Some((nlog2, r, p, actual_salt)) +} + +/// Performs Scrypt key derivation on a given password with a specific setting. +/// +/// # Parameters +/// * `passw`: The password to be hashed. It must be a string slice (`&str`). +/// * `setting`: The settings for the Scrypt key derivation. It must be a string slice (`&str`) +/// and should follow the format `$$$

$`. The `` part should be a decimal +/// number representing the logarithm base 2 of the CPU/memory cost factor N for Scrypt. The `` +/// part should be a decimal number representing the block size r. The `

` part should be a decimal +/// number representing the parallelization factor p. The `` part should be a base64-encoded +/// string representing the salt to be used for the Scrypt function. +/// +/// # Returns +/// * `Option`: Returns `Some(String)` if the Scrypt operation was successful, where the +/// returned string is the result of the Scrypt operation formatted according to the Modular +/// Crypt Format (MCF). If the Scrypt operation failed, it returns `None`. +/// +/// # Errors +/// * If the `setting` length is less than 14 characters. +/// * If the `scrypt` function fails to perform the Scrypt operation. +/// +/// # Example +/// ``` +/// let password = "my_password"; +/// let setting = "$7$C6..../....SodiumChloride"; +/// let result = crypt_scrypt(password, setting); +/// assert!(result.is_some()); +/// ``` +/// +/// # Note +/// The `crypt_scrypt` function uses the Scrypt key derivation function for hashing. +/// The output of the Scrypt operation is base64-encoded using the BCrypt variant of base64. +pub fn crypt_scrypt(passw: &str, setting: &str) -> Option { + if setting.len() < 14 { + return None; + } + + let (nlog2, r, p, salt) = read_setting(&setting[3..])?; + + let params = Params::new(nlog2, r, p, 32).ok()?; + let mut output = [0u8; 32]; + + scrypt(passw.as_bytes(), salt.as_bytes(), ¶ms, &mut output).ok()?; + + Some(format!( + "$7${}${}${}", + &setting[3..14], + salt, + Base64Bcrypt::encode_string(&output) + )) +} diff --git a/src/header/crypt/sha.rs b/src/header/crypt/sha.rs new file mode 100644 index 0000000000..a2935a77a2 --- /dev/null +++ b/src/header/crypt/sha.rs @@ -0,0 +1,141 @@ +use alloc::string::{String, ToString}; + +use sha_crypt::{ + ROUNDS_DEFAULT, ROUNDS_MAX, ROUNDS_MIN, Sha256Params, Sha512Params, sha256_crypt_b64, + sha512_crypt_b64, +}; + +use crate::platform::types::c_ulong; + +// key limit is not part of the original design, added for DoS protection. +// rounds limit has been lowered (versus the reference/spec), also for DoS +// protection. runtime is O(klen^2 + klen*rounds) +const KEY_MAX: usize = 256; +const SALT_MAX: usize = 16; +const RSTRING: &str = "rounds="; + +pub enum ShaType { + Sha256, + Sha512, +} + +/// Performs SHA hashing on a given password with a specific setting. +/// +/// # Parameters +/// * `passw`: The password to be hashed. It must be a string slice (`&str`). +/// * `setting`: The settings for the SHA hashing. It must be a string slice (`&str`) +/// and should start with "$5$" for SHA256 or "$6$" for SHA512. The rest of the string should represent the salt +/// to be used for the SHA hashing. +/// * `cipher`: The type of SHA algorithm to use. It should be either `ShaType::Sha256` or `ShaType::Sha512`. +/// +/// # Returns +/// * `Option`: Returns `Some(String)` if the SHA operation was successful, where the +/// returned string is the result of the SHA operation formatted according to the Modular +/// Crypt Format (MCF). If the SHA operation failed, it returns `None`. +/// +/// # Errors +/// * If the `passw` length exceeds `KEY_MAX`. +/// * If the `setting` does not start with "$5$" or "$6$". +/// * If the `setting` does not contain a '$' character. +/// * If the `setting` contains another '$' character after the first one. +/// * If the `setting` contains invalid characters. +/// * If the `setting` contains an invalid number of rounds. +/// * If the `sha256_crypt_b64` or `sha512_crypt_b64` function fails to hash the password. +/// +/// # Example +/// ``` +/// let password = "my_password"; +/// let setting = "$5$rounds=1400$anotherlongsaltstringg"; +/// let result = crypt_sha(password, setting, ShaType::Sha256); +/// assert!(result.is_some()); +/// ``` +/// +/// # Note +/// The `crypt_sha` function uses the SHA256 or SHA512 hashing algorithm for hashing. +/// The output of the SHA operation is base64-encoded using the BCrypt variant of base64. +pub fn crypt_sha(passw: &str, setting: &str, cipher: ShaType) -> Option { + let mut cursor = 3; + let rounds; + + /* reject large keys */ + if passw.len() > KEY_MAX { + return None; + } + + // SHA256 + // setting: $5$rounds=n$setting$ (rounds=n$ and closing $ are optional) + // SHA512 + // setting: $6$rounds=n$setting$ (rounds=n$ and closing $ are optional) + let param = match cipher { + ShaType::Sha256 => "$5$", + ShaType::Sha512 => "$6$", + }; + + if &setting[0..3] != param { + return None; + } + + let has_round; + // 7 is len("rounds=") + if &setting[cursor..cursor + 7] == RSTRING { + cursor += 7; + has_round = true; + if let Some(c_end) = setting[cursor..].chars().position(|r| r == '$') { + if let Ok(u) = setting[cursor..cursor + c_end].parse::() { + cursor += c_end + 1; + rounds = u.min(ROUNDS_MAX as c_ulong).max(ROUNDS_MIN as c_ulong); + } else { + return None; + } + } else { + return None; + } + } else { + has_round = false; + rounds = ROUNDS_DEFAULT as c_ulong; + } + + let mut slen = cursor; + + for i in 0..SALT_MAX.min(setting.len() - cursor) { + let idx = cursor + i; + + if &setting[idx..idx + 1] == "$" { + break; + } + + // reject characters that interfere with /etc/shadow parsing + if &setting[idx..idx + 1] == "\n" || &setting[idx..idx + 1] == ":" { + return None; + } + slen += 1; + } + + let setting = &setting[cursor..slen]; + + if let Ok(enc) = match cipher { + ShaType::Sha256 => { + let params = Sha256Params::new(rounds as usize) + .unwrap_or(Sha256Params::new(ROUNDS_DEFAULT).unwrap()); + sha256_crypt_b64(passw.as_bytes(), setting.as_bytes(), ¶ms) + } + ShaType::Sha512 => { + let params = Sha512Params::new(rounds as usize) + .unwrap_or(Sha512Params::new(ROUNDS_DEFAULT).unwrap()); + sha512_crypt_b64(passw.as_bytes(), setting.as_bytes(), ¶ms) + } + } { + let (r_slice, rn_slice) = if has_round { + (RSTRING, rounds.to_string() + "$") + } else { + ("", String::new()) + }; + + Some(format!( + "{}{}{}{}${}", + param, r_slice, rn_slice, setting, enc + )) + } else { + None + } +} diff --git a/src/header/ctype/cbindgen.toml b/src/header/ctype/cbindgen.toml new file mode 100644 index 0000000000..c0b42f8c49 --- /dev/null +++ b/src/header/ctype/cbindgen.toml @@ -0,0 +1,21 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/ctype.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the locale_t type as described in , representing a locale object." +# +# features.h included for #[deprecated] annotation +sys_includes = ["features.h"] +include_guard = "_RELIBC_CTYPE_H" +after_includes = """ +#include // for locale_t + +#define _tolower(c) tolower(c) +#define _toupper(c) toupper(c) +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/ctype/mod.rs b/src/header/ctype/mod.rs new file mode 100644 index 0000000000..3d8da3cffe --- /dev/null +++ b/src/header/ctype/mod.rs @@ -0,0 +1,205 @@ +//! `ctype.h` implementation. +//! +//! See . + +use crate::{header::bits_locale_t::locale_t, platform::types::c_int}; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isalnum(c: c_int) -> c_int { + c_int::from(isdigit(c) != 0 || isalpha(c) != 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isalnum_l(c: c_int, _loc: locale_t) -> c_int { + isalnum(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isalpha(c: c_int) -> c_int { + c_int::from(islower(c) != 0 || isupper(c) != 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isalpha_l(c: c_int, _loc: locale_t) -> c_int { + isalpha(c) +} + +/// See . +/// +/// The `isascii()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn isascii(c: c_int) -> c_int { + c_int::from((c & !0x7f) == 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isblank(c: c_int) -> c_int { + c_int::from(c == c_int::from(b' ') || c == c_int::from(b'\t')) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isblank_l(c: c_int, _loc: locale_t) -> c_int { + isblank(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iscntrl(c: c_int) -> c_int { + c_int::from((0x00..=0x1f).contains(&c) || c == 0x7f) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iscntrl_l(c: c_int, _loc: locale_t) -> c_int { + iscntrl(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isdigit(c: c_int) -> c_int { + c_int::from(c >= c_int::from(b'0') && c <= c_int::from(b'9')) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isdigit_l(c: c_int, _loc: locale_t) -> c_int { + isdigit(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isgraph(c: c_int) -> c_int { + c_int::from((0x21..=0x7e).contains(&c)) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isgraph_l(c: c_int, _loc: locale_t) -> c_int { + isgraph(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn islower(c: c_int) -> c_int { + c_int::from(c >= c_int::from(b'a') && c <= c_int::from(b'z')) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn islower_l(c: c_int, _loc: locale_t) -> c_int { + islower(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isprint(c: c_int) -> c_int { + c_int::from((0x20..0x7f).contains(&c)) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isprint_l(c: c_int, _loc: locale_t) -> c_int { + isprint(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ispunct(c: c_int) -> c_int { + c_int::from( + (c >= c_int::from(b'!') && c <= c_int::from(b'/')) + || (c >= c_int::from(b':') && c <= c_int::from(b'@')) + || (c >= c_int::from(b'[') && c <= c_int::from(b'`')) + || (c >= c_int::from(b'{') && c <= c_int::from(b'~')), + ) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ispunct_l(c: c_int, _loc: locale_t) -> c_int { + ispunct(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isspace(c: c_int) -> c_int { + c_int::from( + c == c_int::from(b' ') + || c == c_int::from(b'\t') + || c == c_int::from(b'\n') + || c == c_int::from(b'\r') + || c == 0x0b + || c == 0x0c, + ) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isspace_l(c: c_int, _loc: locale_t) -> c_int { + isspace(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isupper(c: c_int) -> c_int { + c_int::from(c >= c_int::from(b'A') && c <= c_int::from(b'Z')) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isupper_l(c: c_int, _loc: locale_t) -> c_int { + isupper(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isxdigit(c: c_int) -> c_int { + c_int::from(isdigit(c) != 0 || (c | 32 >= c_int::from(b'a') && c | 32 <= c_int::from(b'f'))) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isxdigit_l(c: c_int, _loc: locale_t) -> c_int { + isxdigit(c) +} + +/// See . +/// +/// The `toascii()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn toascii(c: c_int) -> c_int { + c & 0x7f +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn tolower(c: c_int) -> c_int { + if isupper(c) != 0 { c | 0x20 } else { c } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn tolower_l(c: c_int, _loc: locale_t) -> c_int { + tolower(c) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn toupper(c: c_int) -> c_int { + if islower(c) != 0 { c & !0x20 } else { c } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn toupper_l(c: c_int, _loc: locale_t) -> c_int { + toupper(c) +} diff --git a/src/header/dirent/cbindgen.toml b/src/header/dirent/cbindgen.toml new file mode 100644 index 0000000000..e229472647 --- /dev/null +++ b/src/header/dirent/cbindgen.toml @@ -0,0 +1,20 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the ino_t, reclen_t, size_t, and ssize_t types as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_DIRENT_H" +language = "C" +style = "Both" +after_includes = """ + +// Shamelessly stolen from musl +// Macros to convert between struct dirent and struct stat types. +#define IFTODT(x) ((x)>>12 & 017) +#define DTTOIF(x) ((x)<<12) +""" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs new file mode 100644 index 0000000000..17eccf0b4e --- /dev/null +++ b/src/header/dirent/mod.rs @@ -0,0 +1,399 @@ +//! `dirent.h` implementation. +//! +//! See . + +use alloc::{boxed::Box, vec::Vec}; +use core::{mem, ptr, slice}; + +use crate::{ + c_str::CStr, + c_vec::CVec, + error::{Errno, ResultExt, ResultExtPtrMut}, + fs::File, + header::{ + errno::{EINVAL, EIO, ENOMEM, ENOTDIR}, + fcntl, stdlib, string, sys_stat, + }, + out::Out, + platform::{ + self, Pal, Sys, + types::{ + c_char, c_int, c_long, c_uchar, c_ushort, c_void, ino_t, off_t, reclen_t, size_t, + ssize_t, + }, + }, +}; + +// values of below constants taken from musl + +/// Unknown file type. +pub const DT_UNKNOWN: c_int = 0; +/// FIFO special. +pub const DT_FIFO: c_int = 1; +/// Character special. +pub const DT_CHR: c_int = 2; +/// Directory. +pub const DT_DIR: c_int = 4; +/// Block special. +pub const DT_BLK: c_int = 6; +/// Regular. +pub const DT_REG: c_int = 8; +/// Symbolic link. +pub const DT_LNK: c_int = 10; +/// Socket. +pub const DT_SOCK: c_int = 12; +/// Non-POSIX. +/// Whiteout inode. +pub const DT_WHT: c_int = 14; + +// values of above constants taken from musl + +/// cbindgen:ignore +const INITIAL_BUFSIZE: usize = 512; + +/// See . +// No repr(C) needed, as this is a completely opaque struct. Being accessed as a pointer, in C it's +// just defined as `struct DIR`. +pub struct DIR { + file: File, + buf: Vec, + buf_offset: usize, + + // The last value of d_off, used by telldir + opaque_offset: u64, +} +impl DIR { + pub fn new(path: CStr) -> Result, Errno> { + Ok(Box::new(Self { + file: File::open( + path, + fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC, + )?, + buf: Vec::with_capacity(INITIAL_BUFSIZE), + buf_offset: 0, + opaque_offset: 0, + })) + } + pub fn from_fd(fd: c_int) -> Result, Errno> { + let mut stat = sys_stat::stat::default(); + Sys::fstat(fd, Out::from_mut(&mut stat))?; + if (stat.st_mode & sys_stat::S_IFMT) != sys_stat::S_IFDIR { + return Err(Errno(ENOTDIR)); + } + Sys::fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as _)?; + + // Take ownership now but not earlier so we don't close the fd on error. + let file = File::new(fd); + Ok(Self { + file, + buf: Vec::with_capacity(INITIAL_BUFSIZE), + buf_offset: 0, + opaque_offset: 0, + } + .into()) + } + fn next_dirent(&mut self) -> Result<*mut dirent, Errno> { + let mut this_dent = self.buf.get(self.buf_offset..).ok_or(Errno(EIO))?; + if this_dent.is_empty() { + let size = loop { + self.buf.resize(self.buf.capacity(), 0_u8); + // TODO: uninitialized memory? + match Sys::getdents(*self.file, &mut self.buf, self.opaque_offset) { + Ok(size) => break size, + Err(Errno(EINVAL)) => { + self.buf + .try_reserve_exact(self.buf.len()) + .map_err(|_| Errno(ENOMEM))?; + continue; + } + Err(Errno(other)) => return Err(Errno(other)), + } + }; + self.buf.truncate(size); + self.buf_offset = 0; + + if size == 0 { + return Ok(core::ptr::null_mut()); + } + this_dent = &self.buf; + } + let (this_reclen, this_next_opaque) = + unsafe { Sys::dent_reclen_offset(this_dent, self.buf_offset).ok_or(Errno(EIO))? }; + + //println!("CDENT {} {}+{}", self.opaque_offset, self.buf_offset, this_reclen); + + let next_off = self + .buf_offset + .checked_add(usize::from(this_reclen)) + .ok_or(Errno(EIO))?; + if next_off > self.buf.len() { + return Err(Errno(EIO)); + } + if this_dent.len() < usize::from(this_reclen) { + // Don't want memory corruption if a scheme is adversarial. + return Err(Errno(EIO)); + } + let dent_ptr = this_dent.as_ptr() as *mut dirent; + + self.opaque_offset = this_next_opaque; + self.buf_offset = next_off; + Ok(dent_ptr) + } + fn seek(&mut self, off: u64) { + let Ok(_) = Sys::dir_seek(*self.file, off) else { + return; + }; + self.buf.clear(); + self.buf_offset = 0; + self.opaque_offset = off; + } + fn rewind(&mut self) { + self.opaque_offset = 0; + let Ok(_) = Sys::dir_seek(*self.file, 0) else { + return; + }; + self.buf.clear(); + self.buf_offset = 0; + self.opaque_offset = 0; + } + fn close(mut self) -> Result<(), Errno> { + // Reference files aren't closed when dropped + self.file.reference = true; + + // TODO: result + Sys::close(*self.file) + } +} + +/// See . +#[repr(C)] +#[derive(Clone)] +pub struct dirent { + pub d_ino: ino_t, + pub d_off: off_t, + pub d_reclen: c_ushort, + pub d_type: c_uchar, + pub d_name: [c_char; 256], +} + +/// See . +/// must have the same struct layout as dirent +#[repr(C)] +#[derive(Clone)] +pub struct posix_dent { + pub d_ino: ino_t, + pub d_off: off_t, // not specified by posix + pub d_reclen: reclen_t, + pub d_type: c_uchar, + pub d_name: [c_char; 256], +} + +/// cbindgen:ignore +#[cfg(target_os = "redox")] +const _: () = { + use core::mem::{offset_of, size_of}; + use syscall::dirent::DirentHeader; + + if offset_of!(dirent, d_ino) != offset_of!(DirentHeader, inode) { + panic!("struct dirent layout mismatch (inode)"); + } + if offset_of!(dirent, d_off) != offset_of!(DirentHeader, next_opaque_id) { + panic!("struct dirent layout mismatch (inode)"); + } + if offset_of!(dirent, d_reclen) != offset_of!(DirentHeader, record_len) { + panic!("struct dirent layout mismatch (len)"); + } + if offset_of!(dirent, d_type) != offset_of!(DirentHeader, kind) { + panic!("struct dirent layout mismatch (kind)"); + } + if offset_of!(dirent, d_name) != size_of::() { + panic!("struct dirent layout mismatch (name)"); + } +}; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int { + unsafe { string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr()) } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn closedir(dir: Box

) -> c_int { + dir.close().map(|()| 0).or_minus_one_errno() +} + +/// See +/// +/// FreeBSD extension that transfers ownership of the directory file descriptor to the user. +/// +/// It doesn't matter if DIR was opened with [`opendir`] or [`fdopendir`]. +#[unsafe(no_mangle)] +pub extern "C" fn fdclosedir(dir: Box) -> c_int { + let mut file = dir.file; + file.reference = true; + + *file +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn dirfd(dir: &mut DIR) -> c_int { + *dir.file +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR { + let path = unsafe { CStr::from_ptr(path) }; + + DIR::new(path).or_errno_null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fdopendir(fd: c_int) -> *mut DIR { + DIR::from_fd(fd).or_errno_null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn posix_getdents( + fildes: c_int, + buf: *mut c_void, + nbyte: size_t, + _flags: c_int, +) -> ssize_t { + let slice = unsafe { slice::from_raw_parts_mut(buf.cast::(), nbyte) }; + + Sys::posix_getdents(fildes, slice) + .map(|s| s as ssize_t) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn readdir(dir: &mut DIR) -> *mut dirent { + dir.next_dirent().or_errno_null_mut() +} + +/// See . +/// +/// # Deprecation +/// The `readdir_r()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 8. +#[deprecated] +// #[unsafe(no_mangle)] +pub extern "C" fn readdir_r( + _dir: *mut DIR, + _entry: *mut dirent, + _result: *mut *mut dirent, +) -> *mut dirent { + unimplemented!(); // plus, deprecated +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn rewinddir(dir: &mut DIR) { + dir.rewind(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn scandir( + dirp: *const c_char, + namelist: *mut *mut *mut dirent, + filter: Option c_int>, + compare: Option c_int>, +) -> c_int { + let dir = unsafe { opendir(dirp) }; + if dir.is_null() { + return -1; + } + + let mut vec = match CVec::with_capacity(4) { + Ok(vec) => vec, + Err(err) => return -1, + }; + + let old_errno = platform::ERRNO.get(); + platform::ERRNO.set(0); + + loop { + let entry: *mut dirent = readdir(unsafe { &mut *dir }); + if entry.is_null() { + break; + } + + if let Some(filter) = filter + && filter(entry) == 0 + { + continue; + } + + let copy = unsafe { platform::alloc(mem::size_of::()) }.cast::(); + if copy.is_null() { + break; + } + unsafe { ptr::write(copy, (*entry).clone()) }; + if vec.push(copy).is_err() { + break; + } + } + + closedir(unsafe { Box::from_raw(dir) }); + + let len = vec.len(); + if vec.shrink_to_fit().is_err() { + return -1; + } + + if platform::ERRNO.get() != 0 { + for ptr in &mut vec { + unsafe { platform::free((*ptr).cast::()) }; + } + -1 + } else { + unsafe { + // Empty CVecs use a dangling pointer which cannot be freed, return null instead + if vec.is_empty() { + *namelist = ptr::null_mut(); + } else { + *namelist = vec.leak(); + } + } + + platform::ERRNO.set(old_errno); + unsafe { + stdlib::qsort( + (*namelist).cast::(), + len as size_t, + mem::size_of::<*mut dirent>(), + #[allow(clippy::missing_transmute_annotations)] // too verbose + mem::transmute(compare), + ) + }; + + len as c_int + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn seekdir(dir: &mut DIR, off: c_long) { + let Ok(off) = off.try_into() else { + platform::ERRNO.set(EINVAL); + return; + }; + + dir.seek(off); +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn telldir(dir: &mut DIR) -> c_long { + dir.opaque_offset as c_long +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_posix_dent(_: posix_dent) {} diff --git a/src/header/dl-tls/cbindgen.toml b/src/header/dl-tls/cbindgen.toml new file mode 100644 index 0000000000..c09ca25349 --- /dev/null +++ b/src/header/dl-tls/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = [] +include_guard = "_DL_TLS_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/dl-tls/mod.rs b/src/header/dl-tls/mod.rs new file mode 100644 index 0000000000..0196bf7bc8 --- /dev/null +++ b/src/header/dl-tls/mod.rs @@ -0,0 +1,102 @@ +//! dl-tls implementation for Redox + +// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is +// dropped from src/lib.rs. +#![warn(warnings, unused_variables)] + +#[cfg(target_arch = "x86")] +use core::arch::global_asm; + +use alloc::alloc::alloc; +use core::{alloc::Layout, ptr}; + +use crate::{ld_so::tcb::Tcb, platform::types::c_void}; + +#[repr(C)] +#[allow(non_camel_case_types)] +pub struct dl_tls_index { + pub ti_module: usize, + pub ti_offset: usize, +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void { + let tcb = unsafe { Tcb::current().unwrap() }; + let ti = unsafe { &*ti }; + let masters = tcb.masters().unwrap(); + + #[cfg(feature = "trace_tls")] + log::trace!( + "__tls_get_addr({:p}: {:#x}, {:#x}, masters_len={}, dtv_len={})", + ti, + ti.ti_module, + ti.ti_offset, + masters.len(), + tcb.dtv_mut().len() + ); + + if tcb.dtv_mut().len() < masters.len() { + // Reallocate DTV. + tcb.setup_dtv(masters.len()); + } + + let dtv_index = ti.ti_module - 1; + + if tcb.dtv_mut()[dtv_index].is_null() { + // Allocate TLS for module. + let master = &masters[dtv_index]; + + let module_tls = unsafe { + // FIXME(andypython): master.align + let layout = Layout::from_size_align_unchecked(master.segment_size, 16); + let ptr = alloc(layout); + + ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size); + ptr::write_bytes( + ptr.add(master.image_size), + 0, + master.segment_size - master.image_size, + ); + + ptr + }; + + // Set the DTV entry. + tcb.dtv_mut()[dtv_index] = module_tls; + } + + let mut ptr = tcb.dtv_mut()[dtv_index]; + + if ptr.is_null() { + panic!( + "__tls_get_addr({ti:p}: {:#x}, {:#x})", + ti.ti_module, ti.ti_offset + ); + } + + if cfg!(target_arch = "riscv64") { + ptr = unsafe { ptr.add(0x800 + ti.ti_offset) }; // dynamic offsets are 0x800-based on risc-v + } else { + ptr = unsafe { ptr.add(ti.ti_offset) }; + } + + ptr.cast::() +} + +// x86 can define a version that passes a pointer to dl_tls_index in eax +#[cfg(target_arch = "x86")] +global_asm!( + " + .globl ___tls_get_addr + .type ___tls_get_addr, @function +___tls_get_addr: + push ebp + mov ebp, esp + push eax + call __tls_get_addr + add esp, 4 + pop ebp + ret + .size ___tls_get_addr, . - ___tls_get_addr +" +); diff --git a/src/header/dlfcn/cbindgen.toml b/src/header/dlfcn/cbindgen.toml new file mode 100644 index 0000000000..4ea36e2a96 --- /dev/null +++ b/src/header/dlfcn/cbindgen.toml @@ -0,0 +1,12 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +include_guard = "_RELIBC_DLFCN_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs new file mode 100644 index 0000000000..923c057169 --- /dev/null +++ b/src/header/dlfcn/mod.rs @@ -0,0 +1,197 @@ +//! `dlfcn.h` implementation. +//! +//! See . + +// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is +// dropped from src/lib.rs. +#![warn(warnings, unused_variables)] + +use core::{ + ptr, str, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use crate::{ + c_str::CStr, + ld_so::{ + linker::{DlError, ObjectHandle, Resolve, ScopeKind}, + tcb::Tcb, + }, + platform::types::{c_char, c_int, c_void}, +}; + +pub const RTLD_LAZY: c_int = 1 << 0; +pub const RTLD_NOW: c_int = 1 << 1; +pub const RTLD_NOLOAD: c_int = 1 << 2; +pub const RTLD_GLOBAL: c_int = 1 << 8; +pub const RTLD_LOCAL: c_int = 0x0000; + +#[allow(clippy::zero_ptr)] // related cbindgen issue: https://github.com/mozilla/cbindgen/issues/948 +pub const RTLD_DEFAULT: *mut c_void = 0 as *mut c_void; // XXX: cbindgen doesn't like ptr::null_mut() for publically exported constants + +static ERROR_NOT_SUPPORTED: &core::ffi::CStr = c"dlfcn not supported"; + +#[thread_local] +static ERROR: AtomicUsize = AtomicUsize::new(0); + +fn set_last_error(error: DlError) { + ERROR.store(error.repr().as_ptr() as usize, Ordering::SeqCst); +} + +/// See . +#[repr(C)] +#[allow(non_camel_case_types)] +pub struct Dl_info_t { + dli_fname: *const c_char, + dli_fbase: *mut c_void, + dli_sname: *const c_char, + dli_saddr: *mut c_void, +} + +/// alias as per spec update: +pub type Dl_info = Dl_info_t; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dladdr(_addr: *const c_void, info: *mut Dl_info_t) -> c_int { + //TODO + unsafe { + (*info).dli_fname = ptr::null(); + (*info).dli_fbase = ptr::null_mut(); + (*info).dli_sname = ptr::null(); + (*info).dli_saddr = ptr::null_mut(); + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void { + //TODO support all sort of flags + let resolve = if flags & RTLD_NOW == RTLD_NOW { + Resolve::Now + } else { + Resolve::Lazy + }; + + let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL { + ScopeKind::Global + } else { + ScopeKind::Local + }; + + let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD; + + let filename = if cfilename.is_null() { + None + } else { + unsafe { + Some(str::from_utf8_unchecked( + CStr::from_ptr(cfilename).to_bytes(), + )) + } + }; + + let tcb = match unsafe { Tcb::current() } { + Some(tcb) => tcb, + None => { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); + } + }; + + if tcb.linker_ptr.is_null() { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); + } + + let mut linker = unsafe { (*tcb.linker_ptr).lock() }; + + let cbs_c = linker.cbs.clone(); + let cbs = cbs_c.borrow(); + + match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) { + Ok(handle) => handle.as_ptr().cast_mut(), + Err(error) => { + set_last_error(error); + ptr::null_mut() + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void { + let handle = ObjectHandle::from_ptr(handle); + + if symbol.is_null() { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); + } + + let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) }; + + // FIXME(andypython): just call obj.scope.get_sym() directly or search the + // global scope. The rest is unnecessary as Linker::get_sym() does not + // depend on the Linker state. + let tcb = match unsafe { Tcb::current() } { + Some(tcb) => tcb, + None => { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); + } + }; + + if tcb.linker_ptr.is_null() { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return ptr::null_mut(); + } + + let linker = unsafe { (*tcb.linker_ptr).lock() }; + let cbs_c = linker.cbs.clone(); + let cbs = cbs_c.borrow(); + match (cbs.get_sym)(&linker, handle, symbol_str) { + Some(sym) => sym, + _ => { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + ptr::null_mut() + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int { + let tcb = match unsafe { Tcb::current() } { + Some(tcb) => tcb, + None => { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return -1; + } + }; + + if tcb.linker_ptr.is_null() { + ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst); + return -1; + }; + + let Some(handle) = ObjectHandle::from_ptr(handle) else { + set_last_error(DlError::InvalidHandle); + return -1; + }; + + let mut linker = unsafe { (*tcb.linker_ptr).lock() }; + let cbs_c = linker.cbs.clone(); + let cbs = cbs_c.borrow(); + (cbs.unload)(&mut linker, handle); + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn dlerror() -> *mut c_char { + ERROR.swap(0, Ordering::SeqCst) as *mut c_char +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_alias_dlinfo_for_dladdr(_: Dl_info) {} diff --git a/src/header/elf/cbindgen.toml b/src/header/elf/cbindgen.toml new file mode 100644 index 0000000000..cf53dfec97 --- /dev/null +++ b/src/header/elf/cbindgen.toml @@ -0,0 +1,46 @@ +after_includes = """ +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) + +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) + +#define DT_VERNEEDNUM 0x6fffffff +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) + +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) +""" +include_guard = "_ELF_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/elf/mod.rs b/src/header/elf/mod.rs new file mode 100644 index 0000000000..cb471271f1 --- /dev/null +++ b/src/header/elf/mod.rs @@ -0,0 +1,996 @@ +//! `elf.h` implementation. +//! +//! Non-POSIX, see . + +use crate::platform::types::{c_char, c_uchar, int32_t, int64_t, uint16_t, uint32_t, uint64_t}; + +pub type Elf32_Half = uint16_t; +pub type Elf64_Half = uint16_t; + +pub type Elf32_Word = uint32_t; +pub type Elf32_Sword = int32_t; +pub type Elf64_Word = uint32_t; +pub type Elf64_Sword = int32_t; + +pub type Elf32_Xword = uint64_t; +pub type Elf32_Sxword = int64_t; +pub type Elf64_Xword = uint64_t; +pub type Elf64_Sxword = int64_t; + +pub type Elf32_Addr = uint32_t; +pub type Elf64_Addr = uint64_t; + +pub type Elf32_Off = uint32_t; +pub type Elf64_Off = uint64_t; + +pub type Elf32_Section = uint16_t; +pub type Elf64_Section = uint16_t; + +pub type Elf32_Versym = Elf32_Half; +pub type Elf64_Versym = Elf64_Half; + +pub const EI_NIDENT: usize = 16; + +/// See . +#[repr(C)] +pub struct Elf32_Ehdr { + pub e_ident: [c_uchar; EI_NIDENT], + pub e_type: Elf32_Half, + pub e_machine: Elf32_Half, + pub e_version: Elf32_Word, + pub e_entry: Elf32_Addr, + pub e_phoff: Elf32_Off, + pub e_shoff: Elf32_Off, + pub e_flags: Elf32_Word, + pub e_ehsize: Elf32_Half, + pub e_phentsize: Elf32_Half, + pub e_phnum: Elf32_Half, + pub e_shentsize: Elf32_Half, + pub e_shnum: Elf32_Half, + pub e_shstrndx: Elf32_Half, +} + +/// See . +#[repr(C)] +pub struct Elf64_Ehdr { + pub e_ident: [c_uchar; EI_NIDENT], + pub e_type: Elf64_Half, + pub e_machine: Elf64_Half, + pub e_version: Elf64_Word, + pub e_entry: Elf64_Addr, + pub e_phoff: Elf64_Off, + pub e_shoff: Elf64_Off, + pub e_flags: Elf64_Word, + pub e_ehsize: Elf64_Half, + pub e_phentsize: Elf64_Half, + pub e_phnum: Elf64_Half, + pub e_shentsize: Elf64_Half, + pub e_shnum: Elf64_Half, + pub e_shstrndx: Elf64_Half, +} + +pub const EI_MAG0: usize = 0; +pub const ELFMAG0: usize = 0x7f; +pub const EI_MAG1: usize = 1; +pub const ELFMAG1: c_char = 'E' as c_char; +pub const EI_MAG2: usize = 2; +pub const ELFMAG2: c_char = 'L' as c_char; +pub const EI_MAG3: usize = 3; +pub const ELFMAG3: c_char = 'F' as c_char; +pub const ELFMAG: &str = "\x7fELF"; +pub const SELFMAG: usize = 4; +pub const EI_CLASS: usize = 4; +pub const ELFCLASSNONE: usize = 0; +pub const ELFCLASS32: usize = 1; +pub const ELFCLASS64: usize = 2; +pub const ELFCLASSNUM: usize = 3; +pub const EI_DATA: usize = 5; +pub const ELFDATANONE: usize = 0; +pub const ELFDATA2LSB: usize = 1; +pub const ELFDATA2MSB: usize = 2; +pub const ELFDATANUM: usize = 3; +pub const EI_VERSION: usize = 6; +pub const EI_OSABI: usize = 7; +pub const ELFOSABI_NONE: usize = 0; +pub const ELFOSABI_SYSV: usize = 0; +pub const ELFOSABI_HPUX: usize = 1; +pub const ELFOSABI_NETBSD: usize = 2; +pub const ELFOSABI_LINUX: usize = 3; +pub const ELFOSABI_GNU: usize = 3; +pub const ELFOSABI_SOLARIS: usize = 6; +pub const ELFOSABI_AIX: usize = 7; +pub const ELFOSABI_IRIX: usize = 8; +pub const ELFOSABI_FREEBSD: usize = 9; +pub const ELFOSABI_TRU64: usize = 10; +pub const ELFOSABI_MODESTO: usize = 11; +pub const ELFOSABI_OPENBSD: usize = 12; +pub const ELFOSABI_ARM: usize = 97; +pub const ELFOSABI_STANDALONE: usize = 255; +pub const EI_ABIVERSION: usize = 8; +pub const EI_PAD: usize = 9; +pub const ET_NONE: usize = 0; +pub const ET_REL: usize = 1; +pub const ET_EXEC: usize = 2; +pub const ET_DYN: usize = 3; +pub const ET_CORE: usize = 4; +pub const ET_NUM: usize = 5; +pub const ET_LOOS: usize = 0xfe00; +pub const ET_HIOS: usize = 0xfeff; +pub const ET_LOPROC: usize = 0xff00; +pub const ET_HIPROC: usize = 0xffff; +pub const EM_NONE: usize = 0; +pub const EM_M32: usize = 1; +pub const EM_SPARC: usize = 2; +pub const EM_386: usize = 3; +pub const EM_68K: usize = 4; +pub const EM_88K: usize = 5; +pub const EM_860: usize = 7; +pub const EM_MIPS: usize = 8; +pub const EM_S370: usize = 9; +pub const EM_MIPS_RS3_LE: usize = 10; +pub const EM_PARISC: usize = 15; +pub const EM_VPP500: usize = 17; +pub const EM_SPARC32PLUS: usize = 18; +pub const EM_960: usize = 19; +pub const EM_PPC: usize = 20; +pub const EM_PPC64: usize = 21; +pub const EM_S390: usize = 22; +pub const EM_V800: usize = 36; +pub const EM_FR20: usize = 37; +pub const EM_RH32: usize = 38; +pub const EM_RCE: usize = 39; +pub const EM_ARM: usize = 40; +pub const EM_FAKE_ALPHA: usize = 41; +pub const EM_SH: usize = 42; +pub const EM_SPARCV9: usize = 43; +pub const EM_TRICORE: usize = 44; +pub const EM_ARC: usize = 45; +pub const EM_H8_300: usize = 46; +pub const EM_H8_300H: usize = 47; +pub const EM_H8S: usize = 48; +pub const EM_H8_500: usize = 49; +pub const EM_IA_64: usize = 50; +pub const EM_MIPS_X: usize = 51; +pub const EM_COLDFIRE: usize = 52; +pub const EM_68HC12: usize = 53; +pub const EM_MMA: usize = 54; +pub const EM_PCP: usize = 55; +pub const EM_NCPU: usize = 56; +pub const EM_NDR1: usize = 57; +pub const EM_STARCORE: usize = 58; +pub const EM_ME16: usize = 59; +pub const EM_ST100: usize = 60; +pub const EM_TINYJ: usize = 61; +pub const EM_X86_64: usize = 62; +pub const EM_PDSP: usize = 63; +pub const EM_FX66: usize = 66; +pub const EM_ST9PLUS: usize = 67; +pub const EM_ST7: usize = 68; +pub const EM_68HC16: usize = 69; +pub const EM_68HC11: usize = 70; +pub const EM_68HC08: usize = 71; +pub const EM_68HC05: usize = 72; +pub const EM_SVX: usize = 73; +pub const EM_ST19: usize = 74; +pub const EM_VAX: usize = 75; +pub const EM_CRIS: usize = 76; +pub const EM_JAVELIN: usize = 77; +pub const EM_FIREPATH: usize = 78; +pub const EM_ZSP: usize = 79; +pub const EM_MMIX: usize = 80; +pub const EM_HUANY: usize = 81; +pub const EM_PRISM: usize = 82; +pub const EM_AVR: usize = 83; +pub const EM_FR30: usize = 84; +pub const EM_D10V: usize = 85; +pub const EM_D30V: usize = 86; +pub const EM_V850: usize = 87; +pub const EM_M32R: usize = 88; +pub const EM_MN10300: usize = 89; +pub const EM_MN10200: usize = 90; +pub const EM_PJ: usize = 91; +pub const EM_OR1K: usize = 92; +pub const EM_ARC_A5: usize = 93; +pub const EM_XTENSA: usize = 94; +pub const EM_AARCH64: usize = 183; +pub const EM_TILEPRO: usize = 188; +pub const EM_MICROBLAZE: usize = 189; +pub const EM_TILEGX: usize = 191; +pub const EM_NUM: usize = 192; +pub const EM_ALPHA: usize = 0x9026; +pub const EV_NONE: usize = 0; +pub const EV_CURRENT: usize = 1; +pub const EV_NUM: usize = 2; + +/// See . +#[repr(C)] +pub struct Elf32_Shdr { + pub sh_name: Elf32_Word, + pub sh_type: Elf32_Word, + pub sh_flags: Elf32_Word, + pub sh_addr: Elf32_Addr, + pub sh_offset: Elf32_Off, + pub sh_size: Elf32_Word, + pub sh_link: Elf32_Word, + pub sh_info: Elf32_Word, + pub sh_addralign: Elf32_Word, + pub sh_entsize: Elf32_Word, +} + +/// See . +#[repr(C)] +pub struct Elf64_Shdr { + pub sh_name: Elf64_Word, + pub sh_type: Elf64_Word, + pub sh_flags: Elf64_Xword, + pub sh_addr: Elf64_Addr, + pub sh_offset: Elf64_Off, + pub sh_size: Elf64_Xword, + pub sh_link: Elf64_Word, + pub sh_info: Elf64_Word, + pub sh_addralign: Elf64_Xword, + pub sh_entsize: Elf64_Xword, +} + +pub const SHN_UNDEF: usize = 0; +pub const SHN_LORESERVE: usize = 0xff00; +pub const SHN_LOPROC: usize = 0xff00; +pub const SHN_BEFORE: usize = 0xff00; +pub const SHN_AFTER: usize = 0xff01; +pub const SHN_HIPROC: usize = 0xff1f; +pub const SHN_LOOS: usize = 0xff20; +pub const SHN_HIOS: usize = 0xff3f; +pub const SHN_ABS: usize = 0xfff1; +pub const SHN_COMMON: usize = 0xfff2; +pub const SHN_XINDEX: usize = 0xffff; +pub const SHN_HIRESERVE: usize = 0xffff; +pub const SHT_NULL: usize = 0; +pub const SHT_PROGBITS: usize = 1; +pub const SHT_SYMTAB: usize = 2; +pub const SHT_STRTAB: usize = 3; +pub const SHT_RELA: usize = 4; +pub const SHT_HASH: usize = 5; +pub const SHT_DYNAMIC: usize = 6; +pub const SHT_NOTE: usize = 7; +pub const SHT_NOBITS: usize = 8; +pub const SHT_REL: usize = 9; +pub const SHT_SHLIB: usize = 10; +pub const SHT_DYNSYM: usize = 11; +pub const SHT_INIT_ARRAY: usize = 14; +pub const SHT_FINI_ARRAY: usize = 15; +pub const SHT_PREINIT_ARRAY: usize = 16; +pub const SHT_GROUP: usize = 17; +pub const SHT_SYMTAB_SHNDX: usize = 18; +pub const SHT_NUM: usize = 19; +pub const SHT_LOOS: usize = 0x60000000; +pub const SHT_GNU_ATTRIBUTES: usize = 0x6ffffff5; +pub const SHT_GNU_HASH: usize = 0x6ffffff6; +pub const SHT_GNU_LIBLIST: usize = 0x6ffffff7; +pub const SHT_CHECKSUM: usize = 0x6ffffff8; +pub const SHT_LOSUNW: usize = 0x6ffffffa; +pub const SHT_SUNW_move: usize = 0x6ffffffa; +pub const SHT_SUNW_COMDAT: usize = 0x6ffffffb; +pub const SHT_SUNW_syminfo: usize = 0x6ffffffc; +pub const SHT_GNU_verdef: usize = 0x6ffffffd; +pub const SHT_GNU_verneed: usize = 0x6ffffffe; +pub const SHT_GNU_versym: usize = 0x6fffffff; +pub const SHT_HISUNW: usize = 0x6fffffff; +pub const SHT_HIOS: usize = 0x6fffffff; +pub const SHT_LOPROC: usize = 0x70000000; +pub const SHT_HIPROC: usize = 0x7fffffff; +pub const SHT_LOUSER: usize = 0x80000000; +pub const SHT_HIUSER: usize = 0x8fffffff; + +pub const SHF_WRITE: usize = 1 << 0; +pub const SHF_ALLOC: usize = 1 << 1; +pub const SHF_EXECINSTR: usize = 1 << 2; +pub const SHF_MERGE: usize = 1 << 4; +pub const SHF_STRINGS: usize = 1 << 5; +pub const SHF_INFO_LINK: usize = 1 << 6; +pub const SHF_LINK_ORDER: usize = 1 << 7; +pub const SHF_OS_NONCONFORMING: usize = 1 << 8; +pub const SHF_GROUP: usize = 1 << 9; +pub const SHF_TLS: usize = 1 << 10; +pub const SHF_MASKOS: usize = 0x0ff00000; +pub const SHF_MASKPROC: usize = 0xf0000000; +pub const SHF_ORDERED: usize = 1 << 30; +pub const SHF_EXCLUDE: usize = 1 << 31; +pub const GRP_COMDAT: usize = 0x1; + +/// See . +#[repr(C)] +pub struct Elf32_Sym { + pub st_name: Elf32_Word, + pub st_value: Elf32_Addr, + pub st_size: Elf32_Word, + pub st_info: c_uchar, + pub st_other: c_uchar, + pub st_shndx: Elf32_Section, +} + +/// See . +#[repr(C)] +pub struct Elf64_Sym { + pub st_name: Elf64_Word, + pub st_info: c_uchar, + pub st_other: c_uchar, + pub st_shndx: Elf64_Section, + pub st_value: Elf64_Addr, + pub st_size: Elf64_Xword, +} + +#[repr(C)] +pub struct Elf32_Syminfo { + pub si_boundto: Elf32_Half, + pub si_flags: Elf32_Half, +} + +#[repr(C)] +pub struct Elf64_Syminfo { + pub si_boundto: Elf64_Half, + pub si_flags: Elf64_Half, +} + +pub const SYMINFO_BT_SELF: usize = 0xffff; +pub const SYMINFO_BT_PARENT: usize = 0xfffe; +pub const SYMINFO_BT_LOWRESERVE: usize = 0xff00; +pub const SYMINFO_FLG_DIRECT: usize = 0x0001; +pub const SYMINFO_FLG_PASSTHRU: usize = 0x0002; +pub const SYMINFO_FLG_COPY: usize = 0x0004; +pub const SYMINFO_FLG_LAZYLOAD: usize = 0x0008; +pub const SYMINFO_NONE: usize = 0; +pub const SYMINFO_CURRENT: usize = 1; +pub const SYMINFO_NUM: usize = 2; + +pub const STB_LOCAL: usize = 0; +pub const STB_GLOBAL: usize = 1; +pub const STB_WEAK: usize = 2; +pub const STB_NUM: usize = 3; +pub const STB_LOOS: usize = 10; +pub const STB_GNU_UNIQUE: usize = 10; +pub const STB_HIOS: usize = 12; +pub const STB_LOPROC: usize = 13; +pub const STB_HIPROC: usize = 15; +pub const STT_NOTYPE: usize = 0; +pub const STT_OBJECT: usize = 1; +pub const STT_FUNC: usize = 2; +pub const STT_SECTION: usize = 3; +pub const STT_FILE: usize = 4; +pub const STT_COMMON: usize = 5; +pub const STT_TLS: usize = 6; +pub const STT_NUM: usize = 7; +pub const STT_LOOS: usize = 10; +pub const STT_GNU_IFUNC: usize = 10; +pub const STT_HIOS: usize = 12; +pub const STT_LOPROC: usize = 13; +pub const STT_HIPROC: usize = 15; +pub const STN_UNDEF: usize = 0; + +pub const STV_DEFAULT: usize = 0; +pub const STV_INTERNAL: usize = 1; +pub const STV_HIDDEN: usize = 2; +pub const STV_PROTECTED: usize = 3; + +/// See . +#[repr(C)] +pub struct Elf32_Rel { + pub r_offset: Elf32_Addr, + pub r_info: Elf32_Word, +} + +/// See . +#[repr(C)] +pub struct Elf64_Rel { + pub r_offset: Elf64_Addr, + pub r_info: Elf64_Xword, +} + +/// See . +#[repr(C)] +pub struct Elf32_Rela { + pub r_offset: Elf32_Addr, + pub r_info: Elf32_Word, + pub r_addend: Elf32_Sword, +} + +/// See . +#[repr(C)] +pub struct Elf64_Rela { + pub r_offset: Elf64_Addr, + pub r_info: Elf64_Xword, + pub r_addend: Elf64_Sxword, +} + +/// See . +#[repr(C)] +pub struct Elf32_Phdr { + pub p_type: Elf32_Word, + pub p_offset: Elf32_Off, + pub p_vaddr: Elf32_Addr, + pub p_paddr: Elf32_Addr, + pub p_filesz: Elf32_Word, + pub p_memsz: Elf32_Word, + pub p_flags: Elf32_Word, + pub p_align: Elf32_Word, +} + +/// See . +#[repr(C)] +pub struct Elf64_Phdr { + pub p_type: Elf64_Word, + pub p_flags: Elf64_Word, + pub p_offset: Elf64_Off, + pub p_vaddr: Elf64_Addr, + pub p_paddr: Elf64_Addr, + pub p_filesz: Elf64_Xword, + pub p_memsz: Elf64_Xword, + pub p_align: Elf64_Xword, +} + +pub const PT_NULL: usize = 0; +pub const PT_LOAD: usize = 1; +pub const PT_DYNAMIC: usize = 2; +pub const PT_INTERP: usize = 3; +pub const PT_NOTE: usize = 4; +pub const PT_SHLIB: usize = 5; +pub const PT_PHDR: usize = 6; +pub const PT_TLS: usize = 7; +pub const PT_NUM: usize = 8; +pub const PT_LOOS: usize = 0x60000000; +pub const PT_GNU_EH_FRAME: usize = 0x6474e550; +pub const PT_GNU_STACK: usize = 0x6474e551; +pub const PT_GNU_RELRO: usize = 0x6474e552; +pub const PT_LOSUNW: usize = 0x6ffffffa; +pub const PT_SUNWBSS: usize = 0x6ffffffa; +pub const PT_SUNWSTACK: usize = 0x6ffffffb; +pub const PT_HISUNW: usize = 0x6fffffff; +pub const PT_HIOS: usize = 0x6fffffff; +pub const PT_LOPROC: usize = 0x70000000; +pub const PT_HIPROC: usize = 0x7fffffff; +pub const PN_XNUM: usize = 0xffff; +pub const PF_X: usize = 1 << 0; +pub const PF_W: usize = 1 << 1; +pub const PF_R: usize = 1 << 2; +pub const PF_MASKOS: usize = 0x0ff00000; +pub const PF_MASKPROC: usize = 0xf0000000; + +pub const NT_PRSTATUS: usize = 1; +pub const NT_FPREGSET: usize = 2; +pub const NT_PRPSINFO: usize = 3; +pub const NT_PRXREG: usize = 4; +pub const NT_TASKSTRUCT: usize = 4; +pub const NT_PLATFORM: usize = 5; +pub const NT_AUXV: usize = 6; +pub const NT_GWINDOWS: usize = 7; +pub const NT_ASRS: usize = 8; +pub const NT_PSTATUS: usize = 10; +pub const NT_PSINFO: usize = 13; +pub const NT_PRCRED: usize = 14; +pub const NT_UTSNAME: usize = 15; +pub const NT_LWPSTATUS: usize = 16; +pub const NT_LWPSINFO: usize = 17; +pub const NT_PRFPXREG: usize = 20; +pub const NT_SIGINFO: usize = 0x53494749; +pub const NT_FILE: usize = 0x46494c45; +pub const NT_PRXFPREG: usize = 0x46e62b7f; +pub const NT_PPC_VMX: usize = 0x100; +pub const NT_PPC_SPE: usize = 0x101; +pub const NT_PPC_VSX: usize = 0x102; +pub const NT_386_TLS: usize = 0x200; +pub const NT_386_IOPERM: usize = 0x201; +pub const NT_X86_XSTATE: usize = 0x202; +pub const NT_S390_HIGH_GPRS: usize = 0x300; +pub const NT_S390_TIMER: usize = 0x301; +pub const NT_S390_TODCMP: usize = 0x302; +pub const NT_S390_TODPREG: usize = 0x303; +pub const NT_S390_CTRS: usize = 0x304; +pub const NT_S390_PREFIX: usize = 0x305; +pub const NT_S390_LAST_BREAK: usize = 0x306; +pub const NT_S390_SYSTEM_CALL: usize = 0x307; +pub const NT_S390_TDB: usize = 0x308; +pub const NT_ARM_VFP: usize = 0x400; +pub const NT_ARM_TLS: usize = 0x401; +pub const NT_ARM_HW_BREAK: usize = 0x402; +pub const NT_ARM_HW_WATCH: usize = 0x403; +pub const NT_METAG_CBUF: usize = 0x500; +pub const NT_METAG_RPIPE: usize = 0x501; +pub const NT_METAG_TLS: usize = 0x502; +pub const NT_VERSION: usize = 1; + +#[repr(C)] +pub union Elf32_Dyn_Union { + d_val: Elf32_Word, + d_ptr: Elf32_Addr, +} + +/// See . +#[repr(C)] +pub struct Elf32_Dyn { + pub d_tag: Elf32_Sword, + pub d_un: Elf32_Dyn_Union, +} + +#[repr(C)] +pub union Elf64_Dyn_Union { + d_val: Elf64_Xword, + d_ptr: Elf64_Addr, +} + +/// See . +#[repr(C)] +pub struct Elf64_Dyn { + pub d_tag: Elf64_Sxword, + pub d_un: Elf64_Dyn_Union, +} + +pub const DT_NULL: usize = 0; +pub const DT_NEEDED: usize = 1; +pub const DT_PLTRELSZ: usize = 2; +pub const DT_PLTGOT: usize = 3; +pub const DT_HASH: usize = 4; +pub const DT_STRTAB: usize = 5; +pub const DT_SYMTAB: usize = 6; +pub const DT_RELA: usize = 7; +pub const DT_RELASZ: usize = 8; +pub const DT_RELAENT: usize = 9; +pub const DT_STRSZ: usize = 10; +pub const DT_SYMENT: usize = 11; +pub const DT_INIT: usize = 12; +pub const DT_FINI: usize = 13; +pub const DT_SONAME: usize = 14; +pub const DT_RPATH: usize = 15; +pub const DT_SYMBOLIC: usize = 16; +pub const DT_REL: usize = 17; +pub const DT_RELSZ: usize = 18; +pub const DT_RELENT: usize = 19; +pub const DT_PLTREL: usize = 20; +pub const DT_DEBUG: usize = 21; +pub const DT_TEXTREL: usize = 22; +pub const DT_JMPREL: usize = 23; +pub const DT_BIND_NOW: usize = 24; +pub const DT_INIT_ARRAY: usize = 25; +pub const DT_FINI_ARRAY: usize = 26; +pub const DT_INIT_ARRAYSZ: usize = 27; +pub const DT_FINI_ARRAYSZ: usize = 28; +pub const DT_RUNPATH: usize = 29; +pub const DT_FLAGS: usize = 30; +pub const DT_ENCODING: usize = 32; +pub const DT_PREINIT_ARRAY: usize = 32; +pub const DT_PREINIT_ARRAYSZ: usize = 33; +pub const DT_NUM: usize = 34; +pub const DT_LOOS: usize = 0x6000000d; +pub const DT_HIOS: usize = 0x6ffff000; +pub const DT_LOPROC: usize = 0x70000000; +pub const DT_HIPROC: usize = 0x7fffffff; +pub const DT_VALRNGLO: usize = 0x6ffffd00; +pub const DT_GNU_PRELINKED: usize = 0x6ffffdf5; +pub const DT_GNU_CONFLICTSZ: usize = 0x6ffffdf6; +pub const DT_GNU_LIBLISTSZ: usize = 0x6ffffdf7; +pub const DT_CHECKSUM: usize = 0x6ffffdf8; +pub const DT_PLTPADSZ: usize = 0x6ffffdf9; +pub const DT_MOVEENT: usize = 0x6ffffdfa; +pub const DT_MOVESZ: usize = 0x6ffffdfb; +pub const DT_FEATURE_1: usize = 0x6ffffdfc; +pub const DT_POSFLAG_1: usize = 0x6ffffdfd; +pub const DT_SYMINSZ: usize = 0x6ffffdfe; +pub const DT_SYMINENT: usize = 0x6ffffdff; +pub const DT_VALNUM: usize = 12; + +pub const DT_ADDRRNGLO: usize = 0x6ffffe00; +pub const DT_GNU_HASH: usize = 0x6ffffef5; +pub const DT_TLSDESC_PLT: usize = 0x6ffffef6; +pub const DT_TLSDESC_GOT: usize = 0x6ffffef7; +pub const DT_GNU_CONFLICT: usize = 0x6ffffef8; +pub const DT_GNU_LIBLIST: usize = 0x6ffffef9; +pub const DT_CONFIG: usize = 0x6ffffefa; +pub const DT_DEPAUDIT: usize = 0x6ffffefb; +pub const DT_AUDIT: usize = 0x6ffffefc; +pub const DT_PLTPAD: usize = 0x6ffffefd; +pub const DT_MOVETAB: usize = 0x6ffffefe; +pub const DT_SYMINFO: usize = 0x6ffffeff; +pub const DT_ADDRNUM: usize = 11; +pub const DT_VERSYM: usize = 0x6ffffff0; +pub const DT_RELACOUNT: usize = 0x6ffffff9; +pub const DT_RELCOUNT: usize = 0x6ffffffa; +pub const DT_FLAGS_1: usize = 0x6ffffffb; +pub const DT_VERDEF: usize = 0x6ffffffc; +pub const DT_VERDEFNUM: usize = 0x6ffffffd; +pub const DT_VERNEED: usize = 0x6ffffffe; +pub const DT_VERSIONTAGNUM: usize = 16; +pub const DT_AUXILIARY: usize = 0x7ffffffd; +pub const DT_FILTER: usize = 0x7fffffff; +pub const DT_EXTRANUM: usize = 3; +pub const DF_ORIGIN: usize = 0x00000001; +pub const DF_SYMBOLIC: usize = 0x00000002; +pub const DF_TEXTREL: usize = 0x00000004; +pub const DF_BIND_NOW: usize = 0x00000008; +pub const DF_STATIC_TLS: usize = 0x00000010; +pub const DF_1_NOW: usize = 0x00000001; +pub const DF_1_GLOBAL: usize = 0x00000002; +pub const DF_1_GROUP: usize = 0x00000004; +pub const DF_1_NODELETE: usize = 0x00000008; +pub const DF_1_LOADFLTR: usize = 0x00000010; +pub const DF_1_INITFIRST: usize = 0x00000020; +pub const DF_1_NOOPEN: usize = 0x00000040; +pub const DF_1_ORIGIN: usize = 0x00000080; +pub const DF_1_DIRECT: usize = 0x00000100; +pub const DF_1_TRANS: usize = 0x00000200; +pub const DF_1_INTERPOSE: usize = 0x00000400; +pub const DF_1_NODEFLIB: usize = 0x00000800; +pub const DF_1_NODUMP: usize = 0x00001000; +pub const DF_1_CONFALT: usize = 0x00002000; +pub const DF_1_ENDFILTEE: usize = 0x00004000; +pub const DF_1_DISPRELDNE: usize = 0x00008000; +pub const DF_1_DISPRELPND: usize = 0x00010000; +pub const DF_1_NODIRECT: usize = 0x00020000; +pub const DF_1_IGNMULDEF: usize = 0x00040000; +pub const DF_1_NOKSYMS: usize = 0x00080000; +pub const DF_1_NOHDR: usize = 0x00100000; +pub const DF_1_EDITED: usize = 0x00200000; +pub const DF_1_NORELOC: usize = 0x00400000; +pub const DF_1_SYMINTPOSE: usize = 0x00800000; +pub const DF_1_GLOBAUDIT: usize = 0x01000000; +pub const DF_1_SINGLETON: usize = 0x02000000; +pub const DTF_1_PARINIT: usize = 0x00000001; +pub const DTF_1_CONFEXP: usize = 0x00000002; +pub const DF_P1_LAZYLOAD: usize = 0x00000001; +pub const DF_P1_GROUPPERM: usize = 0x00000002; + +#[repr(C)] +pub struct Elf32_Verdef { + pub vd_version: Elf32_Half, + pub vd_flags: Elf32_Half, + pub vd_ndx: Elf32_Half, + pub vd_cnt: Elf32_Half, + pub vd_hash: Elf32_Word, + pub vd_aux: Elf32_Word, + pub vd_next: Elf32_Word, +} + +#[repr(C)] +pub struct Elf64_Verdef { + pub vd_version: Elf64_Half, + pub vd_flags: Elf64_Half, + pub vd_ndx: Elf64_Half, + pub vd_cnt: Elf64_Half, + pub vd_hash: Elf64_Word, + pub vd_aux: Elf64_Word, + pub vd_next: Elf64_Word, +} + +pub const VER_DEF_NONE: usize = 0; +pub const VER_DEF_CURRENT: usize = 1; +pub const VER_DEF_NUM: usize = 2; + +pub const VER_FLG_BASE: usize = 0x1; +pub const VER_FLG_WEAK: usize = 0x2; +pub const VER_NDX_LOCAL: usize = 0; +pub const VER_NDX_GLOBAL: usize = 1; +pub const VER_NDX_LORESERVE: usize = 0xff00; +pub const VER_NDX_ELIMINATE: usize = 0xff01; + +#[repr(C)] +pub struct Elf32_Verdaux { + pub vda_name: Elf32_Word, + pub vda_next: Elf32_Word, +} + +#[repr(C)] +pub struct Elf64_Verdaux { + pub vda_name: Elf64_Word, + pub vda_next: Elf64_Word, +} + +#[repr(C)] +pub struct Elf32_Verneed { + pub vn_version: Elf32_Half, + pub vn_cnt: Elf32_Half, + pub vn_file: Elf32_Word, + pub vn_aux: Elf32_Word, + pub vn_next: Elf32_Word, +} + +#[repr(C)] +pub struct Elf64_Verneed { + pub vn_version: Elf64_Half, + pub vn_cnt: Elf64_Half, + pub vn_file: Elf64_Word, + pub vn_aux: Elf64_Word, + pub vn_next: Elf64_Word, +} + +pub const VER_NEED_NONE: usize = 0; +pub const VER_NEED_CURRENT: usize = 1; +pub const VER_NEED_NUM: usize = 2; + +#[repr(C)] +pub struct Elf64_Vernaux { + pub vna_hash: Elf64_Word, + pub vna_flags: Elf64_Half, + pub vna_other: Elf64_Half, + pub vna_name: Elf64_Word, + pub vna_next: Elf64_Word, +} + +#[repr(C)] +pub union A_UN { + a_val: uint64_t, +} + +#[repr(C)] +pub struct Elf64_auxv_t { + pub a_type: uint64_t, + pub a_un: A_UN, +} + +pub const AT_NULL: usize = 0; +pub const AT_IGNORE: usize = 1; +pub const AT_EXECFD: usize = 2; +pub const AT_PHDR: usize = 3; +pub const AT_PHENT: usize = 4; +pub const AT_PHNUM: usize = 5; +pub const AT_PAGESZ: usize = 6; +pub const AT_BASE: usize = 7; +pub const AT_FLAGS: usize = 8; +pub const AT_ENTRY: usize = 9; +pub const AT_NOTELF: usize = 10; +pub const AT_UID: usize = 11; +pub const AT_EUID: usize = 12; +pub const AT_GID: usize = 13; +pub const AT_EGID: usize = 14; +pub const AT_CLKTCK: usize = 17; +pub const AT_PLATFORM: usize = 15; +pub const AT_HWCAP: usize = 16; +pub const AT_FPUCW: usize = 18; +pub const AT_DCACHEBSIZE: usize = 19; +pub const AT_ICACHEBSIZE: usize = 20; +pub const AT_UCACHEBSIZE: usize = 21; +pub const AT_IGNOREPPC: usize = 22; +pub const AT_SECURE: usize = 23; +pub const AT_BASE_PLATFORM: usize = 24; +pub const AT_RANDOM: usize = 25; +pub const AT_HWCAP2: usize = 26; +pub const AT_EXECFN: usize = 31; +pub const AT_SYSINFO: usize = 32; +pub const AT_SYSINFO_EHDR: usize = 33; +pub const AT_L1I_CACHESHAPE: usize = 34; +pub const AT_L1D_CACHESHAPE: usize = 35; +pub const AT_L2_CACHESHAPE: usize = 36; +pub const AT_L3_CACHESHAPE: usize = 37; + +/// See . +#[repr(C)] +pub struct Elf32_Nhdr { + pub n_namesz: Elf32_Word, + pub n_descsz: Elf32_Word, + pub n_type: Elf32_Word, +} + +/// See . +#[repr(C)] +pub struct Elf64_Nhdr { + pub n_namesz: Elf64_Word, + pub n_descsz: Elf64_Word, + pub n_type: Elf64_Word, +} + +pub const ELF_NOTE_SOLARIS: &str = "SUNW Solaris"; +pub const ELF_NOTE_GNU: &str = "GNU"; + +pub const ELF_NOTE_PAGESIZE_HINT: usize = 1; + +pub const NT_GNU_ABI_TAG: usize = 1; +pub const ELF_NOTE_ABI: usize = NT_GNU_ABI_TAG; + +pub const ELF_NOTE_OS_LINUX: usize = 0; +pub const ELF_NOTE_OS_GNU: usize = 1; +pub const ELF_NOTE_OS_SOLARIS2: usize = 2; +pub const ELF_NOTE_OS_FREEBSD: usize = 3; + +pub const NT_GNU_BUILD_ID: usize = 3; +pub const NT_GNU_GOLD_VERSION: usize = 4; + +#[repr(C)] +pub struct Elf64_Move { + pub m_value: Elf64_Xword, + pub m_info: Elf64_Xword, + pub m_poffset: Elf64_Xword, + pub m_repeat: Elf64_Half, + pub m_stride: Elf64_Half, +} + +pub const R_AARCH64_NONE: usize = 0; +pub const R_AARCH64_ABS64: usize = 257; +pub const R_AARCH64_ABS32: usize = 258; +pub const R_AARCH64_ABS16: usize = 259; +pub const R_AARCH64_PREL64: usize = 260; +pub const R_AARCH64_PREL32: usize = 261; +pub const R_AARCH64_PREL16: usize = 262; +pub const R_AARCH64_MOVW_UABS_G0: usize = 263; +pub const R_AARCH64_MOVW_UABS_G0_NC: usize = 264; +pub const R_AARCH64_MOVW_UABS_G1: usize = 265; +pub const R_AARCH64_MOVW_UABS_G1_NC: usize = 266; +pub const R_AARCH64_MOVW_UABS_G2: usize = 267; +pub const R_AARCH64_MOVW_UABS_G2_NC: usize = 268; +pub const R_AARCH64_MOVW_UABS_G3: usize = 269; +pub const R_AARCH64_MOVW_SABS_G0: usize = 270; +pub const R_AARCH64_MOVW_SABS_G1: usize = 271; +pub const R_AARCH64_MOVW_SABS_G2: usize = 272; +pub const R_AARCH64_LD_PREL_LO19: usize = 273; +pub const R_AARCH64_ADR_PREL_LO21: usize = 274; +pub const R_AARCH64_ADR_PREL_PG_HI21: usize = 275; +pub const R_AARCH64_ADR_PREL_PG_HI21_NC: usize = 276; +pub const R_AARCH64_ADD_ABS_LO12_NC: usize = 277; +pub const R_AARCH64_LDST8_ABS_LO12_NC: usize = 278; +pub const R_AARCH64_TSTBR14: usize = 279; +pub const R_AARCH64_CONDBR19: usize = 280; +pub const R_AARCH64_JUMP26: usize = 282; +pub const R_AARCH64_CALL26: usize = 283; +pub const R_AARCH64_LDST16_ABS_LO12_NC: usize = 284; +pub const R_AARCH64_LDST32_ABS_LO12_NC: usize = 285; +pub const R_AARCH64_LDST64_ABS_LO12_NC: usize = 286; +pub const R_AARCH64_MOVW_PREL_G0: usize = 287; +pub const R_AARCH64_MOVW_PREL_G0_NC: usize = 288; +pub const R_AARCH64_MOVW_PREL_G1: usize = 289; +pub const R_AARCH64_MOVW_PREL_G1_NC: usize = 290; +pub const R_AARCH64_MOVW_PREL_G2: usize = 291; +pub const R_AARCH64_MOVW_PREL_G2_NC: usize = 292; +pub const R_AARCH64_MOVW_PREL_G3: usize = 293; +pub const R_AARCH64_LDST128_ABS_LO12_NC: usize = 299; +pub const R_AARCH64_MOVW_GOTOFF_G0: usize = 300; +pub const R_AARCH64_MOVW_GOTOFF_G0_NC: usize = 301; +pub const R_AARCH64_MOVW_GOTOFF_G1: usize = 302; +pub const R_AARCH64_MOVW_GOTOFF_G1_NC: usize = 303; +pub const R_AARCH64_MOVW_GOTOFF_G2: usize = 304; +pub const R_AARCH64_MOVW_GOTOFF_G2_NC: usize = 305; +pub const R_AARCH64_MOVW_GOTOFF_G3: usize = 306; +pub const R_AARCH64_GOTREL64: usize = 307; +pub const R_AARCH64_GOTREL32: usize = 308; +pub const R_AARCH64_GOT_LD_PREL19: usize = 309; +pub const R_AARCH64_LD64_GOTOFF_LO15: usize = 310; +pub const R_AARCH64_ADR_GOT_PAGE: usize = 311; +pub const R_AARCH64_LD64_GOT_LO12_NC: usize = 312; +pub const R_AARCH64_LD64_GOTPAGE_LO15: usize = 313; +pub const R_AARCH64_TLSGD_ADR_PREL21: usize = 512; +pub const R_AARCH64_TLSGD_ADR_PAGE21: usize = 513; +pub const R_AARCH64_TLSGD_ADD_LO12_NC: usize = 514; +pub const R_AARCH64_TLSGD_MOVW_G1: usize = 515; +pub const R_AARCH64_TLSGD_MOVW_G0_NC: usize = 516; +pub const R_AARCH64_TLSLD_ADR_PREL21: usize = 517; +pub const R_AARCH64_TLSLD_ADR_PAGE21: usize = 518; +pub const R_AARCH64_TLSLD_ADD_LO12_NC: usize = 519; +pub const R_AARCH64_TLSLD_MOVW_G1: usize = 520; +pub const R_AARCH64_TLSLD_MOVW_G0_NC: usize = 521; +pub const R_AARCH64_TLSLD_LD_PREL19: usize = 522; +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G2: usize = 523; +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G1: usize = 524; +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC: usize = 525; +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G0: usize = 526; +pub const R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: usize = 527; +pub const R_AARCH64_TLSLD_ADD_DTPREL_HI12: usize = 528; +pub const R_AARCH64_TLSLD_ADD_DTPREL_LO12: usize = 529; +pub const R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: usize = 530; +pub const R_AARCH64_TLSLD_LDST8_DTPREL_LO12: usize = 531; +pub const R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC: usize = 532; +pub const R_AARCH64_TLSLD_LDST16_DTPREL_LO12: usize = 533; +pub const R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC: usize = 534; +pub const R_AARCH64_TLSLD_LDST32_DTPREL_LO12: usize = 535; +pub const R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC: usize = 536; +pub const R_AARCH64_TLSLD_LDST64_DTPREL_LO12: usize = 537; +pub const R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC: usize = 538; +pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G1: usize = 539; +pub const R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: usize = 540; +pub const R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: usize = 541; +pub const R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: usize = 542; +pub const R_AARCH64_TLSIE_LD_GOTTPREL_PREL19: usize = 543; +pub const R_AARCH64_TLSLE_MOVW_TPREL_G2: usize = 544; +pub const R_AARCH64_TLSLE_MOVW_TPREL_G1: usize = 545; +pub const R_AARCH64_TLSLE_MOVW_TPREL_G1_NC: usize = 546; +pub const R_AARCH64_TLSLE_MOVW_TPREL_G0: usize = 547; +pub const R_AARCH64_TLSLE_MOVW_TPREL_G0_NC: usize = 548; +pub const R_AARCH64_TLSLE_ADD_TPREL_HI12: usize = 549; +pub const R_AARCH64_TLSLE_ADD_TPREL_LO12: usize = 550; +pub const R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: usize = 551; +pub const R_AARCH64_TLSLE_LDST8_TPREL_LO12: usize = 552; +pub const R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC: usize = 553; +pub const R_AARCH64_TLSLE_LDST16_TPREL_LO12: usize = 554; +pub const R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC: usize = 555; +pub const R_AARCH64_TLSLE_LDST32_TPREL_LO12: usize = 556; +pub const R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: usize = 557; +pub const R_AARCH64_TLSLE_LDST64_TPREL_LO12: usize = 558; +pub const R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: usize = 559; +pub const R_AARCH64_TLSDESC_LD_PREL19: usize = 560; +pub const R_AARCH64_TLSDESC_ADR_PREL21: usize = 561; +pub const R_AARCH64_TLSDESC_ADR_PAGE21: usize = 562; +pub const R_AARCH64_TLSDESC_LD64_LO12: usize = 563; +pub const R_AARCH64_TLSDESC_ADD_LO12: usize = 564; +pub const R_AARCH64_TLSDESC_OFF_G1: usize = 565; +pub const R_AARCH64_TLSDESC_OFF_G0_NC: usize = 566; +pub const R_AARCH64_TLSDESC_LDR: usize = 567; +pub const R_AARCH64_TLSDESC_ADD: usize = 568; +pub const R_AARCH64_TLSDESC_CALL: usize = 569; +pub const R_AARCH64_TLSLE_LDST128_TPREL_LO12: usize = 570; +pub const R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: usize = 571; +pub const R_AARCH64_TLSLD_LDST128_DTPREL_LO12: usize = 572; +pub const R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC: usize = 573; +pub const R_AARCH64_COPY: usize = 1024; +pub const R_AARCH64_GLOB_DAT: usize = 1025; +pub const R_AARCH64_JUMP_SLOT: usize = 1026; +pub const R_AARCH64_RELATIVE: usize = 1027; +pub const R_AARCH64_TLS_DTPMOD64: usize = 1028; +pub const R_AARCH64_TLS_DTPREL64: usize = 1029; +pub const R_AARCH64_TLS_TPREL64: usize = 1030; +pub const R_AARCH64_TLSDESC: usize = 1031; + +pub const R_X86_64_NONE: usize = 0; +pub const R_X86_64_64: usize = 1; +pub const R_X86_64_PC32: usize = 2; +pub const R_X86_64_GOT32: usize = 3; +pub const R_X86_64_PLT32: usize = 4; +pub const R_X86_64_COPY: usize = 5; +pub const R_X86_64_GLOB_DAT: usize = 6; +pub const R_X86_64_JUMP_SLOT: usize = 7; +pub const R_X86_64_RELATIVE: usize = 8; +pub const R_X86_64_GOTPCREL: usize = 9; +pub const R_X86_64_32: usize = 10; +pub const R_X86_64_32S: usize = 11; +pub const R_X86_64_16: usize = 12; +pub const R_X86_64_PC16: usize = 13; +pub const R_X86_64_8: usize = 14; +pub const R_X86_64_PC8: usize = 15; +pub const R_X86_64_DTPMOD64: usize = 16; +pub const R_X86_64_DTPOFF64: usize = 17; +pub const R_X86_64_TPOFF64: usize = 18; +pub const R_X86_64_TLSGD: usize = 19; +pub const R_X86_64_TLSLD: usize = 20; +pub const R_X86_64_DTPOFF32: usize = 21; +pub const R_X86_64_GOTTPOFF: usize = 22; +pub const R_X86_64_TPOFF32: usize = 23; +pub const R_X86_64_PC64: usize = 24; +pub const R_X86_64_GOTOFF64: usize = 25; +pub const R_X86_64_GOTPC32: usize = 26; +pub const R_X86_64_GOT64: usize = 27; +pub const R_X86_64_GOTPCREL64: usize = 28; +pub const R_X86_64_GOTPC64: usize = 29; +pub const R_X86_64_GOTPLT64: usize = 30; +pub const R_X86_64_PLTOFF64: usize = 31; +pub const R_X86_64_SIZE32: usize = 32; +pub const R_X86_64_SIZE64: usize = 33; +pub const R_X86_64_GOTPC32_TLSDESC: usize = 34; +pub const R_X86_64_TLSDESC_CALL: usize = 35; +pub const R_X86_64_TLSDESC: usize = 36; +pub const R_X86_64_IRELATIVE: usize = 37; +pub const R_X86_64_RELATIVE64: usize = 38; +pub const R_X86_64_NUM: usize = 39; + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_elf( + a: Elf32_Ehdr, + b: Elf64_Ehdr, + c: Elf32_Shdr, + d: Elf64_Shdr, + e: Elf32_Sym, + f: Elf64_Sym, + g: Elf32_Syminfo, + h: Elf64_Syminfo, + i: Elf32_Rel, + j: Elf64_Rel, + k: Elf32_Rela, + l: Elf64_Rela, + m: Elf32_Phdr, + n: Elf64_Phdr, + o: Elf32_Dyn, + p: Elf64_Dyn, + q: Elf32_Verdef, + r: Elf64_Verdef, + s: Elf32_Verdaux, + t: Elf64_Verdaux, + u: Elf32_Verneed, + v: Elf64_Verneed, + w: Elf64_Vernaux, + x: Elf64_auxv_t, + y: Elf32_Nhdr, + z: Elf64_Nhdr, + aa: Elf64_Move, +) { +} diff --git a/src/header/endian/cbindgen.toml b/src/header/endian/cbindgen.toml new file mode 100644 index 0000000000..698eb0088c --- /dev/null +++ b/src/header/endian/cbindgen.toml @@ -0,0 +1,12 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the uint16_t, uint32_t, and uint64_t types as described in ." +# - "Inclusion of the header may also make visible all symbols from ." +sys_includes = ["stdint.h"] +include_guard = "_RELIBC_ENDIAN_H" +trailer = "#include " +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true diff --git a/src/header/endian/mod.rs b/src/header/endian/mod.rs new file mode 100644 index 0000000000..8c78b3bdfa --- /dev/null +++ b/src/header/endian/mod.rs @@ -0,0 +1,77 @@ +//! `endian.h` implementation. +//! +//! See . + +use crate::platform::types::{uint16_t, uint32_t, uint64_t}; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn be16toh(x: uint16_t) -> uint16_t { + uint16_t::from_be(x) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn be32toh(x: uint32_t) -> uint32_t { + uint32_t::from_be(x) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn be64toh(x: uint64_t) -> uint64_t { + uint64_t::from_be(x) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htobe16(x: uint16_t) -> uint16_t { + x.to_be() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htobe32(x: uint32_t) -> uint32_t { + x.to_be() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htobe64(x: uint64_t) -> uint64_t { + x.to_be() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htole16(x: uint16_t) -> uint16_t { + x.to_le() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htole32(x: uint32_t) -> uint32_t { + x.to_le() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn htole64(x: uint64_t) -> uint64_t { + x.to_le() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn le16toh(x: uint16_t) -> uint16_t { + uint16_t::from_le(x) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn le32toh(x: uint32_t) -> uint32_t { + uint32_t::from_le(x) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn le64toh(x: uint64_t) -> uint64_t { + uint64_t::from_le(x) +} diff --git a/src/header/err/cbindgen.toml b/src/header/err/cbindgen.toml new file mode 100644 index 0000000000..7319d182ab --- /dev/null +++ b/src/header/err/cbindgen.toml @@ -0,0 +1,5 @@ +sys_includes = ["stdarg.h", "stdio.h", "features.h"] +include_guard = "_RELIBC_ERR_H" +language = "C" +no_includes = true +cpp_compat = true diff --git a/src/header/err/mod.rs b/src/header/err/mod.rs new file mode 100644 index 0000000000..5e47ee4e7c --- /dev/null +++ b/src/header/err/mod.rs @@ -0,0 +1,204 @@ +//! `err.h` implementation. +//! +//! See +//! +//! `err.h` is a BSD extension to the C library which provides functions for printing formatted +//! errors. Errors are printed to [`stdio::stderr`] by default or to a file set by +//! [`err_set_file`]. This family of functions is non-portable, but it is also supported by `glibc` +//! and `musl`. +//! +//! The functions come in sets of three. Each of them print the program binary name (the last path +//! segment of `argv[0]`) and an optional user message along with these differences: +//! * No suffix: Prints an error message for ERRNO based on [`strerror`] +//! * `c` suffix: Prints an error message for an arbitrary error code +//! * `x` suffix: Does not print an error code +//! +//! For example, `err` does not have a suffix so it would print the program name, the user message, +//! and an error string for ERRNO. `errc` would operate in the same way except the functions takes +//! an error code for which to print an error string. + +// Allow is intentional. Almost every line of the simple functions below are unsafe. +// unsafe_op_in_unsafe_fn only adds visual noise or a needless indentation here. +#![allow(unsafe_op_in_unsafe_fn)] + +use core::{ + ffi::{VaList as va_list, c_char, c_int}, + ptr, +}; + +use crate::{ + header::{ + stdio::{self, FILE, fprintf, fputc, fputs, vfprintf}, + stdlib::exit, + string::strerror, + }, + platform::{self, ERRNO}, +}; + +// Optional callback from user invoked on exit. +type ExitCallback = Option; +static mut on_exit: ExitCallback = None; + +// Messages from this module are written to this sink. +static mut error_sink: *mut FILE = ptr::null_mut(); + +/// Set global [`FILE`] sink to write errors and warnings. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn err_set_file(fp: *mut FILE) { + if fp.is_null() { + error_sink = stdio::stderr; + } else { + error_sink = fp; + } +} + +/// Set or remove a callback to invoke before exiting on error. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn err_set_exit(ef: ExitCallback) { + on_exit = ef; +} + +/// Print a user message then an error message for [`ERRNO`] followed by exiting with `eval`. +/// +/// The message format is `progname: fmt: strerror(ERRNO)` +/// +/// # Return +/// Does not return. Exits with `eval` as an error code. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn err(eval: c_int, fmt: *const c_char, mut va_list: ...) -> ! { + let code = Some(ERRNO.get()); + err_exit(eval, code, fmt, va_list.as_va_list()) +} + +/// Print a user message then an error message for `code` before exiting with `eval` as a return. +/// +/// The message format is `progname: fmt: strerror(code)` +/// +/// # Return +/// Exits with `eval` as an error code. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn errc(eval: c_int, code: c_int, fmt: *const c_char, mut va_list: ...) -> ! { + err_exit(eval, Some(code), fmt, va_list.as_va_list()) +} + +/// Print a user message then exits with `eval` as a return. +/// +/// The message format is `progname: fmt` +/// +/// # Return +/// Exits with `eval` as an error code. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn errx(eval: c_int, fmt: *const c_char, mut va_list: ...) -> ! { + err_exit(eval, None, fmt, va_list.as_va_list()) +} + +/// Print a user message and then an error message for [`ERRNO`]. +/// +/// The message format is `progname: fmt: strerror(ERRNO)` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn warn(fmt: *const c_char, mut va_list: ...) { + let code = Some(ERRNO.get()); + display_message(code, fmt, va_list.as_va_list()); +} + +/// Print a user message then an error message for `code`. +/// +/// The message format is `progname: fmt: strerror(code)` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn warnc(code: c_int, fmt: *const c_char, mut va_list: ...) { + display_message(Some(code), fmt, va_list.as_va_list()); +} + +/// Print a user message as a warning. +/// +/// The message format is `progname: fmt` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn warnx(fmt: *const c_char, mut va_list: ...) { + display_message(None, fmt, va_list.as_va_list()); +} + +/// See [`err`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn verr(eval: c_int, fmt: *const c_char, args: va_list) -> ! { + let code = Some(ERRNO.get()); + err_exit(eval, code, fmt, args); +} + +/// See [`errc`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn verrc(eval: c_int, code: c_int, fmt: *const c_char, args: va_list) -> ! { + err_exit(eval, Some(code), fmt, args) +} + +/// See [`errx`]; +#[unsafe(no_mangle)] +pub unsafe extern "C" fn verrx(eval: c_int, fmt: *const c_char, args: va_list) -> ! { + err_exit(eval, None, fmt, args) +} + +/// See [`warn`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vwarn(fmt: *const c_char, args: va_list) { + let code = Some(ERRNO.get()); + display_message(code, fmt, args); +} + +/// See [`warnc`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vwarnc(code: c_int, fmt: *const c_char, args: va_list) { + display_message(Some(code), fmt, args); +} + +/// See [`warnx`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vwarnx(fmt: *const c_char, args: va_list) { + display_message(None, fmt, args); +} + +// Write error messages for err and warn to the currently set sink. +unsafe fn display_message(code: Option, fmt: *const c_char, args: va_list) { + // SAFETY: + // * error_sink is only null once on start but otherwise always stderr or a user set file + // * User is trusted to pass in a valid file pointer if err_set_file is used + if error_sink.is_null() { + error_sink = stdio::stderr; + } + let sink = error_sink; + + // "progname:" is always printed + // SAFETY: + // * program_invocation_short_name is never null as it is set on start + // * program_invocation_short_name is not globally mutable so the user can't mangle it + fprintf( + sink, + c"%s".as_ptr(), + platform::program_invocation_short_name, + ); + + // Print user message if any + if !fmt.is_null() { + fputs(c": ".as_ptr(), sink); + vfprintf(sink, fmt, args); + } + + // Print error message for non-x functions + if let Some(code) = code { + let message = strerror(code); + fprintf(sink, c": %s".as_ptr(), message); + } + + // Always write new line + fputc(b'\n'.into(), sink); +} + +// Write an error message as per err and then exit. +unsafe fn err_exit(eval: c_int, code: Option, fmt: *const c_char, args: va_list) -> ! { + display_message(code, fmt, args); + + if let Some(callback) = on_exit { + // errx will hit the unwrap. + callback(code.unwrap_or_else(|| ERRNO.get())); + } + + exit(eval); +} diff --git a/src/header/errno/cbindgen.toml b/src/header/errno/cbindgen.toml new file mode 100644 index 0000000000..93f418381d --- /dev/null +++ b/src/header/errno/cbindgen.toml @@ -0,0 +1,17 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +after_includes = """ +#define errno (*__errno_location()) +#define program_invocation_name (*__program_invocation_name()) +#define program_invocation_short_name (*__program_invocation_short_name()) +""" +include_guard = "_RELIBC_ERRNO_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/errno/mod.rs b/src/header/errno/mod.rs new file mode 100644 index 0000000000..aceb031138 --- /dev/null +++ b/src/header/errno/mod.rs @@ -0,0 +1,344 @@ +//! `errno.h` implementation. +//! +//! See . + +use crate::platform::{ + self, + types::{c_char, c_int}, +}; + +//TODO: Consider removing, provided for compatibility with newlib +#[unsafe(no_mangle)] +pub extern "C" fn __errno() -> *mut c_int { + __errno_location() +} + +/// Provide a pointer to relibc's internal `errno` variable. +/// +/// This is the basis of the C-facing macro definition of `errno`, and should +/// not be used directly. +#[unsafe(no_mangle)] +pub extern "C" fn __errno_location() -> *mut c_int { + platform::ERRNO.as_ptr() +} + +/// Get the name used to invoke the program, similar to `argv[0]`. +/// +/// This provides the basis for the C-facing macro definition of +/// `program_invocation_name`, and should not be used directly. +/// +/// The `program_invocation_name` variable is a GNU extension. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __program_invocation_name() -> *mut *mut c_char { + &raw mut platform::program_invocation_name +} + +/// Get the directory-less name used to invoke the program. +/// +/// This provides the basis for the C-facing macro definition of +/// `program_invocation_short_name`, and should not be used directly. +/// +/// The `program_invocation_short_name` variable is a GNU extension. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __program_invocation_short_name() -> *mut *mut c_char { + &raw mut platform::program_invocation_short_name +} + +pub const EPERM: c_int = 1; /* Operation not permitted */ +pub const ENOENT: c_int = 2; /* No such file or directory */ +pub const ESRCH: c_int = 3; /* No such process */ +pub const EINTR: c_int = 4; /* Interrupted system call */ +pub const EIO: c_int = 5; /* I/O error */ +pub const ENXIO: c_int = 6; /* No such device or address */ +pub const E2BIG: c_int = 7; /* Argument list too long */ +pub const ENOEXEC: c_int = 8; /* Exec format error */ +pub const EBADF: c_int = 9; /* Bad file number */ +pub const ECHILD: c_int = 10; /* No child processes */ +pub const EAGAIN: c_int = 11; /* Try again */ +pub const ENOMEM: c_int = 12; /* Out of memory */ +pub const EACCES: c_int = 13; /* Permission denied */ +pub const EFAULT: c_int = 14; /* Bad address */ +pub const ENOTBLK: c_int = 15; /* Block device required */ +pub const EBUSY: c_int = 16; /* Device or resource busy */ +pub const EEXIST: c_int = 17; /* File exists */ +pub const EXDEV: c_int = 18; /* Cross-device link */ +pub const ENODEV: c_int = 19; /* No such device */ +pub const ENOTDIR: c_int = 20; /* Not a directory */ +pub const EISDIR: c_int = 21; /* Is a directory */ +pub const EINVAL: c_int = 22; /* Invalid argument */ +pub const ENFILE: c_int = 23; /* File table overflow */ +pub const EMFILE: c_int = 24; /* Too many open files */ +pub const ENOTTY: c_int = 25; /* Not a typewriter */ +pub const ETXTBSY: c_int = 26; /* Text file busy */ +pub const EFBIG: c_int = 27; /* File too large */ +pub const ENOSPC: c_int = 28; /* No space left on device */ +pub const ESPIPE: c_int = 29; /* Illegal seek */ +pub const EROFS: c_int = 30; /* Read-only file system */ +pub const EMLINK: c_int = 31; /* Too many links */ +pub const EPIPE: c_int = 32; /* Broken pipe */ +pub const EDOM: c_int = 33; /* Math argument out of domain of func */ +pub const ERANGE: c_int = 34; /* Math result not representable */ +pub const EDEADLK: c_int = 35; /* Resource deadlock would occur */ +pub const ENAMETOOLONG: c_int = 36; /* File name too long */ +pub const ENOLCK: c_int = 37; /* No record locks available */ +pub const ENOSYS: c_int = 38; /* Function not implemented */ +pub const ENOTEMPTY: c_int = 39; /* Directory not empty */ +pub const ELOOP: c_int = 40; /* Too many symbolic links encountered */ +pub const EWOULDBLOCK: c_int = 41; /* Operation would block */ +pub const ENOMSG: c_int = 42; /* No message of desired type */ +pub const EIDRM: c_int = 43; /* Identifier removed */ +pub const ECHRNG: c_int = 44; /* Channel number out of range */ +pub const EL2NSYNC: c_int = 45; /* Level 2 not synchronized */ +pub const EL3HLT: c_int = 46; /* Level 3 halted */ +pub const EL3RST: c_int = 47; /* Level 3 reset */ +pub const ELNRNG: c_int = 48; /* Link number out of range */ +pub const EUNATCH: c_int = 49; /* Protocol driver not attached */ +pub const ENOCSI: c_int = 50; /* No CSI structure available */ +pub const EL2HLT: c_int = 51; /* Level 2 halted */ +pub const EBADE: c_int = 52; /* Invalid exchange */ +pub const EBADR: c_int = 53; /* Invalid request descriptor */ +pub const EXFULL: c_int = 54; /* Exchange full */ +pub const ENOANO: c_int = 55; /* No anode */ +pub const EBADRQC: c_int = 56; /* Invalid request code */ +pub const EBADSLT: c_int = 57; /* Invalid slot */ +pub const EDEADLOCK: c_int = 58; /* Resource deadlock would occur */ +pub const EBFONT: c_int = 59; /* Bad font file format */ +pub const ENOSTR: c_int = 60; /* Device not a stream */ +pub const ENODATA: c_int = 61; /* No data available */ +pub const ETIME: c_int = 62; /* Timer expired */ +pub const ENOSR: c_int = 63; /* Out of streams resources */ +pub const ENONET: c_int = 64; /* Machine is not on the network */ +pub const ENOPKG: c_int = 65; /* Package not installed */ +pub const EREMOTE: c_int = 66; /* Object is remote */ +pub const ENOLINK: c_int = 67; /* Link has been severed */ +pub const EADV: c_int = 68; /* Advertise error */ +pub const ESRMNT: c_int = 69; /* Srmount error */ +pub const ECOMM: c_int = 70; /* Communication error on send */ +pub const EPROTO: c_int = 71; /* Protocol error */ +pub const EMULTIHOP: c_int = 72; /* Multihop attempted */ +pub const EDOTDOT: c_int = 73; /* RFS specific error */ +pub const EBADMSG: c_int = 74; /* Not a data message */ +pub const EOVERFLOW: c_int = 75; /* Value too large for defined data type */ +pub const ENOTUNIQ: c_int = 76; /* Name not unique on network */ +pub const EBADFD: c_int = 77; /* File descriptor in bad state */ +pub const EREMCHG: c_int = 78; /* Remote address changed */ +pub const ELIBACC: c_int = 79; /* Can not access a needed shared library */ +pub const ELIBBAD: c_int = 80; /* Accessing a corrupted shared library */ +pub const ELIBSCN: c_int = 81; /* .lib section in a.out corrupted */ +pub const ELIBMAX: c_int = 82; /* Attempting to link in too many shared libraries */ +pub const ELIBEXEC: c_int = 83; /* Cannot exec a shared library directly */ +pub const EILSEQ: c_int = 84; /* Illegal byte sequence */ +pub const ERESTART: c_int = 85; /* Interrupted system call should be restarted */ +pub const ESTRPIPE: c_int = 86; /* Streams pipe error */ +pub const EUSERS: c_int = 87; /* Too many users */ +pub const ENOTSOCK: c_int = 88; /* Socket operation on non-socket */ +pub const EDESTADDRREQ: c_int = 89; /* Destination address required */ +pub const EMSGSIZE: c_int = 90; /* Message too long */ +pub const EPROTOTYPE: c_int = 91; /* Protocol wrong type for socket */ +pub const ENOPROTOOPT: c_int = 92; /* Protocol not available */ +pub const EPROTONOSUPPORT: c_int = 93; /* Protocol not supported */ +pub const ESOCKTNOSUPPORT: c_int = 94; /* Socket type not supported */ +pub const EOPNOTSUPP: c_int = 95; /* Operation not supported on transport endpoint */ +pub const ENOTSUP: c_int = EOPNOTSUPP; /* Not supported */ +pub const EPFNOSUPPORT: c_int = 96; /* Protocol family not supported */ +pub const EAFNOSUPPORT: c_int = 97; /* Address family not supported by protocol */ +pub const EADDRINUSE: c_int = 98; /* Address already in use */ +pub const EADDRNOTAVAIL: c_int = 99; /* Cannot assign requested address */ +pub const ENETDOWN: c_int = 100; /* Network is down */ +pub const ENETUNREACH: c_int = 101; /* Network is unreachable */ +pub const ENETRESET: c_int = 102; /* Network dropped connection because of reset */ +pub const ECONNABORTED: c_int = 103; /* Software caused connection abort */ +pub const ECONNRESET: c_int = 104; /* Connection reset by peer */ +pub const ENOBUFS: c_int = 105; /* No buffer space available */ +pub const EISCONN: c_int = 106; /* Transport endpoint is already connected */ +pub const ENOTCONN: c_int = 107; /* Transport endpoint is not connected */ +pub const ESHUTDOWN: c_int = 108; /* Cannot send after transport endpoint shutdown */ +pub const ETOOMANYREFS: c_int = 109; /* Too many references: cannot splice */ +pub const ETIMEDOUT: c_int = 110; /* Connection timed out */ +pub const ECONNREFUSED: c_int = 111; /* Connection refused */ +pub const EHOSTDOWN: c_int = 112; /* Host is down */ +pub const EHOSTUNREACH: c_int = 113; /* No route to host */ +pub const EALREADY: c_int = 114; /* Operation already in progress */ +pub const EINPROGRESS: c_int = 115; /* Operation now in progress */ +pub const ESTALE: c_int = 116; /* Stale NFS file handle */ +pub const EUCLEAN: c_int = 117; /* Structure needs cleaning */ +pub const ENOTNAM: c_int = 118; /* Not a XENIX named type file */ +pub const ENAVAIL: c_int = 119; /* No XENIX semaphores available */ +pub const EISNAM: c_int = 120; /* Is a named type file */ +pub const EREMOTEIO: c_int = 121; /* Remote I/O error */ +pub const EDQUOT: c_int = 122; /* Quota exceeded */ +pub const ENOMEDIUM: c_int = 123; /* No medium found */ +pub const EMEDIUMTYPE: c_int = 124; /* Wrong medium type */ +pub const ECANCELED: c_int = 125; /* Operation Canceled */ +pub const ENOKEY: c_int = 126; /* Required key not available */ +pub const EKEYEXPIRED: c_int = 127; /* Key has expired */ +pub const EKEYREVOKED: c_int = 128; /* Key has been revoked */ +pub const EKEYREJECTED: c_int = 129; /* Key was rejected by service */ +pub const EOWNERDEAD: c_int = 130; /* Owner died */ +pub const ENOTRECOVERABLE: c_int = 131; /* State not recoverable */ + +/// String representations for the respective `errno` values. +pub(crate) const STR_ERROR: [&str; 132] = [ + "Success", + "Operation not permitted", + "No such file or directory", + "No such process", + "Interrupted system call", + "I/O error", + "No such device or address", + "Argument list too long", + "Exec format error", + "Bad file number", + "No child processes", + "Try again", + "Out of memory", + "Permission denied", + "Bad address", + "Block device required", + "Device or resource busy", + "File exists", + "Cross-device link", + "No such device", + "Not a directory", + "Is a directory", + "Invalid argument", + "File table overflow", + "Too many open files", + "Not a typewriter", + "Text file busy", + "File too large", + "No space left on device", + "Illegal seek", + "Read-only file system", + "Too many links", + "Broken pipe", + "Math argument out of domain of func", + "Math result not representable", + "Resource deadlock would occur", + "File name too long", + "No record locks available", + "Function not implemented", + "Directory not empty", + "Too many symbolic links encountered", + "Operation would block", + "No message of desired type", + "Identifier removed", + "Channel number out of range", + "Level 2 not synchronized", + "Level 3 halted", + "Level 3 reset", + "Link number out of range", + "Protocol driver not attached", + "No CSI structure available", + "Level 2 halted", + "Invalid exchange", + "Invalid request descriptor", + "Exchange full", + "No anode", + "Invalid request code", + "Invalid slot", + "Resource deadlock would occur", + "Bad font file format", + "Device not a stream", + "No data available", + "Timer expired", + "Out of streams resources", + "Machine is not on the network", + "Package not installed", + "Object is remote", + "Link has been severed", + "Advertise error", + "Srmount error", + "Communication error on send", + "Protocol error", + "Multihop attempted", + "RFS specific error", + "Not a data message", + "Value too large for defined data type", + "Name not unique on network", + "File descriptor in bad state", + "Remote address changed", + "Can not access a needed shared library", + "Accessing a corrupted shared library", + ".lib section in a.out corrupted", + "Attempting to link in too many shared libraries", + "Cannot exec a shared library directly", + "Illegal byte sequence", + "Interrupted system call should be restarted", + "Streams pipe error", + "Too many users", + "Socket operation on non-socket", + "Destination address required", + "Message too long", + "Protocol wrong type for socket", + "Protocol not available", + "Protocol not supported", + "Socket type not supported", + "Operation not supported on transport endpoint", + "Protocol family not supported", + "Address family not supported by protocol", + "Address already in use", + "Cannot assign requested address", + "Network is down", + "Network is unreachable", + "Network dropped connection because of reset", + "Software caused connection abort", + "Connection reset by peer", + "No buffer space available", + "Transport endpoint is already connected", + "Transport endpoint is not connected", + "Cannot send after transport endpoint shutdown", + "Too many references: cannot splice", + "Connection timed out", + "Connection refused", + "Host is down", + "No route to host", + "Operation already in progress", + "Operation now in progress", + "Stale NFS file handle", + "Structure needs cleaning", + "Not a XENIX named type file", + "No XENIX semaphores available", + "Is a named type file", + "Remote I/O error", + "Quota exceeded", + "No medium found", + "Wrong medium type", + "Operation Canceled", + "Required key not available", + "Key has expired", + "Key has been revoked", + "Key was rejected by service", + "Owner died", + "State not recoverable", +]; + +/// Longest error message length +pub(crate) const STRERROR_MAX: usize = { + // Number of digits of the max value of this platform's c_int + let digits = { + let mut len = 0; + let mut i = c_int::MAX; + + while i > 0 { + i /= 10; + len += 1; + } + + len + }; + + let mut longest: usize = "Unknown error: ".len() + digits + 1; + + let mut i = 0; + while i < STR_ERROR.len() { + let len = STR_ERROR[i].len() + 1; + if len > longest { + longest = len; + } + + i += 1; + } + + longest +}; diff --git a/src/header/fcntl/cbindgen.toml b/src/header/fcntl/cbindgen.toml new file mode 100644 index 0000000000..1e54313f42 --- /dev/null +++ b/src/header/fcntl/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["stdarg.h", "sys/stat.h", "unistd.h"] +include_guard = "_RELIBC_FCNTL_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/fcntl/linux.rs b/src/header/fcntl/linux.rs new file mode 100644 index 0000000000..6be617fd26 --- /dev/null +++ b/src/header/fcntl/linux.rs @@ -0,0 +1,31 @@ +use crate::platform::types::c_int; + +pub const O_RDONLY: c_int = 0x0000; +pub const O_WRONLY: c_int = 0x0001; +pub const O_RDWR: c_int = 0x0002; +pub const O_ACCMODE: c_int = 0x0003; +pub const O_CREAT: c_int = 0x0040; +pub const O_EXCL: c_int = 0x0080; +pub const O_NOCTTY: c_int = 0x0100; +pub const O_TRUNC: c_int = 0x0200; +pub const O_APPEND: c_int = 0x0400; +pub const O_NONBLOCK: c_int = 0x0800; +pub const O_DIRECTORY: c_int = 0x1_0000; +pub const O_NOFOLLOW: c_int = 0x2_0000; +pub const O_CLOEXEC: c_int = 0x8_0000; +pub const O_PATH: c_int = 0x20_0000; + +pub const FD_CLOEXEC: c_int = 0x8_0000; + +// Defined for compatibility +pub const O_NDELAY: c_int = O_NONBLOCK; + +// Flags for capability based "at" functions +pub const AT_FDCWD: c_int = -100; +pub const AT_SYMLINK_NOFOLLOW: c_int = 0x100; +// AT_EACCESS only used for faccessat +pub const AT_EACCESS: c_int = 0x200; +// AT_REMOVEDIR only used for unlinkat +pub const AT_REMOVEDIR: c_int = 0x200; +pub const AT_SYMLINK_FOLLOW: c_int = 0x400; +pub const AT_EMPTY_PATH: c_int = 0x1000; diff --git a/src/header/fcntl/mod.rs b/src/header/fcntl/mod.rs new file mode 100644 index 0000000000..972575fff4 --- /dev/null +++ b/src/header/fcntl/mod.rs @@ -0,0 +1,142 @@ +//! `fcntl.h` implementation. +//! +//! See . + +use core::num::NonZeroU64; + +use crate::{ + c_str::CStr, + error::ResultExt, + header::unistd::close, + platform::{ + Pal, Sys, + types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t}, + }, +}; + +pub use self::sys::*; + +use super::errno::EINVAL; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +pub const F_DUPFD: c_int = 0; +pub const F_GETFD: c_int = 1; +pub const F_SETFD: c_int = 2; +pub const F_GETFL: c_int = 3; +pub const F_SETFL: c_int = 4; +pub const F_GETLK: c_int = 5; +pub const F_SETLK: c_int = 6; +pub const F_SETLKW: c_int = 7; +pub const F_OFD_GETLK: c_int = 36; +pub const F_OFD_SETLK: c_int = 37; +pub const F_OFD_SETLKW: c_int = 38; +pub const F_DUPFD_CLOEXEC: c_int = 1030; + +pub const F_RDLCK: c_int = 0; +pub const F_WRLCK: c_int = 1; +pub const F_UNLCK: c_int = 2; + +pub const F_ULOCK: c_int = 0; +pub const F_LOCK: c_int = 1; +pub const F_TLOCK: c_int = 2; +pub const F_TEST: c_int = 3; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn creat(path: *const c_char, mode: mode_t) -> c_int { + unsafe { open(path, O_WRONLY | O_CREAT | O_TRUNC, mode) } +} + +/// See . +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct flock { + pub l_type: c_short, + pub l_whence: c_short, + pub l_start: off_t, + pub l_len: off_t, + pub l_pid: pid_t, +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fcntl(fildes: c_int, cmd: c_int, mut __valist: ...) -> c_int { + // c_ulonglong + let arg = match cmd { + F_DUPFD | F_SETFD | F_SETFL | F_GETLK | F_SETLK | F_SETLKW | F_OFD_GETLK | F_OFD_SETLK + | F_OFD_SETLKW | F_DUPFD_CLOEXEC => unsafe { __valist.arg::() }, + _ => 0, + }; + + if cmd == F_DUPFD_CLOEXEC { + let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno(); + if new_fd >= 0 { + return new_fd; + } + + let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno(); + if new_fd < 0 { + return -1; + } + if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 { + let _ = close(new_fd); + return -1; + } + return new_fd; + } + + Sys::fcntl(fildes, cmd, arg).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn open(path: *const c_char, oflag: c_int, mut __valist: ...) -> c_int { + unsafe { openat(AT_FDCWD, path, oflag, __valist) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn openat( + fd: c_int, + path: *const c_char, + oflag: c_int, + mut __valist: ... +) -> c_int { + let mode = if oflag & O_CREAT == O_CREAT + /* || oflag & O_TMPFILE == O_TMPFILE */ + { + unsafe { __valist.arg::() } + } else { + 0 + }; + + let path = unsafe { CStr::from_ptr(path) }; + Sys::openat(fd, path, oflag, mode).or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_fcntl(_: flock) {} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_fallocate(fd: c_int, offset: off_t, length: off_t) -> c_int { + // Length can't be zero and offset must be positive. + let Ok(offset) = offset.try_into() else { + return EINVAL; + }; + let Some(length) = length.try_into().ok().and_then(NonZeroU64::new) else { + return EINVAL; + }; + + // posix_fallocate does not set errno but instead returns it. + Sys::posix_fallocate(fd, offset, length) + .err() + .map(|e| e.0) + .unwrap_or_default() +} diff --git a/src/header/fcntl/redox.rs b/src/header/fcntl/redox.rs new file mode 100644 index 0000000000..0d54c33883 --- /dev/null +++ b/src/header/fcntl/redox.rs @@ -0,0 +1,40 @@ +use crate::platform::types::c_int; + +pub const O_RDONLY: c_int = 0x0001_0000; +pub const O_WRONLY: c_int = 0x0002_0000; +pub const O_RDWR: c_int = 0x0003_0000; +pub const O_ACCMODE: c_int = 0x0003_0000; +pub const O_NONBLOCK: c_int = 0x0004_0000; +pub const O_APPEND: c_int = 0x0008_0000; +pub const O_SHLOCK: c_int = 0x0010_0000; +pub const O_EXLOCK: c_int = 0x0020_0000; +pub const O_ASYNC: c_int = 0x0040_0000; +pub const O_FSYNC: c_int = 0x0080_0000; +pub const O_SYNC: c_int = O_FSYNC; +pub const O_CLOEXEC: c_int = 0x0100_0000; +pub const O_CREAT: c_int = 0x0200_0000; +pub const O_TRUNC: c_int = 0x0400_0000; +pub const O_EXCL: c_int = 0x0800_0000; +pub const O_DIRECTORY: c_int = 0x1000_0000; +pub const O_PATH: c_int = 0x2000_0000; +pub const O_SYMLINK: c_int = 0x4000_0000; +// Negative to allow it to be used as int +pub const O_NOFOLLOW: c_int = -0x8000_0000; + +pub const FD_CLOEXEC: c_int = 0x0100_0000; + +pub const O_NOCTTY: c_int = 0x00000200; + +// Defined for compatibility +pub const O_NDELAY: c_int = O_NONBLOCK; + +// Flags for capability based "at" functions +pub const AT_FDCWD: c_int = -100; +pub const AT_SYMLINK_NOFOLLOW: c_int = 0x200; +pub const AT_REMOVEDIR: c_int = 0x200; +// Used by linkat() +pub const AT_SYMLINK_FOLLOW: c_int = 0x2000; +// nonstandard extension, but likely to be in a future standard +pub const AT_EMPTY_PATH: c_int = 0x4000; +// only used for faccessat() +pub const AT_EACCESS: c_int = 0x400; diff --git a/src/header/float/cbindgen.toml b/src/header/float/cbindgen.toml new file mode 100644 index 0000000000..dc4839c9ae --- /dev/null +++ b/src/header/float/cbindgen.toml @@ -0,0 +1,62 @@ +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_FLOAT_H" +after_includes = """ +#ifndef _RELIBC_BITS_FLOAT_H +#define _RELIBC_BITS_FLOAT_H + +#define FLT_ROUNDS (flt_rounds()) + +// Shamelessly copy pasted from musl: + +#define FLT_TRUE_MIN 1.40129846432481707092e-45F +#define FLT_MIN 1.17549435082228750797e-38F +#define FLT_MAX 3.40282346638528859812e+38F +#define FLT_EPSILON 1.1920928955078125e-07F + +#define FLT_MANT_DIG 24 +#define FLT_MIN_EXP (-125) +#define FLT_MAX_EXP 128 +#define FLT_HAS_SUBNORM 1 + +#define FLT_DIG 6 +#define FLT_DECIMAL_DIG 9 +#define FLT_MIN_10_EXP (-37) +#define FLT_MAX_10_EXP 38 + +#define DBL_TRUE_MIN 4.94065645841246544177e-324 +#define DBL_MIN 2.22507385850720138309e-308 +#define DBL_MAX 1.79769313486231570815e+308 +#define DBL_EPSILON 2.22044604925031308085e-16 + +#define DBL_MANT_DIG 53 +#define DBL_MIN_EXP (-1021) +#define DBL_MAX_EXP 1024 +#define DBL_HAS_SUBNORM 1 + +#define DBL_DIG 15 +#define DBL_DECIMAL_DIG 17 +#define DBL_MIN_10_EXP (-307) +#define DBL_MAX_10_EXP 308 + +#define LDBL_HAS_SUBNORM 1 +#define LDBL_DECIMAL_DIG DECIMAL_DIG + +// TODO: Support more architectures than x86_64 here: +#define LDBL_TRUE_MIN 3.6451995318824746025e-4951L +#define LDBL_MIN 3.3621031431120935063e-4932L +#define LDBL_MAX 1.1897314953572317650e+4932L +#define LDBL_EPSILON 1.0842021724855044340e-19L + +#define LDBL_MANT_DIG 64 +#define LDBL_MIN_EXP (-16381) +#define LDBL_MAX_EXP 16384 + +#endif // _RELIBC_BITS_FLOAT_H +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/float/mod.rs b/src/header/float/mod.rs new file mode 100644 index 0000000000..e806f0ce91 --- /dev/null +++ b/src/header/float/mod.rs @@ -0,0 +1,20 @@ +//! `float.h` implementation. +//! +//! See . + +use crate::{ + header::_fenv::{FE_TONEAREST, fegetround}, + platform::types::c_int, +}; + +/// See . +pub const FLT_RADIX: c_int = 2; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn flt_rounds() -> c_int { + match unsafe { fegetround() } { + FE_TONEAREST => 1, + _ => -1, + } +} diff --git a/src/header/fnmatch/cbindgen.toml b/src/header/fnmatch/cbindgen.toml new file mode 100644 index 0000000000..f28cb9123e --- /dev/null +++ b/src/header/fnmatch/cbindgen.toml @@ -0,0 +1,11 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fnmatch.h.html +# +# There are no spec quotations relating to includes +include_guard = "_RELIBC_FNMATCH_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/fnmatch/mod.rs b/src/header/fnmatch/mod.rs new file mode 100644 index 0000000000..8b09bc0759 --- /dev/null +++ b/src/header/fnmatch/mod.rs @@ -0,0 +1,159 @@ +//! `fnmatch.h` implementation. +//! +//! See . + +use alloc::{borrow::Cow, vec::Vec}; +use core::slice; + +use crate::platform::types::{c_char, c_int}; +use posix_regex::{ + PosixRegex, + compile::{Collation, Range, Token}, + tree::{Tree, TreeBuilder}, +}; + +const ONCE: Range = Range(1, Some(1)); + +pub const FNM_NOMATCH: c_int = 1; + +pub const FNM_NOESCAPE: c_int = 1; +pub const FNM_PATHNAME: c_int = 2; +pub const FNM_PERIOD: c_int = 4; +pub const FNM_CASEFOLD: c_int = 8; +pub const FNM_IGNORECASE: c_int = FNM_CASEFOLD; +// TODO: FNM_EXTMATCH + +unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Tree { + fn any(leading: bool, flags: c_int) -> Token { + let mut list = Vec::new(); + if flags & FNM_PATHNAME == FNM_PATHNAME { + list.push(Collation::Char(b'/')) + } + if leading && flags & FNM_PERIOD == FNM_PERIOD { + list.push(Collation::Char(b'.')) + } + Token::OneOf { invert: true, list } + } + fn can_push(leading: bool, flags: c_int, c: u8) -> bool { + (c != b'/' || flags & FNM_PATHNAME != FNM_PATHNAME) + && (c != b'.' || !leading || flags & FNM_PERIOD != FNM_PERIOD) + } + fn is_leading(flags: c_int, c: u8) -> bool { + c == b'/' && flags & FNM_PATHNAME == FNM_PATHNAME + } + + let mut leading = true; + let mut need_collapsing = false; + + let mut builder = TreeBuilder::default(); + builder.start_internal(Token::Root, Range(1, Some(1))); + builder.start_internal(Token::Alternative, Range(1, Some(1))); + + while unsafe { *pattern != 0 } { + let was_leading = leading; + leading = false; + + let c = unsafe { *pattern }; + pattern = unsafe { pattern.offset(1) }; + + match (c == b'*', need_collapsing) { + (true, true) => continue, + (true, false) => need_collapsing = true, + (false, _) => need_collapsing = false, + } + + let (token, range) = match c { + b'\\' if flags & FNM_NOESCAPE == FNM_NOESCAPE => { + let c = unsafe { *pattern }; + if c == 0 { + // Trailing backslash. Maybe error here? + break; + } + pattern = unsafe { pattern.offset(1) }; + leading = is_leading(flags, c); + (Token::Char(c), ONCE) + } + b'?' => (any(was_leading, flags), ONCE), + b'*' => (any(was_leading, flags), Range(0, None)), + b'[' => { + let mut list: Vec = Vec::new(); + let invert = if unsafe { *pattern == b'!' } { + pattern = unsafe { pattern.offset(1) }; + true + } else { + false + }; + + loop { + let mut c = unsafe { *pattern }; + if c == 0 { + break; + } + pattern = unsafe { pattern.offset(1) }; + match c { + b']' => break, + b'\\' => { + c = unsafe { *pattern }; + pattern = unsafe { pattern.offset(1) }; + if c == 0 { + // Trailing backslash. Maybe error? + break; + } + } + _ => (), + } + if unsafe { *pattern == b'-' && *pattern.offset(1) != 0 } { + let end = unsafe { *pattern.offset(1) }; + pattern = unsafe { pattern.offset(2) }; + for c in c..=end { + if can_push(was_leading, flags, c) { + list.push(Collation::Char(c)); + } + } + } else if can_push(was_leading, flags, c) { + list.push(Collation::Char(c)); + } + } + // Otherwise, there was no closing ]. Maybe error? + + (Token::OneOf { invert, list }, ONCE) + } + c => { + leading = is_leading(flags, c); + (Token::Char(c), ONCE) + } + }; + builder.leaf(token, range); + } + builder.leaf(Token::End, ONCE); + builder.finish_internal(); + builder.finish_internal(); + builder.finish() +} + +/// See . +#[unsafe(no_mangle)] +#[linkage = "weak"] // often redefined in GNU programs +pub unsafe extern "C" fn fnmatch( + pattern: *const c_char, + input: *const c_char, + flags: c_int, +) -> c_int { + let mut len = 0; + while unsafe { *input.offset(len) != 0 } { + len += 1; + } + let input = unsafe { slice::from_raw_parts(input.cast::(), len as usize) }; + + let tokens = unsafe { tokenize(pattern.cast::(), flags) }; + + if PosixRegex::new(Cow::Owned(tokens)) + .case_insensitive(flags & FNM_CASEFOLD == FNM_CASEFOLD) + .matches_exact(input) + .is_some() + { + 0 + } else { + FNM_NOMATCH + } +} diff --git a/src/header/getopt/cbindgen.toml b/src/header/getopt/cbindgen.toml new file mode 100644 index 0000000000..36e260094b --- /dev/null +++ b/src/header/getopt/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["unistd.h"] +include_guard = "_RELIBC_GETOPT_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/getopt/mod.rs b/src/header/getopt/mod.rs new file mode 100644 index 0000000000..1cc310a677 --- /dev/null +++ b/src/header/getopt/mod.rs @@ -0,0 +1,245 @@ +//! `getopt.h` implementation. +//! +//! Non-POSIX, see . + +use crate::{ + header::{ + stdio, string, + unistd::{optarg, opterr, optind, optopt}, + }, + platform::types::{c_char, c_int, size_t}, +}; +use core::ptr; + +/// cbindgen:ignore +static mut CURRENT_OPT: *mut c_char = ptr::null_mut(); + +/// Non-POSIX, see . +pub const no_argument: c_int = 0; +/// Non-POSIX, see . +pub const required_argument: c_int = 1; +/// Non-POSIX, see . +pub const optional_argument: c_int = 2; + +/// Non-POSIX, see . +#[repr(C)] +pub struct option { + name: *const c_char, + has_arg: c_int, + flag: *mut c_int, + val: c_int, +} + +/// Non-POSIX, see . +/// +/// Functions the same as `getopt` but also accepts long options. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getopt_long( + argc: c_int, + argv: *const *mut c_char, + optstring: *const c_char, + longopts: *const option, + longindex: *mut c_int, +) -> c_int { + // if optarg is not set, we still don't want the previous value leaking + unsafe { + optarg = ptr::null_mut(); + } + + // handle reinitialization request + unsafe { + if optind == 0 { + optind = 1; + CURRENT_OPT = ptr::null_mut(); + } + } + + if unsafe { CURRENT_OPT.is_null() || *CURRENT_OPT == 0 } { + if unsafe { optind >= argc } { + -1 + } else { + let current_arg = unsafe { *argv.offset(optind as isize) }; + if unsafe { + current_arg.is_null() + || *current_arg != b'-' as c_char + || *current_arg.offset(1) == 0 + } { + -1 + } else if unsafe { string::strcmp(current_arg, c"--".as_ptr()) == 0 } { + unsafe { + optind += 1; + } + -1 + } else { + // remove the '-' + let current_arg = unsafe { current_arg.offset(1) }; + + if unsafe { *current_arg == b'-' as c_char } && !longopts.is_null() { + let current_arg = unsafe { current_arg.offset(1) }; + // is a long option + for i in 0.. { + let opt = unsafe { &*longopts.offset(i) }; + if opt.name.is_null() { + break; + } + + let mut end = 0; + while { + let c = unsafe { *current_arg.offset(end) }; + c != 0 && c != b'=' as c_char + } { + end += 1; + } + + if unsafe { string::strncmp(current_arg, opt.name, end as size_t) == 0 } { + unsafe { + optind += 1; + if !longindex.is_null() { + *longindex = i as c_int; + } + } + + if opt.has_arg == optional_argument { + unsafe { + if *current_arg.offset(end) == b'=' as c_char { + optarg = current_arg.offset(end + 1); + } + } + } else if opt.has_arg == required_argument { + unsafe { + if *current_arg.offset(end) == b'=' as c_char { + optarg = current_arg.offset(end + 1); + } else if optind < argc { + optarg = *argv.offset(optind as isize); + optind += 1; + } else if *optstring == b':' as c_char { + return c_int::from(b':'); + } else { + stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr); + stdio::fputs( + c": option '--".as_ptr().cast(), + &raw mut *stdio::stderr, + ); + stdio::fputs(current_arg, &raw mut *stdio::stderr); + stdio::fputs( + c"' requires an argument\n".as_ptr().cast(), + &raw mut *stdio::stderr, + ); + return c_int::from(b'?'); + } + } + } + + if opt.flag.is_null() { + return opt.val; + } else { + unsafe { *opt.flag = opt.val }; + return 0; + } + } + } + } + + unsafe { parse_arg(argc, argv, current_arg, optstring) } + } + } + } else { + unsafe { parse_arg(argc, argv, CURRENT_OPT, optstring) } + } +} + +unsafe fn parse_arg( + argc: c_int, + argv: *const *mut c_char, + current_arg: *mut c_char, + optstring: *const c_char, +) -> c_int { + let update_current_opt = || unsafe { + CURRENT_OPT = current_arg.offset(1); + if *CURRENT_OPT == 0 { + optind += 1; + } + }; + + let print_error = |desc: &[u8]| unsafe { + // NOTE: we don't use fprintf to get around the usage of va_list + stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr); + stdio::fputs(desc.as_ptr().cast(), &raw mut *stdio::stderr); + stdio::fputc((*current_arg).into(), &raw mut *stdio::stderr); + stdio::fputc(b'\n'.into(), &raw mut *stdio::stderr); + }; + + match unsafe { find_option(*current_arg, optstring) } { + Some(GetoptOption::Flag) => { + update_current_opt(); + + unsafe { c_int::from(*current_arg) } + } + Some(GetoptOption::OptArg) => unsafe { + CURRENT_OPT = c"".as_ptr().cast_mut(); + if *current_arg.offset(1) == 0 { + optind += 2; + if optind > argc { + CURRENT_OPT = ptr::null_mut(); + + optopt = c_int::from(*current_arg); + let errch = if *optstring == b':' as c_char { + b':' + } else { + if opterr != 0 { + print_error(b": option requries an argument -- \0"); + } + + b'?' + }; + c_int::from(errch) + } else { + optarg = *argv.offset(optind as isize - 1); + + c_int::from(*current_arg) + } + } else { + optarg = current_arg.offset(1); + optind += 1; + + c_int::from(*current_arg) + } + }, + None => { + // couldn't find the given option in optstring + if unsafe { opterr != 0 } { + print_error(b": illegal option -- \0"); + } + + update_current_opt(); + + unsafe { + optopt = c_int::from(*current_arg); + } + c_int::from(b'?') + } + } +} + +enum GetoptOption { + Flag, + OptArg, +} + +unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option { + let mut i = 0; + + while unsafe { *optstring.offset(i) != 0 } { + if unsafe { *optstring.offset(i) == ch } { + let result = if unsafe { *optstring.offset(i + 1) == b':' as c_char } { + GetoptOption::OptArg + } else { + GetoptOption::Flag + }; + return Some(result); + } + i += 1; + } + + None +} diff --git a/src/header/glob/cbindgen.toml b/src/header/glob/cbindgen.toml new file mode 100644 index 0000000000..d8de241ec3 --- /dev/null +++ b/src/header/glob/cbindgen.toml @@ -0,0 +1,16 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the size_t type as described in ." +# +# size_t is defined in stddef.h (sys/types.h gets size_t by including stddef.h) +# just use stddef.h to avoid importing all of sys/types.h +sys_includes = ["stddef.h"] +include_guard = "_RELIBC_GLOB_H" +language = "C" +style = "type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/glob/mod.rs b/src/header/glob/mod.rs new file mode 100644 index 0000000000..fcbaa7f7b0 --- /dev/null +++ b/src/header/glob/mod.rs @@ -0,0 +1,401 @@ +//! `glob.h` implementation. +//! +//! See . + +use core::ptr; + +use alloc::{boxed::Box, vec::Vec}; + +use crate::{ + c_str::{CStr, CString}, + header::{ + dirent::{closedir, opendir, readdir}, + errno::*, + fnmatch::{FNM_NOESCAPE, FNM_PERIOD, fnmatch}, + sys_stat::{S_IFDIR, S_IFMT, stat}, + }, + platform::{ + self, + types::{c_char, c_int, c_uchar, c_void, size_t}, + }, +}; + +// Cause glob() to return on error +pub const GLOB_ERR: c_int = 0x0001; +// Each pathname that is a directory that matches pattern has a slash appended +pub const GLOB_MARK: c_int = 0x0002; +// Do not sort returned pathnames +pub const GLOB_NOSORT: c_int = 0x0004; +// Add gl_offs amount of null pointers to the beginning of `gl_pathv` +pub const GLOB_DOOFFS: c_int = 0x0008; +// If pattern does not match, return a list containing only pattern +pub const GLOB_NOCHECK: c_int = 0x0010; +// Append generated pathnames to those previously obtained +pub const GLOB_APPEND: c_int = 0x0020; +// Disable backslash escaping +pub const GLOB_NOESCAPE: c_int = 0x0040; +// Allow wildcards to match '.' (GNU extension) +pub const GLOB_PERIOD: c_int = 0x0080; + +// Attempt to allocate memory failed +pub const GLOB_NOSPACE: c_int = 1; +// Scan was stopped because GLOB_ERR was set or `errfunc` returned non-zero +pub const GLOB_ABORTED: c_int = 2; +// Pattern does not match any existing pathname, and GLOB_NOCHECK was not set +pub const GLOB_NOMATCH: c_int = 3; + +/// See . +#[derive(Debug)] +#[repr(C)] +pub struct glob_t { + pub gl_pathc: size_t, // Count of paths matched by pattern (POSIX required field) + pub gl_offs: size_t, // Slots to reserve at the beginning of gl_pathv (POSIX required field) + pub gl_pathv: *mut *mut c_char, // Pointer to list of matched pathnames (POSIX required field) + + // Opaque pointer to allocation data + __opaque: *mut c_void, // Vec<*mut c_char> +} + +/// See . +#[linkage = "weak"] // GNU prefers its own glob e.g. in Make +#[unsafe(no_mangle)] +pub unsafe extern "C" fn glob( + pattern: *const c_char, + flags: c_int, + errfunc: Option c_int>, + pglob: *mut glob_t, +) -> c_int { + if flags & GLOB_APPEND != GLOB_APPEND { + unsafe { + (*pglob).gl_pathc = 0; + (*pglob).gl_pathv = ptr::null_mut(); + (*pglob).__opaque = ptr::null_mut(); + } + } + + let glob_expr = unsafe { CStr::from_ptr(pattern) }; + + if glob_expr.to_bytes() == b"" { + return GLOB_NOMATCH; + } + + let base_path = unsafe { + CStr::from_bytes_with_nul_unchecked(if glob_expr.to_bytes().first() == Some(&b'/') { + b"/\0" + } else { + b"\0" + }) + }; + + let errfunc = match errfunc { + Some(f) => f, + None => default_errfunc, + }; + + // Do the globbing + let mut results = match inner_glob(&base_path, &glob_expr, flags, errfunc) { + Ok(res) => res, + Err(e) => return e, + }; + + // Handle GLOB_NOCHECK and no matches + if results.is_empty() { + if flags & GLOB_NOCHECK == GLOB_NOCHECK { + results.push(glob_expr.to_owned_cstring()); + } else { + return GLOB_NOMATCH; + } + } + + // Handle GLOB_NOSORT + if flags & GLOB_NOSORT != GLOB_NOSORT { + results.sort(); + } + + // Set gl_pathc + if flags & GLOB_APPEND == GLOB_APPEND { + unsafe { + (*pglob).gl_pathc += results.len(); + } + } else { + unsafe { + (*pglob).gl_pathc = results.len(); + } + } + + let mut pathv: Box>; + if flags & GLOB_APPEND == GLOB_APPEND { + pathv = unsafe { Box::from_raw((*pglob).__opaque.cast()) }; + pathv.pop(); // Remove NULL from end + } else { + pathv = Box::new(Vec::new()); + if flags & GLOB_DOOFFS == GLOB_DOOFFS { + let gl_offs = unsafe { (*pglob).gl_offs }; + pathv.reserve(gl_offs); + for _ in 0..gl_offs { + pathv.push(ptr::null_mut()); + } + } + } + + pathv.reserve_exact(results.len() + 1); + pathv.extend(results.into_iter().map(|s| s.into_raw())); + + pathv.push(ptr::null_mut()); + + unsafe { + (*pglob).gl_pathv = pathv.as_ptr().cast_mut(); + (*pglob).__opaque = Box::into_raw(pathv).cast(); + } + + 0 +} + +/// See . +#[linkage = "weak"] // GNU prefers its own glob e.g. in Make +#[unsafe(no_mangle)] +pub unsafe extern "C" fn globfree(pglob: *mut glob_t) { + // Retake ownership + if unsafe { !(*pglob).__opaque.is_null() } { + let pathv: Box> = unsafe { Box::from_raw((*pglob).__opaque.cast()) }; + for (idx, path) in pathv.into_iter().enumerate() { + if unsafe { idx < (*pglob).gl_offs } { + continue; + } + if !path.is_null() { + unsafe { + drop(CString::from_raw(path)); + } + } + } + unsafe { + (*pglob).gl_pathv = ptr::null_mut(); + } + } +} + +type GlobErrorFunc = unsafe extern "C" fn(epath: *const c_char, eerrno: c_int) -> c_int; + +struct DirEntry { + name: CString, + is_dir: bool, +} + +unsafe extern "C" fn default_errfunc(epath: *const c_char, eerrno: c_int) -> c_int { + 0 +} + +fn list_dir( + path: &CStr, + errfunc: GlobErrorFunc, + abort_on_error: bool, +) -> Result, c_int> { + const DT_DIR: c_uchar = 4; // From dirent.h + const DT_LNK: c_uchar = 10; // From dirent.h + + let old_errno = platform::ERRNO.get(); + let mut results: Vec = Vec::new(); + let open_path = if path.to_bytes().is_empty() { + unsafe { &CStr::from_bytes_with_nul_unchecked(b".\0") } + } else { + path + }; + let dir = unsafe { opendir(open_path.as_ptr()) }; + + if dir.is_null() { + let new_errno = platform::ERRNO.get(); + platform::ERRNO.set(old_errno); + + if unsafe { errfunc(path.as_ptr(), new_errno) } != 0 || abort_on_error { + return Err(GLOB_ABORTED); + } + + return Ok(results); + } + + platform::ERRNO.set(0); + + loop { + let entry = unsafe { readdir(&mut *dir) }; + if entry.is_null() { + break; + } + + let name = unsafe { CStr::from_ptr((*entry).d_name.as_ptr()).to_owned_cstring() }; + + if name.as_bytes() == b"." || name.as_bytes() == b".." { + continue; + } + + let is_dir: bool = unsafe { + if (*entry).d_type == DT_DIR { + true + } else if (*entry).d_type == DT_LNK { + // Resolve symbolic link + let mut full_path = path.to_owned_cstring().into_string().unwrap(); + if !full_path.ends_with('/') { + full_path.push('/'); + } + full_path.push_str(name.to_str().unwrap()); + full_path.push('\0'); + + let mut link_info = stat::default(); + if stat( + full_path.as_ptr().cast::(), + ptr::from_mut(&mut link_info), + ) != 0 + { + let errno = platform::ERRNO.get(); + platform::ERRNO.set(old_errno); + if errfunc(full_path.as_ptr().cast::(), errno) != 0 || abort_on_error { + return Err(GLOB_ABORTED); + } + } + link_info.st_mode & S_IFMT == S_IFDIR + } else { + false + } + }; + + results.push(DirEntry { name, is_dir }); + } + + // Check if entry == NULL because of an error + let errno = platform::ERRNO.get(); + + unsafe { closedir(Box::from_raw(dir)) }; + + // Restore the old errno + platform::ERRNO.set(old_errno); + + if errno != 0 && (unsafe { errfunc(path.as_ptr(), errno) } != 0 || abort_on_error) { + return Err(GLOB_ABORTED); + } + + Ok(results) +} + +fn inner_glob( + current_dir: &CStr, + glob_expr: &CStr, + flags: c_int, + errfunc: GlobErrorFunc, +) -> Result, c_int> { + let mut pattern: Vec = Vec::new(); + + // Remove any '/' chars at the start of the expression + let glob_expr = { + let mut expr = glob_expr.to_bytes_with_nul(); + while expr.first() == Some(&b'/') { + expr = &expr[1..]; + } + unsafe { CStr::from_bytes_with_nul_unchecked(expr) } + }; + + // Get the next section of the glob expression (up to non-escaped '/') + let glob_iter = glob_expr.to_bytes(); + let mut in_bracket = false; + let mut escaped = false; + let mut glob_consumed = 0; + + for ch in glob_iter { + // Don't consume nul + if ch == &b'\0' { + break; + } + + glob_consumed += 1; + + if ch == &b'/' && !escaped { + break; + } + + if ch == &b'[' && !escaped { + in_bracket = true; + } else if ch == &b']' { + // '\' is a normal character in brackets so doesn't escape + in_bracket = false; + } + + escaped = + ch == &b'\\' && !in_bracket && !escaped && (flags & GLOB_NOESCAPE != GLOB_NOESCAPE); + + pattern.push(*ch); + } + + // Needs to be C-string + pattern.push(b'\0'); + + let new_glob_expr = unsafe { + CStr::from_bytes_with_nul_unchecked(&glob_expr.to_bytes_with_nul()[glob_consumed..]) + }; + + // Handle special path sections + if pattern == b".\0" || pattern == b"..\0" { + let mut new_dir: Vec = Vec::new(); + new_dir.extend_from_slice(current_dir.to_bytes()); + new_dir.extend_from_slice(&pattern); + let new_dir_c = unsafe { CStr::from_bytes_with_nul_unchecked(&new_dir) }; + return inner_glob(&new_dir_c, &new_glob_expr, flags, errfunc); + } + + let mut fnmatch_flags = 0; + if flags & GLOB_NOESCAPE == GLOB_NOESCAPE { + fnmatch_flags |= FNM_NOESCAPE; + } + if flags & GLOB_PERIOD == GLOB_PERIOD { + fnmatch_flags |= FNM_PERIOD; + } + + let mut matches: Vec = Vec::new(); + + for entry in list_dir(current_dir, errfunc, flags & GLOB_ERR == GLOB_ERR)? { + // If we still have pattern to match ignore non-directories + if !new_glob_expr.to_bytes().is_empty() && !entry.is_dir { + continue; + } + + let mut path = current_dir.to_bytes().to_vec(); + + if path != b"" && !path.ends_with(b"/") { + path.push(b'/'); + } + path.extend_from_slice(entry.name.as_bytes()); + + if flags & GLOB_MARK == GLOB_MARK && new_glob_expr.to_bytes() == b"" && entry.is_dir { + path.push(b'/'); + } + + // This shouldn't ever panic, we know the vec has no nul bytes + let path = CString::new(path).unwrap(); + + if unsafe { + fnmatch( + pattern.as_ptr().cast::(), + entry.name.as_ptr(), + fnmatch_flags, + ) + } == 0 + { + if entry.is_dir && new_glob_expr.to_bytes() != b"" { + let new_matches = inner_glob(&CStr::borrow(&path), &new_glob_expr, flags, errfunc)?; + matches.extend(new_matches); + } else { + matches.push(path); + } + } + } + + // It is an error if we don't find a directory when we expect one + if matches.is_empty() && !new_glob_expr.to_bytes().is_empty() { + let mut path = current_dir.to_bytes().to_vec(); + path.extend_from_slice(&pattern); + if unsafe { errfunc(path.as_ptr().cast::(), ENOENT) } != 0 + || flags & GLOB_ERR == GLOB_ERR + { + return Err(GLOB_ABORTED); + } + } + + Ok(matches) +} diff --git a/src/header/grp/cbindgen.toml b/src/header/grp/cbindgen.toml new file mode 100644 index 0000000000..5383c8adf6 --- /dev/null +++ b/src/header/grp/cbindgen.toml @@ -0,0 +1,17 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the gid_t and size_t types as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_GRP_H" +language = "C" +style = "Tag" +usize_is_size_t = true +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export] +include = ["group"] diff --git a/src/header/grp/mod.rs b/src/header/grp/mod.rs new file mode 100644 index 0000000000..1f5ef44a6f --- /dev/null +++ b/src/header/grp/mod.rs @@ -0,0 +1,558 @@ +//! `grp.h` implementation. +//! +//! See . + +use core::{ + cell::SyncUnsafeCell, + convert::TryInto, + mem::{self, MaybeUninit}, + num::ParseIntError, + ops::{Deref, DerefMut}, + pin::Pin, + ptr, slice, +}; + +use alloc::{ + borrow::ToOwned, + string::{FromUtf8Error, String}, +}; + +use crate::{ + error::ResultExt, + fs::File, + header::{errno, fcntl, limits, string::strlen}, + io::{self, BufReader, Lines, prelude::*}, + platform::{ + self, Pal, Sys, + types::{c_char, c_int, c_void, gid_t, size_t}, + }, +}; + +use super::{errno::*, string::strncmp}; + +#[cfg(target_os = "linux")] +const SEPARATOR: char = ':'; + +#[cfg(target_os = "redox")] +const SEPARATOR: char = ';'; + +const GROUP_FILE: &core::ffi::CStr = c"/etc/group"; + +#[derive(Clone, Copy, Debug)] +struct DestBuffer { + ptr: *mut u8, + len: usize, +} + +// Shamelessly stolen from pwd/mod.rs +#[derive(Debug)] +enum MaybeAllocated { + Owned(Pin>), + Borrowed(DestBuffer), +} +impl Deref for MaybeAllocated { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts(dst.ptr, dst.len) + }, + } + } +} +impl DerefMut for MaybeAllocated { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts_mut(dst.ptr, dst.len) + }, + } + } +} + +static mut GROUP_BUF: Option = None; +static mut GROUP: group = group { + gr_name: ptr::null_mut(), + gr_passwd: ptr::null_mut(), + gr_gid: 0, + gr_mem: ptr::null_mut(), +}; + +static LINE_READER: SyncUnsafeCell>>> = SyncUnsafeCell::new(None); + +/// See . +#[repr(C)] +#[derive(Debug)] +pub struct group { + pub gr_name: *mut c_char, + pub gr_passwd: *mut c_char, + pub gr_gid: gid_t, + pub gr_mem: *mut *mut c_char, +} + +#[derive(Debug)] +enum Error { + EOF, + SyntaxError, + BufTooSmall, + Misc(io::Error), + FromUtf8Error(FromUtf8Error), + ParseIntError(ParseIntError), + Other, +} + +#[derive(Debug)] +struct OwnedGrp { + buffer: MaybeAllocated, + reference: group, +} + +impl OwnedGrp { + fn into_global(self) -> *mut group { + unsafe { + GROUP_BUF = Some(self.buffer); + GROUP = self.reference; + &raw mut GROUP + } + } +} + +fn split(buf: &mut [u8]) -> Option { + let gr_gid = match buf[0..mem::size_of::()].try_into() { + Ok(buf) => gid_t::from_ne_bytes(buf), + Err(err) => return None, + }; + + // Get address of buffer for fixing up gr_mem + let buf_addr = buf.as_ptr() as usize; + + // We moved the gid to the beginning of the byte buffer so we can do this. + let mut parts = buf[mem::size_of::()..].split_mut(|&c| c == b'\0'); + let gr_name = parts.next()?.as_mut_ptr().cast::(); + let gr_passwd = parts.next()?.as_mut_ptr().cast::(); + let gr_mem = parts.next()?.as_mut_ptr().cast::(); + + // Adjust gr_mem address by buffer base address + // TODO: max group members length? + for i in 0..4096 { + unsafe { + if *gr_mem.add(i) == 0 { + // End of gr_mem pointer array + break; + } + *gr_mem.add(i) += buf_addr; + } + } + + Some(group { + gr_name, + gr_passwd, + gr_gid, + gr_mem: gr_mem.cast::<*mut c_char>(), + }) +} + +fn parse_grp(line: String, destbuf: Option) -> Result { + let buffer = line.to_owned().into_bytes(); + + let mut buffer = buffer + .into_iter() + .map(|i| if i == SEPARATOR as u8 { b'\0' } else { i }) + .chain([b'\0']) + .collect::>(); + let mut buffer = buffer.split_mut(|i| *i == b'\0'); + + let strings = { + let mut vec: Vec = Vec::new(); + + let gr_name = buffer.next().ok_or(Error::EOF)?.to_vec(); + let gr_passwd = buffer.next().ok_or(Error::EOF)?.to_vec(); + let gr_gid = String::from_utf8(buffer.next().ok_or(Error::EOF)?.to_vec()) + .map_err(Error::FromUtf8Error)? + .parse::() + .map_err(Error::ParseIntError)?; + + // Place the gid at the beginning of the byte buffer to make getting it back out again later, much faster. + + vec.extend(gr_gid.to_ne_bytes()); + vec.extend(gr_name); + vec.push(0); + vec.extend(gr_passwd); + vec.push(0); + + let members = buffer.next().ok_or(Error::EOF)?; + + // Get the offset of the members array + let member_array_start = vec.len(); + + // Push enough null pointers to fit all members + for _member in members + .split(|b| *b == b',') + .filter(|member| !member.is_empty()) + { + vec.extend(0usize.to_ne_bytes()); + } + let member_array_end = vec.len(); + // Push a null pointer to terminate the members array + vec.extend(0usize.to_ne_bytes()); + + // Fill in member names + for (i, member) in members + .split(|b| *b == b',') + .filter(|member| !member.is_empty()) + .enumerate() + { + let cur_offset = vec.len(); + + // This must be recomputed each time, because `vec` is undergoing extensions and so + // its backing memory might be reallocated and moved and its old memory deallocated. + let member_array = &mut vec[member_array_start..member_array_end]; + let member_ptr = { + const SIZEOF_PTR: usize = mem::size_of::<*mut c_void>(); + let start = i * SIZEOF_PTR; + let end = start + SIZEOF_PTR; + &mut member_array[start..end] + }; + + // Store offset to start of member, MUST BE ADJUSTED LATER BASED ON THE ADDRESS OF THE BUFFER + member_ptr.copy_from_slice(&cur_offset.to_ne_bytes()); + + vec.extend(member); + vec.push(0); + } + + vec + }; + + let mut buffer = match destbuf { + None => MaybeAllocated::Owned(Box::into_pin(strings.into_boxed_slice())), + Some(buf) => { + let mut buf = MaybeAllocated::Borrowed(buf); + + if buf.len() < strings.len() { + platform::ERRNO.set(errno::ERANGE); + return Err(Error::BufTooSmall); + } + + buf[..strings.len()].copy_from_slice(&strings); + buf + } + }; + let reference = split(&mut buffer).ok_or(Error::Other)?; + + Ok(OwnedGrp { buffer, reference }) +} + +/// MT-Unsafe race:grgid locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut group { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return ptr::null_mut(); + }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { + return ptr::null_mut(); + }; + let Ok(grp) = parse_grp(line, None) else { + return ptr::null_mut(); + }; + + if grp.reference.gr_gid == gid { + return grp.into_global(); + } + } + + ptr::null_mut() +} + +/// MT-Unsafe race:grnam locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut group { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return ptr::null_mut(); + }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { + return ptr::null_mut(); + }; + + let Ok(grp) = parse_grp(line, None) else { + return ptr::null_mut(); + }; + + // Attempt to prevent BO vulnerabilities + if unsafe { + strncmp( + grp.reference.gr_name, + name, + strlen(grp.reference.gr_name).min(strlen(name)), + ) == 0 + } { + return grp.into_global(); + } + } + + ptr::null_mut() +} + +/// MT-Safe locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgrgid_r( + gid: gid_t, + result_buf: *mut group, + buffer: *mut c_char, + buflen: usize, + result: *mut *mut group, +) -> c_int { + // In case of error or the requested entry is not found. + unsafe { + *result = ptr::null_mut(); + } + + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return ENOENT; + }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { return EINVAL }; + let grp = match parse_grp( + line, + Some(DestBuffer { + ptr: buffer.cast::(), + len: buflen, + }), + ) { + Ok(grp) => grp, + Err(err) => { + return match err { + Error::BufTooSmall => ERANGE, + Error::EOF + | Error::SyntaxError + | Error::FromUtf8Error(_) + | Error::ParseIntError(_) + | Error::Other => EINVAL, + Error::Misc(io_err) => match io_err.kind() { + io::ErrorKind::InvalidData | io::ErrorKind::UnexpectedEof => EINVAL, + io::ErrorKind::NotFound => ENOENT, + _ => EIO, + }, + }; + } + }; + + if grp.reference.gr_gid == gid { + unsafe { + *result_buf = grp.reference; + *result = result_buf; + } + + return 0; + } + } + + // The requested entry was not found. + 0 +} + +/// MT-Safe locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgrnam_r( + name: *const c_char, + result_buf: *mut group, + buffer: *mut c_char, + buflen: usize, + result: *mut *mut group, +) -> c_int { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return ENOENT; + }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { return EINVAL }; + let Ok(grp) = parse_grp( + line, + Some(DestBuffer { + ptr: buffer.cast::(), + len: buflen, + }), + ) else { + return EINVAL; + }; + + if unsafe { + strncmp( + grp.reference.gr_name, + name, + strlen(grp.reference.gr_name).min(strlen(name)), + ) == 0 + } { + unsafe { + *result_buf = grp.reference; + *result = result_buf; + } + + return 0; + } + } + + ENOENT +} + +/// MT-Unsafe race:grent race:grentbuf locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgrent() -> *mut group { + let mut line_reader = unsafe { &mut *LINE_READER.get() }; + + if line_reader.is_none() { + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return ptr::null_mut(); + }; + *line_reader = Some(BufReader::new(db).lines()); + } + + if let Some(lines) = line_reader.deref_mut() { + let Some(line) = lines.next() else { + return ptr::null_mut(); + }; + let Ok(line) = line else { + return ptr::null_mut(); + }; + + if let Ok(grp) = parse_grp(line, None) { + grp.into_global() + } else { + ptr::null_mut() + } + } else { + ptr::null_mut() + } +} + +/// MT-Unsafe race:grent locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn endgrent() { + unsafe { + *(&mut *LINE_READER.get()) = None; + } +} + +/// MT-Unsafe race:grent locale +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setgrent() { + let line_reader = unsafe { &mut *LINE_READER.get() }; + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return; + }; + *line_reader = Some(BufReader::new(db).lines()); +} + +/// MT-Safe locale +/// Not POSIX +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgrouplist( + user: *const c_char, + group: gid_t, + groups: *mut gid_t, + ngroups: *mut c_int, +) -> c_int { + let grps = unsafe { + slice::from_raw_parts_mut(groups.cast::>(), ngroups.read() as usize) + }; + + // FIXME: This API probably expects the group database to already exist in memory, as it + // doesn't seem to have any documented error handling. + + let Ok(user) = (unsafe { crate::c_str::CStr::from_ptr(user).to_str() }) else { + return 0; + }; + + let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else { + return 0; + }; + + let mut groups_found: c_int = 0; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { + return 0; + }; + + let mut parts = line.split(SEPARATOR); + + let group_name = parts.next().unwrap_or(""); + let group_password = parts.next().unwrap_or(""); + let group_id = parts.next().unwrap_or("-1").parse::().unwrap(); + let members = parts + .next() + .unwrap_or("") + .split(",") + .map(|i| i.trim()) + .collect::>(); + + if !members.contains(&user) { + continue; + } + + if let Some(dst) = grps.get_mut(groups_found as usize) { + dst.write(group_id); + } + + groups_found = match groups_found.checked_add(1) { + Some(g) => g, + None => break, + }; + } + + unsafe { + ngroups.write(groups_found); + } + + if groups_found as usize > grps.len() { + -1 + } else { + groups_found + } +} + +/// MT-Safe locale +/// Not POSIX +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn initgroups(user: *const c_char, gid: gid_t) -> c_int { + let mut groups = [0; limits::NGROUPS_MAX]; + let mut count = groups.len() as c_int; + if unsafe { getgrouplist(user, gid, groups.as_mut_ptr(), &raw mut count) < 0 } { + return -1; + } + unsafe { setgroups(count as size_t, groups.as_ptr()) } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setgroups(size: size_t, list: *const gid_t) -> c_int { + unsafe { Sys::setgroups(size, list) } + .map(|()| 0) + .or_minus_one_errno() +} diff --git a/src/header/ifaddrs/cbindgen.toml b/src/header/ifaddrs/cbindgen.toml new file mode 100644 index 0000000000..b507c6f75f --- /dev/null +++ b/src/header/ifaddrs/cbindgen.toml @@ -0,0 +1,17 @@ +# netinet/in.h brings in sys/socket.h +sys_includes = ["features.h", "netinet/in.h"] +include_guard = "_RELIBC_IFADDRS_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true +trailer = """ +#define ifa_broadaddr ifa_ifu.ifu_broadaddr +#define ifa_dstaddr ifa_ifu.ifu_dstaddr +""" + +[export.rename] +"sockaddr" = "struct sockaddr" + +[enum] +prefix_with_name = true diff --git a/src/header/ifaddrs/mod.rs b/src/header/ifaddrs/mod.rs new file mode 100644 index 0000000000..bddb69b858 --- /dev/null +++ b/src/header/ifaddrs/mod.rs @@ -0,0 +1,44 @@ +//! `ifaddrs.h` implementation. +//! +//! Non-POSIX, see . + +use crate::{ + header::{errno, stdlib, sys_socket::sockaddr}, + platform::{ + self, + types::{c_char, c_int, c_uint, c_void}, + }, +}; + +#[repr(C)] +union ifaddrs_ifa_ifu { + ifu_broadaddr: *mut sockaddr, + ifu_dstaddr: *mut sockaddr, +} + +#[repr(C)] +pub struct ifaddrs { + ifa_next: *mut ifaddrs, + ifa_name: *mut c_char, + ifa_flags: c_uint, + ifa_addr: *mut sockaddr, + ifa_netmask: *mut sockaddr, + ifa_ifu: ifaddrs_ifa_ifu, + ifa_data: *mut c_void, +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) { + while !ifa.is_null() { + let next = unsafe { (*ifa).ifa_next }; + unsafe { stdlib::free(ifa.cast()) }; + ifa = next; + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int { + //TODO: implement getifaddrs + platform::ERRNO.set(errno::ENOSYS); + -1 +} diff --git a/src/header/inttypes/cbindgen.toml b/src/header/inttypes/cbindgen.toml new file mode 100644 index 0000000000..568f0cd552 --- /dev/null +++ b/src/header/inttypes/cbindgen.toml @@ -0,0 +1,213 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html +# +# Spec quotations relating to includes: +# - "The header shall include the header." +# - "wchar_t As described in ." +# +# wchar.h brings in both stdint.h and stddef.h +sys_includes = ["wchar.h"] +include_guard = "_RELIBC_INTTYPES_H" +trailer = """ +#ifndef _RELIBC_BITS_INTTYPES_H +#define _RELIBC_BITS_INTTYPES_H + +#define PRId8 "hhd" +#define PRId16 "hd" +#define PRId32 "d" +#define PRId64 "ld" + +#define PRIdLEAST8 "hhd" +#define PRIdLEAST16 "hd" +#define PRIdLEAST32 "d" +#define PRIdLEAST64 "ld" + +#define PRIdFAST8 "hhd" +#define PRIdFAST16 "hd" +#define PRIdFAST32 "d" +#define PRIdFAST64 "ld" + +#define PRIi8 "hhi" +#define PRIi16 "hi" +#define PRIi32 "i" +#define PRIi64 "li" + +#define PRIiLEAST8 "hhi" +#define PRIiLEAST16 "hi" +#define PRIiLEAST32 "i" +#define PRIiLEAST64 "li" + +#define PRIiFAST8 "hhi" +#define PRIiFAST16 "hi" +#define PRIiFAST32 "i" +#define PRIiFAST64 "li" + +#define PRIo8 "hho" +#define PRIo16 "ho" +#define PRIo32 "o" +#define PRIo64 "lo" + +#define PRIoLEAST8 "hho" +#define PRIoLEAST16 "ho" +#define PRIoLEAST32 "o" +#define PRIoLEAST64 "lo" + +#define PRIoFAST8 "hho" +#define PRIoFAST16 "ho" +#define PRIoFAST32 "o" +#define PRIoFAST64 "lo" + +#define PRIu8 "hhu" +#define PRIu16 "hu" +#define PRIu32 "u" +#define PRIu64 "lu" + +#define PRIuLEAST8 "hhu" +#define PRIuLEAST16 "hu" +#define PRIuLEAST32 "u" +#define PRIuLEAST64 "lu" + +#define PRIuFAST8 "hhu" +#define PRIuFAST16 "hu" +#define PRIuFAST32 "u" +#define PRIuFAST64 "lu" + +#define PRIx8 "hhx" +#define PRIx16 "hx" +#define PRIx32 "x" +#define PRIx64 "lx" + +#define PRIxLEAST8 "hhx" +#define PRIxLEAST16 "hx" +#define PRIxLEAST32 "x" +#define PRIxLEAST64 "lx" + +#define PRIxFAST8 "hhx" +#define PRIxFAST16 "hx" +#define PRIxFAST32 "x" +#define PRIxFAST64 "lx" + +#define PRIX8 "hhX" +#define PRIX16 "hX" +#define PRIX32 "X" +#define PRIX64 "lX" + +#define PRIXLEAST8 "hhX" +#define PRIXLEAST16 "hX" +#define PRIXLEAST32 "X" +#define PRIXLEAST64 "lX" + +#define PRIXFAST8 "hhX" +#define PRIXFAST16 "hX" +#define PRIXFAST32 "X" +#define PRIXFAST64 "lX" + +#define PRIdMAX "jd" +#define PRIiMAX "ji" +#define PRIoMAX "jo" +#define PRIuMAX "ju" +#define PRIxMAX "jx" +#define PRIXMAX "jX" + +#define PRIdPTR "td" +#define PRIiPTR "ti" +#define PRIoPTR "to" +#define PRIuPTR "tu" +#define PRIxPTR "tx" +#define PRIXPTR "tX" + +#define SCNd8 PRId8 +#define SCNd16 PRId16 +#define SCNd32 PRId32 +#define SCNd64 PRId64 + +#define SCNdLEAST8 PRIdLEAST8 +#define SCNdLEAST16 PRIdLEAST16 +#define SCNdLEAST32 PRIdLEAST32 +#define SCNdLEAST64 PRIdLEAST64 + +#define SCNdFAST8 PRIdFAST8 +#define SCNdFAST16 PRIdFAST16 +#define SCNdFAST32 PRIdFAST32 +#define SCNdFAST64 PRIdFAST64 + +#define SCNi8 PRIi8 +#define SCNi16 PRIi16 +#define SCNi32 PRIi32 +#define SCNi64 PRIi64 + +#define SCNiLEAST8 PRIiLEAST8 +#define SCNiLEAST16 PRIiLEAST16 +#define SCNiLEAST32 PRIiLEAST32 +#define SCNiLEAST64 PRIiLEAST64 + +#define SCNiFAST8 PRIiFAST8 +#define SCNiFAST16 PRIiFAST16 +#define SCNiFAST32 PRIiFAST32 +#define SCNiFAST64 PRIiFAST64 + +#define SCNo8 PRIo8 +#define SCNo16 PRIo16 +#define SCNo32 PRIo32 +#define SCNo64 PRIo64 + +#define SCNoLEAST8 PRIoLEAST8 +#define SCNoLEAST16 PRIoLEAST16 +#define SCNoLEAST32 PRIoLEAST32 +#define SCNoLEAST64 PRIoLEAST64 + +#define SCNoFAST8 PRIoFAST8 +#define SCNoFAST16 PRIoFAST16 +#define SCNoFAST32 PRIoFAST32 +#define SCNoFAST64 PRIoFAST64 + +#define SCNu8 PRIu8 +#define SCNu16 PRIu16 +#define SCNu32 PRIu32 +#define SCNu64 PRIu64 + +#define SCNuLEAST8 PRIuLEAST8 +#define SCNuLEAST16 PRIuLEAST16 +#define SCNuLEAST32 PRIuLEAST32 +#define SCNuLEAST64 PRIuLEAST64 + +#define SCNuFAST8 PRIuFAST8 +#define SCNuFAST16 PRIuFAST16 +#define SCNuFAST32 PRIuFAST32 +#define SCNuFAST64 PRIuFAST64 + +#define SCNx8 PRIx8 +#define SCNx16 PRIx16 +#define SCNx32 PRIx32 +#define SCNx64 PRIx64 + +#define SCNxLEAST8 PRIxLEAST8 +#define SCNxLEAST16 PRIxLEAST16 +#define SCNxLEAST32 PRIxLEAST32 +#define SCNxLEAST64 PRIxLEAST64 + +#define SCNxFAST8 PRIxFAST8 +#define SCNxFAST16 PRIxFAST16 +#define SCNxFAST32 PRIxFAST32 +#define SCNxFAST64 PRIxFAST64 + +#define SCNdMAX PRIdMAX +#define SCNiMAX PRIiMAX +#define SCNoMAX PRIoMAX +#define SCNuMAX PRIuMAX +#define SCNxMAX PRIxMAX + +#define SCNdPTR PRIdPTR +#define SCNiPTR PRIiPTR +#define SCNoPTR PRIoPTR +#define SCNuPTR PRIuPTR +#define SCNxPTR PRIxPTR + +#endif // _RELIBC_BITS_INTTYPES_H +""" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/inttypes/mod.rs b/src/header/inttypes/mod.rs new file mode 100644 index 0000000000..29eff295dd --- /dev/null +++ b/src/header/inttypes/mod.rs @@ -0,0 +1,103 @@ +//! `inttypes.h` implementation. +//! +//! See . + +use crate::{ + header::{ + ctype::{self, isspace}, + errno::{EINVAL, ERANGE}, + stdlib::{convert_hex, convert_integer, convert_octal, detect_base, is_positive}, + }, + platform::{ + self, + types::{c_char, c_int, c_long, intmax_t, uintmax_t, wchar_t}, + }, +}; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn imaxabs(i: intmax_t) -> intmax_t { + i.abs() +} + +/// See . +#[repr(C)] +pub struct imaxdiv_t { + quot: intmax_t, + rem: intmax_t, +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn imaxdiv(i: intmax_t, j: intmax_t) -> imaxdiv_t { + imaxdiv_t { + quot: i / j, + rem: i % j, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtoimax( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> intmax_t { + strto_impl!( + intmax_t, + false, + intmax_t::MAX, + intmax_t::MIN, + s, + endptr, + base + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtoumax( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> uintmax_t { + strto_impl!( + uintmax_t, + false, + uintmax_t::MAX, + uintmax_t::MIN, + s, + endptr, + base + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstoimax( + mut ptr: *const wchar_t, + end: *mut *mut wchar_t, + base: c_int, +) -> intmax_t { + skipws!(ptr); + let result = strto_impl!(intmax_t, ptr, base); + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstoumax( + mut ptr: *const wchar_t, + end: *mut *mut wchar_t, + base: c_int, +) -> uintmax_t { + skipws!(ptr); + let result = strtou_impl!(uintmax_t, ptr, base); + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} diff --git a/src/header/langinfo/cbindgen.toml b/src/header/langinfo/cbindgen.toml new file mode 100644 index 0000000000..993abcb8ed --- /dev/null +++ b/src/header/langinfo/cbindgen.toml @@ -0,0 +1,10 @@ +sys_includes = ["stddef.h", "stdint.h", "features.h"] +include_guard = "_RELIBC_LANGINFO_H" +after_includes = "#include // locale_t" +language = "C" +style = "tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/langinfo/mod.rs b/src/header/langinfo/mod.rs new file mode 100644 index 0000000000..8f1eb6f81d --- /dev/null +++ b/src/header/langinfo/mod.rs @@ -0,0 +1,225 @@ +//! `langinfo.h` implementation. +//! +//! See . + +// TODO : involve loading locale data. Currently, the implementation only supports the "C" locale. + +use crate::header::bits_locale_t::locale_t; +use core::ffi::c_char; + +/// See . +/// +/// POSIX type for items used with `nl_langinfo` +/// In practice, this is an integer index into the string table. +pub type nl_item = i32; + +// Static string table for langinfo constants +static STRING_TABLE: [&[u8]; 81] = [ + b"UTF-8\0", // CODESET + b"%a %b %e %H:%M:%S %Y\0", // D_T_FMT + b"%m/%d/%y\0", // D_FMT + b"%H:%M:%S\0", // T_FMT + b"%I:%M:%S %p\0", // T_FMT_AMPM + b"AM\0", // AM_STR + b"PM\0", // PM_STR + b"Sunday\0", // DAY_1 + b"Monday\0", // DAY_2 + b"Tuesday\0", // DAY_3 + b"Wednesday\0", // DAY_4 + b"Thursday\0", // DAY_5 + b"Friday\0", // DAY_6 + b"Saturday\0", // DAY_7 + b"Sun\0", // ABDAY_1 + b"Mon\0", // ABDAY_2 + b"Tue\0", // ABDAY_3 + b"Wed\0", // ABDAY_4 + b"Thu\0", // ABDAY_5 + b"Fri\0", // ABDAY_6 + b"Sat\0", // ABDAY_7 + b"January\0", // MON_1 + b"February\0", // MON_2 + b"March\0", // MON_3 + b"April\0", // MON_4 + b"May\0", // MON_5 + b"June\0", // MON_6 + b"July\0", // MON_7 + b"August\0", // MON_8 + b"September\0", // MON_9 + b"October\0", // MON_10 + b"November\0", // MON_11 + b"December\0", // MON_12 + b"Jan\0", // ABMON_1 + b"Feb\0", // ABMON_2 + b"Mar\0", // ABMON_3 + b"Apr\0", // ABMON_4 + b"May\0", // ABMON_5 + b"Jun\0", // ABMON_6 + b"Jul\0", // ABMON_7 + b"Aug\0", // ABMON_8 + b"Sep\0", // ABMON_9 + b"Oct\0", // ABMON_10 + b"Nov\0", // ABMON_11 + b"Dec\0", // ABMON_12 + b"\0", // ERA + b"\0", // ERA_D_FMT + b"\0", // ERA_D_T_FMT + b"\0", // ERA_T_FMT + b"\0", // ALT_DIGITS + b".\0", // RADIXCHAR + b"\0", // THOUSEP + b"^[yY]\0", // YESEXPR + b"^[nN]\0", // NOEXPR + b"yes\0", // YESSTR + b"no\0", // NOSTR + b".\0", // CRNCYSTR + // Some languages have alternative names for + // months. For the "C" locale, we just use one. + b"January\0", // ALTMON_1 + b"February\0", // ALTMON_2 + b"March\0", // ALTMON_3 + b"April\0", // ALTMON_4 + b"May\0", // ALTMON_5 + b"June\0", // ALTMON_6 + b"July\0", // ALTMON_7 + b"August\0", // ALTMON_8 + b"September\0", // ALTMON_9 + b"October\0", // ALTMON_10 + b"November\0", // ALTMON_11 + b"December\0", // ALTMON_12 + b"Jan\0", // ABALTMON_1 + b"Feb\0", // ABALTMON_2 + b"Mar\0", // ABALTMON_3 + b"Apr\0", // ABALTMON_4 + b"May\0", // ABALTMON_5 + b"Jun\0", // ABALTMON_6 + b"Jul\0", // ABALTMON_7 + b"Aug\0", // ABALTMON_8 + b"Sep\0", // ABALTMON_9 + b"Oct\0", // ABALTMON_10 + b"Nov\0", // ABALTMON_11 + b"Dec\0", // ABALTMON_12 +]; + +// Item constants +pub const CODESET: nl_item = 0; +pub const D_T_FMT: nl_item = 1; +pub const D_FMT: nl_item = 2; +pub const T_FMT: nl_item = 3; +pub const T_FMT_AMPM: nl_item = 4; +pub const AM_STR: nl_item = 5; +pub const PM_STR: nl_item = 6; + +pub const DAY_1: nl_item = 7; +pub const DAY_2: nl_item = 8; +pub const DAY_3: nl_item = 9; +pub const DAY_4: nl_item = 10; +pub const DAY_5: nl_item = 11; +pub const DAY_6: nl_item = 12; +pub const DAY_7: nl_item = 13; + +pub const ABDAY_1: nl_item = 14; +pub const ABDAY_2: nl_item = 15; +pub const ABDAY_3: nl_item = 16; +pub const ABDAY_4: nl_item = 17; +pub const ABDAY_5: nl_item = 18; +pub const ABDAY_6: nl_item = 19; +pub const ABDAY_7: nl_item = 20; + +pub const MON_1: nl_item = 21; +pub const MON_2: nl_item = 22; +pub const MON_3: nl_item = 23; +pub const MON_4: nl_item = 24; +pub const MON_5: nl_item = 25; +pub const MON_6: nl_item = 26; +pub const MON_7: nl_item = 27; +pub const MON_8: nl_item = 28; +pub const MON_9: nl_item = 29; +pub const MON_10: nl_item = 30; +pub const MON_11: nl_item = 31; +pub const MON_12: nl_item = 32; + +pub const ABMON_1: nl_item = 33; +pub const ABMON_2: nl_item = 34; +pub const ABMON_3: nl_item = 35; +pub const ABMON_4: nl_item = 36; +pub const ABMON_5: nl_item = 37; +pub const ABMON_6: nl_item = 38; +pub const ABMON_7: nl_item = 39; +pub const ABMON_8: nl_item = 40; +pub const ABMON_9: nl_item = 41; +pub const ABMON_10: nl_item = 42; +pub const ABMON_11: nl_item = 43; +pub const ABMON_12: nl_item = 44; + +pub const ERA: nl_item = 45; +pub const ERA_D_FMT: nl_item = 46; +pub const ERA_D_T_FMT: nl_item = 47; +pub const ERA_T_FMT: nl_item = 48; +pub const ALT_DIGITS: nl_item = 49; +pub const RADIXCHAR: nl_item = 50; +pub const THOUSEP: nl_item = 51; +pub const YESEXPR: nl_item = 52; +pub const NOEXPR: nl_item = 53; +pub const YESSTR: nl_item = 54; // Legacy +pub const NOSTR: nl_item = 55; // Legacy +pub const CRNCYSTR: nl_item = 56; + +pub const ALTMON_1: nl_item = 57; +pub const ALTMON_2: nl_item = 58; +pub const ALTMON_3: nl_item = 59; +pub const ALTMON_4: nl_item = 60; +pub const ALTMON_5: nl_item = 61; +pub const ALTMON_6: nl_item = 62; +pub const ALTMON_7: nl_item = 63; +pub const ALTMON_8: nl_item = 64; +pub const ALTMON_9: nl_item = 65; +pub const ALTMON_10: nl_item = 66; +pub const ALTMON_11: nl_item = 67; +pub const ALTMON_12: nl_item = 68; + +pub const ABALTMON_1: nl_item = 69; +pub const ABALTMON_2: nl_item = 70; +pub const ABALTMON_3: nl_item = 71; +pub const ABALTMON_4: nl_item = 72; +pub const ABALTMON_5: nl_item = 73; +pub const ABALTMON_6: nl_item = 74; +pub const ABALTMON_7: nl_item = 75; +pub const ABALTMON_8: nl_item = 76; +pub const ABALTMON_9: nl_item = 77; +pub const ABALTMON_10: nl_item = 78; +pub const ABALTMON_11: nl_item = 79; +pub const ABALTMON_12: nl_item = 80; + +/// See . +/// +/// Get a string from the langinfo table +/// +/// # Safety +/// - Caller must ensure `item` is a valid `nl_item` index. +/// - Returns a pointer to a null-terminated string, or an empty string if the item is invalid. +/// - Compatibility requires mutable pointer to be returned, but it should not be mutated! +#[unsafe(no_mangle)] +pub unsafe extern "C" fn nl_langinfo(item: nl_item) -> *mut c_char { + // Validate the item and perform the lookup + let ptr = if (item as usize) < STRING_TABLE.len() { + STRING_TABLE[item as usize].as_ptr().cast::() + } else { + // Return a pointer to an empty string if the item is invalid + c"".as_ptr().cast::() + }; + // Mutable pointer is required (unsafe!) + ptr.cast_mut() +} + +/// See . +/// +/// Get a string from the langinfo table +/// +/// # Safety +/// - Caller must ensure `item` is a valid `nl_item` index. +/// - Returns a pointer to a null-terminated string, or an empty string if the item is invalid. +/// - Compatibility requires mutable pointer to be returned, but it should not be mutated! +#[unsafe(no_mangle)] +pub unsafe extern "C" fn nl_langinfo_l(item: nl_item, _loc: locale_t) -> *mut c_char { + unsafe { nl_langinfo(item) } +} diff --git a/src/header/libgen/cbindgen.toml b/src/header/libgen/cbindgen.toml new file mode 100644 index 0000000000..0dfb9a44a7 --- /dev/null +++ b/src/header/libgen/cbindgen.toml @@ -0,0 +1,12 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/libgen.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +include_guard = "_RELIBC_LIBGEN_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/libgen/mod.rs b/src/header/libgen/mod.rs new file mode 100644 index 0000000000..a4e39c7584 --- /dev/null +++ b/src/header/libgen/mod.rs @@ -0,0 +1,55 @@ +//! `libgen.h` implementation. +//! +//! See . + +use crate::platform::types::c_char; + +use crate::header::string::strlen; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn basename(str: *mut c_char) -> *mut c_char { + if str.is_null() || unsafe { strlen(str) == 0 } { + return c".".as_ptr().cast_mut(); + } + let mut end = unsafe { strlen(str) as isize - 1 }; + while end >= 0 && unsafe { *str.offset(end) == b'/' as c_char } { + end -= 1; + } + if end == -1 { + return c"/".as_ptr().cast_mut(); + } + let mut begin = end; + while begin >= 0 && unsafe { *str.offset(begin) != b'/' as c_char } { + begin -= 1; + } + unsafe { + *str.offset(end + 1) = 0; + str.offset(begin + 1).cast::() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dirname(str: *mut c_char) -> *mut c_char { + if str.is_null() || unsafe { strlen(str) == 0 } { + return c".".as_ptr().cast_mut(); + } + let mut end = unsafe { strlen(str) as isize - 1 }; + while end > 0 && unsafe { *str.offset(end) == b'/' as c_char } { + end -= 1; + } + while end >= 0 && unsafe { *str.offset(end) != b'/' as c_char } { + end -= 1; + } + while end > 0 && unsafe { *str.offset(end) == b'/' as c_char } { + end -= 1; + } + if end == -1 { + return c".".as_ptr().cast_mut(); + } + unsafe { + *str.offset(end + 1) = 0; + } + str +} diff --git a/src/header/limits/cbindgen.toml b/src/header/limits/cbindgen.toml new file mode 100644 index 0000000000..bd3b52720e --- /dev/null +++ b/src/header/limits/cbindgen.toml @@ -0,0 +1,12 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/limits.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +include_guard = "_RELIBC_LIMITS_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/limits/mod.rs b/src/header/limits/mod.rs new file mode 100644 index 0000000000..45c37f06ca --- /dev/null +++ b/src/header/limits/mod.rs @@ -0,0 +1,131 @@ +//! `limits.h` implementation. +//! +//! See . + +use crate::platform::types::{ + c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, + c_ushort, ssize_t, +}; + +pub const NAME_MAX: usize = 255; +pub const PASS_MAX: usize = 128; +pub const PATH_MAX: usize = 4096; +pub const NGROUPS_MAX: usize = 65536; + +pub const CHAR_BIT: u32 = 8; +pub const WORD_BIT: u32 = 32; +#[cfg(target_pointer_width = "32")] +pub const LONG_BIT: u32 = 32; +#[cfg(target_pointer_width = "64")] +pub const LONG_BIT: u32 = 64; + +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +pub const CHAR_MAX: c_char = 0xFF; +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +pub const CHAR_MAX: c_char = 0x7F; +pub const SCHAR_MAX: c_schar = 0x7F; +pub const SHRT_MAX: c_short = 0x7FFF; +pub const INT_MAX: c_int = 0x7FFF_FFFF; +#[cfg(target_pointer_width = "32")] +pub const LONG_MAX: c_long = 0x7FFF_FFFF; +#[cfg(target_pointer_width = "64")] +pub const LONG_MAX: c_long = 0x7FFF_FFFF_FFFF_FFFF; +pub const LLONG_MAX: c_longlong = 0x7FFF_FFFF_FFFF_FFFF; +#[cfg(target_pointer_width = "32")] +pub const SSIZE_MAX: ssize_t = 0x7FFF_FFFF; +#[cfg(target_pointer_width = "64")] +pub const SSIZE_MAX: ssize_t = 0x7FFF_FFFF_FFFF_FFFF; +pub const UCHAR_MAX: c_uchar = 0xFF; +pub const USHRT_MAX: c_ushort = 0xFFFF; +pub const UINT_MAX: c_uint = 0xFFFFFFFF; + +#[cfg(target_pointer_width = "32")] +pub const ULONG_MAX: c_ulong = 0xFFFF_FFFF; +#[cfg(target_pointer_width = "64")] +pub const ULONG_MAX: c_ulong = 0xFFFF_FFFF_FFFF_FFFF; +pub const ULLONG_MAX: c_ulonglong = 0xFFFF_FFFF_FFFF_FFFF; + +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] +pub const CHAR_MIN: c_char = 0; +#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +pub const CHAR_MIN: c_char = -0x80; +pub const SCHAR_MIN: c_schar = -SCHAR_MAX - 1; +pub const SHRT_MIN: c_short = -SHRT_MAX - 1; +pub const INT_MIN: c_int = -INT_MAX - 1; +pub const LONG_MIN: c_long = -LONG_MAX - 1; +pub const LLONG_MIN: c_longlong = -LLONG_MAX - 1; + +// TODO: 4096 for most architectures as determined by a quick grep of musl's source; need a better +// way to determine it for other archs or to hard code a value. +#[cfg(target_os = "linux")] +pub const PAGE_SIZE: usize = 4096; + +// These POSIX symbols must have these values regardless of OS +pub const _POSIX_AIO_LISTIO_MAX: c_long = 2; +pub const _POSIX_AIO_MAX: c_long = 1; +pub const _POSIX_ARG_MAX: c_long = 4096; +pub const _POSIX_CHILD_MAX: c_long = 25; +pub const _POSIX_CLOCKRES_MIN: c_long = 20000000; +pub const _POSIX_DELAYTIMER_MAX: c_long = 32; +pub const _POSIX_HOST_NAME_MAX: c_long = 255; +pub const _POSIX_LINK_MAX: c_long = 8; +pub const _POSIX_LOGIN_NAME_MAX: c_long = 9; +pub const _POSIX_MAX_CANON: c_long = 255; +pub const _POSIX_MAX_INPUT: c_long = 255; +pub const _POSIX_NAME_MAX: c_long = 14; +pub const _POSIX_NGROUPS_MAX: c_long = 8; +pub const _POSIX_OPEN_MAX: c_long = 20; +pub const _POSIX_PATH_MAX: c_long = 256; +pub const _POSIX_PIPE_BUF: c_long = 512; +pub const _POSIX_RE_DUP_MAX: c_long = 255; +pub const _POSIX_RTSIG_MAX: c_long = 8; +pub const _POSIX_SEM_NSEMS_MAX: c_long = 256; +pub const _POSIX_SEM_VALUE_MAX: c_long = 32767; +pub const _POSIX_SIGQUEUE_MAX: c_long = 32; +pub const _POSIX_SSIZE_MAX: c_long = 32767; +pub const _POSIX_STREAM_MAX: c_long = 8; +pub const _POSIX_SYMLINK_MAX: c_long = 255; +pub const _POSIX_SYMLOOP_MAX: c_long = 8; +pub const _POSIX_THREAD_DESTRUCTOR_ITERATIONS: c_long = 4; +pub const _POSIX_THREAD_KEYS_MAX: c_long = 128; +pub const _POSIX_THREAD_THREADS_MAX: c_long = 64; +pub const _POSIX_TIMER_MAX: c_long = 32; +pub const _POSIX_TTY_NAME_MAX: c_long = 9; +pub const _POSIX_TZNAME_MAX: c_long = 6; + +pub const _POSIX2_BC_BASE_MAX: c_long = 99; +pub const _POSIX2_BC_DIM_MAX: c_long = 2048; +pub const _POSIX2_BC_SCALE_MAX: c_long = 99; +pub const _POSIX2_BC_STRING_MAX: c_long = 1000; +pub const _POSIX2_CHARCLASS_NAME_MAX: c_long = 14; +pub const _POSIX2_COLL_WEIGHTS_MAX: c_long = 2; +pub const _POSIX2_EXPR_NEST_MAX: c_long = 32; +pub const _POSIX2_LINE_MAX: c_long = 2048; +pub const _POSIX2_RE_DUP_MAX: c_long = 255; + +// These symbols must be at least the POSIX values, and sysconf will return the actual value between +// the posix minimum and this maximum. +pub const BC_BASE_MAX: c_long = _POSIX2_BC_BASE_MAX; +pub const BC_DIM_MAX: c_long = _POSIX2_BC_DIM_MAX; +pub const BC_SCALE_MAX: c_long = _POSIX2_BC_SCALE_MAX; +pub const BC_STRING_MAX: c_long = _POSIX2_BC_STRING_MAX; +pub const CHARCLASS_NAME_MAX: c_long = _POSIX2_CHARCLASS_NAME_MAX; +pub const COLL_WEIGHTS_MAX: c_long = _POSIX2_COLL_WEIGHTS_MAX; +pub const EXPR_NEST_MAX: c_long = _POSIX2_EXPR_NEST_MAX; +pub const LINE_MAX: c_long = _POSIX2_LINE_MAX; +pub const RE_DUP_MAX: c_long = _POSIX2_RE_DUP_MAX; +pub const HOST_NAME_MAX: c_long = _POSIX_HOST_NAME_MAX; +pub const LOGIN_NAME_MAX: c_long = 255; +pub const GETENTROPY_MAX: c_long = 256; +pub const LINK_MAX: c_long = 127; +pub const PIPE_BUF: c_long = 4096; +pub const FILESIZEBITS: c_long = 64; +pub const MAX_CANON: c_long = _POSIX_MAX_CANON; +pub const MAX_INPUT: c_long = _POSIX_MAX_INPUT; +pub const SYMLINK_MAX: c_long = _POSIX_SYMLINK_MAX; +pub const POSIX_ALLOC_SIZE_MIN: c_long = 4096; + +pub const PTHREAD_DESTRUCTOR_ITERATIONS: c_long = _POSIX_THREAD_DESTRUCTOR_ITERATIONS; +// TODO: What should this limit be? Both glibc and musl have it as 1024 +pub const PTHREAD_KEYS_MAX: c_long = 4096 * 32; +pub const PTHREAD_STACK_MIN: c_long = 65536; diff --git a/src/header/locale/cbindgen.toml b/src/header/locale/cbindgen.toml new file mode 100644 index 0000000000..f8e9cb6a83 --- /dev/null +++ b/src/header/locale/cbindgen.toml @@ -0,0 +1,16 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html +# +# Spec quotations relating to includes: +# - "The header shall define NULL (as described in ) and at least the following as macros:" +sys_includes = ["stddef.h"] +include_guard = "_RELIBC_LOCALE_H" +after_includes = """ +#include // for locale_t +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/locale/constants.rs b/src/header/locale/constants.rs new file mode 100644 index 0000000000..0652ba8dad --- /dev/null +++ b/src/header/locale/constants.rs @@ -0,0 +1,16 @@ +use crate::platform::types::c_int; + +pub const LC_COLLATE: c_int = 0; +pub const LC_CTYPE: c_int = 1; +pub const LC_MESSAGES: c_int = 2; +pub const LC_MONETARY: c_int = 3; +pub const LC_NUMERIC: c_int = 4; +pub const LC_TIME: c_int = 5; +pub const LC_ALL: c_int = 6; +pub const LC_COLLATE_MASK: c_int = 0x1; +pub const LC_CTYPE_MASK: c_int = 0x2; +pub const LC_MESSAGES_MASK: c_int = 0x4; +pub const LC_MONETARY_MASK: c_int = 0x8; +pub const LC_NUMERIC_MASK: c_int = 0x10; +pub const LC_TIME_MASK: c_int = 0x20; +pub const LC_ALL_MASK: c_int = 0b111111; diff --git a/src/header/locale/data.rs b/src/header/locale/data.rs new file mode 100644 index 0000000000..128f39a184 --- /dev/null +++ b/src/header/locale/data.rs @@ -0,0 +1,392 @@ +use core::str::FromStr; + +use alloc::{boxed::Box, ffi::CString, string::String, vec::Vec}; + +use super::constants::*; +use crate::platform::types::{c_char, c_int}; + +/// See . +/// this struct is not ordered like in the posix spec for readability +#[repr(C)] +#[derive(Clone)] +pub struct lconv { + pub decimal_point: *mut c_char, + pub thousands_sep: *mut c_char, + pub grouping: *mut c_char, + pub int_curr_symbol: *mut c_char, + pub currency_symbol: *mut c_char, + pub mon_decimal_point: *mut c_char, + pub mon_thousands_sep: *mut c_char, + pub mon_grouping: *mut c_char, + pub positive_sign: *mut c_char, + pub negative_sign: *mut c_char, + pub int_frac_digits: c_char, + pub frac_digits: c_char, + pub p_cs_precedes: c_char, + pub p_sep_by_space: c_char, + pub n_cs_precedes: c_char, + pub n_sep_by_space: c_char, + pub p_sign_posn: c_char, + pub n_sign_posn: c_char, + pub int_p_cs_precedes: c_char, + pub int_p_sep_by_space: c_char, + pub int_n_cs_precedes: c_char, + pub int_n_sep_by_space: c_char, + pub int_p_sign_posn: c_char, + pub int_n_sign_posn: c_char, +} +unsafe impl Sync for lconv {} + +/// "POSIX" or "C" default +pub(crate) const fn posix_lconv() -> lconv { + lconv { + // numeric, non-monetary + decimal_point: c".".as_ptr().cast_mut(), + thousands_sep: c"".as_ptr().cast_mut(), + grouping: c"".as_ptr().cast_mut(), + // local monetary + int_curr_symbol: c"".as_ptr().cast_mut(), + currency_symbol: c"".as_ptr().cast_mut(), + mon_decimal_point: c"".as_ptr().cast_mut(), + mon_thousands_sep: c"".as_ptr().cast_mut(), + mon_grouping: c"".as_ptr().cast_mut(), + positive_sign: c"".as_ptr().cast_mut(), + negative_sign: c"".as_ptr().cast_mut(), + // delimiters, unspecified + int_frac_digits: c_char::MAX, + frac_digits: c_char::MAX, + p_cs_precedes: c_char::MAX, + p_sep_by_space: c_char::MAX, + n_cs_precedes: c_char::MAX, + n_sep_by_space: c_char::MAX, + p_sign_posn: c_char::MAX, + n_sign_posn: c_char::MAX, + // international format + int_p_cs_precedes: c_char::MAX, + int_p_sep_by_space: c_char::MAX, + int_n_cs_precedes: c_char::MAX, + int_n_sep_by_space: c_char::MAX, + int_p_sign_posn: c_char::MAX, + int_n_sign_posn: c_char::MAX, + } +} + +#[repr(C)] +pub(crate) struct LocaleData { + pub name: CString, + pub lconv: lconv, + // Owned memory buffers + pub decimal_point: CString, + pub thousands_sep: CString, + pub grouping: Vec, + pub int_curr_symbol: CString, + pub currency_symbol: CString, + pub mon_decimal_point: CString, + pub mon_thousands_sep: CString, + pub mon_grouping: Vec, + pub positive_sign: CString, + pub negative_sign: CString, +} +unsafe impl Sync for LocaleData {} + +impl LocaleData { + pub fn new(name: CString, defs: PosixLocaleDef) -> Box { + let mut data = Box::new(LocaleData { + name, + decimal_point: Self::to_cstring(defs.decimal_point), + thousands_sep: Self::to_cstring(defs.thousands_sep), + grouping: Self::to_grouping_char(defs.grouping), + int_curr_symbol: Self::to_cstring(defs.int_curr_symbol), + currency_symbol: Self::to_cstring(defs.currency_symbol), + mon_decimal_point: Self::to_cstring(defs.mon_decimal_point), + mon_thousands_sep: Self::to_cstring(defs.mon_thousands_sep), + mon_grouping: Self::to_grouping_char(defs.mon_grouping), + positive_sign: Self::to_cstring(defs.positive_sign), + negative_sign: Self::to_cstring(defs.negative_sign), + lconv: unsafe { core::mem::zeroed() }, + }); + + data.lconv.int_frac_digits = defs.int_frac_digits.unwrap_or(c_char::MAX); + data.lconv.frac_digits = defs.frac_digits.unwrap_or(c_char::MAX); + data.lconv.p_cs_precedes = defs.p_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.p_sep_by_space = defs.p_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.n_cs_precedes = defs.n_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.n_sep_by_space = defs.n_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.p_sign_posn = defs.p_sign_posn.unwrap_or(c_char::MAX); + data.lconv.n_sign_posn = defs.n_sign_posn.unwrap_or(c_char::MAX); + data.lconv.int_p_cs_precedes = defs.int_p_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.int_p_sep_by_space = defs.int_p_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.int_n_cs_precedes = defs.int_n_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.int_n_sep_by_space = defs.int_n_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.int_p_sign_posn = defs.int_p_sign_posn.unwrap_or(c_char::MAX); + data.lconv.int_n_sign_posn = defs.int_n_sign_posn.unwrap_or(c_char::MAX); + + data.update_lconv_pointers(); + data + } + + pub fn posix() -> Box { + LocaleData::new(CString::from_str("C").unwrap(), PosixLocaleDef::default()) + } + + fn update_lconv_pointers(&mut self) { + self.lconv.decimal_point = self.decimal_point.as_ptr().cast_mut(); + self.lconv.thousands_sep = self.thousands_sep.as_ptr().cast_mut(); + self.lconv.grouping = self.grouping.as_ptr().cast_mut(); + self.lconv.int_curr_symbol = self.int_curr_symbol.as_ptr().cast_mut(); + self.lconv.currency_symbol = self.currency_symbol.as_ptr().cast_mut(); + self.lconv.mon_decimal_point = self.mon_decimal_point.as_ptr().cast_mut(); + self.lconv.mon_thousands_sep = self.mon_thousands_sep.as_ptr().cast_mut(); + self.lconv.mon_grouping = self.mon_grouping.as_ptr().cast_mut(); + self.lconv.positive_sign = self.positive_sign.as_ptr().cast_mut(); + self.lconv.negative_sign = self.negative_sign.as_ptr().cast_mut(); + } + + pub fn copy_category(&mut self, other: &Self, category: c_int) { + match category { + LC_NUMERIC => { + self.decimal_point = other.decimal_point.clone(); + self.thousands_sep = other.thousands_sep.clone(); + self.grouping = other.grouping.clone(); + self.lconv.frac_digits = other.lconv.frac_digits; + } + LC_MONETARY => { + self.int_curr_symbol = other.int_curr_symbol.clone(); + self.currency_symbol = other.currency_symbol.clone(); + self.mon_decimal_point = other.mon_decimal_point.clone(); + self.mon_thousands_sep = other.mon_thousands_sep.clone(); + self.mon_grouping = other.mon_grouping.clone(); + self.positive_sign = other.positive_sign.clone(); + self.negative_sign = other.negative_sign.clone(); + self.lconv.int_frac_digits = other.lconv.int_frac_digits; + self.lconv.p_cs_precedes = other.lconv.p_cs_precedes; + self.lconv.p_sep_by_space = other.lconv.p_sep_by_space; + self.lconv.n_cs_precedes = other.lconv.n_cs_precedes; + self.lconv.n_sep_by_space = other.lconv.n_sep_by_space; + self.lconv.p_sign_posn = other.lconv.p_sign_posn; + self.lconv.n_sign_posn = other.lconv.n_sign_posn; + self.lconv.int_p_cs_precedes = other.lconv.int_p_cs_precedes; + self.lconv.int_p_sep_by_space = other.lconv.int_p_sep_by_space; + self.lconv.int_n_cs_precedes = other.lconv.int_n_cs_precedes; + self.lconv.int_n_sep_by_space = other.lconv.int_n_sep_by_space; + self.lconv.int_p_sign_posn = other.lconv.int_p_sign_posn; + self.lconv.int_n_sign_posn = other.lconv.int_n_sign_posn; + } + LC_ALL => { + *self = other.clone(); + } + _ => {} + } + + self.update_lconv_pointers(); + } + + fn to_cstring(opt: Option) -> CString { + opt.unwrap_or_else(|| CString::new("").unwrap()) + } + + fn to_grouping_char(opt: Vec>) -> Vec { + let mut v: Vec = opt.into_iter().map(Self::to_char).collect(); + v.push(0); + v + } + + fn to_char(opt: Option) -> c_char { + opt.unwrap_or(c_char::MAX) + } +} + +impl Clone for LocaleData { + fn clone(&self) -> Self { + let mut data = Self { + name: self.name.clone(), + lconv: self.lconv.clone(), + decimal_point: self.decimal_point.clone(), + thousands_sep: self.thousands_sep.clone(), + grouping: self.grouping.clone(), + int_curr_symbol: self.int_curr_symbol.clone(), + currency_symbol: self.currency_symbol.clone(), + mon_decimal_point: self.mon_decimal_point.clone(), + mon_thousands_sep: self.mon_thousands_sep.clone(), + mon_grouping: self.mon_grouping.clone(), + positive_sign: self.positive_sign.clone(), + negative_sign: self.negative_sign.clone(), + }; + data.update_lconv_pointers(); + data + } +} + +#[derive(Clone)] +pub(crate) struct GlobalLocaleData { + // names per LC_* constant + pub names: [CString; 7], + pub data: LocaleData, +} + +impl GlobalLocaleData { + pub fn new() -> Box { + let data = LocaleData::posix(); + let names = [ + data.name.clone(), + data.name.clone(), + data.name.clone(), + data.name.clone(), + data.name.clone(), + data.name.clone(), + data.name.clone(), + ]; + let mut r = Box::new(GlobalLocaleData { data: *data, names }); + r.data.update_lconv_pointers(); + r + } + + pub fn get_name(&self, category: i32) -> Option<&CString> { + self.names.get(category as usize) + } + pub fn set_name(&mut self, category: i32, name: CString) -> Option<&CString> { + if self.names.get(category as usize).is_some() { + self.names[category as usize] = name; + self.names.get(category as usize) + } else { + None + } + } +} +unsafe impl Sync for GlobalLocaleData {} + +#[derive(Default)] +pub(crate) struct PosixLocaleDef { + pub decimal_point: Option, + pub thousands_sep: Option, + pub grouping: Vec>, + pub int_curr_symbol: Option, + pub currency_symbol: Option, + pub mon_decimal_point: Option, + pub mon_thousands_sep: Option, + pub mon_grouping: Vec>, + pub positive_sign: Option, + pub negative_sign: Option, + pub int_frac_digits: Option, + pub frac_digits: Option, + pub p_cs_precedes: Option, + pub p_sep_by_space: Option, + pub n_cs_precedes: Option, + pub n_sep_by_space: Option, + pub p_sign_posn: Option, + pub n_sign_posn: Option, + pub int_p_cs_precedes: Option, + pub int_p_sep_by_space: Option, + pub int_n_cs_precedes: Option, + pub int_n_sep_by_space: Option, + pub int_p_sign_posn: Option, + pub int_n_sign_posn: Option, +} +impl PosixLocaleDef { + //! See + pub fn parse(content: &str) -> Self { + let mut locale = PosixLocaleDef::default(); + + let mut lines = content.lines(); + loop { + let Some(line) = lines.next() else { + break; + }; + + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + + let mut parts = trimmed.split_ascii_whitespace(); + let Some(key) = parts.next() else { + continue; + }; + let mut val: String = String::new(); + loop { + let Some(chunk) = parts.next() else { + break; + }; + if !chunk.ends_with('\\') { + val.push_str(chunk); + break; + } + // multiline values + val.push_str(&chunk[0..chunk.len() - 1]); + let Some(next_line) = lines.next() else { + break; + }; + parts = next_line.split_ascii_whitespace(); + } + + if key.is_empty() || val.is_empty() { + continue; + } + + match key { + "decimal_point" => locale.decimal_point = Self::parse_str(&val), + "thousands_sep" => locale.thousands_sep = Self::parse_str(&val), + "int_curr_symbol" => locale.int_curr_symbol = Self::parse_str(&val), + "currency_symbol" => locale.currency_symbol = Self::parse_str(&val), + "mon_decimal_point" => locale.mon_decimal_point = Self::parse_str(&val), + "mon_thousands_sep" => locale.mon_thousands_sep = Self::parse_str(&val), + "positive_sign" => locale.positive_sign = Self::parse_str(&val), + "negative_sign" => locale.negative_sign = Self::parse_str(&val), + "grouping" => locale.grouping = Self::parse_int_group(&val), + "mon_grouping" => locale.mon_grouping = Self::parse_int_group(&val), + "int_frac_digits" => locale.int_frac_digits = Self::parse_int(&val), + "frac_digits" => locale.frac_digits = Self::parse_int(&val), + "p_cs_precedes" => locale.p_cs_precedes = Self::parse_int(&val), + "p_sep_by_space" => locale.p_sep_by_space = Self::parse_int(&val), + "n_cs_precedes" => locale.n_cs_precedes = Self::parse_int(&val), + "n_sep_by_space" => locale.n_sep_by_space = Self::parse_int(&val), + "p_sign_posn" => locale.p_sign_posn = Self::parse_int(&val), + "n_sign_posn" => locale.n_sign_posn = Self::parse_int(&val), + "int_p_cs_precedes" => locale.int_p_cs_precedes = Self::parse_int(&val), + "int_p_sep_by_space" => locale.int_p_sep_by_space = Self::parse_int(&val), + "int_n_cs_precedes" => locale.int_n_cs_precedes = Self::parse_int(&val), + "int_n_sep_by_space" => locale.int_n_sep_by_space = Self::parse_int(&val), + "int_p_sign_posn" => locale.int_p_sign_posn = Self::parse_int(&val), + "int_n_sign_posn" => locale.int_n_sign_posn = Self::parse_int(&val), + _ => {} + } + } + locale + } + + /// parse e.g. `3;3;0` -> [ 3,3,0 ], `-1` -> [ None ] + fn parse_int_group(val: &str) -> Vec> { + val.split(';').map(Self::parse_int).collect() + } + + /// parse e.g. `-1` -> None, `1` -> Some(1) + fn parse_int(val: &str) -> Option { + let r = val.trim().parse::().ok(); + // c_char is u8 on aarch64 and riscv64 so comparison is useless + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + if r.is_some_and(|i| i < 0) { + return None; + } + r + } + + /// parse e.g. `""` + fn parse_str(val: &str) -> Option { + let mut r = String::new(); + let mut v = val.chars(); + if v.next() != Some('"') { + return None; + } + while let Some(c) = v.next() { + if c == '"' { + if v.next().is_some() { + return None; + } + break; + } + // TODO: Parse <..> + r.push(c); + } + CString::new(r).ok() + } +} diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs new file mode 100644 index 0000000000..d8f9e54bd3 --- /dev/null +++ b/src/header/locale/mod.rs @@ -0,0 +1,182 @@ +//! `locale.h` implementation. +//! +//! See . + +use alloc::{boxed::Box, ffi::CString, string::String}; +use core::{ptr, str::FromStr}; + +use crate::{ + c_str::CStr, + error::{Errno, ResultExtPtrMut}, + fs::File, + header::{errno, fcntl}, + io::Read, + platform::types::{c_char, c_int}, +}; + +// Can't use &str because of the mutability +static mut C_LOCALE: [c_char; 2] = [b'C' as c_char, 0]; + +mod constants; +use constants::*; +mod data; +use data::*; + +use super::bits_locale_t::locale_t; +/// constant struct to "C" or "POSIX" locale +/// mutable because POSIX demands a mutable pointer +static mut POSIX_LOCALE: lconv = posix_lconv(); +pub const LC_GLOBAL_LOCALE: locale_t = -1isize as locale_t; +/// process-wide locale, used by setlocale() and localeconv() +static mut GLOBAL_LOCALE: *mut GlobalLocaleData = ptr::null_mut(); +/// thread-wide locale, used by uselocale() and localeconv() +#[thread_local] +pub(crate) static mut THREAD_LOCALE: *mut LocaleData = ptr::null_mut(); + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn localeconv() -> *mut lconv { + let current = unsafe { uselocale(ptr::null_mut()) }; + if current == LC_GLOBAL_LOCALE || current.is_null() { + if !unsafe { GLOBAL_LOCALE.is_null() } { + // safety: GLOBAL_LOCALE is never set to null again + unsafe { &raw mut (*GLOBAL_LOCALE).data.lconv } + } else { + &raw mut POSIX_LOCALE + } + } else { + let current = current.cast::(); + unsafe { &raw mut (*current).lconv } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setlocale(category: c_int, locale: *const c_char) -> *mut c_char { + if unsafe { GLOBAL_LOCALE.is_null() } { + let new_global = GlobalLocaleData::new(); + unsafe { GLOBAL_LOCALE = Box::into_raw(new_global) }; + }; + let Some(global) = (unsafe { GLOBAL_LOCALE.as_mut() }) else { + return ptr::null_mut(); + }; + + if locale.is_null() { + let Some(name) = global.get_name(category) else { + return ptr::null_mut(); + }; + return name.as_ptr().cast_mut(); + } + + let name = unsafe { CStr::from_ptr(locale).to_str().unwrap_or("C") }; + + let locale_file = if name.is_empty() || name == "C" || name == "POSIX" { + // TODO: name == "" should read from LANG env + Ok(LocaleData::posix()) + } else { + load_locale_file(name) + }; + + match locale_file { + Ok(loc_ptr) => { + global.data.copy_category(&loc_ptr, category); + let Some(name) = global.set_name(category, CString::from_str(name).unwrap()) else { + return ptr::null_mut(); + }; + name.as_ptr().cast_mut() + } + Err(_) => ptr::null_mut(), + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn uselocale(newloc: locale_t) -> locale_t { + let old_loc = if unsafe { THREAD_LOCALE.is_null() } { + LC_GLOBAL_LOCALE + } else { + (unsafe { THREAD_LOCALE }) as locale_t + }; + + if !newloc.is_null() { + unsafe { + THREAD_LOCALE = if newloc == LC_GLOBAL_LOCALE { + ptr::null_mut() + } else { + newloc.cast::() + } + }; + } + + old_loc +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t { + let name = unsafe { CStr::from_ptr(locale) } + .to_string_lossy() + .into_owned(); + let name = name.as_str(); + let mut new_locale = if name.is_empty() || name == "C" || name == "POSIX" { + // TODO: name == "" should read from LANG env + Ok(LocaleData::posix()) + } else { + load_locale_file(name) + }; + if base != LC_GLOBAL_LOCALE { + // borrowing here + let base = base.cast_const().cast::(); + if let Ok(new_locale) = new_locale.as_mut() + && let Some(base) = unsafe { base.as_ref() } + { + // copy old values if not containing the mask + if (mask & LC_NUMERIC_MASK) == 0 { + new_locale.copy_category(base, LC_NUMERIC); + } + if (mask & LC_MONETARY_MASK) == 0 { + new_locale.copy_category(base, LC_MONETARY); + } + // TODO: other categories? + } + } + new_locale.or_errno_null_mut().cast() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn freelocale(loc: locale_t) { + if !loc.is_null() && loc != LC_GLOBAL_LOCALE { + drop(unsafe { Box::from_raw(loc.cast::()) }); + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn duplocale(loc: locale_t) -> locale_t { + if loc.is_null() { + // TODO: errno? + loc + } else if loc == LC_GLOBAL_LOCALE { + Box::into_raw(LocaleData::posix()) as locale_t + } else { + // borrowing here + let loc = loc.cast_const().cast::(); + Box::into_raw(unsafe { Box::from((*loc).clone()) }) as locale_t + } +} + +pub(crate) fn load_locale_file(name: &str) -> Result, Errno> { + let mut path = String::from("/usr/share/i18n/locales/"); + path.push_str(name); + + let path_c = CString::new(path).map_err(|_| Errno(errno::EINVAL))?; + let mut content = String::new(); + + let mut file = File::open(path_c.as_c_str().into(), fcntl::O_RDONLY)?; + file.read_to_string(&mut content) + .map_err(|_| Errno(errno::EIO))?; + + let toml = PosixLocaleDef::parse(&content); + Ok(LocaleData::new(CString::from_str(name).unwrap(), toml)) +} diff --git a/src/header/malloc/cbindgen.toml b/src/header/malloc/cbindgen.toml new file mode 100644 index 0000000000..18db86526d --- /dev/null +++ b/src/header/malloc/cbindgen.toml @@ -0,0 +1,30 @@ +sys_includes = ["features.h", "stddef.h"] +include_guard = "_RELIBC_MALLOC_H" +trailer = """ +#ifndef _RELIBC_MALLOC_EXTRA_H +#define _RELIBC_MALLOC_EXTRA_H + +#ifdef __cplusplus +extern "C" { +#endif + +void *calloc(size_t nelem, size_t elsize); +void free(void *ptr); +void *malloc(size_t size); +void *memalign(size_t alignment, size_t size); +void *realloc(void *ptr, size_t size); +void *valloc(size_t size); + +#ifdef __cplusplus +} +#endif + +#endif +""" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/malloc/mod.rs b/src/header/malloc/mod.rs new file mode 100644 index 0000000000..2b73052b42 --- /dev/null +++ b/src/header/malloc/mod.rs @@ -0,0 +1,48 @@ +//! `malloc.h` implementation. +//! +//! Non-POSIX, see . + +use crate::{ + header::errno::ENOMEM, + platform::{ + self, Pal, Sys, + types::{c_void, size_t}, + }, +}; +use core::ptr; + +/// See . +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pvalloc(size: size_t) -> *mut c_void { + let page_size = Sys::getpagesize(); + // Find the smallest multiple of the page size in which the requested size + // will fit. The result of the division will always be less than or equal + // to size_t::MAX - 1, and the num_pages calculation will therefore never + // overflow. + let num_pages = if size != 0 { + (size - 1) / page_size + 1 + } else { + 0 + }; + + match num_pages.checked_mul(page_size) { + Some(alloc_size) => { + let ptr = unsafe { platform::alloc_align(alloc_size, page_size) }; + if ptr.is_null() { + platform::ERRNO.set(ENOMEM); + } + ptr + } + None => { + platform::ERRNO.set(ENOMEM); + ptr::null_mut() + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn malloc_usable_size(ptr: *mut c_void) -> size_t { + unsafe { platform::alloc_usable_size(ptr) } +} diff --git a/src/header/math/cbindgen.toml b/src/header/math/cbindgen.toml new file mode 100644 index 0000000000..97345d7770 --- /dev/null +++ b/src/header/math/cbindgen.toml @@ -0,0 +1,57 @@ +# Note: changing this file requires `make clean`. +sys_includes = [] +include_guard = "_RELIBC_MATH_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true +# constants from openlibm +after_includes = """ + +#define HUGE_VALF (float)HUGE_VAL +#define HUGE_VALL (long double)HUGE_VAL +#define INFINITY HUGE_VALF +#define NAN (__nan.__uf) + +#define FP_ILOGB0 (-INT_MAX) +#define FP_ILOGBNAN INT_MAX + +#define MAXFLOAT 3.40282346638528859812e+38F + +#define M_E 2.7182818284590452354 /* e */ +#define M_LOG2E 1.4426950408889634074 /* log_2 e */ +#define M_LOG10E 0.43429448190325182765 /* log_10 e */ +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#define M_PI 3.14159265358979323846 /* pi */ +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#define M_PI_4 0.78539816339744830962 /* pi/4 */ +#define M_1_PI 0.31830988618379067154 /* 1/pi */ +#define M_2_PI 0.63661977236758134308 /* 2/pi */ +#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ + +#define FP_NAN 0 +#define FP_INFINITE 1 +#define FP_ZERO 2 +#define FP_SUBNORMAL 3 +#define FP_NORMAL 4 +#define fpclassify(x) __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x) + +#define signbit(x) __builtin_signbit(x) +#define isnan(x) __builtin_isnan(x) +#define isinf(x) __builtin_isinf_sign(x) +#define isfinite(x) __builtin_isfinite(x) +#define isnormal(x) __builtin_isnormal(x) +#define isgreater(x, y) __builtin_isgreater((x), (y)) +#define isgreaterequal(x, y) __builtin_isgreaterequal((x), (y)) +#define isless(x, y) __builtin_isless((x), (y)) +#define islessequal(x, y) __builtin_islessequal((x), (y)) +#define islessgreater(x, y) __builtin_islessgreater((x), (y)) +#define isunordered(x, y) __builtin_isunordered((x), (y)) + +""" + +[enum] +prefix_with_name = true diff --git a/src/header/math/mod.rs b/src/header/math/mod.rs new file mode 100644 index 0000000000..b529bd2633 --- /dev/null +++ b/src/header/math/mod.rs @@ -0,0 +1,867 @@ +//! `math.h` implementation. +//! +//! See . + +use crate::platform::types::{c_double, c_float, c_int, c_long, c_longlong}; + +// TODO move constants from cbindgen (openlibm) +// TODO all is* function is defined as "real-floating", so it can both f32 and f64 +// TODO cover edge cases (EDOM and ERANGE) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn acos(x: c_double) -> c_double { + libm::acos(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn acosf(x: c_float) -> c_float { + libm::acosf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn acosh(x: c_double) -> c_double { + libm::acosh(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn acoshf(x: c_float) -> c_float { + libm::acoshf(x) +} + +// TODO acoshl, acosl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asin(x: c_double) -> c_double { + libm::asin(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asinf(x: c_float) -> c_float { + libm::asinf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asinh(x: c_double) -> c_double { + libm::asinh(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asinhf(x: c_float) -> c_float { + libm::asinhf(x) +} + +// TODO asinhl, asinl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atan(x: c_double) -> c_double { + libm::atan(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atan2(y: c_double, x: c_double) -> c_double { + libm::atan2(y, x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atan2f(y: c_float, x: c_float) -> c_float { + libm::atan2f(y, x) +} + +// TODO atan2l (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atanf(x: c_float) -> c_float { + libm::atanf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atanh(x: c_double) -> c_double { + libm::atanh(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atanhf(x: c_float) -> c_float { + libm::atanhf(x) +} + +// TODO atanhl, atanl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbrt(x: c_double) -> c_double { + libm::cbrt(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbrtf(x: c_float) -> c_float { + libm::cbrtf(x) +} + +// TODO cbrtl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ceil(x: c_double) -> c_double { + libm::ceil(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ceilf(x: c_float) -> c_float { + libm::ceilf(x) +} + +// TODO ceill (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn copysign(x: c_double, y: c_double) -> c_double { + libm::copysign(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn copysignf(x: c_float, y: c_float) -> c_float { + libm::copysignf(x, y) +} + +// TODO copysignl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cos(x: c_double) -> c_double { + libm::cos(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cosf(x: c_float) -> c_float { + libm::cosf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cosh(x: c_double) -> c_double { + libm::cosh(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn coshf(x: c_float) -> c_float { + libm::coshf(x) +} + +// TODO coshl, cosl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn erf(x: c_double) -> c_double { + libm::erf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn erfc(x: c_double) -> c_double { + libm::erfc(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn erfcf(x: c_float) -> c_float { + libm::erfcf(x) +} + +// TODO erfcl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn erff(x: c_float) -> c_float { + libm::erff(x) +} + +// TODO erfl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn exp(x: c_double) -> c_double { + libm::exp(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn exp2(x: c_double) -> c_double { + libm::exp2(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn exp2f(x: c_float) -> c_float { + libm::exp2f(x) +} + +// TODO exp2l (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn expf(x: c_float) -> c_float { + libm::expf(x) +} + +// TODO expl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn expm1(x: c_double) -> c_double { + libm::expm1(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn expm1f(x: c_float) -> c_float { + libm::expm1f(x) +} + +// TODO expm1l (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fabs(x: c_double) -> c_double { + libm::fabs(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fabsf(x: c_float) -> c_float { + libm::fabsf(x) +} + +// TODO fabsl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fdim(x: c_double, y: c_double) -> c_double { + libm::fdim(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fdimf(x: c_float, y: c_float) -> c_float { + libm::fdimf(x, y) +} + +// TODO fdiml (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn floor(x: c_double) -> c_double { + libm::floor(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn floorf(x: c_float) -> c_float { + libm::floorf(x) +} + +// TODO floorl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fma(x: c_double, y: c_double, z: c_double) -> c_double { + libm::fma(x, y, z) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmaf(x: c_float, y: c_float, z: c_float) -> c_float { + libm::fmaf(x, y, z) +} + +// TODO fmal (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmax(x: c_double, y: c_double) -> c_double { + libm::fmax(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmaxf(x: c_float, y: c_float) -> c_float { + libm::fmaxf(x, y) +} + +// TODO fmaxl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmin(x: c_double, y: c_double) -> c_double { + libm::fmin(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fminf(x: c_float, y: c_float) -> c_float { + libm::fminf(x, y) +} + +// TODO fminl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmod(x: c_double, y: c_double) -> c_double { + libm::fmod(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmodf(x: c_float, y: c_float) -> c_float { + libm::fmodf(x, y) +} + +// TODO fmodl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn frexp(num: c_double, exp: *mut c_int) -> c_double { + let (number, exponent) = libm::frexp(num); + unsafe { + *exp = exponent; + } + number +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn frexpf(num: c_float, exp: *mut c_int) -> c_float { + let (number, exponent) = libm::frexpf(num); + unsafe { + *exp = exponent; + } + number +} + +// TODO frexpl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn hypot(x: c_double, y: c_double) -> c_double { + libm::hypot(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn hypotf(x: c_float, y: c_float) -> c_float { + libm::hypotf(x, y) +} + +// TODO hypotl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ilogb(x: c_double) -> c_int { + libm::ilogb(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ilogbf(x: c_float) -> c_int { + libm::ilogbf(x) +} + +// TODO ilogbl (long double) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn isfinite(x: c_double) -> c_int { + if x.is_finite() { 1 } else { 0 } +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn isinf(x: c_double) -> c_int { + match x { + f64::INFINITY => 1, + f64::NEG_INFINITY => -1, + _ => 0, + } +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn isnan(x: c_double) -> c_int { + if x.is_nan() { 1 } else { 0 } +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn isnormal(x: c_double) -> c_int { + if x.is_normal() { 1 } else { 0 } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn j0(x: c_double) -> c_double { + libm::j0(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn j1(x: c_double) -> c_double { + libm::j1(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn jn(n: c_int, x: c_double) -> c_double { + libm::jn(n, x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ldexp(x: c_double, exp: c_int) -> c_double { + libm::ldexp(x, exp) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ldexpf(x: c_float, exp: c_int) -> c_float { + libm::ldexpf(x, exp) +} + +// TODO ldexpl (long double) + +#[unsafe(no_mangle)] +static mut signgam: c_int = 0; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lgamma(x: c_double) -> c_double { + let (a, b) = libm::lgamma_r(x); + unsafe { signgam = b }; + a +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lgammaf(x: c_float) -> c_float { + let (a, b) = libm::lgammaf_r(x); + unsafe { signgam = b }; + a +} + +// TODO lgammal (long double) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn llrint(x: c_double) -> c_longlong { + libm::rint(x) as _ +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn llrintf(x: c_float) -> c_longlong { + libm::rintf(x) as _ +} + +// TODO llrintl (long double to c_longlong) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn llround(x: c_double) -> c_longlong { + libm::round(x) as _ +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn llroundf(x: c_float) -> c_longlong { + libm::roundf(x) as _ +} + +// TODO llroundl (long double to c_longlong) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log(x: c_double) -> c_double { + libm::log(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log10(x: c_double) -> c_double { + libm::log10(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log10f(x: c_float) -> c_float { + libm::log10f(x) +} + +// TODO log10l (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log1p(x: c_double) -> c_double { + libm::log1p(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log1pf(x: c_float) -> c_float { + libm::log1pf(x) +} + +// TODO log1pl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log2(x: c_double) -> c_double { + libm::log2(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn log2f(x: c_float) -> c_float { + libm::log2f(x) +} + +// TODO log2l (long double) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn logb(x: c_double) -> c_double { + // TODO: errno + libm::ilogb(x) as _ +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn logbf(x: c_float) -> c_float { + // TODO: errno + libm::ilogbf(x) as _ +} + +// TODO logbl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn logf(x: c_float) -> c_float { + libm::logf(x) +} + +// TODO logl (long double) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn lrint(x: c_double) -> c_long { + // TODO: errno + libm::rint(x) as _ +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn lrintf(x: c_float) -> c_long { + // TODO: errno + libm::rintf(x) as _ +} + +// TODO lrintl (long double) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn lround(x: c_double) -> c_long { + libm::round(x) as _ +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn lroundf(x: c_float) -> c_long { + libm::roundf(x) as _ +} + +// TODO lroundl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn modf(x: c_double, iptr: *mut c_double) -> c_double { + let (integral, fractional) = libm::modf(x); + unsafe { + *iptr = integral; + } + fractional +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn modff(value: c_float, iptr: *mut c_float) -> c_float { + let (integral, fractional) = libm::modff(value); + unsafe { + *iptr = integral; + } + fractional +} + +// TODO modfl (long double) + +// TODO do we want to support quiet NaN or just return 0? +// TODO nan (c_double) +// TODO nanf (c_float) +// TODO nanl (long double) + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn nearbyint(x: c_double) -> c_double { + libm::rint(x) +} + +/// See . +#[unsafe(no_mangle)] +unsafe extern "C" fn nearbyintf(x: c_float) -> c_float { + libm::rintf(x) +} + +// TODO nearbyintl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn nextafter(x: c_double, y: c_double) -> c_double { + libm::nextafter(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn nextafterf(x: c_float, y: c_float) -> c_float { + libm::nextafterf(x, y) +} + +// TODO nextafterl (long double) + +// TODO nexttoward (c_double) +// TODO nexttowardf (c_float) +// TODO nexttowardl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pow(x: c_double, y: c_double) -> c_double { + libm::pow(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn powf(x: c_float, y: c_float) -> c_float { + libm::powf(x, y) +} + +// TODO powl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn remainder(x: c_double, y: c_double) -> c_double { + libm::remainder(x, y) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn remainderf(x: c_float, y: c_float) -> c_float { + libm::remainderf(x, y) +} + +// TODO remainderl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn remquo(x: c_double, y: c_double, quo: *mut c_int) -> c_double { + let (remainder, quotient) = libm::remquo(x, y); + unsafe { + *quo = quotient; + } + remainder +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn remquof(x: c_float, y: c_float, quo: *mut c_int) -> c_float { + let (remainder, quotient) = libm::remquof(x, y); + unsafe { + *quo = quotient; + } + remainder +} + +// TODO remquol (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rint(x: c_double) -> c_double { + libm::rint(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rintf(x: c_float) -> c_float { + libm::rintf(x) +} + +// TODO rintl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn round(x: c_double) -> c_double { + libm::round(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn roundf(x: c_float) -> c_float { + libm::roundf(x) +} + +// TODO roundl (long double) + +// TODO scalbln (c_double, c_long) +// TODO scalblnf (c_float, c_long) +// TODO scalblnl (long double, c_long) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn scalbn(x: c_double, n: c_int) -> c_double { + libm::scalbn(x, n) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn scalbnf(x: c_float, n: c_int) -> c_float { + libm::scalbnf(x, n) +} + +// TODO scalbnl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sin(x: c_double) -> c_double { + libm::sin(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sinf(x: c_float) -> c_float { + libm::sinf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sinh(x: c_double) -> c_double { + libm::sinh(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sinhf(x: c_float) -> c_float { + libm::sinhf(x) +} + +// TODO sinhl (long double) +// TODO sinl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sqrt(x: c_double) -> c_double { + libm::sqrt(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sqrtf(x: c_float) -> c_float { + libm::sqrtf(x) +} + +// TODO sqrtl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tan(x: c_double) -> c_double { + libm::tan(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tanf(x: c_float) -> c_float { + libm::tanf(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tanh(x: c_double) -> c_double { + libm::tanh(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tanhf(x: c_float) -> c_float { + libm::tanhf(x) +} + +// TODO tanhl (long double) +// TODO tanl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tgamma(x: c_double) -> c_double { + libm::tgamma(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tgammaf(x: c_float) -> c_float { + libm::tgammaf(x) +} + +// TODO tgammal (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn trunc(x: c_double) -> c_double { + libm::trunc(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn truncf(x: c_float) -> c_float { + libm::truncf(x) +} + +// TODO truncl (long double) + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn y0(x: c_double) -> c_double { + libm::y0(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn y1(x: c_double) -> c_double { + libm::y1(x) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn yn(n: c_int, x: c_double) -> c_double { + libm::yn(n, x) +} diff --git a/src/header/mod.rs b/src/header/mod.rs new file mode 100644 index 0000000000..3eecb0057e --- /dev/null +++ b/src/header/mod.rs @@ -0,0 +1,149 @@ +//! POSIX header implementations. + +pub mod _aio; +pub mod _fenv; +pub mod arpa_inet; +pub mod assert; +pub mod bits_arpainet; +pub mod bits_iovec; +#[path = "bits_locale-t/mod.rs"] +pub mod bits_locale_t; +pub mod bits_pthread; +#[path = "bits_safamily-t/mod.rs"] +pub mod bits_safamily_t; +#[path = "bits_sigset-t/mod.rs"] +pub mod bits_sigset_t; +#[path = "bits_socklen-t/mod.rs"] +pub mod bits_socklen_t; +pub mod bits_timespec; +// complex.h implemented in C +pub mod cpio; +pub mod crypt; +pub mod ctype; +// TODO: curses.h (deprecated) +// TODO: devctl.h +pub mod dirent; +#[path = "dl-tls/mod.rs"] +pub mod dl_tls; +pub mod dlfcn; +pub mod elf; +pub mod endian; +pub mod err; +pub mod errno; +pub mod fcntl; +pub mod float; +// TODO: fmtmsg.h +pub mod fnmatch; +// TODO: ftw.h +pub mod getopt; +pub mod glob; +pub mod grp; +// TODO: iconv.h +pub mod ifaddrs; +pub mod inttypes; +// iso646.h implemented in C +pub mod langinfo; +// TODO: libintl.h +pub mod libgen; +pub mod limits; +pub mod locale; +pub mod malloc; +// TODO unfinished, unguard feature when ready +#[cfg(feature = "math_libm")] +pub mod math; +pub mod monetary; +// TODO: mqueue.h +// TODO: ndbm.h +pub mod net_if; +pub mod netdb; +pub mod netinet_in; +pub mod netinet_ip; +pub mod netinet_tcp; +// TODO: nl_types.h +// TODO: Remove C header paths.h when cbindgen can export C/Rust strs +// pub mod paths; +pub mod poll; +pub mod pthread; +pub mod pty; +pub mod pwd; +// TODO: re_comp.h (deprecated) +pub mod regex; +// TODO: regexp.h (deprecated) +pub mod sched; +// TODO: search.h +pub mod semaphore; +pub mod setjmp; +pub mod sgtty; +pub mod shadow; +pub mod signal; +// TODO: spawn.h +// TODO: stdalign.h (likely C implementation) +// stdarg.h implemented in C +// stdatomic.h implemented in C +// stdbool.h implemented in C +// stddef.h implemented in C +// stdint.h implemented in C +pub mod stdio; +pub mod stdlib; +// TODO: stdnoreturn.h (likely C implementation) +pub mod string; +pub mod strings; +// TODO: stropts.h (deprecated) +pub mod sys_auxv; +pub mod sys_epoll; +pub mod sys_eventfd; +pub mod sys_file; +pub mod sys_ioctl; +// TODO: sys/ipc.h +pub mod sys_mman; +// TODO: sys/msg.h +pub mod sys_ptrace; +pub mod sys_resource; +pub mod sys_select; +// TODO: sys/sem.h +// TODO: sys/shm.h +pub mod sys_socket; +pub mod sys_stat; +pub mod sys_statvfs; +#[allow(non_upper_case_globals)] +pub mod sys_syscall; +pub mod sys_time; +#[deprecated] +pub mod sys_timeb; +//pub mod sys_times; +pub mod arch_aarch64_user; +pub mod arch_riscv64_user; +pub mod arch_x64_user; +pub mod sys_signalfd; +#[cfg(not(target_arch = "x86"))] // TODO: x86 +pub mod sys_procfs; +pub mod sys_random; +pub mod sys_timerfd; +pub mod sys_syslog; +pub mod sys_types; +#[allow(non_camel_case_types)] +pub mod sys_types_internal; +pub mod sys_uio; +pub mod sys_un; +pub mod sys_utsname; +pub mod sys_wait; +pub mod tar; +// TODO: term.h (deprecated) +pub mod termios; +// TODO: tgmath.h (likely C implementation) +// TODO: threads.h +pub mod time; +// TODO: uchar.h +// TODO: ucontext.h (deprecated) +// TODO: ulimit.h (deprecated) +// TODO: unctrl.h (deprecated) +pub mod unistd; +#[deprecated] +pub mod utime; +pub mod utmp; +// TODO: utmpx.h +// TODO: varargs.h (deprecated) +pub mod wchar; +pub mod wctype; +// TODO: wordexp.h +// TODO: xti.h (deprecated) diff --git a/src/header/monetary/cbindgen.toml b/src/header/monetary/cbindgen.toml new file mode 100644 index 0000000000..003cbd5026 --- /dev/null +++ b/src/header/monetary/cbindgen.toml @@ -0,0 +1,21 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/monetary.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the locale_t type as described in ." +# - "The header shall define the size_t type as described in ." +# - "The header shall define the ssize_t type as described in ." +# +# sys/types.h brings in stddef.h +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_MONETARY_H" +after_includes = """ +#include // for locale_t from locale.h +""" +language = "C" +style = "tag" +no_includes = true +cpp_compat = true +usize_is_size_t = true + +[enum] +prefix_with_name = true diff --git a/src/header/monetary/mod.rs b/src/header/monetary/mod.rs new file mode 100644 index 0000000000..973b1830a3 --- /dev/null +++ b/src/header/monetary/mod.rs @@ -0,0 +1,102 @@ +//! `monetary.h` implementation. +//! +//! See . + +// We should provide a strfmon() implementation that formats a monetary value, +// according to the current locale (TODO). + +use alloc::string::{String, ToString}; +use core::str; + +use libm::{fabs, pow, round, trunc}; + +extern crate alloc; + +mod strfmon; +#[repr(C)] +struct LocaleMonetaryInfo { + int_curr_symbol: &'static str, + currency_symbol: &'static str, + mon_decimal_point: &'static str, + mon_thousands_sep: &'static str, + mon_grouping: &'static [u8], + positive_sign: &'static str, + negative_sign: &'static str, + int_frac_digits: u8, + frac_digits: u8, + p_cs_precedes: bool, + p_sep_by_space: bool, + p_sign_posn: u8, + n_cs_precedes: bool, + n_sep_by_space: bool, + n_sign_posn: u8, +} + +const DEFAULT_MONETARY: LocaleMonetaryInfo = LocaleMonetaryInfo { + int_curr_symbol: "USD ", + currency_symbol: "$", + mon_decimal_point: ".", + mon_thousands_sep: ",", + mon_grouping: &[3, 3], + positive_sign: "", + negative_sign: "-", + int_frac_digits: 2, + frac_digits: 2, + p_cs_precedes: true, + p_sep_by_space: false, + p_sign_posn: 1, + n_cs_precedes: true, + n_sep_by_space: false, + n_sign_posn: 1, +}; + +#[derive(Default)] +struct FormatFlags { + left_justify: bool, + force_sign: bool, + space_sign: bool, + use_parens: bool, + suppress_symbol: bool, + field_width: Option, + left_precision: Option, + right_precision: Option, + international: bool, +} + +/// Formats a monetary value according to the current locale. +fn apply_grouping(int_str: &str, monetary: &LocaleMonetaryInfo) -> String { + let mut grouped = String::with_capacity(int_str.len() * 2); + let mut group_idx = 0; + let current_grouping = &monetary.mon_grouping; + let separator = monetary.mon_thousands_sep; + + for (count, c) in int_str.chars().enumerate() { + if count > 0 { + let current_group = current_grouping[group_idx.min(current_grouping.len() - 1)]; + if current_group > 0 && (count as u8).is_multiple_of(current_group) { + grouped.push_str(separator); + if group_idx + 1 < current_grouping.len() { + group_idx += 1; + } + } + } + grouped.push(c); + } + + grouped +} + +/// Safe handling of large monetary values. Returns None if the value is too large to format +fn format_value_parts(value: f64, frac_digits: usize) -> Option<(String, i64)> { + let abs_value = fabs(value); + if abs_value > (i64::MAX as f64) { + // Check if the value is too large to format + return None; + } + + let int_part = trunc(abs_value) as i64; + let scale = pow(10.0, frac_digits as f64); + let frac_part = round((abs_value - int_part as f64) * scale) as i64; + + Some((int_part.to_string(), frac_part)) +} diff --git a/src/header/monetary/strfmon.rs b/src/header/monetary/strfmon.rs new file mode 100644 index 0000000000..c0f380604e --- /dev/null +++ b/src/header/monetary/strfmon.rs @@ -0,0 +1,326 @@ +use crate::platform::types::{c_char, size_t, ssize_t}; + +use super::{DEFAULT_MONETARY, FormatFlags, LocaleMonetaryInfo, apply_grouping}; +use alloc::string::{String, ToString}; +use core::{ffi::CStr, slice}; +use libm::{fabs, pow, round, trunc}; + +/// See . +/// +/// The `strfmon()` function formats a monetary value according to the format string `format` +/// and writes the result to the character array `s` of size `maxsize`. +/// The format string can contain plain characters and format specifiers. +/// +/// Returns: +/// - The number of characters written (excluding the null terminator), or -1 if +/// an error occurs (e.g., invalid input, buffer overflow) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strfmon( + s: *mut c_char, // Output buffer + maxsize: size_t, // Maximum size of the buffer + format: *const c_char, // Format string + mut args: ... // Variadic arguments for monetary values +) -> ssize_t { + // Validate input pointers and buffer size + if s.is_null() || format.is_null() || maxsize == 0 { + return -1; // Invalid input + } + + // Convert the format string from C to string + let format_str = match unsafe { CStr::from_ptr(format) }.to_str() { + Ok(s) => s, + Err(_) => return -1, // Invalid format string + }; + + // Create a mutable slice for the output buffer + let buffer = unsafe { slice::from_raw_parts_mut(s.cast::(), maxsize) }; + let mut pos = 0; + let mut format_chars = format_str.chars().peekable(); + + // Parse the format string + while let Some(c) = format_chars.next() { + // Handle plain characters (non-`%`) + if c != '%' { + if pos >= buffer.len() { + return -1; // Buffer overflow + } + buffer[pos] = c as u8; // Write the character to the buffer + pos += 1; + continue; + } + + // Handle `%%` escape sequence + if format_chars.peek() == Some(&'%') { + if pos >= buffer.len() { + return -1; // Buffer overflow + } + buffer[pos] = b'%'; // Write a literal `%` + pos += 1; + format_chars.next(); // Skip the second `%` + continue; + } + + // Parse format specifiers + let mut flags = FormatFlags::default(); + + // Parse flags (`+`, `(`, ...) + while let Some(&next_char) = format_chars.peek() { + match next_char { + '=' => flags.left_justify = true, + '+' => flags.force_sign = true, + ' ' => flags.space_sign = true, + '(' => flags.use_parens = true, + '!' => flags.suppress_symbol = true, + _ => break, // Stop when no more flags are found + } + format_chars.next(); + } + + // Parse field width + let mut num_str = String::new(); + while let Some(&c) = format_chars.peek() { + if !c.is_ascii_digit() { + break; + } + num_str.push(c); + format_chars.next(); + } + if !num_str.is_empty() { + flags.field_width = num_str.parse().ok(); // Parse as integer + } + + // Parse left precision (`#`) + if format_chars.peek() == Some(&'#') { + format_chars.next(); // Skip `#` + num_str.clear(); // Clear the string for precision + while let Some(&c) = format_chars.peek() { + if !c.is_ascii_digit() { + break; + } + num_str.push(c); + format_chars.next(); + } + flags.left_precision = num_str.parse().ok(); // Parse as integer + } + + // Parse right precision indicated by `.` + if format_chars.peek() == Some(&'.') { + format_chars.next(); // Skip `.` + num_str.clear(); // Clear the string for precision + while let Some(&c) = format_chars.peek() { + if !c.is_ascii_digit() { + break; + } + num_str.push(c); + format_chars.next(); + } + flags.right_precision = num_str.parse().ok(); // Parse as integer + } + + // Handle conversion specifiers (`i` or `n`) + match format_chars.next() { + Some('i') => { + // International formatting + flags.international = true; + let value = unsafe { args.arg::() }; // Get the argument as f64 + if let Some(written) = + format_monetary(&mut buffer[pos..], value, &DEFAULT_MONETARY, &flags) + { + pos += written; // Update the position + } else { + return -1; // Formatting failed + } + } + Some('n') => { + // Locale-specific formatting + let value = unsafe { args.arg::() }; // Get the argument as f64 + if let Some(written) = + format_monetary(&mut buffer[pos..], value, &DEFAULT_MONETARY, &flags) + { + pos += written; // Update the position + } else { + return -1; // Failed to format the value + } + } + _ => return -1, + } + } + + // Ensure there is space for the null terminator + if pos >= buffer.len() { + return -1; // Buffer overflow + } + buffer[pos] = 0; // Null-terminate the buffer + pos as ssize_t // Return the number of characters written +} + +/// Formats a monetary value into the given `buffer` using locale-specific rules +/// from `monetary` and formatting options from `flags`. +/// Returns `Some(len)` if successful, where `len` is the number of bytes written, +/// or `None` on error. +/// +/// # Parameters +/// - `buffer`: The output slice in which to write the formatted string +/// - `value`: The numeric value to format as monetary +/// - `monetary`: Locale-specific formatting rules, including symbols, separators, +/// and grouping sizes +/// - `flags`: Additional formatting options +/// +fn format_monetary( + buffer: &mut [u8], + value: f64, + monetary: &LocaleMonetaryInfo, + flags: &FormatFlags, +) -> Option { + // 1) determine sign and absolute value + let is_negative = value < 0.0; + let abs_value = fabs(value); + + // 2) figure out how many fractionals digits to use + let frac_digits = if flags.international { + flags + .right_precision + .unwrap_or(monetary.int_frac_digits as usize) + } else { + flags + .right_precision + .unwrap_or(monetary.frac_digits as usize) + }; + + // 3) split the value into integer and fractional parts + let scale = pow(10.0, frac_digits as f64); + + // Check for overflow + let max_safe_int = (i64::MAX as f64) / scale; + if abs_value >= max_safe_int { + return None; + } + + let mut int_part = trunc(abs_value) as i64; + let mut frac_part = round((abs_value - int_part as f64) * scale) as i64; + + // 4) handle carry-over if frac_part equals or exceeds scale after rounding + if frac_part >= scale as i64 { + int_part += 1; + frac_part = 0; + } + + // 5) convert the integer part to string + let mut int_str = int_part.to_string(); + + // 6) apply left precision if specified (padding with '0') + // So if left_precision is 5 and int_str is "42", it becomes "00042". + if let Some(left_prec) = flags.left_precision { + if int_str.len() > left_prec { + // The integer part is too large to fit the precision + return None; + } + // Right-align the number in a field of `left_prec` width, padded with '0' + int_str = format!("{:0>width$}", int_str, width = left_prec); + } + + // 7) build the final formatted output in a temporary String + let mut result = String::with_capacity(int_str.len() * 2 + 20); + + // 7a) determine currency symbol placement and sign rules + let (cs_precedes, sep_by_space, sign_posn) = if is_negative { + ( + monetary.n_cs_precedes, + monetary.n_sep_by_space, + monetary.n_sign_posn, + ) + } else { + ( + monetary.p_cs_precedes, + monetary.p_sep_by_space, + monetary.p_sign_posn, + ) + }; + + // 7b) determine which sign to display + // - negative sign if value is negative + // - positive_sign if user forced sign + // - space if space_sign is set and value is positive + // - empty otherwise + let sign = match (is_negative, flags.force_sign, flags.space_sign) { + (true, _, _) => monetary.negative_sign, + (false, true, _) => monetary.positive_sign, + (false, false, true) => " ", + _ => "", + }; + + // 7c) choose which currency symbol to display + // - maybe empty if suppressed + // - int_curr_symbol for international format + // - currency_symbol for local format + let symbol = if flags.suppress_symbol { + "" + } else if flags.international { + monetary.int_curr_symbol + } else { + monetary.currency_symbol + }; + + // 8) add opening parenthesis if sign position is 0 + if sign_posn == 0 { + result.push('('); + } else if sign_posn == 1 { + result.push_str(sign); + } + + // 9) add currency symbol if it precedes the amount + if cs_precedes { + result.push_str(symbol); + if sep_by_space { + result.push(' '); + } + } + + // 10) group the integer string and append it + let grouped = apply_grouping(&int_str, monetary); + result.push_str(&grouped); + + // 11) append the fractional part, if any + if frac_digits > 0 { + result.push_str(monetary.mon_decimal_point); + // Zero-pad fractional part to the specified width + result.push_str(&format!("{:0>width$}", frac_part, width = frac_digits)); + } + + // 12) if the currency symbol follows the amount, add it now + if !cs_precedes { + if sep_by_space { + result.push(' '); + } + result.push_str(symbol); + } + + // 13) if sign_posn == 0, close the parenthesis + if sign_posn == 0 { + result.push(')'); + } + + // 14) checks if the user specified a total field width + // - if the final result is shorter, we padd it + // - if `left_justify` is true, padd on the right otherwise padd on the left + if let Some(width) = flags.field_width + && result.len() < width + { + let padding = " ".repeat(width - result.len()); + result = if flags.left_justify { + result + &padding + } else { + padding + &result + }; + } + + // 15) write the final string to the buffer + if result.len() > buffer.len() { + // Not enough space in the output buffer + return None; + } + buffer[..result.len()].copy_from_slice(result.as_bytes()); + + // 16) return how many bytes we wrote + Some(result.len()) +} diff --git a/src/header/net_if/cbindgen.toml b/src/header/net_if/cbindgen.toml new file mode 100644 index 0000000000..a434e2f0dc --- /dev/null +++ b/src/header/net_if/cbindgen.toml @@ -0,0 +1,12 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/net_if.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +include_guard = "_NET_IF_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/net_if/mod.rs b/src/header/net_if/mod.rs new file mode 100644 index 0000000000..edbfedec50 --- /dev/null +++ b/src/header/net_if/mod.rs @@ -0,0 +1,119 @@ +//! `net/if.h` implementation. +//! +//! See . + +use core::ptr::null; + +use crate::{ + c_str::CStr, + platform::{ + ERRNO, + types::{c_char, c_int, c_uint}, + }, +}; + +use super::errno::ENXIO; + +/// See . +#[repr(C)] +pub struct if_nameindex { + if_index: c_uint, + if_name: *const c_char, +} + +/// See . +pub const IF_NAMESIZE: usize = 16; + +const IF_STUB_INTERFACE: *const c_char = (c"stub").as_ptr(); + +const INTERFACES: &[if_nameindex] = &[ + if_nameindex { + if_index: 1, + if_name: IF_STUB_INTERFACE, + }, + if_nameindex { + if_index: 0, + if_name: null::(), + }, +]; + +/// See . +/// +/// # Safety +/// this is a no-op: the list returned by if_nameindex() is a ref to a constant +#[unsafe(no_mangle)] +pub unsafe extern "C" fn if_freenameindex(s: *mut if_nameindex) {} + +/// See . +/// +/// # Safety +/// Returns only static lifetime references to const names, does not reuse the buf pointer. +/// Returns NULL in case of not found + ERRNO being set to ENXIO. +/// Currently only checks against inteface index 1. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn if_indextoname(idx: c_uint, buf: *mut c_char) -> *const c_char { + if idx == 1 { + return IF_STUB_INTERFACE; + } + ERRNO.set(ENXIO); + null::() +} + +/// See . +/// +/// # Safety +/// Returns a constant pointer to a pre defined const stub list +/// The end of the list is determined by an if_nameindex struct having if_index 0 and if_name NULL +#[unsafe(no_mangle)] +pub unsafe extern "C" fn if_nameindex() -> *const if_nameindex { + core::ptr::from_ref::(&INTERFACES[0]) +} + +/// See . +/// +/// # Safety +/// Compares the name to a constant string and only returns an int as a result. +/// An invalid name string will return an index of 0 +#[unsafe(no_mangle)] +pub unsafe extern "C" fn if_nametoindex(name: *const c_char) -> c_uint { + if name.is_null() { + return 0; + } + let name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("") }; + if name.eq("stub") { + return 1; + } + 0 +} + +// Nonstandard, used alongside ifaddrs.h +// See https://man7.org/linux/man-pages/man7/netdevice.7.html + +pub const IFF_UP: c_int = 0x1; +pub const IFF_BROADCAST: c_int = 0x2; +pub const IFF_DEBUG: c_int = 0x4; +pub const IFF_LOOPBACK: c_int = 0x8; +pub const IFF_POINTOPOINT: c_int = 0x10; +pub const IFF_NOTRAILERS: c_int = 0x20; +pub const IFF_RUNNING: c_int = 0x40; +pub const IFF_NOARP: c_int = 0x80; +pub const IFF_PROMISC: c_int = 0x100; +pub const IFF_ALLMULTI: c_int = 0x200; +pub const IFF_MASTER: c_int = 0x400; +pub const IFF_SLAVE: c_int = 0x800; +pub const IFF_MULTICAST: c_int = 0x1000; +pub const IFF_PORTSEL: c_int = 0x2000; +pub const IFF_AUTOMEDIA: c_int = 0x4000; +pub const IFF_DYNAMIC: c_int = 0x8000; +pub const IFF_LOWER_UP: c_int = 0x10000; +pub const IFF_DORMANT: c_int = 0x20000; +pub const IFF_ECHO: c_int = 0x40000; +pub const IFF_VOLATILE: c_int = IFF_LOOPBACK + | IFF_POINTOPOINT + | IFF_BROADCAST + | IFF_ECHO + | IFF_MASTER + | IFF_SLAVE + | IFF_RUNNING + | IFF_LOWER_UP + | IFF_DORMANT; diff --git a/src/header/netdb/cbindgen.toml b/src/header/netdb/cbindgen.toml new file mode 100644 index 0000000000..b031875f72 --- /dev/null +++ b/src/header/netdb/cbindgen.toml @@ -0,0 +1,22 @@ +# netinet/in.h brings in sys/socket.h +sys_includes = ["inttypes.h", "netinet/in.h", "features.h"] +include_guard = "_RELIBC_NETDB_H" +trailer = """ +#ifndef _RELIBC_BITS_NETDB_H +#define _RELIBC_BITS_NETDB_H + +#define h_errno (*__h_errno_location()) +#define h_addr h_addr_list[0] /* Address, for backward compatibility.*/ + +#endif // _RELIBC_BITS_NETDB_H +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[export.rename] +"sockaddr" = "struct sockaddr" + +[enum] +prefix_with_name = true diff --git a/src/header/netdb/dns/answer.rs b/src/header/netdb/dns/answer.rs new file mode 100644 index 0000000000..544c77f558 --- /dev/null +++ b/src/header/netdb/dns/answer.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::{string::String, vec::Vec}; + +#[derive(Clone, Debug)] +pub struct DnsAnswer { + pub name: String, + pub a_type: u16, + pub a_class: u16, + pub ttl_a: u16, + pub ttl_b: u16, + pub data: Vec, +} diff --git a/src/header/netdb/dns/mod.rs b/src/header/netdb/dns/mod.rs new file mode 100644 index 0000000000..9d7e44bd1c --- /dev/null +++ b/src/header/netdb/dns/mod.rs @@ -0,0 +1,166 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::{answer::DnsAnswer, query::DnsQuery}; + +use alloc::{string::String, vec::Vec}; + +mod answer; +mod query; + +#[derive(Clone, Debug)] +pub struct Dns { + pub transaction_id: u16, + pub flags: u16, + pub queries: Vec, + pub answers: Vec, +} + +impl Dns { + pub fn compile(&self) -> Vec { + let mut data = Vec::new(); + + macro_rules! push_u8 { + ($value:expr) => { + data.push($value); + }; + } + + macro_rules! push_n16 { + ($value:expr) => { + data.extend_from_slice(&u16::to_be_bytes($value)); + }; + } + + push_n16!(self.transaction_id); + push_n16!(self.flags); + push_n16!(self.queries.len() as u16); + push_n16!(self.answers.len() as u16); + push_n16!(0); + push_n16!(0); + + for query in self.queries.iter() { + for part in query.name.split('.') { + push_u8!(part.len() as u8); + data.extend_from_slice(part.as_bytes()); + } + push_u8!(0); + push_n16!(query.q_type); + push_n16!(query.q_class); + } + data + } + + pub fn parse(data: &[u8]) -> Result { + let name_ind = 0b1100_0000; + let mut i = 0; + + macro_rules! pop_u8 { + () => {{ + i += 1; + if i > data.len() { + return Err(format!("{}: {}: pop_u8", file!(), line!())); + } + data[i - 1] + }}; + } + + macro_rules! pop_n16 { + () => {{ + use core::convert::TryInto; + i += 2; + if i > data.len() { + return Err(format!("{}: {}: pop_n16", file!(), line!())); + } + let bytes: [u8; 2] = data[i - 2..i].try_into().unwrap(); + u16::from_be_bytes(bytes) + }}; + } + + macro_rules! pop_data { + () => {{ + let mut data = Vec::new(); + + let data_len = pop_n16!(); + for _data_i in 0..data_len { + data.push(pop_u8!()); + } + + data + }}; + } + + macro_rules! pop_name { + () => {{ + let mut name = String::new(); + let old_i = i; + + loop { + let name_len = pop_u8!(); + if name_len & name_ind == name_ind { + i -= 1; + i = (pop_n16!() - ((name_ind as u16) << 8)) as usize; + continue; + } + if name_len == 0 { + break; + } + if !name.is_empty() { + name.push('.'); + } + for _name_i in 0..name_len { + name.push(pop_u8!() as char); + } + } + + if i <= old_i { + i = old_i + 2; + } + + name + }}; + } + + let transaction_id = pop_n16!(); + let flags = pop_n16!(); + let queries_len = pop_n16!(); + let answers_len = pop_n16!(); + let _ = pop_n16!(); + let _ = pop_n16!(); + + let mut queries = Vec::new(); + for _query_i in 0..queries_len { + queries.push(DnsQuery { + name: pop_name!(), + q_type: pop_n16!(), + q_class: pop_n16!(), + }); + } + + let mut answers = Vec::new(); + for _answer_i in 0..answers_len { + answers.push(DnsAnswer { + name: pop_name!(), + a_type: pop_n16!(), + a_class: pop_n16!(), + ttl_a: pop_n16!(), + ttl_b: pop_n16!(), + data: pop_data!(), + }); + } + + Ok(Dns { + transaction_id, + flags, + queries, + answers, + }) + } +} diff --git a/src/header/netdb/dns/query.rs b/src/header/netdb/dns/query.rs new file mode 100644 index 0000000000..0ac852f4e4 --- /dev/null +++ b/src/header/netdb/dns/query.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::string::String; + +#[derive(Clone, Debug)] +pub struct DnsQuery { + pub name: String, + pub q_type: u16, + pub q_class: u16, +} diff --git a/src/header/netdb/host.rs b/src/header/netdb/host.rs new file mode 100644 index 0000000000..9d8c6a4347 --- /dev/null +++ b/src/header/netdb/host.rs @@ -0,0 +1,132 @@ +use alloc::{boxed::Box, str::SplitWhitespace, vec::Vec}; +use core::{mem, ptr}; + +use crate::{ + error::ResultExt, + header::{ + arpa_inet::inet_aton, fcntl::O_RDONLY, netinet_in::in_addr, sys_socket::constants::AF_INET, + unistd::SEEK_SET, + }, + platform::{ + Pal, Sys, + rlb::{Line, RawLineBuffer}, + types::{c_char, c_int}, + }, + raw_cell::RawCell, +}; + +use super::{bytes_to_box_str, hostent}; + +static mut HOSTDB: c_int = -1; +pub static mut HOST_ENTRY: hostent = hostent { + h_name: ptr::null_mut(), + h_aliases: ptr::null_mut(), + h_addrtype: 0, + h_length: 0, + h_addr_list: ptr::null_mut(), +}; +pub static HOST_NAME: RawCell>> = RawCell::new(None); +pub static HOST_ALIASES: RawCell>>> = RawCell::new(None); +static _HOST_ALIASES: RawCell>> = RawCell::new(None); +pub static mut HOST_ADDR: Option = None; +pub static mut HOST_ADDR_LIST: [*mut c_char; 2] = [ptr::null_mut(); 2]; +pub static mut _HOST_ADDR_LIST: [u8; 4] = [0u8; 4]; +static mut H_POS: usize = 0; +pub static mut HOST_STAYOPEN: c_int = 0; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn endhostent() { + if unsafe { HOSTDB } >= 0 + && let Ok(()) = Sys::close(unsafe { HOSTDB }) + {} // TODO handle error + unsafe { HOSTDB = -1 }; +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sethostent(stayopen: c_int) { + unsafe { HOST_STAYOPEN = stayopen }; + if unsafe { HOSTDB } < 0 { + unsafe { HOSTDB = Sys::open(c"/etc/hosts".into(), O_RDONLY, 0).or_minus_one_errno() } + } else if Sys::lseek(unsafe { HOSTDB }, 0, SEEK_SET).is_ok() { + } // TODO handle error + unsafe { H_POS = 0 }; +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gethostent() -> *mut hostent { + if unsafe { HOSTDB } < 0 { + unsafe { HOSTDB = Sys::open(c"/etc/hosts".into(), O_RDONLY, 0).or_minus_one_errno() }; + } + let mut rlb = RawLineBuffer::new(unsafe { HOSTDB }); + rlb.seek(unsafe { H_POS }); + + let mut r: Box = Box::default(); + while r.is_empty() || r.split_whitespace().next().is_none() || r.starts_with('#') { + r = match rlb.next() { + Line::Some(s) => bytes_to_box_str(s), + _ => { + if unsafe { HOST_STAYOPEN } == 0 { + unsafe { endhostent() }; + } + return ptr::null_mut(); + } + }; + } + rlb.next(); + unsafe { H_POS = rlb.line_pos() }; + + let mut iter: SplitWhitespace = r.split_whitespace(); + + let addr_vec: Vec = iter.next().unwrap().bytes().chain(Some(b'\0')).collect(); + let addr_cstr = addr_vec.as_slice().as_ptr().cast::(); + let mut addr = mem::MaybeUninit::uninit(); + unsafe { inet_aton(addr_cstr, addr.as_mut_ptr()) }; + let addr = unsafe { addr.assume_init() }; + + unsafe { + _HOST_ADDR_LIST = addr.s_addr.to_ne_bytes(); + HOST_ADDR_LIST = [(&raw mut _HOST_ADDR_LIST).cast::(), ptr::null_mut()]; + + HOST_ADDR = Some(addr); + } + + let host_name = iter.next().unwrap().bytes().chain(Some(b'\0')).collect(); + + let mut _host_aliases: Vec> = iter + .map(|alias| alias.bytes().chain(Some(b'\0')).collect()) + .collect(); + unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) }; + + let mut host_aliases: Vec<*mut c_char> = unsafe { + HOST_ALIASES + .unsafe_mut() + .as_mut() + .unwrap() + .iter_mut() + .map(|x| x.as_mut_ptr().cast::()) + .chain([ptr::null_mut(), ptr::null_mut()]) + .collect() + }; + + unsafe { HOST_NAME.unsafe_set(Some(host_name)) }; + + unsafe { + HOST_ENTRY = hostent { + h_name: HOST_NAME + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_ptr() + .cast::(), + h_aliases: host_aliases.as_mut_slice().as_mut_ptr(), + h_addrtype: AF_INET, + h_length: 4, + h_addr_list: (&raw mut HOST_ADDR_LIST).cast(), + }; + _HOST_ALIASES.unsafe_set(Some(host_aliases)); + } + if unsafe { HOST_STAYOPEN } == 0 { + unsafe { endhostent() }; + } + (&raw mut HOST_ENTRY).cast::() +} diff --git a/src/header/netdb/linux.rs b/src/header/netdb/linux.rs new file mode 100644 index 0000000000..2601051164 --- /dev/null +++ b/src/header/netdb/linux.rs @@ -0,0 +1,19 @@ +use crate::{ + error::Errno, + fs::File, + header::{errno, fcntl}, + io::{BufRead, BufReader}, +}; +use alloc::string::String; + +pub fn get_dns_server() -> Result { + let file = File::open(c"/etc/resolv.conf".into(), fcntl::O_RDONLY).map(BufReader::new)?; + + for line in file.lines().map_while(Result::ok) { + if let Some(dns) = line.strip_prefix("nameserver ") { + return Ok(dns.into()); + } + } + + Err(Errno(errno::EIO).sync()) +} diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs new file mode 100644 index 0000000000..0734eec668 --- /dev/null +++ b/src/header/netdb/lookup.rs @@ -0,0 +1,371 @@ +use alloc::{boxed::Box, string::ToString, vec::Vec}; +use core::{mem, ptr}; + +use crate::{ + out::Out, + platform::{ + Pal, Sys, + types::{c_int, c_void}, + }, +}; + +use crate::header::{ + bits_arpainet::htons, + bits_socklen_t::socklen_t, + bits_timespec::timespec, + errno::*, + netinet_in::{IPPROTO_UDP, in_addr, sockaddr_in}, + sys_socket::{ + self, + constants::{AF_INET, SOCK_DGRAM}, + sockaddr, + }, + time, +}; + +use super::{ + dns::{Dns, DnsQuery}, + sys::get_dns_server, +}; + +pub type LookupHost = Vec; + +pub fn lookup_host(host: &str) -> Result { + if let Some(host_direct_addr) = parse_ipv4_string(host) { + // already an ip address + return Ok(vec![in_addr { + s_addr: host_direct_addr, + }]); + } + + let dns_string = get_dns_server().map_err(|e| e.0)?; + + if let Some(dns_addr) = parse_ipv4_string(&dns_string) { + let mut timespec = timespec::default(); + if let Ok(()) = Sys::clock_gettime( + time::constants::CLOCK_REALTIME, + Out::from_mut(&mut timespec), + ) {}; // TODO handle error + let tid = (timespec.tv_nsec >> 16) as u16; + + let packet = Dns { + transaction_id: tid, + flags: 0x0100, + queries: vec![DnsQuery { + name: host.to_string(), + q_type: 0x0001, + q_class: 0x0001, + }], + answers: vec![], + }; + + let packet_data = packet.compile(); + let packet_data_len = packet_data.len(); + + let packet_data_box = packet_data.into_boxed_slice(); + let packet_data_ptr = Box::into_raw(packet_data_box) as *mut _ as *mut c_void; + + let dest = sockaddr_in { + sin_family: AF_INET as u16, + sin_port: htons(53), + sin_addr: in_addr { s_addr: dns_addr }, + ..Default::default() + }; + let dest_ptr = ptr::from_ref(&dest).cast::(); + + let sock = unsafe { + let sock = sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP)); + if sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) < 0 { + return Err(EIO); + } + if sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) < 0 { + drop(Box::from_raw(packet_data_ptr)); + return Err(EIO); + } + sock + }; + + unsafe { + drop(Box::from_raw(packet_data_ptr)); + } + + let i = 0 as socklen_t; + let mut buf = vec![0u8; 65536]; + let buf_ptr = buf.as_mut_ptr().cast::(); + + let count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) }; + if count < 0 { + return Err(EIO); + } + + match Dns::parse(&buf[..count as usize]) { + Ok(response) => { + let addrs: Vec<_> = response + .answers + .into_iter() + .filter_map(|answer| { + if answer.a_type == 0x0001 + && answer.a_class == 0x0001 + && answer.data.len() == 4 + { + let addr = in_addr { + s_addr: u32::from_ne_bytes([ + answer.data[0], + answer.data[1], + answer.data[2], + answer.data[3], + ]), + }; + Some(addr) + } else { + None + } + }) + .collect(); + + Ok(addrs) + } + Err(_err) => Err(EINVAL), + } + } else { + Err(EINVAL) + } +} + +pub fn lookup_addr(addr: in_addr) -> Result>, c_int> { + let dns_string = get_dns_server().map_err(|e| e.0)?; + + if let Some(dns_addr) = parse_ipv4_string(&dns_string) { + let addr: [u8; 4] = addr.s_addr.to_ne_bytes(); + // Address intentionally backwards for reverse lookup + let name = format!( + "{}.{}.{}.{}.in-addr.arpa", + addr[3], addr[2], addr[1], addr[0] + ); + + let mut timespec = timespec::default(); + if let Ok(()) = Sys::clock_gettime( + time::constants::CLOCK_REALTIME, + Out::from_mut(&mut timespec), + ) {}; // TODO handle error + let tid = (timespec.tv_nsec >> 16) as u16; + + let packet = Dns { + transaction_id: tid, + flags: 0x0100, + queries: vec![DnsQuery { + name, + q_type: 0x000C, + q_class: 0x0001, + }], + answers: vec![], + }; + + let packet_data = packet.compile(); + let packet_data_len = packet_data.len(); + let packet_data_box = packet_data.into_boxed_slice(); + let packet_data_ptr = Box::into_raw(packet_data_box) as *mut _ as *mut c_void; + + let dest = sockaddr_in { + sin_family: AF_INET as u16, + sin_port: htons(53), + sin_addr: in_addr { s_addr: dns_addr }, + ..Default::default() + }; + + let dest_ptr = ptr::from_ref(&dest).cast::(); + + let sock = unsafe { + let sock = sys_socket::socket(AF_INET, SOCK_DGRAM, i32::from(IPPROTO_UDP)); + if sys_socket::connect(sock, dest_ptr, mem::size_of_val(&dest) as socklen_t) < 0 { + return Err(EIO); + } + sock + }; + + unsafe { + if sys_socket::send(sock, packet_data_ptr, packet_data_len, 0) < 0 { + return Err(EIO); + } + } + + unsafe { + drop(Box::from_raw(packet_data_ptr)); + } + + let i = mem::size_of::() as socklen_t; + let mut buf = [0u8; 65536]; + let buf_ptr = buf.as_mut_ptr().cast::(); + + let count = unsafe { sys_socket::recv(sock, buf_ptr, 65536, 0) }; + if count < 0 { + return Err(EIO); + } + + match Dns::parse(&buf[..count as usize]) { + Ok(response) => { + let names = response + .answers + .into_iter() + .filter_map(|answer| { + if answer.a_type == 0x000C && answer.a_class == 0x0001 { + // answer.data is encoded kinda weird. + // Basically length-prefixed strings for each + // subsection of the domain. + // We need to parse this to insert periods where + // they belong (ie at the end of each string) + Some(parse_revdns_answer(&answer.data)) + } else { + None + } + }) + .collect(); + Ok(names) + } + Err(_err) => Err(EINVAL), + } + } else { + Err(EINVAL) + } +} + +fn parse_revdns_answer(data: &[u8]) -> Vec { + if data.is_empty() || data[0] == 0 { + return vec![0]; + } + + let mut cursor = 0; + let mut index = 0; + // First byte is a length; discard + let mut output = data[1..].to_vec(); + let length = data.len() - 1; + while index < length { + let offset = data[index] as usize; + // CVE-2024-21342 + if offset > length { + return vec![0]; + } + index = cursor + offset + 1; + // First byte was skipped so index is one less + output[index - 1] = b'.'; + cursor = index; + } + // Response is NUL terminated so we must preserve that for C + match output.last_mut() { + Some(nul) => *nul = b'\0', + // XXX: Likely unreachable + None => { + debug_assert!(output.is_empty()); + output = vec![0]; + } + } + output +} + +pub fn parse_ipv4_string(ip_string: &str) -> Option { + let dns_vec: Vec = ip_string + .trim() + .split('.') + .map(|octet| octet.parse::().unwrap_or(0)) + .collect(); + + if dns_vec.len() != 4 { + return None; + } + + let mut dns_arr = [0u8; 4]; + + for (i, octet) in dns_vec.iter().enumerate() { + dns_arr[i] = *octet; + } + + Some(u32::from_ne_bytes(dns_arr)) +} + +#[cfg(test)] +mod tests { + use alloc::str; + use core::ffi::CStr; + + use super::parse_revdns_answer; + + // Actual response from a query + const DNS_GOOGLE: &[u8] = &[3, 100, 110, 115, 6, 103, 111, 111, 103, 108, 101, 0]; + const EXPECTED_DNS_GOOGLE: &str = "dns.google\0"; + const EXPECTED_DNS_GOOGLE_RT: &str = "dns.google"; + + // Fake response that has numbers within the name (e.g. like CDNs) + const FAKE_WITH_NUMS: &[u8] = &[ + 14, 102, 97, 107, 101, 45, 49, 48, 48, 45, 50, 45, 51, 45, 52, 8, 102, 111, 111, 98, 97, + 114, 50, 52, 4, 102, 97, 107, 101, 0, + ]; + const EXPECTED_FAKE_RESPONSE: &str = "fake-100-2-3-4.foobar24.fake\0"; + const EXPECTED_FAKE_RESPONSE_RT: &str = "fake-100-2-3-4.foobar24.fake"; + + const EMPTY_RESPONSE: &[u8] = &[0]; + const EXPECTED_EMPTY_RESPONSE: &str = "\0"; + const EXPECTED_EMPTY_RESPONSE_RT: &str = ""; + + #[test] + fn dns_response_dns_google() { + let response = parse_revdns_answer(DNS_GOOGLE); + assert_eq!( + 0, + *response.last().unwrap(), + "Response should end with a NUL byte" + ); + + let response_str = str::from_utf8(&response) + .expect("Response is valid UTF-8; parsing shouldn't change that"); + assert_eq!(EXPECTED_DNS_GOOGLE, response_str); + + let response_cstr = CStr::from_bytes_with_nul(&response) + .expect("Parsed response should have only one NUL byte"); + let response_cstr_str = response_cstr + .to_str() + .expect("Valid UTF-8 bytes to CStr to Rust str should be valid"); + assert_eq!(EXPECTED_DNS_GOOGLE_RT, response_cstr_str); + } + + #[test] + fn dns_response_fake_with_nums() { + let response = parse_revdns_answer(FAKE_WITH_NUMS); + assert_eq!( + 0, + *response.last().unwrap(), + "Response should end with a NUL byte" + ); + + let response_str = str::from_utf8(&response) + .expect("Response is valid UTF-8; parsing shouldn't change that"); + assert_eq!(EXPECTED_FAKE_RESPONSE, response_str); + + let response_cstr = CStr::from_bytes_with_nul(&response) + .expect("Parsed response should have only one NUL byte"); + let response_cstr_str = response_cstr + .to_str() + .expect("Valid UTF-8 bytes to CStr to Rust str should be valid"); + assert_eq!(EXPECTED_FAKE_RESPONSE_RT, response_cstr_str); + } + + #[test] + fn dns_response_empty() { + let response = parse_revdns_answer(EMPTY_RESPONSE); + assert_eq!( + 0, + *response.last().unwrap(), + "Response should end with a NUL byte" + ); + + let response_str = str::from_utf8(&response) + .expect("Response is valid UTF-8; parsing shouldn't change that"); + assert_eq!(EXPECTED_EMPTY_RESPONSE, response_str); + + let response_cstr = CStr::from_bytes_with_nul(&response) + .expect("Parsed response should have only one NUL byte"); + let response_cstr_str = response_cstr + .to_str() + .expect("Valid UTF-8 bytes to CStr to Rust str should be valid"); + assert_eq!(EXPECTED_EMPTY_RESPONSE_RT, response_cstr_str); + } +} diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs new file mode 100644 index 0000000000..1d9941cbc5 --- /dev/null +++ b/src/header/netdb/mod.rs @@ -0,0 +1,1158 @@ +//! `netdb.h` implementation. +//! +//! See . + +mod dns; + +use core::{cell::Cell, fmt::Write, mem, net::Ipv4Addr, ptr, str}; + +use alloc::{boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec}; + +use crate::{ + c_str::{CStr, CString}, + error::ResultExt, + header::{ + arpa_inet::inet_aton, + bits_arpainet::{htons, ntohl}, + bits_safamily_t::sa_family_t, + bits_socklen_t::socklen_t, + errno::*, + fcntl::O_RDONLY, + netinet_in::{in_addr, sockaddr_in, sockaddr_in6}, + stdlib::atoi, + strings::strcasecmp, + sys_socket::{constants::AF_INET, sockaddr}, + unistd::SEEK_SET, + }, + platform::{ + self, Pal, Sys, + rlb::{Line, RawLineBuffer}, + types::{c_char, c_int, c_void, uint32_t}, + }, + raw_cell::RawCell, +}; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +pub use self::host::*; +pub mod host; + +pub use self::lookup::*; +pub mod lookup; + +/// See . +#[repr(C)] +pub struct hostent { + h_name: *mut c_char, + h_aliases: *mut *mut c_char, + h_addrtype: c_int, + h_length: c_int, + h_addr_list: *mut *mut c_char, +} + +/// See . +#[repr(C)] +pub struct netent { + n_name: *mut c_char, /* official name of net */ + n_aliases: *mut *mut c_char, /* alias list */ + n_addrtype: c_int, /* net address type */ + n_net: uint32_t, /* network # */ +} + +/// See . +#[repr(C)] +pub struct protoent { + p_name: *mut c_char, /* official protocol name */ + p_aliases: *mut *mut c_char, /* alias list */ + p_proto: c_int, /* protocol # */ +} + +/// See . +#[repr(C)] +pub struct servent { + s_name: *mut c_char, /* official service name */ + s_aliases: *mut *mut c_char, /* alias list */ + s_port: c_int, /* port # */ + s_proto: *mut c_char, /* protocol to use */ +} + +/// See . +#[repr(C)] +#[derive(Debug)] +pub struct addrinfo { + ai_flags: c_int, /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */ + ai_family: c_int, /* PF_xxx */ + ai_socktype: c_int, /* SOCK_xxx */ + ai_protocol: c_int, /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + ai_addrlen: socklen_t, /* length of ai_addr */ + ai_canonname: *mut c_char, /* canonical name for hostname */ + ai_addr: *mut sockaddr, /* binary address */ + ai_next: *mut addrinfo, /* next structure in linked list */ +} + +pub const AI_PASSIVE: c_int = 0x0001; +pub const AI_CANONNAME: c_int = 0x0002; +pub const AI_NUMERICHOST: c_int = 0x0004; +pub const AI_V4MAPPED: c_int = 0x0008; +pub const AI_ALL: c_int = 0x0010; +pub const AI_ADDRCONFIG: c_int = 0x0020; +pub const AI_NUMERICSERV: c_int = 0x0400; + +pub const EAI_BADFLAGS: c_int = -1; +pub const EAI_NONAME: c_int = -2; +pub const EAI_AGAIN: c_int = -3; +pub const EAI_FAIL: c_int = -4; +pub const EAI_NODATA: c_int = -5; +pub const EAI_FAMILY: c_int = -6; +pub const EAI_SOCKTYPE: c_int = -7; +pub const EAI_SERVICE: c_int = -8; +pub const EAI_ADDRFAMILY: c_int = -9; +pub const EAI_MEMORY: c_int = -10; +pub const EAI_SYSTEM: c_int = -11; +pub const EAI_OVERFLOW: c_int = -12; + +pub const NI_MAXHOST: c_int = 1025; +pub const NI_MAXSERV: c_int = 32; + +pub const NI_NUMERICHOST: c_int = 0x0001; +pub const NI_NUMERICSERV: c_int = 0x0002; +pub const NI_NOFQDN: c_int = 0x0004; +pub const NI_NAMEREQD: c_int = 0x0008; +pub const NI_DGRAM: c_int = 0x0010; + +static mut NETDB: c_int = 0; +pub static mut NET_ENTRY: netent = netent { + n_name: ptr::null_mut(), + n_aliases: ptr::null_mut(), + n_addrtype: 0, + n_net: 0, +}; +pub static NET_NAME: RawCell>> = RawCell::new(None); +pub static NET_ALIASES: RawCell>>> = RawCell::new(None); +pub static mut NET_ADDR: Option = None; +static mut N_POS: usize = 0; +static mut NET_STAYOPEN: c_int = 0; + +#[thread_local] +pub static H_ERRNO: Cell = Cell::new(0); +const H_UNSET: c_int = 0; +pub const HOST_NOT_FOUND: c_int = 1; +pub const NO_DATA: c_int = 2; +pub const NO_RECOVERY: c_int = 3; +pub const TRY_AGAIN: c_int = 4; + +// Expected length of addresses +const SOCKLEN_AF_INET4: socklen_t = 4; +const SOCKLEN_AF_INET6: socklen_t = 16; + +static mut PROTODB: c_int = 0; +static mut PROTO_ENTRY: protoent = protoent { + p_name: ptr::null_mut(), + p_aliases: ptr::null_mut(), + p_proto: 0 as c_int, +}; +static PROTO_NAME: RawCell>> = RawCell::new(None); +static PROTO_ALIASES: RawCell>>> = RawCell::new(None); +static mut PROTO_NUM: Option = None; +static mut P_POS: usize = 0; +static mut PROTO_STAYOPEN: c_int = 0; + +static mut SERVDB: c_int = 0; +static mut SERV_ENTRY: servent = servent { + s_name: ptr::null_mut(), + s_aliases: ptr::null_mut(), + s_port: 0 as c_int, + s_proto: ptr::null_mut(), +}; +static SERV_NAME: RawCell>> = RawCell::new(None); +static SERV_ALIASES: RawCell>>> = RawCell::new(None); +static mut SERV_PORT: Option = None; +static SERV_PROTO: RawCell>> = RawCell::new(None); +static mut S_POS: usize = 0; +static mut SERV_STAYOPEN: c_int = 0; + +fn bytes_to_box_str(bytes: &[u8]) -> Box { + Box::from(core::str::from_utf8(bytes).unwrap_or("")) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn endnetent() { + if let Ok(()) = Sys::close(unsafe { NETDB }) {}; // TODO handle error + unsafe { NETDB = 0 }; +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn endprotoent() { + if let Ok(()) = Sys::close(unsafe { PROTODB }) {}; // TODO handle error + unsafe { PROTODB = 0 }; +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn endservent() { + if let Ok(()) = Sys::close(unsafe { SERVDB }) {}; // TODO handle error + unsafe { SERVDB = 0 }; +} + +/// See . +/// Resolve a host name from a given network address. +/// +/// # Arguments +/// * `v` - Address to resolve as a non-null [`in_addr`] +/// * `length` - +/// * `format` - AF_INET or AF_INET6 +/// +/// # Safety +/// * `v` must be a valid pointer. +/// * `length` must correctly match the size of `v` as expected by `format` (usually 4 or 16). +/// * This function is not reentrant and may modify static data. +/// +/// # Panics +/// Panics if `v` is a null pointer. +/// +/// # Deprecation +/// Deprecated as of POSIX.1-2001 and removed in POSIX.1-2008. +/// New code should use [`getaddrinfo`] instead. +#[unsafe(no_mangle)] +#[deprecated] +pub unsafe extern "C" fn gethostbyaddr( + v: *const c_void, + length: socklen_t, + format: c_int, +) -> *mut hostent { + assert!( + !v.is_null(), + "`gethostbyaddr()` called with null `v` (in_addr)" + ); + // Uncomment if optional IPv6 support is added + // if length != SOCKLEN_AF_INET4 || length != SOCKLEN_AF_INET6 { + // H_ERRNO.set(NO_RECOVERY); + // return ptr::null_mut(); + // } + if length != SOCKLEN_AF_INET4 { + H_ERRNO.set(NO_RECOVERY); + return ptr::null_mut(); + } + let addr: in_addr = unsafe { (*(v as *mut in_addr)).clone() }; + + // check the hosts file first + let mut p: *mut hostent; + unsafe { sethostent(HOST_STAYOPEN) }; + while { + p = unsafe { gethostent() }; + !p.is_null() + } { + let mut cp = unsafe { (*p).h_addr_list }; + loop { + if cp.is_null() { + break; + } + if (unsafe { *cp }).is_null() { + break; + } + let mut cp_slice: [c_char; 4] = [0; 4]; + unsafe { (*cp).copy_to(cp_slice.as_mut_ptr(), 4) }; + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] + let cp_s_addr = unsafe { mem::transmute::<[c_char; 4], u32>(cp_slice) }; + #[cfg(any(target_arch = "riscv64", target_arch = "aarch64"))] + let cp_s_addr = u32::from_ne_bytes(cp_slice); + if cp_s_addr == addr.s_addr { + unsafe { sethostent(HOST_STAYOPEN) }; + return p; + } + cp = unsafe { cp.offset(1) }; + } + } + + //TODO actually get aliases + let mut _host_aliases: Vec> = Vec::new(); + _host_aliases.push(vec![b'\0']); + let mut host_aliases: Vec<*mut c_char> = Vec::new(); + host_aliases.push(ptr::null_mut()); + unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) }; + + match lookup_addr(addr.clone()).map(|host_names| host_names.into_iter().next()) { + Ok(Some(host_name)) => { + unsafe { _HOST_ADDR_LIST = addr.s_addr.to_ne_bytes() }; + unsafe { + HOST_ADDR_LIST = [(&raw mut _HOST_ADDR_LIST).cast::(), ptr::null_mut()] + }; + unsafe { HOST_NAME.unsafe_set(Some(host_name)) }; + unsafe { + HOST_ENTRY = hostent { + h_name: HOST_NAME + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_ptr() + .cast::(), + h_aliases: host_aliases.as_mut_slice().as_mut_ptr(), + h_addrtype: format, + h_length: length as i32, + h_addr_list: (&raw mut HOST_ADDR_LIST).cast(), + } + }; + &raw mut HOST_ENTRY + } + // `glibc` sets errno if an address doesn't have a host name + // `musl` uses the address as the host name in said case + Ok(None) => { + H_ERRNO.set(HOST_NOT_FOUND); + ptr::null_mut() + } + Err(e) => { + // TODO: Better error separation in lookup_addr + H_ERRNO.set(NO_RECOVERY); + ptr::null_mut() + } + } +} + +/// See . +/// Resolve host information by name or IP address. +/// +/// # Arguments +/// * `name` - Host name or IP address. +/// +/// # Safety +/// `name` must be a valid string. +/// This function is not reentrant and may modify static data. +/// +/// # Panics +/// Panics if `name` is a null pointer. +/// +/// # Deprecation +/// Deprecated as of POSIX.1-2001 and removed in POSIX.1-2008. +/// New code should use [`getaddrinfo`] instead. +#[unsafe(no_mangle)] +#[deprecated] +pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { + let name_cstr = unsafe { + CStr::from_nullable_ptr(name).expect("gethostbyname() called with a NULL pointer") + }; + let Ok(name_str) = str::from_utf8(name_cstr.to_bytes()) else { + H_ERRNO.set(NO_RECOVERY); + return ptr::null_mut(); + }; + + // Addresses and hostnames are both valid, so we'll check addresses first + // The standard doesn't define what to do when called with addresses + // Some implementations just skip resolution and copy the address to h_name + if let Some(s_addr) = parse_ipv4_string(name_str) { + let addr = in_addr { s_addr }; + return unsafe { + #[allow(deprecated)] + gethostbyaddr(ptr::from_ref(&addr).cast::(), 4, AF_INET) + }; + } + + // check the hosts file first + let mut p: *mut hostent; + unsafe { sethostent(HOST_STAYOPEN) }; + while { + p = unsafe { gethostent() }; + !p.is_null() + } { + if unsafe { strcasecmp((*p).h_name, name) } == 0 { + unsafe { sethostent(HOST_STAYOPEN) }; + return p; + } + let mut cp = unsafe { (*p).h_aliases }; + loop { + if cp.is_null() { + break; + } + if (unsafe { *cp }).is_null() { + break; + } + if unsafe { strcasecmp(*cp, name) } == 0 { + unsafe { sethostent(HOST_STAYOPEN) }; + return p; + } + cp = unsafe { cp.offset(1) }; + } + } + + let host = match lookup_host(name_str) { + Ok(lookuphost) => lookuphost, + Err(e) => { + H_ERRNO.set(NO_RECOVERY); + return ptr::null_mut(); + } + }; + let host_addr = match host.into_iter().next() { + Some(result) => result, + None => { + H_ERRNO.set(HOST_NOT_FOUND); + return ptr::null_mut(); + } + }; + + let host_name: Vec = name_cstr.to_bytes().to_vec(); + unsafe { HOST_NAME.unsafe_set(Some(host_name)) }; + unsafe { _HOST_ADDR_LIST = host_addr.s_addr.to_ne_bytes() }; + unsafe { HOST_ADDR_LIST = [(&raw mut _HOST_ADDR_LIST).cast::(), ptr::null_mut()] }; + unsafe { HOST_ADDR = Some(host_addr) }; + + //TODO actually get aliases + let mut _host_aliases: Vec> = Vec::new(); + _host_aliases.push(vec![b'\0']); + let mut host_aliases: Vec<*mut c_char> = Vec::new(); + host_aliases.push(ptr::null_mut()); + host_aliases.push(ptr::null_mut()); + unsafe { HOST_ALIASES.unsafe_set(Some(_host_aliases)) }; + + unsafe { + HOST_ENTRY = hostent { + h_name: HOST_NAME + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_ptr() + .cast::(), + h_aliases: host_aliases.as_mut_slice().as_mut_ptr(), + h_addrtype: AF_INET, + h_length: 4, + h_addr_list: (&raw mut HOST_ADDR_LIST).cast(), + } + }; + unsafe { sethostent(HOST_STAYOPEN) }; + (&raw mut HOST_ENTRY).cast::() +} + +/// See . +pub unsafe extern "C" fn getnetbyaddr(net: u32, net_type: c_int) -> *mut netent { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getnetbyname(name: *const c_char) -> *mut netent { + let mut n: *mut netent; + unsafe { setnetent(NET_STAYOPEN) }; + while { + n = unsafe { getnetent() }; + !n.is_null() + } { + if unsafe { strcasecmp((*n).n_name, name) } == 0 { + unsafe { setnetent(NET_STAYOPEN) }; + return n; + } + } + unsafe { setnetent(NET_STAYOPEN) }; + + platform::ERRNO.set(ENOENT); + ptr::null_mut::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getnetent() -> *mut netent { + // TODO: Rustify implementation + + if unsafe { NETDB } == 0 { + unsafe { NETDB = Sys::open(c"/etc/networks".into(), O_RDONLY, 0).or_minus_one_errno() }; + } + + let mut rlb = RawLineBuffer::new(unsafe { NETDB }); + rlb.seek(unsafe { N_POS }); + + let mut r: Box = Box::default(); + while r.is_empty() || r.split_whitespace().next().is_none() || r.starts_with('#') { + r = match rlb.next() { + Line::Some(s) => bytes_to_box_str(s), + _ => { + if unsafe { NET_STAYOPEN } == 0 { + unsafe { endnetent() }; + } + return ptr::null_mut(); + } + }; + } + rlb.next(); + unsafe { N_POS = rlb.line_pos() }; + + let mut iter: SplitWhitespace = r.split_whitespace(); + + let net_name = iter.next().unwrap().bytes().chain(Some(b'\0')).collect(); + unsafe { NET_NAME.unsafe_set(Some(net_name)) }; + + let addr_vec: Vec = iter.next().unwrap().bytes().chain(Some(b'\0')).collect(); + let addr_cstr = addr_vec.as_slice().as_ptr().cast::(); + let mut addr = mem::MaybeUninit::uninit(); + unsafe { inet_aton(addr_cstr, addr.as_mut_ptr()) }; + let addr = unsafe { addr.assume_init() }; + unsafe { NET_ADDR = Some(ntohl(addr.s_addr)) }; + + let mut _net_aliases: Vec> = iter + .map(|alias| alias.bytes().chain(Some(b'\0')).collect()) + .collect(); + let mut net_aliases: Vec<*mut c_char> = _net_aliases + .iter_mut() + .map(|x| x.as_mut_ptr().cast::()) + .chain(Some(ptr::null_mut())) + .collect(); + unsafe { NET_ALIASES.unsafe_set(Some(_net_aliases)) }; + + unsafe { + NET_ENTRY = netent { + n_name: NET_NAME + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_ptr() + .cast::(), + n_aliases: net_aliases.as_mut_slice().as_mut_ptr(), + n_addrtype: AF_INET, + n_net: uint32_t::from(NET_ADDR.unwrap()), + } + }; + (&raw mut NET_ENTRY).cast::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getprotobyname(name: *const c_char) -> *mut protoent { + let mut p: *mut protoent; + unsafe { setprotoent(PROTO_STAYOPEN) }; + while { + p = unsafe { getprotoent() }; + !p.is_null() + } { + if unsafe { strcasecmp((*p).p_name, name) } == 0 { + unsafe { setprotoent(PROTO_STAYOPEN) }; + return p; + } + + let mut cp = unsafe { (*p).p_aliases }; + loop { + if cp.is_null() { + unsafe { setprotoent(PROTO_STAYOPEN) }; + break; + } + if (unsafe { *cp }).is_null() { + unsafe { setprotoent(PROTO_STAYOPEN) }; + break; + } + if unsafe { strcasecmp(*cp, name) } == 0 { + unsafe { setprotoent(PROTO_STAYOPEN) }; + return p; + } + cp = unsafe { cp.offset(1) }; + } + } + unsafe { setprotoent(PROTO_STAYOPEN) }; + + platform::ERRNO.set(ENOENT); + ptr::null_mut::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getprotobynumber(number: c_int) -> *mut protoent { + unsafe { setprotoent(PROTO_STAYOPEN) }; + let mut p: *mut protoent; + while { + p = unsafe { getprotoent() }; + !p.is_null() + } { + if unsafe { (*p).p_proto } == number { + unsafe { setprotoent(PROTO_STAYOPEN) }; + return p; + } + } + unsafe { setprotoent(PROTO_STAYOPEN) }; + platform::ERRNO.set(ENOENT); + ptr::null_mut::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getprotoent() -> *mut protoent { + if unsafe { PROTODB } == 0 { + unsafe { PROTODB = Sys::open(c"/etc/protocols".into(), O_RDONLY, 0).or_minus_one_errno() }; + } + + let mut rlb = RawLineBuffer::new(unsafe { PROTODB }); + rlb.seek(unsafe { P_POS }); + + let mut r: Box = Box::default(); + while r.is_empty() || r.split_whitespace().next().is_none() || r.starts_with('#') { + r = match rlb.next() { + Line::Some(s) => bytes_to_box_str(s), + _ => { + if unsafe { PROTO_STAYOPEN } == 0 { + unsafe { endprotoent() }; + } + return ptr::null_mut(); + } + }; + } + rlb.next(); + unsafe { P_POS = rlb.line_pos() }; + + let mut iter: SplitWhitespace = r.split_whitespace(); + + let mut proto_name: Vec = iter.next().unwrap().as_bytes().to_vec(); + proto_name.push(b'\0'); + + let mut num = iter.next().unwrap().as_bytes().to_vec(); + num.push(b'\0'); + unsafe { PROTO_NUM = Some(atoi(num.as_mut_slice().as_mut_ptr().cast::())) }; + + let mut _proto_aliases: Vec> = iter + .map(|alias| alias.bytes().chain(Some(b'\0')).collect()) + .collect(); + let mut proto_aliases: Vec<*mut i8> = _proto_aliases + .iter_mut() + .map(|x| x.as_mut_ptr().cast::()) + .chain(Some(ptr::null_mut())) + .collect(); + + unsafe { PROTO_ALIASES.unsafe_set(Some(_proto_aliases)) }; + unsafe { PROTO_NAME.unsafe_set(Some(proto_name)) }; + + unsafe { + PROTO_ENTRY = protoent { + p_name: PROTO_NAME + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_slice() + .as_mut_ptr() + .cast::(), + p_aliases: proto_aliases + .as_mut_slice() + .as_mut_ptr() + .cast::<*mut c_char>(), + p_proto: PROTO_NUM.unwrap(), + } + }; + if unsafe { PROTO_STAYOPEN } == 0 { + unsafe { endprotoent() }; + } + (&raw mut PROTO_ENTRY).cast::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getservbyname(name: *const c_char, proto: *const c_char) -> *mut servent { + unsafe { setservent(SERV_STAYOPEN) }; + let mut p: *mut servent; + if proto.is_null() { + while { + p = unsafe { getservent() }; + !p.is_null() + } { + if unsafe { strcasecmp((*p).s_name, name) } == 0 { + unsafe { setservent(SERV_STAYOPEN) }; + return p; + } + } + } else { + while { + p = unsafe { getservent() }; + !p.is_null() + } { + if unsafe { strcasecmp((*p).s_name, name) } == 0 + && unsafe { strcasecmp((*p).s_proto, proto) } == 0 + { + unsafe { setservent(SERV_STAYOPEN) }; + return p; + } + } + } + unsafe { setservent(SERV_STAYOPEN) }; + platform::ERRNO.set(ENOENT); + ptr::null_mut::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mut servent { + unsafe { setservent(SERV_STAYOPEN) }; + let mut p: *mut servent; + if proto.is_null() { + while { + p = unsafe { getservent() }; + !p.is_null() + } { + if unsafe { (*p).s_port } == port { + unsafe { setservent(SERV_STAYOPEN) }; + return p; + } + } + } else { + while { + p = unsafe { getservent() }; + !p.is_null() + } { + if unsafe { (*p).s_port } == port && unsafe { strcasecmp((*p).s_proto, proto) } == 0 { + unsafe { setservent(SERV_STAYOPEN) }; + return p; + } + } + } + unsafe { setservent(SERV_STAYOPEN) }; + platform::ERRNO.set(ENOENT); + ptr::null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getservent() -> *mut servent { + if unsafe { SERVDB } == 0 { + // TODO: Rustify + unsafe { SERVDB = Sys::open(c"/etc/services".into(), O_RDONLY, 0).or_minus_one_errno() }; + } + let mut rlb = RawLineBuffer::new(unsafe { SERVDB }); + rlb.seek(unsafe { S_POS }); + + let r: Box = Box::default(); + + loop { + let r = match rlb.next() { + Line::Some(s) => bytes_to_box_str(s), + _ => { + if unsafe { SERV_STAYOPEN } == 0 { + unsafe { endservent() }; + } + return ptr::null_mut(); + } + }; + + let mut iter = r.split_whitespace(); + let serv_name = match iter.next() { + Some(serv_name) => serv_name.bytes().chain(Some(b'\0')).collect(), + None => continue, + }; + let port_proto = match iter.next() { + Some(port_proto) => port_proto, + None => continue, + }; + let mut split = port_proto.split('/'); + let mut port: Vec = match split.next() { + Some(port) => port.bytes().chain(Some(b'\0')).collect(), + None => continue, + }; + unsafe { + SERV_PORT = Some(u32::from(htons( + atoi(port.as_mut_slice().as_mut_ptr().cast::()) as u16, + )) as i32) + }; + let proto = match split.next() { + Some(proto) => proto.bytes().chain(Some(b'\0')).collect(), + None => continue, + }; + + rlb.next(); + unsafe { S_POS = rlb.line_pos() }; + + /* + *let mut _serv_aliases: Vec> = Vec::new(); + *loop { + * let mut alias = match iter.next() { + * Some(s) => s.as_bytes().to_vec(), + * _ => break + * }; + * alias.push(b'\0'); + * _serv_aliases.push(alias); + *} + *let mut serv_aliases: Vec<*mut i8> = _serv_aliases.iter_mut().map(|x| x.as_mut_ptr() as *mut i8).collect(); + *serv_aliases.push(ptr::null_mut()); + * + */ + let mut _serv_aliases: Vec> = Vec::new(); + _serv_aliases.push(vec![b'\0']); + let mut serv_aliases: Vec<*mut i8> = Vec::new(); + serv_aliases.push(ptr::null_mut()); + serv_aliases.push(ptr::null_mut()); + + unsafe { SERV_ALIASES.unsafe_set(Some(_serv_aliases)) }; + unsafe { SERV_NAME.unsafe_set(Some(serv_name)) }; + unsafe { SERV_PROTO.unsafe_set(Some(proto)) }; + + unsafe { + SERV_ENTRY = servent { + s_name: SERV_NAME + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_slice() + .as_mut_ptr() + .cast::(), + s_aliases: serv_aliases + .as_mut_slice() + .as_mut_ptr() + .cast::<*mut c_char>(), + s_port: SERV_PORT.unwrap(), + s_proto: SERV_PROTO + .unsafe_mut() + .as_mut() + .unwrap() + .as_mut_slice() + .as_mut_ptr() + .cast::(), + } + }; + + if unsafe { SERV_STAYOPEN } == 0 { + unsafe { endservent() }; + } + break (&raw mut SERV_ENTRY).cast::(); + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setnetent(stayopen: c_int) { + unsafe { NET_STAYOPEN = stayopen }; + if unsafe { NETDB } == 0 { + unsafe { NETDB = Sys::open(c"/etc/networks".into(), O_RDONLY, 0).or_minus_one_errno() } + } else { + if Sys::lseek(unsafe { NETDB }, 0, SEEK_SET).is_ok() {}; // TODO handle errror + unsafe { N_POS = 0 }; + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setprotoent(stayopen: c_int) { + unsafe { PROTO_STAYOPEN = stayopen }; + if unsafe { PROTODB } == 0 { + unsafe { PROTODB = Sys::open(c"/etc/protocols".into(), O_RDONLY, 0).or_minus_one_errno() } + } else { + if Sys::lseek(unsafe { PROTODB }, 0, SEEK_SET).is_ok() {}; // TODO handle error + unsafe { P_POS = 0 }; + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setservent(stayopen: c_int) { + unsafe { SERV_STAYOPEN = stayopen }; + if unsafe { SERVDB } == 0 { + unsafe { SERVDB = Sys::open(c"/etc/services".into(), O_RDONLY, 0).or_minus_one_errno() } + } else { + if Sys::lseek(unsafe { SERVDB }, 0, SEEK_SET).is_ok() {}; // TODO handle error + unsafe { S_POS = 0 }; + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const addrinfo, + res: *mut *mut addrinfo, +) -> c_int { + let node_opt = unsafe { CStr::from_nullable_ptr(node) }; + let service_opt = unsafe { CStr::from_nullable_ptr(service) }; + + let hints_opt = if hints.is_null() { + None + } else { + Some(unsafe { &*hints }) + }; + + log::trace!( + "getaddrinfo({:?}, {:?}, {:?})", + node_opt.map(|c| unsafe { str::from_utf8_unchecked(c.to_bytes()) }), + service_opt.map(|c| unsafe { str::from_utf8_unchecked(c.to_bytes()) }), + hints_opt + ); + + //TODO: Use hints + let mut ai_flags = hints_opt.map_or(0, |hints| hints.ai_flags); + let mut ai_family; // = hints_opt.map_or(AF_UNSPEC, |hints| hints.ai_family); + let ai_socktype = hints_opt.map_or(0, |hints| hints.ai_socktype); + let mut ai_protocol; // = hints_opt.map_or(0, |hints| hints.ai_protocol); + + unsafe { *res = ptr::null_mut() }; + + let mut port = 0; + if let Some(service) = service_opt { + //TODO: Support other service definitions as well as AI_NUMERICSERV + match unsafe { str::from_utf8_unchecked(service.to_bytes()) }.parse::() { + Ok(ok) => port = ok, + Err(_err) => (), + } + } + let node = node_opt.unwrap_or_else(|| { + //TODO: Optimize by bypassing string parsing + if ai_flags & AI_PASSIVE > 0 { + c"0.0.0.0".into() + } else { + c"127.0.0.1".into() + } + }); + + let lookuphost = if ai_flags & AI_NUMERICHOST > 0 { + match parse_ipv4_string(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) { + Some(s_addr) => vec![in_addr { s_addr }], + None => { + return EAI_NONAME; + } + } + } else { + match lookup_host(unsafe { str::from_utf8_unchecked(node.to_bytes()) }) { + Ok(lookuphost) => lookuphost, + Err(e) => { + platform::ERRNO.set(e); + return EAI_SYSTEM; + } + } + }; + + for in_addr in lookuphost { + ai_family = AF_INET; + ai_protocol = 0; + + let ai_addr = Box::into_raw(Box::new(sockaddr_in { + sin_family: ai_family as sa_family_t, + sin_port: htons(port), + sin_addr: in_addr, + sin_zero: [0; 8], + })) + .cast::(); + + let ai_addrlen = mem::size_of::() as socklen_t; + + let ai_canonname = if ai_flags & AI_CANONNAME > 0 { + if node_opt.is_none() { + return EAI_BADFLAGS; + } + ai_flags &= !AI_CANONNAME; + node.to_owned_cstring().into_raw() + } else { + ptr::null_mut() + }; + + let addrinfo = Box::new(addrinfo { + ai_flags: 0, + ai_family, + ai_socktype, + ai_protocol, + ai_addrlen, + ai_canonname, + ai_addr, + ai_next: ptr::null_mut(), + }); + unsafe { + let mut indirect = res; + while !(*indirect).is_null() { + indirect = &raw mut (**indirect).ai_next; + } + *indirect = Box::into_raw(addrinfo) + } + } + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getnameinfo( + addr: *const sockaddr, + addrlen: socklen_t, + host: *mut c_char, + hostlen: socklen_t, + serv: *mut c_char, + servlen: socklen_t, + flags: c_int, +) -> c_int { + if addr.is_null() || addrlen as usize != mem::size_of::() { + return EAI_FAMILY; + } + + let sa = unsafe { &*(addr.cast::()) }; + + if !serv.is_null() && servlen > 0 { + if flags & NI_NUMERICSERV != 0 { + let port_str = sa.sin_port.to_be().to_string(); + let port_bytes = port_str.as_bytes(); + if (servlen as usize) <= port_bytes.len() { + return EAI_MEMORY; // Buffer too small + } + unsafe { + ptr::copy_nonoverlapping( + port_bytes.as_ptr().cast::(), + serv, + port_bytes.len(), + ) + }; + unsafe { *serv.add(port_bytes.len()) = 0 }; + } else { + // TODO: Implement service name lookup (e.g., from /etc/services) + unsafe { *serv = 0 }; + } + } + + if !host.is_null() && hostlen > 0 { + if flags & NI_NUMERICHOST != 0 { + let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be()); + let ip_str = ip_addr.to_string(); + let ip_bytes = ip_str.as_bytes(); + if (hostlen as usize) <= ip_bytes.len() { + return EAI_MEMORY; // Buffer too small + } + unsafe { + ptr::copy_nonoverlapping(ip_bytes.as_ptr().cast::(), host, ip_bytes.len()) + }; + unsafe { *host.add(ip_bytes.len()) = 0 }; + } else { + match lookup_addr(sa.sin_addr.clone()).map(|host_names| host_names.into_iter().next()) { + Ok(Some(hostname)) => { + if (hostlen as usize) <= hostname.len() { + return EAI_MEMORY; // Buffer too small + } + unsafe { + ptr::copy_nonoverlapping( + hostname.as_ptr().cast::(), + host, + hostname.len(), + ); + *host.add(hostname.len()) = 0; + } + } + Ok(None) => { + if flags & NI_NAMEREQD != 0 { + return EAI_NONAME; + } + } + Err(_) => { + if flags & NI_NAMEREQD != 0 { + return EAI_NONAME; + } + let ip_addr = Ipv4Addr::from(sa.sin_addr.s_addr.to_be()); + let ip_str = ip_addr.to_string(); + let ip_bytes = ip_str.as_bytes(); + if (hostlen as usize) <= ip_bytes.len() { + return EAI_MEMORY; + } + unsafe { + ptr::copy_nonoverlapping( + ip_bytes.as_ptr().cast::(), + host, + ip_bytes.len(), + ); + *host.add(ip_bytes.len()) = 0; + } + } + } + } + } + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn freeaddrinfo(res: *mut addrinfo) { + let mut ai = res; + while !ai.is_null() { + let bai = unsafe { Box::from_raw(ai) }; + if !bai.ai_canonname.is_null() { + drop(unsafe { CString::from_raw(bai.ai_canonname) }); + } + if !bai.ai_addr.is_null() { + if bai.ai_addrlen == mem::size_of::() as socklen_t { + unsafe { drop(Box::from_raw(bai.ai_addr.cast::())) }; + } else if bai.ai_addrlen == mem::size_of::() as socklen_t { + unsafe { drop(Box::from_raw(bai.ai_addr.cast::())) }; + } else { + todo_skip!(0, "freeaddrinfo: unknown ai_addrlen {}", bai.ai_addrlen); + } + } + ai = bai.ai_next; + } +} + +/// See . +#[unsafe(no_mangle)] +pub const extern "C" fn gai_strerror(errcode: c_int) -> *const c_char { + match errcode { + EAI_BADFLAGS => c"Invalid flags", + EAI_NONAME => c"Name does not resolve", + EAI_AGAIN => c"Try again", + EAI_FAIL => c"Non-recoverable error", + EAI_NODATA => c"Unknown error", + EAI_FAMILY => c"Unrecognized address family or invalid length", + EAI_SOCKTYPE => c"Unrecognized socket type", + EAI_SERVICE => c"Unrecognized service", + EAI_ADDRFAMILY => c"Address family for name not supported", + EAI_MEMORY => c"Out of memory", + EAI_SYSTEM => c"System error", + EAI_OVERFLOW => c"Overflow", + _ => c"Unknown error", + } + .as_ptr() +} + +/// Provide a pointer to relibc's internal [`H_ERRNO`]. +#[unsafe(no_mangle)] +#[deprecated] +pub extern "C" fn __h_errno_location() -> *mut c_int { + H_ERRNO.as_ptr() +} + +#[unsafe(no_mangle)] +#[deprecated] +pub const extern "C" fn hstrerror(errcode: c_int) -> *const c_char { + match errcode { + H_UNSET => c"Resolver error unset", + HOST_NOT_FOUND => c"Unknown hostname", + NO_DATA => c"No address for hostname", + NO_RECOVERY => c"Unknown server error", + TRY_AGAIN => c"Hostname lookup failure", + _ => c"Unknown error", + } + .as_ptr() +} + +/// Print error message associated with [`H_ERRNO`] to stderr. +/// +/// # Arguments +/// * `prefix` - An optional prefix to prepend to the error message. May be null or an empty +/// (`""`) C string. +/// +/// # Safety +/// Like [`crate::header::stdio::perror`], `prefix` should be a valid, NUL terminated C string if +/// used. The caller may safely call this function with a null pointer. +/// +/// # Deprecation +/// [`H_ERRNO`], [`hstrerror`], [`herror`], and other functions are deprecated as of +/// POSIX.1-2001 and removed as of POSIX.1-2008. These functions are provided for backwards +/// compatibility but should not be used by new code. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +#[unsafe(no_mangle)] +#[deprecated] +pub extern "C" fn herror(prefix: *const c_char) { + let code = H_ERRNO.get(); + // Safety: `hstrerror` handles every error code case and always returns a valid C string + let error = unsafe { + #[allow(deprecated)] + let msg_cstr = CStr::from_ptr(hstrerror(code)); + str::from_utf8_unchecked(msg_cstr.to_bytes()) + }; + + let mut writer = platform::FileWriter::new(2); + // Prefix is optional + match unsafe { CStr::from_nullable_ptr(prefix) } + .and_then(|prefix| str::from_utf8(prefix.to_bytes()).ok()) + { + Some(prefix) if !prefix.is_empty() => writer + .write_fmt(format_args!("{prefix}: {error}\n")) + .unwrap(), + _ => writer.write_fmt(format_args!("{error}\n")).unwrap(), + } +} diff --git a/src/header/netdb/redox.rs b/src/header/netdb/redox.rs new file mode 100644 index 0000000000..314dffcd05 --- /dev/null +++ b/src/header/netdb/redox.rs @@ -0,0 +1,16 @@ +use crate::{ + error::Errno, + fs::File, + header::{errno, fcntl}, + io::Read, +}; +use alloc::string::String; + +pub fn get_dns_server() -> Result { + let mut string = String::new(); + let mut file = File::open(c"/etc/net/dns".into(), fcntl::O_RDONLY)?; + file.read_to_string(&mut string) + .map_err(|_| Errno(errno::EIO).sync())?; + + Ok(string) +} diff --git a/src/header/netinet_in/cbindgen.toml b/src/header/netinet_in/cbindgen.toml new file mode 100644 index 0000000000..24fe4bcea2 --- /dev/null +++ b/src/header/netinet_in/cbindgen.toml @@ -0,0 +1,123 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_in.h.html +# +# Spec quotations relating to includes: +# - "The header shall define in_port_t Equivalent to the type uint16_t as described in ." +# - "The header shall define in_addr_t Equivalent to the type uint32_t as described in ." +# - "The header shall define the sa_family_t type as described in ." +# - "The header shall define the uint8_t and uint32_t types as described in ." +# - "Inclusion of the header may also make visible all symbols from and ." +# - "The htonl(), htons(), ntohl(), and ntohs() functions shall be available as described in ." +# - "Inclusion of the header may also make visible all symbols from ." +# +# Possible cycle between arpa/inet.h and netinet/in.h solved by: +# - including netinet/in.h in arpa/inet.h +# - splitting out functions (htonl, htons, ntohl, ntohs) into bits/arpainet.h +# +# sys/types.h needed for c_int and c_char +# bits/arpainet.h brings in inttypes.h +sys_includes = ["sys/types.h", "sys/socket.h"] +after_includes = """ +#include // for htonl, htons, ntohl, ntohs +""" +include_guard = "_RELIBC_NETINET_IN_H" +trailer = """ +#ifndef _RELIBC_BITS_NETINET_IN_H +#define _RELIBC_BITS_NETINET_IN_H + +extern const struct in6_addr in6addr_any; +extern const struct in6_addr in6addr_loopback; +#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } +#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } + +// from musl { +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ + ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0) + +#define IN6_IS_ADDR_LOOPBACK(a) \ + (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ + ((uint32_t *) (a))[2] == 0 && \ + ((uint8_t *) (a))[12] == 0 && ((uint8_t *) (a))[13] == 0 && \ + ((uint8_t *) (a))[14] == 0 && ((uint8_t *) (a))[15] == 1 ) + +#define IN6_IS_ADDR_MULTICAST(a) (((uint8_t *) (a))[0] == 0xff) + +#define IN6_IS_ADDR_LINKLOCAL(a) \ + ((((uint8_t *) (a))[0]) == 0xfe && (((uint8_t *) (a))[1] & 0xc0) == 0x80) + +#define IN6_IS_ADDR_SITELOCAL(a) \ + ((((uint8_t *) (a))[0]) == 0xfe && (((uint8_t *) (a))[1] & 0xc0) == 0xc0) + +#define IN6_IS_ADDR_V4MAPPED(a) \ + (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ + ((uint8_t *) (a))[8] == 0 && ((uint8_t *) (a))[9] == 0 && \ + ((uint8_t *) (a))[10] == 0xff && ((uint8_t *) (a))[11] == 0xff) + +#define IN6_IS_ADDR_V4COMPAT(a) \ + (((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \ + ((uint32_t *) (a))[2] == 0 && ((uint8_t *) (a))[15] > 1) + +#define IN6_IS_ADDR_MC_NODELOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *) (a))[1] & 0xf) == 0x1)) + +#define IN6_IS_ADDR_MC_LINKLOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *) (a))[1] & 0xf) == 0x2)) + +#define IN6_IS_ADDR_MC_SITELOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *) (a))[1] & 0xf) == 0x5)) + +#define IN6_IS_ADDR_MC_ORGLOCAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *) (a))[1] & 0xf) == 0x8)) + +#define IN6_IS_ADDR_MC_GLOBAL(a) \ + (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *) (a))[1] & 0xf) == 0xe)) + +#define __ARE_4_EQUAL(a,b) \ + (!( (0[a]-0[b]) | (1[a]-1[b]) | (2[a]-2[b]) | (3[a]-3[b]) )) +#define IN6_ARE_ADDR_EQUAL(a,b) \ + __ARE_4_EQUAL((const uint32_t *)(a), (const uint32_t *)(b)) + +#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 +#define IN_CLASSB(a) ((((in_addr_t)(a)) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 +#define IN_CLASSC(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) +#define IN_CLASSD(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xe0000000) +#define IN_MULTICAST(a) IN_CLASSD(a) +#define IN_EXPERIMENTAL(a) ((((in_addr_t)(a)) & 0xe0000000) == 0xe0000000) +#define IN_BADCLASS(a) ((((in_addr_t)(a)) & 0xf0000000) == 0xf0000000) +// } from musl + +#endif // _RELIBC_BITS_NETINET_IN_H +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[export] +include = [ + "sockaddr_in6", + "sockaddr_in", + "ipv6_mreq", + "ip_mreq", + "ip_mreq_source", + "group_req", + "group_source_req", + "in6_pktinfo", +] + +[export.rename] +"sockaddr_storage" = "struct sockaddr_storage" + +[enum] +prefix_with_name = true diff --git a/src/header/netinet_in/mod.rs b/src/header/netinet_in/mod.rs new file mode 100644 index 0000000000..9a2a9131dc --- /dev/null +++ b/src/header/netinet_in/mod.rs @@ -0,0 +1,209 @@ +//! `netinet/in.h` implementation. +//! +//! See . + +#![allow(non_camel_case_types)] + +use crate::{ + header::{bits_safamily_t::sa_family_t, sys_socket::sockaddr_storage}, + platform::types::{c_char, c_int}, +}; + +/// See . +pub type in_addr_t = u32; +/// See . +pub type in_port_t = u16; + +/// See . +#[repr(C)] +#[derive(Debug, Clone, Default)] +pub struct in_addr { + pub s_addr: in_addr_t, +} + +/// See . +#[repr(C)] +pub struct in6_addr { + pub s6_addr: [u8; 16], +} + +/// See . +#[repr(C)] +#[derive(Debug, Default)] +pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: in_port_t, + pub sin_addr: in_addr, + pub sin_zero: [c_char; 8], +} + +/// See . +#[repr(C)] +pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: in_port_t, + pub sin6_flowinfo: u32, + pub sin6_addr: in6_addr, + pub sin6_scope_id: u32, +} + +/// See . +#[repr(C)] +pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: u32, +} + +/// See . +#[repr(C)] +pub struct in6_pktinfo { + pub ipi6_addr: in6_addr, + pub ipi6_ifindex: u32, +} + +impl Clone for in6_pktinfo { + fn clone(&self) -> Self { + Self { + ipi6_addr: in6_addr { s6_addr: self.ipi6_addr.s6_addr }, + ipi6_ifindex: self.ipi6_ifindex, + } + } +} + +impl Default for in6_pktinfo { + fn default() -> Self { + Self { + ipi6_addr: in6_addr { s6_addr: [0; 16] }, + ipi6_ifindex: 0, + } + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_in6_pktinfo(in6_pktinfo: in6_pktinfo) {} + +// Address String Lengths +/// See . +pub const INET_ADDRSTRLEN: c_int = 16; +/// See . +pub const INET6_ADDRSTRLEN: c_int = 46; + +// IP options +/// Non-POSIX, see . +pub const IP_TOS: c_int = 1; +/// Non-POSIX, see . +pub const IP_RECVTOS: c_int = 13; + +// Protocol Numbers +/// See . +pub const IPPROTO_IP: u8 = 0; +/// See . +pub const IPPROTO_ICMP: u8 = 1; +/// Non-POSIX. +pub const IPPROTO_IGMP: u8 = 2; +/// See . +pub const IPPROTO_TCP: u8 = 6; +/// Non-POSIX. +pub const IPPROTO_PUP: u8 = 12; +/// See . +pub const IPPROTO_UDP: u8 = 17; +/// Non-POSIX. +pub const IPPROTO_IDP: u8 = 22; +/// See . +pub const IPPROTO_IPV6: u8 = 41; +/// See . +pub const IPPROTO_RAW: u8 = 0xff; +/// Non-POSIX. +pub const IPPROTO_MAX: u8 = 0xff; + +/// Non-POSIX, see . +pub const IP_TTL: c_int = 2; +/// See . +pub const IPV6_UNICAST_HOPS: c_int = 16; +/// See . +pub const IPV6_MULTICAST_IF: c_int = 17; +/// See . +pub const IPV6_MULTICAST_HOPS: c_int = 18; +/// See . +pub const IPV6_MULTICAST_LOOP: c_int = 19; +/// Non-POSIX. +pub const IPV6_ADD_MEMBERSHIP: c_int = 20; +pub const IPV6_JOIN_GROUP: c_int = 20; +/// Non-POSIX. +pub const IPV6_DROP_MEMBERSHIP: c_int = 21; +pub const IPV6_LEAVE_GROUP: c_int = 21; +/// See . +pub const IPV6_V6ONLY: c_int = 26; +/// Non-POSIX, see . +pub const IPV6_PKTINFO: c_int = 50; +/// Non-POSIX, see . +pub const IPV6_RECVPKTINFO: c_int = 49; +/// Non-POSIX, see . +pub const IP_MULTICAST_IF: c_int = 32; +/// Non-POSIX, see . +pub const IP_MULTICAST_TTL: c_int = 33; +/// Non-POSIX, see . +pub const IP_MULTICAST_LOOP: c_int = 34; +/// Non-POSIX, see . +pub const IP_ADD_MEMBERSHIP: c_int = 35; +/// Non-POSIX, see . +pub const IP_DROP_MEMBERSHIP: c_int = 36; + +/// See . +pub const INADDR_ANY: u32 = 0; // Can't use in_addr_t alias because cbindgen :( +/// See . +pub const INADDR_BROADCAST: u32 = 0xFFFF_FFFF; // Can't use core::u32::MAX because cbindgen :( +/// Non-POSIX. +pub const INADDR_NONE: u32 = 0xFFFF_FFFF; +/// Non-POSIX, see . +pub const INADDR_LOOPBACK: u32 = 0x7F00_0001; +/// Non-POSIX. +pub const INADDR_UNSPEC_GROUP: u32 = 0xE000_0000; +/// Non-POSIX. +pub const INADDR_ALLHOSTS_GROUP: u32 = 0xE000_0001; +/// Non-POSIX. +pub const INADDR_ALLRTRS_GROUP: u32 = 0xE000_0002; +/// Non-POSIX. +pub const INADDR_MAX_LOCAL_GROUP: u32 = 0xE000_00FF; + +/// Non-POSIX, see . +#[repr(C)] +pub struct ip_mreq_source { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, + pub imr_sourceaddr: in_addr, +} + +/// Non-POSIX, see . +#[repr(C)] +pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, +} + +/// Non-POSIX. +#[repr(C)] +pub struct group_req { + pub gr_interface: u32, + pub gr_group: sockaddr_storage, +} + +/// Non-POSIX. +#[repr(C)] +pub struct group_source_req { + pub gsr_interface: u32, + pub gsr_group: sockaddr_storage, + pub gsr_source: sockaddr_storage, +} + +/// See . +#[unsafe(no_mangle)] +pub static in6addr_any: in6_addr = in6_addr { + s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], +}; + +/// See . +#[unsafe(no_mangle)] +pub static in6addr_loopback: in6_addr = in6_addr { + s6_addr: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], +}; diff --git a/src/header/netinet_ip/cbindgen.toml b/src/header/netinet_ip/cbindgen.toml new file mode 100644 index 0000000000..11f2e66c4c --- /dev/null +++ b/src/header/netinet_ip/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = [] +include_guard = "_NETINET_IP_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/netinet_ip/mod.rs b/src/header/netinet_ip/mod.rs new file mode 100644 index 0000000000..30ddb6ebf4 --- /dev/null +++ b/src/header/netinet_ip/mod.rs @@ -0,0 +1,3 @@ +//! `netinet/ip.h` implementation. +//! +//! Non-POSIX. diff --git a/src/header/netinet_tcp/cbindgen.toml b/src/header/netinet_tcp/cbindgen.toml new file mode 100644 index 0000000000..64807753a2 --- /dev/null +++ b/src/header/netinet_tcp/cbindgen.toml @@ -0,0 +1,12 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_tcp.h.html +# +# There are no spec quotations relating to includes +sys_includes = [] +include_guard = "_NETINET_TCP_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/netinet_tcp/mod.rs b/src/header/netinet_tcp/mod.rs new file mode 100644 index 0000000000..99812e14e5 --- /dev/null +++ b/src/header/netinet_tcp/mod.rs @@ -0,0 +1,16 @@ +//! `netinet/tcp.h` implementation. +//! +//! See . + +use crate::platform::types::c_int; + +/// See . +pub const TCP_NODELAY: c_int = 1; +/// Non-POSIX, see . +pub const TCP_MAXSEG: c_int = 2; +/// Non-POSIX, see . +pub const TCP_KEEPIDLE: c_int = 4; +/// Non-POSIX, see . +pub const TCP_KEEPINTVL: c_int = 5; +/// Non-POSIX, see . +pub const TCP_KEEPCNT: c_int = 6; diff --git a/src/header/poll/cbindgen.toml b/src/header/poll/cbindgen.toml new file mode 100644 index 0000000000..246d5924cc --- /dev/null +++ b/src/header/poll/cbindgen.toml @@ -0,0 +1,15 @@ +sys_includes = ["signal.h"] +include_guard = "_RELIBC_POLL_H" +after_includes = """ +#include // for timespec +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"timespec" = "struct timespec" diff --git a/src/header/poll/mod.rs b/src/header/poll/mod.rs new file mode 100644 index 0000000000..8f42cf8366 --- /dev/null +++ b/src/header/poll/mod.rs @@ -0,0 +1,191 @@ +//! `poll.h` implementation. +//! +//! See . + +use core::{mem, ptr, slice}; + +use crate::{ + fs::File, + header::{ + bits_sigset_t::sigset_t, + bits_timespec::timespec, + errno::EBADF, + sys_epoll::{ + EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLHUP, EPOLLIN, EPOLLNVAL, EPOLLOUT, + EPOLLPRI, EPOLLRDBAND, EPOLLRDNORM, EPOLLWRBAND, EPOLLWRNORM, epoll_create1, epoll_ctl, + epoll_data, epoll_event, epoll_pwait, + }, + }, + platform::{ + self, + types::{c_int, c_short, c_ulong}, + }, +}; + +pub const POLLIN: c_short = 0x001; +pub const POLLPRI: c_short = 0x002; +pub const POLLOUT: c_short = 0x004; +pub const POLLERR: c_short = 0x008; +pub const POLLHUP: c_short = 0x010; +pub const POLLNVAL: c_short = 0x020; +pub const POLLRDNORM: c_short = 0x040; +pub const POLLRDBAND: c_short = 0x080; +pub const POLLWRNORM: c_short = 0x100; +pub const POLLWRBAND: c_short = 0x200; + +pub type nfds_t = c_ulong; + +/// See . +#[repr(C)] +pub struct pollfd { + pub fd: c_int, + pub events: c_short, + pub revents: c_short, +} + +pub unsafe fn poll_epoll(fds: &mut [pollfd], timeout: c_int, sigmask: *const sigset_t) -> c_int { + let event_map = [ + (POLLIN, EPOLLIN), + (POLLPRI, EPOLLPRI), + (POLLOUT, EPOLLOUT), + (POLLERR, EPOLLERR), + (POLLHUP, EPOLLHUP), + (POLLNVAL, EPOLLNVAL), + (POLLRDNORM, EPOLLRDNORM), + (POLLWRNORM, EPOLLWRNORM), + (POLLRDBAND, EPOLLRDBAND), + (POLLWRBAND, EPOLLWRBAND), + ]; + + let ep = { + let epfd = epoll_create1(EPOLL_CLOEXEC); + if epfd < 0 { + return -1; + } + File::new(epfd) + }; + + let mut closed = 0; + for (i, fd) in fds.iter_mut().enumerate() { + let pfd = fd; + + pfd.revents = 0; + + // Ignore the entry with negative fd + if pfd.fd < 0 { + continue; + } + + let mut event = epoll_event { + events: 0, + data: epoll_data { u64: i as u64 }, + ..Default::default() + }; + + for (p, ep) in event_map.iter() { + if pfd.events & p > 0 { + event.events |= ep; + } + } + + if unsafe { epoll_ctl(*ep, EPOLL_CTL_ADD, pfd.fd, &raw mut event) } < 0 { + if platform::ERRNO.get() == EBADF { + pfd.revents |= POLLNVAL; + closed += 1; + } else { + return -1; + } + } + } + + // Early exit if there are fds, and all are closed (revents = POLLNVAL) + if closed > 0 && closed == fds.len() { + return closed as i32; + } + + let mut events: [epoll_event; 32] = unsafe { mem::zeroed() }; + let res = unsafe { + epoll_pwait( + *ep, + events.as_mut_ptr(), + events.len() as c_int, + timeout, + sigmask, + ) + }; + if res < 0 { + return -1; + } + + for event in events.iter().take(res as usize) { + let pi = unsafe { event.data.u64 as usize }; + // TODO: Error status when fd does not match? + if let Some(pfd) = fds.get_mut(pi) { + for (p, ep) in event_map.iter() { + if event.events & ep > 0 { + pfd.revents |= p; + } + } + } + } + + let mut count = 0; + for pfd in fds.iter() { + if pfd.revents > 0 { + count += 1; + } + } + count +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: c_int) -> c_int { + trace_expr!( + unsafe { + poll_epoll( + slice::from_raw_parts_mut(fds, nfds as usize), + timeout, + ptr::null_mut(), + ) + }, + "poll({:p}, {}, {})", + fds, + nfds, + timeout, + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ppoll( + fds: *mut pollfd, + nfds: nfds_t, + tmo_p: *const timespec, + sigmask: *const sigset_t, +) -> c_int { + let timeout = if tmo_p.is_null() { + -1 + } else { + let tmo = unsafe { &*tmo_p }; + if tmo.tv_sec > (c_int::MAX / 1000) as _ { + c_int::MAX + } else { + ((tmo.tv_sec as c_int) * 1000) + ((tmo.tv_nsec as c_int) / 1000000) + } + }; + trace_expr!( + unsafe { + poll_epoll( + slice::from_raw_parts_mut(fds, nfds as usize), + timeout, + sigmask, + ) + }, + "ppoll({:p}, {}, {:p}, {:p})", + fds, + nfds, + tmo_p, + sigmask + ) +} diff --git a/src/header/pthread/attr.rs b/src/header/pthread/attr.rs new file mode 100644 index 0000000000..a975f9ea75 --- /dev/null +++ b/src/header/pthread/attr.rs @@ -0,0 +1,228 @@ +use core::{ptr, sync::atomic::Ordering}; + +use super::*; + +use crate::{ + header::bits_pthread::{pthread_attr_t, pthread_t}, + pthread::{Pthread, PthreadFlags}, +}; + +impl Default for RlctAttr { + fn default() -> Self { + Self { + // Default according to POSIX. + detachstate: PTHREAD_CREATE_JOINABLE as _, + // Default according to POSIX. + inheritsched: PTHREAD_INHERIT_SCHED as _, + // TODO: Linux + // Redox uses a round-robin scheduler + schedpolicy: SCHED_RR as _, + // TODO: Linux uses this one. + scope: PTHREAD_SCOPE_SYSTEM as _, + guardsize: Sys::getpagesize(), + // TODO + stack: 0, + // TODO + stacksize: 1024 * 1024, + param: sched_param { + // TODO + sched_priority: 0, + }, + #[cfg(target_pointer_width = "32")] + _pad: [0; 12], + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> c_int { + unsafe { ptr::drop_in_place(attr) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getdetachstate( + attr: *const pthread_attr_t, + detachstate: *mut c_int, +) -> c_int { + unsafe { ptr::write(detachstate, (*attr.cast::()).detachstate.into()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getguardsize( + attr: *const pthread_attr_t, + size: *mut size_t, +) -> c_int { + unsafe { ptr::write(size, (*attr.cast::()).guardsize) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getinheritsched( + attr: *const pthread_attr_t, + inheritsched: *mut c_int, +) -> c_int { + unsafe { ptr::write(inheritsched, (*attr.cast::()).inheritsched.into()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getschedparam( + attr: *const pthread_attr_t, + param: *mut sched_param, +) -> c_int { + unsafe { param.write((*attr.cast::()).param) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getschedpolicy( + attr: *const pthread_attr_t, + policy: *mut c_int, +) -> c_int { + unsafe { ptr::write(policy, (*attr.cast::()).schedpolicy.into()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getscope( + attr: *const pthread_attr_t, + scope: *mut c_int, +) -> c_int { + unsafe { ptr::write(scope, (*attr.cast::()).scope.into()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getstack( + attr: *const pthread_attr_t, + stackaddr: *mut *mut c_void, + stacksize: *mut size_t, +) -> c_int { + unsafe { ptr::write(stackaddr, (*attr.cast::()).stack as _) }; + unsafe { ptr::write(stacksize, (*attr.cast::()).stacksize as _) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_getstacksize( + attr: *const pthread_attr_t, + stacksize: *mut size_t, +) -> c_int { + unsafe { ptr::write(stacksize, (*attr.cast::()).stacksize as _) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int { + unsafe { ptr::write(attr.cast::(), RlctAttr::default()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setdetachstate( + attr: *mut pthread_attr_t, + detachstate: c_int, +) -> c_int { + unsafe { + (*attr.cast::()).detachstate = detachstate as _; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setguardsize( + attr: *mut pthread_attr_t, + guardsize: c_int, +) -> c_int { + unsafe { + (*attr.cast::()).guardsize = guardsize as _; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setinheritsched( + attr: *mut pthread_attr_t, + inheritsched: c_int, +) -> c_int { + unsafe { + (*attr.cast::()).inheritsched = inheritsched as _; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setschedparam( + attr: *mut pthread_attr_t, + param: *const sched_param, +) -> c_int { + unsafe { + (*attr.cast::()).param = param.read(); + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setschedpolicy( + attr: *mut pthread_attr_t, + policy: c_int, +) -> c_int { + unsafe { + (*attr.cast::()).schedpolicy = policy as u8; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setscope(attr: *mut pthread_attr_t, scope: c_int) -> c_int { + unsafe { + (*attr.cast::()).scope = scope as u8; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setstack( + attr: *mut pthread_attr_t, + stackaddr: *mut c_void, + stacksize: size_t, +) -> c_int { + unsafe { + (*attr.cast::()).stack = stackaddr as usize; + } + unsafe { + (*attr.cast::()).stacksize = stacksize; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_attr_setstacksize( + attr: *mut pthread_attr_t, + stacksize: size_t, +) -> c_int { + unsafe { + (*attr.cast::()).stacksize = stacksize; + } + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getattr_np( + thread_ptr: pthread_t, + attr_ptr: *mut pthread_attr_t, +) -> c_int { + let thread = unsafe { &*thread_ptr.cast::() }; + let attr_ptr = attr_ptr.cast::(); + unsafe { ptr::write(attr_ptr, RlctAttr::default()) }; + let attr = unsafe { &mut *attr_ptr }; + if thread.flags.load(Ordering::Acquire) & PthreadFlags::DETACHED.bits() != 0 { + attr.detachstate = PTHREAD_CREATE_DETACHED as _; + } + attr.stack = thread.stack_base as usize; + attr.stacksize = thread.stack_size; + //TODO: more values? + 0 +} diff --git a/src/header/pthread/barrier.rs b/src/header/pthread/barrier.rs new file mode 100644 index 0000000000..dedf715f7e --- /dev/null +++ b/src/header/pthread/barrier.rs @@ -0,0 +1,104 @@ +use crate::header::errno::*; + +use core::num::NonZeroU32; + +use crate::sync::barrier::*; + +use super::*; + +pub(crate) type RlctBarrier = Barrier; + +#[derive(Clone, Copy)] +pub(crate) struct RlctBarrierAttr { + pshared: c_int, +} +impl Default for RlctBarrierAttr { + fn default() -> Self { + // pshared = PTHREAD_PROCESS_PRIVATE is default according to POSIX. + Self { + pshared: PTHREAD_PROCESS_PRIVATE, + } + } +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrier_destroy(barrier: *mut pthread_barrier_t) -> c_int { + // Behavior is undefined if any thread is currently waiting when this is called. + + // No-op, currently. + unsafe { core::ptr::drop_in_place(barrier.cast::()) }; + + 0 +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrier_init( + barrier: *mut pthread_barrier_t, + attr: *const pthread_barrierattr_t, + count: c_uint, +) -> c_int { + let attr = unsafe { attr.cast::().as_ref() } + .copied() + .unwrap_or_default(); + + let Some(count) = NonZeroU32::new(count) else { + return EINVAL; + }; + + unsafe { barrier.cast::().write(RlctBarrier::new(count)) }; + 0 +} + +fn unlikely(condition: bool) -> bool { + condition +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrier_wait(barrier: *mut pthread_barrier_t) -> c_int { + let barrier = unsafe { &*barrier.cast::() }; + + match barrier.wait() { + WaitResult::NotifiedAll => PTHREAD_BARRIER_SERIAL_THREAD, + WaitResult::Waited => 0, + } +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrierattr_init(attr: *mut pthread_barrierattr_t) -> c_int { + unsafe { core::ptr::write(attr.cast::(), RlctBarrierAttr::default()) }; + + 0 +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrierattr_setpshared( + attr: *mut pthread_barrierattr_t, + pshared: c_int, +) -> c_int { + unsafe { + (*attr.cast::()).pshared = pshared; + } + 0 +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrierattr_getpshared( + attr: *const pthread_barrierattr_t, + pshared: *mut c_int, +) -> c_int { + unsafe { core::ptr::write(pshared, (*attr.cast::()).pshared) }; + 0 +} + +// Not async-signal-safe. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_barrierattr_destroy(attr: *mut pthread_barrierattr_t) -> c_int { + unsafe { core::ptr::drop_in_place(attr) }; + 0 +} diff --git a/src/header/pthread/cbindgen.toml b/src/header/pthread/cbindgen.toml new file mode 100644 index 0000000000..04b8d7da80 --- /dev/null +++ b/src/header/pthread/cbindgen.toml @@ -0,0 +1,13 @@ +sys_includes = ["sched.h", "time.h", "bits/pthread.h", "features.h"] +include_guard = "_RELIBC_PTHREAD_H" +language = "C" +style = "tag" +no_includes = true +cpp_compat = true + +[export.rename] +"timespec" = "struct timespec" +"sched_param" = "struct sched_param" + +[enum] +prefix_with_name = true diff --git a/src/header/pthread/cond.rs b/src/header/pthread/cond.rs new file mode 100644 index 0000000000..49467238da --- /dev/null +++ b/src/header/pthread/cond.rs @@ -0,0 +1,132 @@ +// Used design from https://www.remlab.net/op/futex-condvar.shtml + +use crate::header::time::CLOCK_REALTIME; + +use super::*; + +// PTHREAD_COND_INITIALIZER is defined manually in bits_pthread/cbindgen.toml + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> c_int { + e((unsafe { &*cond.cast::() }).broadcast()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> c_int { + // No-op + unsafe { core::ptr::drop_in_place(cond.cast::()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_init( + cond: *mut pthread_cond_t, + attr: *const pthread_condattr_t, +) -> c_int { + let attr = unsafe { attr.cast::().as_ref() } + .copied() + .unwrap_or_default(); + + if attr.clock != CLOCK_REALTIME { + // As monotonic clock always smaller than realtime clock, this always result in instant timeout. + todo_skip!(0, "pthread_cond_init with monotonic clock"); + } + + unsafe { cond.cast::().write(RlctCond::new()) }; + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_signal(cond: *mut pthread_cond_t) -> c_int { + e((unsafe { &*cond.cast::() }).signal()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_timedwait( + cond: *mut pthread_cond_t, + mutex: *mut pthread_mutex_t, + timeout: *const timespec, +) -> c_int { + e((unsafe { &*cond.cast::() }) + .timedwait(unsafe { &*mutex.cast::() }, unsafe { &*timeout })) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_clockwait( + cond: *mut pthread_cond_t, + mutex: *mut pthread_mutex_t, + clock_id: clockid_t, + timeout: *const timespec, +) -> c_int { + e((unsafe { &*cond.cast::() }).clockwait( + unsafe { &*mutex.cast::() }, + unsafe { &*timeout }, + clock_id, + )) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cond_wait( + cond: *mut pthread_cond_t, + mutex: *mut pthread_mutex_t, +) -> c_int { + e((unsafe { &*cond.cast::() }).wait(unsafe { &*mutex.cast::() })) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_condattr_destroy(condattr: *mut pthread_condattr_t) -> c_int { + unsafe { core::ptr::drop_in_place(condattr.cast::()) }; + // No-op + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_condattr_getclock( + condattr: *const pthread_condattr_t, + clock: *mut clockid_t, +) -> c_int { + unsafe { core::ptr::write(clock, (*condattr.cast::()).clock) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_condattr_getpshared( + condattr: *const pthread_condattr_t, + pshared: *mut c_int, +) -> c_int { + unsafe { core::ptr::write(pshared, (*condattr.cast::()).pshared) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_condattr_init(condattr: *mut pthread_condattr_t) -> c_int { + unsafe { + condattr + .cast::() + .write(RlctCondAttr::default()) + }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_condattr_setclock( + condattr: *mut pthread_condattr_t, + clock: clockid_t, +) -> c_int { + (unsafe { *condattr.cast::() }).clock = clock; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_condattr_setpshared( + condattr: *mut pthread_condattr_t, + pshared: c_int, +) -> c_int { + (unsafe { *condattr.cast::() }).pshared = pshared; + 0 +} + +pub(crate) type RlctCondAttr = crate::sync::cond::CondAttr; + +pub(crate) type RlctCond = crate::sync::cond::Cond; diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs new file mode 100644 index 0000000000..c742a425ff --- /dev/null +++ b/src/header/pthread/mod.rs @@ -0,0 +1,352 @@ +//! `pthread.h` implementation. +//! +//! See . + +use alloc::collections::LinkedList; +use core::{cell::Cell, ptr::NonNull}; + +use crate::{ + error::Errno, + header::{bits_timespec::timespec, sched::*}, + platform::{ + Pal, Sys, + types::{ + c_int, c_uchar, c_uint, c_void, clockid_t, pthread_attr_t, pthread_barrier_t, + pthread_barrierattr_t, pthread_cond_t, pthread_condattr_t, pthread_key_t, + pthread_mutex_t, pthread_mutexattr_t, pthread_once_t, pthread_rwlock_t, + pthread_rwlockattr_t, pthread_spinlock_t, pthread_t, size_t, + }, + }, + pthread, +}; + +pub fn e(result: Result<(), Errno>) -> i32 { + match result { + Ok(()) => 0, + Err(Errno(error)) => error, + } +} + +#[derive(Clone)] +pub(crate) struct RlctAttr { + pub detachstate: c_uchar, + pub inheritsched: c_uchar, + pub schedpolicy: c_uchar, + pub scope: c_uchar, + pub guardsize: size_t, + pub stacksize: size_t, + pub stack: size_t, + pub param: sched_param, + #[cfg(target_pointer_width = "32")] + _pad: [u8; 12], +} + +pub const _POSIX_THREADS: c_int = 1; + +pub const PTHREAD_BARRIER_SERIAL_THREAD: c_int = -1; + +pub const PTHREAD_CANCEL_ASYNCHRONOUS: c_int = 0; +pub const PTHREAD_CANCEL_ENABLE: c_int = 1; +pub const PTHREAD_CANCEL_DEFERRED: c_int = 2; +pub const PTHREAD_CANCEL_DISABLE: c_int = 3; +pub const PTHREAD_CANCELED: *mut c_void = (!0_usize) as *mut c_void; + +pub const PTHREAD_CREATE_DETACHED: c_int = 0; +pub const PTHREAD_CREATE_JOINABLE: c_int = 1; + +pub const PTHREAD_EXPLICIT_SCHED: c_int = 0; +pub const PTHREAD_INHERIT_SCHED: c_int = 1; + +pub const PTHREAD_MUTEX_DEFAULT: c_int = 0; +pub const PTHREAD_MUTEX_ERRORCHECK: c_int = 1; +pub const PTHREAD_MUTEX_NORMAL: c_int = 2; +pub const PTHREAD_MUTEX_RECURSIVE: c_int = 3; + +pub const PTHREAD_MUTEX_ROBUST: c_int = 0; +pub const PTHREAD_MUTEX_STALLED: c_int = 1; + +pub const PTHREAD_PRIO_INHERIT: c_int = 0; + +pub const PTHREAD_PRIO_NONE: c_int = 0; + +pub const PTHREAD_PRIO_PROTECT: c_int = 0; + +pub const PTHREAD_PROCESS_SHARED: c_int = 0; +pub const PTHREAD_PROCESS_PRIVATE: c_int = 1; + +pub const PTHREAD_SCOPE_PROCESS: c_int = 0; +pub const PTHREAD_SCOPE_SYSTEM: c_int = 1; + +pub mod attr; +pub use self::attr::*; + +pub mod barrier; +pub use self::barrier::*; + +pub mod cond; +pub use self::cond::*; + +#[thread_local] +pub static mut fork_hooks: [LinkedList; 3] = [const { LinkedList::new() }; 3]; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_cancel(thread: pthread_t) -> c_int { + match unsafe { pthread::cancel(&*thread.cast()) } { + Ok(()) => 0, + Err(Errno(error)) => error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_create( + pthread: *mut pthread_t, + attr: *const pthread_attr_t, + start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void, + arg: *mut c_void, +) -> c_int { + let attr = unsafe { attr.cast::().as_ref() }; + + match unsafe { pthread::create(attr, start_routine, arg) } { + Ok(ptr) => { + unsafe { core::ptr::write(pthread, ptr) }; + 0 + } + Err(Errno(code)) => code, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_detach(pthread: pthread_t) -> c_int { + match unsafe { pthread::detach(&*pthread.cast()) } { + Ok(()) => 0, + Err(Errno(errno)) => errno, + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn pthread_equal(pthread1: pthread_t, pthread2: pthread_t) -> c_int { + core::ptr::eq(pthread1, pthread2).into() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn pthread_atfork( + prepare: Option, + parent: Option, + child: Option, +) -> c_int { + if let Some(prepare) = prepare { + unsafe { + fork_hooks[0].push_back(prepare); + } + } + if let Some(parent) = parent { + unsafe { + fork_hooks[1].push_back(parent); + } + } + if let Some(child) = child { + unsafe { + fork_hooks[2].push_back(child); + } + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_exit(retval: *mut c_void) -> ! { + unsafe { pthread::exit_current_thread(pthread::Retval(retval)) } +} + +// Not in latest POSIX, mark as depreciated? +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getconcurrency() -> c_int { + // Redox and Linux threads are 1:1, not M:N. + 1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getcpuclockid( + thread: pthread_t, + clock_out: *mut clockid_t, +) -> c_int { + match pthread::get_cpu_clkid(unsafe { &*thread.cast() }) { + Ok(clock) => { + unsafe { clock_out.write(clock) }; + 0 + } + Err(Errno(error)) => error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getschedparam( + thread: pthread_t, + policy_out: *mut c_int, + param_out: *mut sched_param, +) -> c_int { + match pthread::get_sched_param(unsafe { &*thread.cast() }) { + Ok((policy, param)) => { + unsafe { policy_out.write(policy) }; + unsafe { param_out.write(param) }; + 0 + } + Err(Errno(error)) => error, + } +} + +pub mod tls; +pub use tls::*; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_join(thread: pthread_t, retval: *mut *mut c_void) -> c_int { + match unsafe { pthread::join(&*thread.cast()) } { + Ok(pthread::Retval(ret)) => { + if !retval.is_null() { + unsafe { core::ptr::write(retval, ret) }; + } + 0 + } + Err(Errno(error)) => error, + } +} + +pub mod mutex; +pub use self::mutex::*; + +pub mod once; +pub use self::once::*; + +pub mod rwlock; +pub use self::rwlock::*; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_self() -> pthread_t { + core::ptr::from_ref(unsafe { pthread::current_thread().unwrap_unchecked() }) as *mut _ +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int { + match pthread::set_cancel_state(state) { + Ok(old) => { + // POSIX doesn't imply oldstate can be NULL anywhere, but a lot of C code probably + // relies on it... + if let Some(oldstate) = NonNull::new(oldstate) { + unsafe { oldstate.write(old) }; + } + 0 + } + Err(Errno(error)) => error, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setcanceltype(ty: c_int, oldty: *mut c_int) -> c_int { + match pthread::set_cancel_type(ty) { + Ok(old) => { + // POSIX doesn't imply oldty can be NULL anywhere, but a lot of C code probably relies + // on it... + if let Some(oldty) = NonNull::new(oldty) { + unsafe { oldty.write(old) }; + } + 0 + } + Err(Errno(error)) => error, + } +} + +// Not in latest POSIX, mark as depreciated? +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn pthread_setconcurrency(concurrency: c_int) -> c_int { + // Redox and Linux threads are 1:1, not M:N. + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setschedparam( + thread: pthread_t, + policy: c_int, + param: *const sched_param, +) -> c_int { + e(pthread::set_sched_param( + unsafe { &*thread.cast() }, + policy, + unsafe { &*param }, + )) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setschedprio(thread: pthread_t, prio: c_int) -> c_int { + e(pthread::set_sched_priority( + unsafe { &*thread.cast() }, + prio, + )) +} + +pub mod spin; +pub use self::spin::*; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_testcancel() { + unsafe { pthread::testcancel() }; +} + +// Must be the same struct as defined in the pthread_cleanup_push macro. +#[repr(C)] +pub(crate) struct CleanupLinkedListEntry { + routine: extern "C" fn(*mut c_void), + arg: *mut c_void, + prev: *const c_void, +} + +#[thread_local] +pub(crate) static CLEANUP_LL_HEAD: Cell<*const CleanupLinkedListEntry> = + Cell::new(core::ptr::null()); + +// TODO: unwind? setjmp/longjmp? + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __relibc_internal_pthread_cleanup_push(new_entry: *mut c_void) { + let new_entry = unsafe { &mut *new_entry.cast::() }; + + new_entry.prev = CLEANUP_LL_HEAD.get().cast(); + CLEANUP_LL_HEAD.set(new_entry); +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __relibc_internal_pthread_cleanup_pop(execute: c_int) { + let prev_head = unsafe { CLEANUP_LL_HEAD.get().read() }; + CLEANUP_LL_HEAD.set(prev_head.prev.cast()); + + if execute != 0 { + (prev_head.routine)(prev_head.arg); + } +} + +pub(crate) unsafe fn run_destructor_stack() { + unsafe { crate::cxa::__cxa_thread_finalize() }; + + let mut ptr = CLEANUP_LL_HEAD.get(); + + while !ptr.is_null() { + let entry = unsafe { ptr.read() }; + ptr = entry.prev.cast(); + + (entry.routine)(entry.arg); + } +} diff --git a/src/header/pthread/mutex.rs b/src/header/pthread/mutex.rs new file mode 100644 index 0000000000..9f53257dbf --- /dev/null +++ b/src/header/pthread/mutex.rs @@ -0,0 +1,217 @@ +// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is +// dropped from src/lib.rs. +#![warn(warnings, unused_variables)] + +use super::*; +pub use crate::sync::pthread_mutex::RlctMutex; +use crate::{error::Errno, header::time::timespec_realtime_to_monotonic}; + +// PTHREAD_MUTEX_INITIALIZER is defined in bits_pthread/cbindgen.toml + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_consistent(mutex: *mut pthread_mutex_t) -> c_int { + e((unsafe { &*mutex.cast::() }).make_consistent()) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_destroy(mutex: *mut pthread_mutex_t) -> c_int { + // No-op + unsafe { core::ptr::drop_in_place(mutex.cast::()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_getprioceiling( + mutex: *const pthread_mutex_t, + prioceiling: *mut c_int, +) -> c_int { + match (unsafe { &*mutex.cast::() }).prioceiling() { + Ok(value) => { + unsafe { prioceiling.write(value) }; + 0 + } + Err(Errno(errno)) => errno, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_init( + mutex: *mut pthread_mutex_t, + attr: *const pthread_mutexattr_t, +) -> c_int { + let attr = unsafe { attr.cast::().as_ref() } + .cloned() + .unwrap_or_default(); + + match RlctMutex::new(&attr) { + Ok(new) => { + unsafe { mutex.cast::().write(new) }; + + 0 + } + Err(Errno(errno)) => errno, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_lock(mutex: *mut pthread_mutex_t) -> c_int { + e((unsafe { &*mutex.cast::() }).lock()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_setprioceiling( + mutex: *mut pthread_mutex_t, + prioceiling: c_int, + old_prioceiling: *mut c_int, +) -> c_int { + match (unsafe { &*mutex.cast::() }).replace_prioceiling(prioceiling) { + Ok(old) => { + unsafe { old_prioceiling.write(old) }; + 0 + } + Err(Errno(errno)) => errno, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_timedlock( + mutex: *mut pthread_mutex_t, + abstime: ×pec, +) -> c_int { + let relative = match timespec_realtime_to_monotonic(abstime.clone()) { + Ok(relative) => relative, + Err(err) => return e(Err(err)), + }; + + e((unsafe { &*mutex.cast::() }).lock_with_timeout(&relative)) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_trylock(mutex: *mut pthread_mutex_t) -> c_int { + e((unsafe { &*mutex.cast::() }).try_lock()) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutex_unlock(mutex: *mut pthread_mutex_t) -> c_int { + e((unsafe { &*mutex.cast::() }).unlock()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_destroy(attr: *mut pthread_mutexattr_t) -> c_int { + // No-op + unsafe { core::ptr::drop_in_place(attr.cast::()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_getprioceiling( + attr: *const pthread_mutexattr_t, + prioceiling: &mut c_int, +) -> c_int { + *prioceiling = unsafe { &*attr.cast::() }.prioceiling; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_getprotocol( + attr: *const pthread_mutexattr_t, + protocol: &mut c_int, +) -> c_int { + *protocol = unsafe { &*attr.cast::() }.protocol; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_getpshared( + attr: *const pthread_mutexattr_t, + pshared: &mut c_int, +) -> c_int { + *pshared = unsafe { &*attr.cast::() }.pshared; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_getrobust( + attr: *const pthread_mutexattr_t, + robust: &mut c_int, +) -> c_int { + *robust = unsafe { &*attr.cast::() }.robust; + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_gettype( + attr: *const pthread_mutexattr_t, + ty: &mut c_int, +) -> c_int { + *ty = unsafe { &*attr.cast::() }.ty; + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> c_int { + unsafe { attr.cast::().write(RlctMutexAttr::default()) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_setprioceiling( + attr: *mut pthread_mutexattr_t, + prioceiling: c_int, +) -> c_int { + unsafe { &mut *attr.cast::() }.prioceiling = prioceiling; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_setprotocol( + attr: *mut pthread_mutexattr_t, + protocol: c_int, +) -> c_int { + unsafe { &mut *attr.cast::() }.protocol = protocol; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_setpshared( + attr: *mut pthread_mutexattr_t, + pshared: c_int, +) -> c_int { + unsafe { &mut *attr.cast::() }.pshared = pshared; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_setrobust( + attr: *mut pthread_mutexattr_t, + robust: c_int, +) -> c_int { + unsafe { &mut *attr.cast::() }.robust = robust; + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_mutexattr_settype( + attr: *mut pthread_mutexattr_t, + ty: c_int, +) -> c_int { + unsafe { &mut *attr.cast::() }.ty = ty; + 0 +} + +#[repr(C)] +#[derive(Clone)] +pub(crate) struct RlctMutexAttr { + pub prioceiling: c_int, + pub protocol: c_int, + pub pshared: c_int, + pub robust: c_int, + pub ty: c_int, +} + +impl Default for RlctMutexAttr { + fn default() -> Self { + Self { + robust: PTHREAD_MUTEX_STALLED, + pshared: PTHREAD_PROCESS_PRIVATE, + protocol: PTHREAD_PRIO_NONE, + // TODO + prioceiling: 0, + ty: PTHREAD_MUTEX_DEFAULT, + } + } +} diff --git a/src/header/pthread/once.rs b/src/header/pthread/once.rs new file mode 100644 index 0000000000..76348bbcf6 --- /dev/null +++ b/src/header/pthread/once.rs @@ -0,0 +1,20 @@ +use super::*; + +// PTHREAD_ONCE_INIT + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_once( + once: *mut pthread_once_t, + constructor: extern "C" fn(), +) -> c_int { + let once = unsafe { &*once.cast::() }; + + // TODO: Cancellation points + + once.call_once(|| constructor()); + + //crate::sync::once::call_once_generic(&once.inner, || constructor()); + + 0 +} +pub(crate) type RlctOnce = crate::sync::Once<()>; diff --git a/src/header/pthread/rwlock.rs b/src/header/pthread/rwlock.rs new file mode 100644 index 0000000000..916579bd86 --- /dev/null +++ b/src/header/pthread/rwlock.rs @@ -0,0 +1,156 @@ +use super::*; + +use crate::header::errno::{EBUSY, EINVAL}; + +use crate::{header::time::CLOCK_REALTIME, pthread::Pshared}; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_init( + rwlock: *mut pthread_rwlock_t, + attr: *const pthread_rwlockattr_t, +) -> c_int { + let attr = unsafe { attr.cast::().as_ref() } + .copied() + .unwrap_or_default(); + + unsafe { + rwlock + .cast::() + .write(RlctRwlock::new(attr.pshared)) + }; + + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_rdlock(rwlock: *mut pthread_rwlock_t) -> c_int { + match unsafe { get(rwlock) }.acquire_read_lock(None) { + Ok(()) => 0, + Err(e) => e.0, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_clockrdlock( + rwlock: *mut pthread_rwlock_t, + clock_id: clockid_t, + abstime: *const timespec, +) -> c_int { + match unsafe { get(rwlock) }.acquire_read_lock(Some((unsafe { &*abstime }, clock_id))) { + Ok(()) => 0, + Err(e) => e.0, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_timedrdlock( + rwlock: *mut pthread_rwlock_t, + abstime: *const timespec, +) -> c_int { + match unsafe { get(rwlock) }.acquire_read_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) { + Ok(()) => 0, + Err(e) => e.0, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_clockwrlock( + rwlock: *mut pthread_rwlock_t, + clock_id: clockid_t, + abstime: *const timespec, +) -> c_int { + match unsafe { get(rwlock) }.acquire_write_lock(Some((unsafe { &*abstime }, clock_id))) { + Ok(()) => 0, + Err(e) => e.0, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_timedwrlock( + rwlock: *mut pthread_rwlock_t, + abstime: *const timespec, +) -> c_int { + match unsafe { get(rwlock) }.acquire_write_lock(Some((unsafe { &*abstime }, CLOCK_REALTIME))) { + Ok(()) => 0, + Err(e) => e.0, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_tryrdlock(rwlock: *mut pthread_rwlock_t) -> c_int { + match unsafe { get(rwlock) }.try_acquire_read_lock() { + Ok(()) => 0, + Err(_) => EBUSY, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_trywrlock(rwlock: *mut pthread_rwlock_t) -> c_int { + match unsafe { get(rwlock) }.try_acquire_write_lock() { + Ok(()) => 0, + Err(_) => EBUSY, + } +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_unlock(rwlock: *mut pthread_rwlock_t) -> c_int { + unsafe { get(rwlock) }.unlock(); + + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_wrlock(rwlock: *mut pthread_rwlock_t) -> c_int { + match unsafe { get(rwlock) }.acquire_write_lock(None) { + Ok(()) => 0, + Err(e) => e.0, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlockattr_init(attr: *mut pthread_rwlockattr_t) -> c_int { + unsafe { + attr.cast::() + .write(RlctRwlockAttr::default()) + }; + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlockattr_getpshared( + attr: *const pthread_rwlockattr_t, + pshared_out: *mut c_int, +) -> c_int { + unsafe { core::ptr::write(pshared_out, (*attr.cast::()).pshared.raw()) }; + + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlockattr_setpshared( + attr: *mut pthread_rwlockattr_t, + pshared: c_int, +) -> c_int { + let Some(pshared) = Pshared::from_raw(pshared) else { + return EINVAL; + }; + + (unsafe { *attr.cast::() }).pshared = pshared; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlockattr_destroy(attr: *mut pthread_rwlockattr_t) -> c_int { + unsafe { core::ptr::drop_in_place(attr) }; + + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_rwlock_destroy(rwlock: *mut pthread_rwlock_t) -> c_int { + unsafe { core::ptr::drop_in_place(rwlock) }; + + 0 +} + +pub(crate) type RlctRwlock = crate::sync::rwlock::InnerRwLock; + +#[derive(Clone, Copy, Default)] +pub(crate) struct RlctRwlockAttr { + pshared: Pshared, +} +#[inline] +unsafe fn get<'a>(ptr: *mut pthread_rwlock_t) -> &'a RlctRwlock { + unsafe { &*ptr.cast() } +} diff --git a/src/header/pthread/spin.rs b/src/header/pthread/spin.rs new file mode 100644 index 0000000000..094a9ea31b --- /dev/null +++ b/src/header/pthread/spin.rs @@ -0,0 +1,75 @@ +use core::sync::atomic::{AtomicI32 as AtomicInt, Ordering}; + +use crate::header::errno::EBUSY; + +use super::*; + +const UNLOCKED: c_int = 0; +const LOCKED: c_int = 1; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_spin_destroy(spinlock: *mut pthread_spinlock_t) -> c_int { + let _spinlock = unsafe { &mut *spinlock.cast::() }; + + // No-op + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_spin_init( + spinlock: *mut pthread_spinlock_t, + _pshared: c_int, +) -> c_int { + // TODO: pshared doesn't matter in most situations, as memory is just memory, but this may be + // different on some architectures... + + unsafe { + spinlock.cast::().write(RlctSpinlock { + inner: AtomicInt::new(UNLOCKED), + }) + }; + + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_spin_lock(spinlock: *mut pthread_spinlock_t) -> c_int { + let spinlock = unsafe { &*spinlock.cast::() }; + + loop { + match spinlock.inner.compare_exchange_weak( + UNLOCKED, + LOCKED, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => break, + Err(_) => core::hint::spin_loop(), + } + } + + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_spin_trylock(spinlock: *mut pthread_spinlock_t) -> c_int { + let spinlock = unsafe { &*spinlock.cast::() }; + + match spinlock + .inner + .compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed) + { + Ok(_) => (), + Err(_) => return EBUSY, + } + + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_spin_unlock(spinlock: *mut pthread_spinlock_t) -> c_int { + let spinlock = unsafe { &*spinlock.cast::() }; + + spinlock.inner.store(UNLOCKED, Ordering::Release); + + 0 +} +pub(crate) struct RlctSpinlock { + pub inner: AtomicInt, +} diff --git a/src/header/pthread/tls.rs b/src/header/pthread/tls.rs new file mode 100644 index 0000000000..83f2826c46 --- /dev/null +++ b/src/header/pthread/tls.rs @@ -0,0 +1,130 @@ +// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is +// dropped from src/lib.rs. +#![warn(warnings, unused_variables)] + +use super::*; + +// TODO: Hashmap? +use alloc::{collections::BTreeMap, vec::Vec}; + +use core::{ + cell::RefCell, + ptr, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use crate::{ + header::{errno::EINVAL, limits::PTHREAD_DESTRUCTOR_ITERATIONS}, + sync::Mutex, +}; + +type Dtor = Option; + +struct Record { + data: *mut c_void, +} + +#[thread_local] +static VALUES: RefCell> = RefCell::new(BTreeMap::new()); +static KEYS: Mutex> = Mutex::new(BTreeMap::new()); +static NEXTKEY: AtomicUsize = AtomicUsize::new(1); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_getspecific(key: pthread_key_t) -> *mut c_void { + // According to POSIX (issue 8): Calling [`pthread_getspecific`] with a key + // that has been deleted with [`pthread_key_delete`] or not obtained from + // [`pthread_key_create`] results in undefined behaviour. Therefore, we only + // do this check when debug assertions are explicitly enabled to avoid + // acquiring the global [`KEYS`] lock when it is not necessary. + debug_assert!(KEYS.lock().contains_key(&key)); + VALUES + .borrow_mut() + .get(&key) + .map(|record| record.data) + .unwrap_or(ptr::null_mut()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int { + if !KEYS.lock().contains_key(&key) { + // We don't have to return anything, but it's not less expensive to ignore it. + //println!("Invalid key for pthread_setspecific key {:#0x} value {:p}", key, value); + return EINVAL; + } + + let mut guard = VALUES.borrow_mut(); + + let record = guard.entry(key).or_insert(Record { + data: core::ptr::null_mut(), + }); + //println!("Valid key for pthread_setspecific key {:#0x} value {:p} (was {:p})", key, value, record.data); + + record.data = value.cast_mut(); + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_key_create( + key_ptr: *mut pthread_key_t, + destructor: Dtor, +) -> c_int { + let key = NEXTKEY.fetch_add(1, Ordering::SeqCst) as pthread_key_t; + + // TODO + //if key >= PTHREAD_KEYS_MAX { + //} + + KEYS.lock().insert(key, destructor); + + unsafe { key_ptr.write(key) }; + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_key_delete(key: pthread_key_t) -> c_int { + if KEYS.lock().remove(&key).is_none() || VALUES.borrow_mut().remove(&key).is_none() { + // We don't have to return anything, but it's not less expensive to ignore it. + return EINVAL; + } + + 0 +} + +pub(crate) unsafe fn run_all_destructors() { + for _ in 0..PTHREAD_DESTRUCTOR_ITERATIONS { + let mut any_run = false; + let dtors = { + let keys = KEYS.lock(); + keys.iter() + .filter_map(|(&key, &dtor)| dtor.map(|dtor| (key, dtor))) + .collect::>() + }; + + // According to POSIX (issue 8): There is no specific order in which we + // have to run the destructors. + for (key, dtor) in dtors { + let mut values = VALUES.borrow_mut(); + if let Some(record) = values.get_mut(&key) { + let val = record.data; + if val.is_null() { + continue; + } + record.data = ptr::null_mut(); + drop(values); + dtor(val); + any_run = true; + } + } + + if !any_run { + break; + } + } + + // According to POSIX (issue 8): If even after + // [`PTHREAD_DESTRUCTOR_ITERATIONS`] iterations there are still some + // non-NULL values with associated destructors, the behaviour is + // implementation-defined. We can choose to stop calling them or continue + // calling them until none are left. Both musl and glibc choose to stop + // calling them so we do the same. +} diff --git a/src/header/pty/cbindgen.toml b/src/header/pty/cbindgen.toml new file mode 100644 index 0000000000..3ecd31cfcf --- /dev/null +++ b/src/header/pty/cbindgen.toml @@ -0,0 +1,13 @@ +sys_includes = ["sys/ioctl.h", "termios.h"] +include_guard = "_RELIBC_PTY_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"winsize" = "struct winsize" +"termios" = "struct termios" \ No newline at end of file diff --git a/src/header/pty/linux.rs b/src/header/pty/linux.rs new file mode 100644 index 0000000000..020e336915 --- /dev/null +++ b/src/header/pty/linux.rs @@ -0,0 +1,58 @@ +use core::ptr; + +use crate::{ + header::{fcntl, sys_ioctl, unistd}, + io::{Cursor, Write}, + platform::types::{c_char, c_int, c_void}, +}; + +pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> { + //TODO: wrap in auto-close struct + let master = unsafe { fcntl::open(c"/dev/ptmx".as_ptr(), fcntl::O_RDWR | fcntl::O_NOCTTY, 0) }; + if master < 0 { + return Err(()); + } + + let mut lock: c_int = 0; + if unsafe { + sys_ioctl::ioctl( + master, + sys_ioctl::TIOCSPTLCK, + ptr::from_mut::(&mut lock).cast::(), + ) + } != 0 + { + unistd::close(master); + return Err(()); + } + + let mut ptn: c_int = 0; + if unsafe { + sys_ioctl::ioctl( + master, + sys_ioctl::TIOCGPTN, + ptr::from_mut::(&mut ptn).cast::(), + ) + } != 0 + { + unistd::close(master); + return Err(()); + } + + let mut cursor = Cursor::new(name); + if let Ok(()) = write!(cursor, "/dev/pts/{}\0", ptn) {}; // TODO handle error + + let slave = unsafe { + fcntl::open( + cursor.get_ref().as_ptr().cast::(), + fcntl::O_RDWR | fcntl::O_NOCTTY, + 0, + ) + }; + if slave < 0 { + unistd::close(master); + return Err(()); + } + + Ok((master, slave)) +} diff --git a/src/header/pty/mod.rs b/src/header/pty/mod.rs new file mode 100644 index 0000000000..995aef8aec --- /dev/null +++ b/src/header/pty/mod.rs @@ -0,0 +1,136 @@ +//! `pty.h` implementation. +//! +//! Non-POSIX, see . + +use core::{mem, ptr, slice}; + +use crate::{ + header::{ + bits_sigset_t, fcntl, limits, pthread, signal, sys_ioctl, sys_wait, termios, unistd, utmp, + }, + platform::{ + self, + types::{c_char, c_int, c_void}, + }, +}; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +mod imp; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +mod imp; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn openpty( + amaster: *mut c_int, + aslave: *mut c_int, + namep: *mut c_char, + termp: *const termios::termios, + winp: *const sys_ioctl::winsize, +) -> c_int { + let mut tmp_name = [0; limits::PATH_MAX]; + let name = if !namep.is_null() { + unsafe { slice::from_raw_parts_mut(namep.cast::(), limits::PATH_MAX) } + } else { + &mut tmp_name + }; + + let (master, slave) = match unsafe { imp::openpty(name) } { + Ok(ok) => ok, + Err(()) => return -1, + }; + + if !termp.is_null() { + unsafe { termios::tcsetattr(slave, termios::TCSANOW, termp) }; + } + + if !winp.is_null() { + unsafe { sys_ioctl::ioctl(slave, sys_ioctl::TIOCSWINSZ, winp as *mut c_void) }; + } + + unsafe { *amaster = master }; + unsafe { *aslave = slave }; + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn forkpty( + pm: *mut c_int, + name: *mut c_char, + tio: *const termios::termios, + ws: *const sys_ioctl::winsize, +) -> c_int { + let mut m = 0; + let mut s = 0; + let mut ec = 0; + let mut p: [c_int; 2] = [0; 2]; + let mut cs = 0; + let mut pid = -1; + let mut set = bits_sigset_t::sigset_t::default(); + let mut oldset = bits_sigset_t::sigset_t::default(); + + if unsafe { openpty(&raw mut m, &raw mut s, name, tio, ws) } < 0 { + return -1; + } + + unsafe { signal::sigfillset(&raw mut set) }; + unsafe { signal::pthread_sigmask(signal::SIG_BLOCK, &raw const set, &raw mut oldset) }; + unsafe { pthread::pthread_setcancelstate(pthread::PTHREAD_CANCEL_DISABLE, &raw mut cs) }; + + if unsafe { unistd::pipe2(p.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 { + unistd::close(s); + } else { + pid = unsafe { unistd::fork() }; + if pid == 0 { + unistd::close(m); + unistd::close(p[0]); + if unsafe { utmp::login_tty(s) } != 0 { + unsafe { + unistd::write( + p[1], + platform::ERRNO.as_ptr().cast(), + mem::size_of_val(&platform::ERRNO), + ) + }; + unistd::_exit(127); + } + unistd::close(p[1]); + unsafe { pthread::pthread_setcancelstate(cs, ptr::null_mut()) }; + unsafe { + signal::pthread_sigmask(signal::SIG_SETMASK, &raw const oldset, ptr::null_mut()) + }; + return 0; + } + + unistd::close(s); + unistd::close(p[1]); + + if unsafe { + unistd::read( + p[0], + ptr::from_mut::(&mut ec).cast::(), + mem::size_of::(), + ) + } > 0 + { + let mut status = 0; + unsafe { sys_wait::waitpid(pid, &raw mut status, 0) }; + pid = -1; + platform::ERRNO.set(ec); + } + unistd::close(p[0]); + } + if pid > 0 { + unsafe { *pm = m }; + } else { + unistd::close(m); + } + unsafe { pthread::pthread_setcancelstate(cs, ptr::null_mut()) }; + unsafe { signal::pthread_sigmask(signal::SIG_SETMASK, &raw const oldset, ptr::null_mut()) }; + pid +} diff --git a/src/header/pty/redox.rs b/src/header/pty/redox.rs new file mode 100644 index 0000000000..a4fad5c608 --- /dev/null +++ b/src/header/pty/redox.rs @@ -0,0 +1,33 @@ +use crate::{ + error::ResultExt, + header::{fcntl, unistd}, + platform::{ + Pal, Sys, + types::{c_char, c_int, ssize_t}, + }, +}; + +pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> { + let master = unsafe { fcntl::open(c"/scheme/pty".as_ptr(), fcntl::O_RDWR, 0) }; + if master < 0 { + return Err(()); + } + + // TODO: better error handling + let count = Sys::fpath(master, name) + .map(|u| u as ssize_t) + .or_minus_one_errno(); + if count < 0 { + unistd::close(master); + return Err(()); + } + name[count as usize] = 0; + + let slave = unsafe { fcntl::open(name.as_ptr() as *const c_char, fcntl::O_RDWR, 0) }; + if slave < 0 { + unistd::close(master); + return Err(()); + } + + Ok((master, slave)) +} diff --git a/src/header/pwd/cbindgen.toml b/src/header/pwd/cbindgen.toml new file mode 100644 index 0000000000..92e53b6f90 --- /dev/null +++ b/src/header/pwd/cbindgen.toml @@ -0,0 +1,13 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/pwd.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the gid_t, uid_t, and size_t types as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_PWD_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/pwd/linux.rs b/src/header/pwd/linux.rs new file mode 100644 index 0000000000..70e1fb1221 --- /dev/null +++ b/src/header/pwd/linux.rs @@ -0,0 +1,15 @@ +use super::{parsed, passwd}; +use crate::platform::types::c_char; + +pub fn split(line: &mut [u8]) -> Option { + let mut parts = line.split_mut(|&c| c == b'\0'); + Some(passwd { + pw_name: parts.next()?.as_mut_ptr().cast::(), + pw_passwd: parts.next()?.as_mut_ptr().cast::(), + pw_uid: parsed(parts.next())?, + pw_gid: parsed(parts.next())?, + pw_gecos: parts.next()?.as_mut_ptr().cast::(), + pw_dir: parts.next()?.as_mut_ptr().cast::(), + pw_shell: parts.next()?.as_mut_ptr().cast::(), + }) +} diff --git a/src/header/pwd/mod.rs b/src/header/pwd/mod.rs new file mode 100644 index 0000000000..e581a74ba0 --- /dev/null +++ b/src/header/pwd/mod.rs @@ -0,0 +1,330 @@ +//! `pwd.h` implementation. +//! +//! See . + +use alloc::{boxed::Box, vec::Vec}; +use core::{ + ops::{Deref, DerefMut}, + pin::Pin, + ptr, +}; + +use crate::{ + fs::File, + header::{errno, fcntl, string::strcmp}, + io::{BufReader, SeekFrom, prelude::*}, + platform::{ + self, + types::{c_char, c_int, gid_t, size_t, uid_t}, + }, + raw_cell::RawCell, +}; + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "redox")] +mod redox; + +#[cfg(target_os = "linux")] +use self::linux as sys; +#[cfg(target_os = "redox")] +use self::redox as sys; + +#[cfg(target_os = "linux")] +const SEPARATOR: u8 = b':'; + +#[cfg(target_os = "redox")] +const SEPARATOR: u8 = b';'; + +/// See +/// for POSIX minimum requirements, and +/// for further +/// details. +#[repr(C)] +#[derive(Debug)] +pub struct passwd { + pub pw_name: *mut c_char, + pub pw_passwd: *mut c_char, + pub pw_uid: uid_t, + pub pw_gid: gid_t, + pub pw_gecos: *mut c_char, + pub pw_dir: *mut c_char, + pub pw_shell: *mut c_char, +} + +static mut PASSWD_BUF: Option = None; +static PASSWD: RawCell = RawCell::new(passwd { + pw_name: ptr::null_mut(), + pw_passwd: ptr::null_mut(), + pw_uid: 0, + pw_gid: 0, + pw_gecos: ptr::null_mut(), + pw_dir: ptr::null_mut(), + pw_shell: ptr::null_mut(), +}); + +#[derive(Clone, Copy, Debug)] +struct DestBuffer { + ptr: *mut u8, + len: usize, +} + +#[derive(Debug)] +enum MaybeAllocated { + Owned(Pin>), + Borrowed(DestBuffer), +} +impl Deref for MaybeAllocated { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts(dst.ptr, dst.len) + }, + } + } +} +impl DerefMut for MaybeAllocated { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts_mut(dst.ptr, dst.len) + }, + } + } +} + +#[derive(Debug)] +struct OwnedPwd { + buffer: MaybeAllocated, + reference: passwd, +} + +impl OwnedPwd { + fn into_global(self) -> *mut passwd { + unsafe { + PASSWD_BUF = Some(self.buffer); + PASSWD.unsafe_set(self.reference); + PASSWD.as_mut_ptr() + } + } +} + +#[derive(Clone, Copy, Debug)] +enum Cause { + Eof, + Other, +} + +static READER: RawCell>> = RawCell::new(None); + +fn parsed(buf: Option) -> Option +where + I: core::borrow::Borrow<[u8]>, + O: core::str::FromStr, +{ + let buf = buf?; + let string = core::str::from_utf8(buf.borrow()).ok()?; + string.parse().ok() +} + +/// See . +/// +/// Non-POSIX +fn getpwent_r( + reader: &mut BufReader, + destination: Option, +) -> Result { + let mut buf = Vec::new(); + if reader + .read_until(b'\n', &mut buf) + .map_err(|_| Cause::Other)? + == 0 + { + return Err(Cause::Eof); + } + + // Replace all occurences of seperator with terminating NUL byte + let mut start = 0; + while let Some(i) = memchr::memchr(SEPARATOR, &buf[start..]) { + buf[start + i] = 0; + start += i + 1; + } + + // Place terminating NUL byte at the end, replace newline + let last = buf.last_mut(); + if last == Some(&mut b'\n') { + *last.unwrap() = 0; + } else { + buf.push(0); + } + + let mut buf = match destination { + None => MaybeAllocated::Owned(Box::into_pin(buf.into_boxed_slice())), + Some(dst) => { + let mut new = MaybeAllocated::Borrowed(dst); + if new.len() < buf.len() { + platform::ERRNO.set(errno::ERANGE); + return Err(Cause::Other); + } + new[..buf.len()].copy_from_slice(&buf); + new + } + }; + + // Chop up the result into a valid structure + let passwd = sys::split(&mut buf).ok_or(Cause::Other)?; + + Ok(OwnedPwd { + buffer: buf, + reference: passwd, + }) +} + +fn pwd_lookup(mut matches: F, destination: Option) -> Result +where + F: FnMut(&passwd) -> bool, +{ + let file = match File::open(c"/etc/passwd".into(), fcntl::O_RDONLY) { + Ok(file) => file, + Err(_) => return Err(Cause::Other), + }; + + let mut reader = BufReader::new(file); + + loop { + let entry = getpwent_r(&mut reader, destination)?; + + if matches(&entry.reference) { + return Ok(entry); + } + } +} + +unsafe fn mux( + status: Result, + out: *mut passwd, + result: *mut *mut passwd, +) -> c_int { + match status { + Ok(owned) => { + unsafe { *out = owned.reference }; + unsafe { *result = out }; + 0 + } + Err(Cause::Eof) => { + unsafe { *result = ptr::null_mut() }; + 0 + } + Err(Cause::Other) => { + unsafe { *result = ptr::null_mut() }; + -1 + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn endpwent() { + unsafe { + READER.unsafe_set(None); + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getpwent() -> *mut passwd { + let reader = match unsafe { &mut *READER.as_mut_ptr() } { + Some(reader) => reader, + None => { + let file = match File::open(c"/etc/passwd".into(), fcntl::O_RDONLY) { + Ok(file) => file, + Err(_) => return ptr::null_mut(), + }; + let reader = BufReader::new(file); + unsafe { + READER.unsafe_set(Some(reader)); + READER.unsafe_mut().as_mut().unwrap() + } + } + }; + getpwent_r(reader, None) + .map(|res| res.into_global()) + .unwrap_or(ptr::null_mut()) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getpwnam(name: *const c_char) -> *mut passwd { + pwd_lookup(|parts| unsafe { strcmp(parts.pw_name, name) } == 0, None) + .map(|res| res.into_global()) + .unwrap_or(ptr::null_mut()) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getpwnam_r( + name: *const c_char, + out: *mut passwd, + buf: *mut c_char, + size: size_t, + result: *mut *mut passwd, +) -> c_int { + unsafe { + mux( + pwd_lookup( + |parts| strcmp(parts.pw_name, name) == 0, + Some(DestBuffer { + ptr: buf.cast::(), + len: size, + }), + ), + out, + result, + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getpwuid(uid: uid_t) -> *mut passwd { + pwd_lookup(|parts| parts.pw_uid == uid, None) + .map(|res| res.into_global()) + .unwrap_or(ptr::null_mut()) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getpwuid_r( + uid: uid_t, + out: *mut passwd, + buf: *mut c_char, + size: size_t, + result: *mut *mut passwd, +) -> c_int { + let slice = unsafe { core::slice::from_raw_parts_mut(buf.cast::(), size) }; + unsafe { + mux( + pwd_lookup( + |part| part.pw_uid == uid, + Some(DestBuffer { + ptr: buf.cast::(), + len: size, + }), + ), + out, + result, + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setpwent() { + if let Some(reader) = unsafe { &mut *READER.as_mut_ptr() } { + let _ = reader.seek(SeekFrom::Start(0)); + } +} diff --git a/src/header/pwd/redox.rs b/src/header/pwd/redox.rs new file mode 100644 index 0000000000..8b3d723907 --- /dev/null +++ b/src/header/pwd/redox.rs @@ -0,0 +1,15 @@ +use super::{parsed, passwd}; +use crate::platform::types::c_char; + +pub fn split(line: &mut [u8]) -> Option { + let mut parts = line.split_mut(|&c| c == b'\0'); + Some(passwd { + pw_name: parts.next()?.as_mut_ptr().cast::(), + pw_passwd: c"x".as_ptr() as *const c_char as *mut c_char, + pw_uid: parsed(parts.next())?, + pw_gid: parsed(parts.next())?, + pw_gecos: parts.next()?.as_mut_ptr().cast::(), + pw_dir: parts.next()?.as_mut_ptr().cast::(), + pw_shell: parts.next()?.as_mut_ptr().cast::(), + }) +} diff --git a/src/header/regex/cbindgen.toml b/src/header/regex/cbindgen.toml new file mode 100644 index 0000000000..fa1c711c04 --- /dev/null +++ b/src/header/regex/cbindgen.toml @@ -0,0 +1,13 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/regex.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the size_t type as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_REGEX_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/regex/mod.rs b/src/header/regex/mod.rs new file mode 100644 index 0000000000..1b8da49041 --- /dev/null +++ b/src/header/regex/mod.rs @@ -0,0 +1,172 @@ +//! `regex.h` implementation. +//! +//! See . + +use crate::{ + header::string::strlen, + platform::types::{c_char, c_int, c_void, size_t}, +}; +use alloc::{borrow::Cow, boxed::Box}; +use core::{ptr, slice}; +use posix_regex::{PosixRegex, PosixRegexBuilder, compile::Error as CompileError, tree::Tree}; + +/// See . +pub type regoff_t = size_t; + +/// See . +#[repr(C)] +pub struct regex_t { + // Points to a posix_regex::Tree + ptr: *mut c_void, + cflags: c_int, + re_nsub: size_t, +} + +/// See . +#[repr(C)] +pub struct regmatch_t { + rm_so: regoff_t, + rm_eo: regoff_t, +} + +pub const REG_EXTENDED: c_int = 1; +pub const REG_ICASE: c_int = 2; +pub const REG_NOSUB: c_int = 4; +pub const REG_NEWLINE: c_int = 8; +pub const REG_NOTBOL: c_int = 16; +pub const REG_NOTEOL: c_int = 32; +pub const REG_MINIMAL: c_int = 64; + +pub const REG_NOMATCH: c_int = 1; +pub const REG_BADPAT: c_int = 2; +pub const REG_ECOLLATE: c_int = 3; +pub const REG_ECTYPE: c_int = 4; +pub const REG_EESCAPE: c_int = 5; +pub const REG_ESUBREG: c_int = 6; +pub const REG_EBRACK: c_int = 7; +pub const REG_ENOSYS: c_int = 8; +pub const REG_EPAREN: c_int = 9; +pub const REG_EBRACE: c_int = 10; +pub const REG_BADBR: c_int = 11; +pub const REG_ERANGE: c_int = 12; +pub const REG_ESPACE: c_int = 13; +pub const REG_BADRPT: c_int = 14; + +/// See . +#[unsafe(no_mangle)] +#[linkage = "weak"] // redefined in GIT +pub unsafe extern "C" fn regcomp(out: *mut regex_t, pat: *const c_char, cflags: c_int) -> c_int { + let pat = unsafe { slice::from_raw_parts(pat.cast::(), strlen(pat)) }; + let res = PosixRegexBuilder::new(pat) + .with_default_classes() + .extended(cflags & REG_EXTENDED == REG_EXTENDED) + .compile_tokens(); + + match res { + Ok(branches) => { + let re_nsub = PosixRegex::new(Cow::Borrowed(&branches)).count_groups(); + unsafe { + *out = regex_t { + ptr: Box::into_raw(Box::new(branches)).cast::(), + + cflags, + re_nsub, + } + }; + 0 + } + Err(CompileError::EmptyRepetition) + | Err(CompileError::IntegerOverflow) + | Err(CompileError::IllegalRange) => REG_BADBR, + Err(CompileError::UnclosedRepetition) => REG_EBRACE, + Err(CompileError::LeadingRepetition) => REG_BADRPT, + Err(CompileError::UnknownCollation) => REG_ECOLLATE, + Err(CompileError::UnknownClass(_)) => REG_ECTYPE, + Err(_) => REG_BADPAT, + } +} + +/// See . +#[unsafe(no_mangle)] +#[linkage = "weak"] // redefined in GIT +pub unsafe extern "C" fn regfree(regex: *mut regex_t) { + unsafe { drop(Box::from_raw((*regex).ptr)) }; +} + +/// See . +#[unsafe(no_mangle)] +#[linkage = "weak"] // redefined in GIT +pub unsafe extern "C" fn regexec( + regex: *const regex_t, + input: *const c_char, + nmatch: size_t, + pmatch: *mut regmatch_t, + eflags: c_int, +) -> c_int { + let regex = unsafe { &*regex }; + + // Allow specifying a compiler argument to the executor and viceversa + // because why not? + let flags = regex.cflags | eflags; + + let input = unsafe { slice::from_raw_parts(input.cast::(), strlen(input)) }; + let branches = unsafe { &*(regex.ptr.cast::()) }; + + let matches = PosixRegex::new(Cow::Borrowed(branches)) + .case_insensitive(flags & REG_ICASE == REG_ICASE) + .newline(flags & REG_NEWLINE == REG_NEWLINE) + .no_start(flags & REG_NOTBOL == REG_NOTBOL) + .no_end(flags & REG_NOTEOL == REG_NOTEOL) + .matches(input, Some(1)); + + if !matches.is_empty() && eflags & REG_NOSUB != REG_NOSUB && !pmatch.is_null() && nmatch > 0 { + let first = &matches[0]; + + for i in 0..nmatch { + let (start, end) = first.get(i).and_then(|&range| range).unwrap_or((!0, !0)); + unsafe { + *pmatch.add(i) = regmatch_t { + rm_so: start, + rm_eo: end, + } + }; + } + } + + if matches.is_empty() { REG_NOMATCH } else { 0 } +} + +/// See . +#[unsafe(no_mangle)] +#[linkage = "weak"] // redefined in GIT +pub extern "C" fn regerror( + code: c_int, + _regex: *const regex_t, + out: *mut c_char, + max: size_t, +) -> size_t { + let string = match code { + 0 => "No error\0", + REG_NOMATCH => "No match\0", + REG_BADPAT => "Invalid regexp\0", + REG_ECOLLATE => "Unknown collating element\0", + REG_ECTYPE => "Unknown character class name\0", + REG_EESCAPE => "Trailing backslash\0", + REG_ESUBREG => "Invalid back reference\0", + REG_EBRACK => "Missing ']'\0", + REG_ENOSYS => "Unsupported operation\0", + REG_EPAREN => "Missing ')'\0", + REG_EBRACE => "Missing '}'\0", + REG_BADBR => "Invalid contents of {}\0", + REG_ERANGE => "Invalid character range\0", + REG_ESPACE => "Out of memory\0", + REG_BADRPT => "Repetition not preceded by valid expression\0", + _ => "Unknown error\0", + }; + + unsafe { + ptr::copy_nonoverlapping(string.as_ptr(), out.cast::(), string.len().min(max)); + } + + string.len() +} diff --git a/src/header/sched/cbindgen.toml b/src/header/sched/cbindgen.toml new file mode 100644 index 0000000000..b361fa4531 --- /dev/null +++ b/src/header/sched/cbindgen.toml @@ -0,0 +1,22 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sched.h.html +# +# Spec quotations relating to includes: +# - "[PS] The header shall define the pid_t type as described in ." +# - "[SS|TSP] The header shall define the time_t type as described in ." +# - "The header shall define the timespec structure as described in ." +# - "Inclusion of the header may make visible all symbols from the header." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_SCHED_H" +after_includes = """ +#include // for timespec +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"timespec" = "struct timespec" diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs new file mode 100644 index 0000000000..bcdd34696c --- /dev/null +++ b/src/header/sched/mod.rs @@ -0,0 +1,76 @@ +//! `sched.h` implementation. +//! +//! See . + +use crate::{ + error::ResultExt, + header::bits_timespec::timespec, + platform::{ + Pal, Sys, + types::{c_int, pid_t}, + }, +}; + +// TODO: There are extensions, but adding more member is breaking ABI for pthread_attr_t +/// See . +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct sched_param { + pub sched_priority: c_int, +} + +/// See . +pub const SCHED_FIFO: c_int = 0; +/// See . +pub const SCHED_RR: c_int = 1; +/// See . +pub const SCHED_OTHER: c_int = 2; + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int { + todo!() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int { + todo!() +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int { + todo!() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn sched_rr_get_interval(pid: pid_t, time: *const timespec) -> c_int { + todo!() +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn sched_setparam(pid: pid_t, param: *const sched_param) -> c_int { + todo!() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn sched_setscheduler( + pid: pid_t, + policy: c_int, + param: *const sched_param, +) -> c_int { + todo!() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn sched_yield() -> c_int { + Sys::sched_yield().map(|()| 0).or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_sched_param(_: sched_param) {} diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml new file mode 100644 index 0000000000..be8f845c58 --- /dev/null +++ b/src/header/semaphore/cbindgen.toml @@ -0,0 +1,15 @@ +sys_includes = [] +include_guard = "_RELIBC_SEMAPHORE_H" +after_includes = """ +#include // for timespec +""" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"timespec" = "struct timespec" diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs new file mode 100644 index 0000000000..0ca2fa9ef1 --- /dev/null +++ b/src/header/semaphore/mod.rs @@ -0,0 +1,115 @@ +//! `semaphore.h` implementation. +//! +//! See . + +use crate::{ + header::{ + bits_timespec::timespec, + time::{CLOCK_MONOTONIC, CLOCK_REALTIME}, + }, + platform::types::{c_char, c_int, c_long, c_uint, clockid_t}, +}; + +/// See . +// TODO: Statically verify size and align +#[repr(C)] +#[derive(Clone, Copy)] +pub union sem_t { + pub size: [c_char; 4], + pub align: c_long, +} +pub type RlctSempahore = crate::sync::Semaphore; + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_close(sem: *mut sem_t) -> c_int { + todo!("named semaphores") +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_destroy(sem: *mut sem_t) -> c_int { + unsafe { core::ptr::drop_in_place(sem.cast::()) }; + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_getvalue(sem: *mut sem_t, sval: *mut c_int) -> c_int { + unsafe { sval.write(get(sem).value() as c_int) }; + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uint) -> c_int { + unsafe { sem.cast::().write(RlctSempahore::new(value)) }; + + 0 +} + +/// See . +// TODO: va_list +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_open( + name: *const c_char, + oflag: c_int, /* (va_list) value: c_uint */ +) -> *mut sem_t { + todo!("named semaphores") +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_post(sem: *mut sem_t) -> c_int { + unsafe { get(sem) }.post(1); + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int { + unsafe { get(sem) }.try_wait(); + + 0 +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int { + todo!("named semaphores") +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_wait(sem: *mut sem_t) -> c_int { + if let Ok(()) = unsafe { get(sem) }.wait(None, CLOCK_MONOTONIC) {}; // TODO handle error + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_clockwait( + sem: *mut sem_t, + clock_id: clockid_t, + abstime: *const timespec, +) -> c_int { + if let Ok(()) = unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), clock_id) {}; // TODO handle error + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sem_timedwait(sem: *mut sem_t, abstime: *const timespec) -> c_int { + if let Ok(()) = unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), CLOCK_REALTIME) { + }; // TODO handle error + + 0 +} + +unsafe fn get<'any>(sem: *mut sem_t) -> &'any RlctSempahore { + unsafe { &*sem.cast() } +} diff --git a/src/header/setjmp/impl/README.md b/src/header/setjmp/impl/README.md new file mode 100644 index 0000000000..be1ebb9ee7 --- /dev/null +++ b/src/header/setjmp/impl/README.md @@ -0,0 +1,6 @@ +# setjmp implementation + +All this code belongs to [musl libc](https://www.musl-libc.org/). +They own it. All rights go to them. +Huge thanks to them for doing this tedious per-arch assembly work! +We will reuse this awesome effort :) diff --git a/src/header/setjmp/impl/aarch64/longjmp.s b/src/header/setjmp/impl/aarch64/longjmp.s new file mode 100644 index 0000000000..7c4655fa96 --- /dev/null +++ b/src/header/setjmp/impl/aarch64/longjmp.s @@ -0,0 +1,24 @@ +.global _longjmp +.global longjmp +.type _longjmp,%function +.type longjmp,%function +_longjmp: +longjmp: + // IHI0055B_aapcs64.pdf 5.1.1, 5.1.2 callee saved registers + ldp x19, x20, [x0,#0] + ldp x21, x22, [x0,#16] + ldp x23, x24, [x0,#32] + ldp x25, x26, [x0,#48] + ldp x27, x28, [x0,#64] + ldp x29, x30, [x0,#80] + ldr x2, [x0,#104] + mov sp, x2 + ldp d8 , d9, [x0,#112] + ldp d10, d11, [x0,#128] + ldp d12, d13, [x0,#144] + ldp d14, d15, [x0,#160] + + mov x0, x1 + cbnz x1, 1f + mov x0, #1 +1: br x30 diff --git a/src/header/setjmp/impl/aarch64/setjmp.s b/src/header/setjmp/impl/aarch64/setjmp.s new file mode 100644 index 0000000000..f49288aa1c --- /dev/null +++ b/src/header/setjmp/impl/aarch64/setjmp.s @@ -0,0 +1,24 @@ +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +__setjmp: +_setjmp: +setjmp: + // IHI0055B_aapcs64.pdf 5.1.1, 5.1.2 callee saved registers + stp x19, x20, [x0,#0] + stp x21, x22, [x0,#16] + stp x23, x24, [x0,#32] + stp x25, x26, [x0,#48] + stp x27, x28, [x0,#64] + stp x29, x30, [x0,#80] + mov x2, sp + str x2, [x0,#104] + stp d8, d9, [x0,#112] + stp d10, d11, [x0,#128] + stp d12, d13, [x0,#144] + stp d14, d15, [x0,#160] + mov x0, #0 + ret diff --git a/src/header/setjmp/impl/aarch64/sigsetjmp.s b/src/header/setjmp/impl/aarch64/sigsetjmp.s new file mode 100644 index 0000000000..75910c4321 --- /dev/null +++ b/src/header/setjmp/impl/aarch64/sigsetjmp.s @@ -0,0 +1,21 @@ +.global sigsetjmp +.global __sigsetjmp +.type sigsetjmp,%function +.type __sigsetjmp,%function +sigsetjmp: +__sigsetjmp: + cbz x1,setjmp + + str x30,[x0,#176] + str x19,[x0,#176+8+8] + mov x19,x0 + + bl setjmp + + mov w1,w0 + mov x0,x19 + ldr x30,[x0,#176] + ldr x19,[x0,#176+8+8] + +.hidden __sigsetjmp_tail + b __sigsetjmp_tail diff --git a/src/header/setjmp/impl/arm/longjmp.s b/src/header/setjmp/impl/arm/longjmp.s new file mode 100644 index 0000000000..76cc2920a3 --- /dev/null +++ b/src/header/setjmp/impl/arm/longjmp.s @@ -0,0 +1,43 @@ +.syntax unified +.global _longjmp +.global longjmp +.type _longjmp,%function +.type longjmp,%function +_longjmp: +longjmp: + mov ip,r0 + movs r0,r1 + moveq r0,#1 + ldmia ip!, {v1,v2,v3,v4,v5,v6,sl,fp} + ldmia ip!, {r2,lr} + mov sp,r2 + + adr r1,1f + ldr r2,1f + ldr r1,[r1,r2] + + tst r1,#0x260 + beq 3f + tst r1,#0x20 + beq 2f + ldc p2, cr4, [ip], #48 +2: tst r1,#0x40 + beq 2f + .fpu vfp + vldmia ip!, {d8-d15} + .fpu softvfp + .eabi_attribute 10, 0 + .eabi_attribute 27, 0 +2: tst r1,#0x200 + beq 3f + ldcl p1, cr10, [ip], #8 + ldcl p1, cr11, [ip], #8 + ldcl p1, cr12, [ip], #8 + ldcl p1, cr13, [ip], #8 + ldcl p1, cr14, [ip], #8 + ldcl p1, cr15, [ip], #8 +3: bx lr + +.hidden __hwcap +.align 2 +1: .word __hwcap-1b diff --git a/src/header/setjmp/impl/arm/setjmp.s b/src/header/setjmp/impl/arm/setjmp.s new file mode 100644 index 0000000000..011315b70f --- /dev/null +++ b/src/header/setjmp/impl/arm/setjmp.s @@ -0,0 +1,45 @@ +.syntax unified +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,%function +.type _setjmp,%function +.type setjmp,%function +__setjmp: +_setjmp: +setjmp: + mov ip,r0 + stmia ip!,{v1,v2,v3,v4,v5,v6,sl,fp} + mov r2,sp + stmia ip!,{r2,lr} + mov r0,#0 + + adr r1,1f + ldr r2,1f + ldr r1,[r1,r2] + + tst r1,#0x260 + beq 3f + tst r1,#0x20 + beq 2f + stc p2, cr4, [ip], #48 +2: tst r1,#0x40 + beq 2f + .fpu vfp + vstmia ip!, {d8-d15} + .fpu softvfp + .eabi_attribute 10, 0 + .eabi_attribute 27, 0 +2: tst r1,#0x200 + beq 3f + stcl p1, cr10, [ip], #8 + stcl p1, cr11, [ip], #8 + stcl p1, cr12, [ip], #8 + stcl p1, cr13, [ip], #8 + stcl p1, cr14, [ip], #8 + stcl p1, cr15, [ip], #8 +3: bx lr + +.hidden __hwcap +.align 2 +1: .word __hwcap-1b diff --git a/src/header/setjmp/impl/i386/longjmp.s b/src/header/setjmp/impl/i386/longjmp.s new file mode 100644 index 0000000000..a2378b9fc3 --- /dev/null +++ b/src/header/setjmp/impl/i386/longjmp.s @@ -0,0 +1,16 @@ +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + mov edx, [esp + 4] + mov eax, [esp + 8] + cmp eax, 1 + adc al, 0 + mov ebx, [edx] + mov esi, [edx + 4] + mov edi, [edx + 8] + mov ebp, [edx + 12] + mov esp, [edx + 16] + jmp [edx + 20] diff --git a/src/header/setjmp/impl/i386/setjmp.s b/src/header/setjmp/impl/i386/setjmp.s new file mode 100644 index 0000000000..03678fdf41 --- /dev/null +++ b/src/header/setjmp/impl/i386/setjmp.s @@ -0,0 +1,23 @@ +.global ___setjmp +.hidden ___setjmp +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +___setjmp: +__setjmp: +_setjmp: +setjmp: + mov eax, [esp + 4] + mov [eax], ebx + mov [eax + 4], esi + mov [eax + 8], edi + mov [eax + 12], ebp + lea ecx, [esp + 4] + mov [eax + 16], ecx + mov ecx, [esp] + mov [eax + 20], ecx + xor eax, eax + ret diff --git a/src/header/setjmp/impl/i386/sigsetjmp.s b/src/header/setjmp/impl/i386/sigsetjmp.s new file mode 100644 index 0000000000..e1be3b444f --- /dev/null +++ b/src/header/setjmp/impl/i386/sigsetjmp.s @@ -0,0 +1,26 @@ +.global sigsetjmp +.global __sigsetjmp +.type sigsetjmp,@function +.type __sigsetjmp,@function +sigsetjmp: +__sigsetjmp: + mov ecx, dword ptr [esp + 8] + jecxz 1f + + mov eax, dword ptr [esp + 4] + pop [eax + 24] + mov dword ptr [eax + 8 + 28], ebx + mov ebx, eax + +.hidden ___setjmp + call ___setjmp + + push [ebx + 24] + mov dword ptr [esp + 4], ebx + mov dword ptr [esp + 8], eax + mov ebx, dword ptr [ebx + 8 + 28] + +.hidden __sigsetjmp_tail + jmp __sigsetjmp_tail + +1: jmp ___setjmp diff --git a/src/header/setjmp/impl/m68k/longjmp.s b/src/header/setjmp/impl/m68k/longjmp.s new file mode 100644 index 0000000000..cdb05fb5a6 --- /dev/null +++ b/src/header/setjmp/impl/m68k/longjmp.s @@ -0,0 +1,14 @@ +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + movea.l 4(%sp),%a0 + move.l 8(%sp),%d0 + bne 1f + move.l #1,%d0 +1: movem.l (%a0),%d2-%d7/%a2-%a7 + fmovem.x 52(%a0),%fp2-%fp7 + move.l 48(%a0),(%sp) + rts diff --git a/src/header/setjmp/impl/m68k/setjmp.s b/src/header/setjmp/impl/m68k/setjmp.s new file mode 100644 index 0000000000..15e549b0ef --- /dev/null +++ b/src/header/setjmp/impl/m68k/setjmp.s @@ -0,0 +1,18 @@ +.global ___setjmp +.hidden ___setjmp +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +___setjmp: +__setjmp: +_setjmp: +setjmp: + movea.l 4(%sp),%a0 + movem.l %d2-%d7/%a2-%a7,(%a0) + move.l (%sp),48(%a0) + fmovem.x %fp2-%fp7,52(%a0) + clr.l %d0 + rts diff --git a/src/header/setjmp/impl/microblaze/longjmp.s b/src/header/setjmp/impl/microblaze/longjmp.s new file mode 100644 index 0000000000..c0760288a7 --- /dev/null +++ b/src/header/setjmp/impl/microblaze/longjmp.s @@ -0,0 +1,29 @@ +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + addi r3, r6, 0 + bnei r3, 1f + addi r3, r3, 1 +1: lwi r1, r5, 0 + lwi r15, r5, 4 + lwi r2, r5, 8 + lwi r13, r5, 12 + lwi r18, r5, 16 + lwi r19, r5, 20 + lwi r20, r5, 24 + lwi r21, r5, 28 + lwi r22, r5, 32 + lwi r23, r5, 36 + lwi r24, r5, 40 + lwi r25, r5, 44 + lwi r26, r5, 48 + lwi r27, r5, 52 + lwi r28, r5, 56 + lwi r29, r5, 60 + lwi r30, r5, 64 + lwi r31, r5, 68 + rtsd r15, 8 + nop diff --git a/src/header/setjmp/impl/microblaze/setjmp.s b/src/header/setjmp/impl/microblaze/setjmp.s new file mode 100644 index 0000000000..605ab20e4b --- /dev/null +++ b/src/header/setjmp/impl/microblaze/setjmp.s @@ -0,0 +1,32 @@ +.global ___setjmp +.hidden ___setjmp +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +___setjmp: +__setjmp: +_setjmp: +setjmp: + swi r1, r5, 0 + swi r15, r5, 4 + swi r2, r5, 8 + swi r13, r5, 12 + swi r18, r5, 16 + swi r19, r5, 20 + swi r20, r5, 24 + swi r21, r5, 28 + swi r22, r5, 32 + swi r23, r5, 36 + swi r24, r5, 40 + swi r25, r5, 44 + swi r26, r5, 48 + swi r27, r5, 52 + swi r28, r5, 56 + swi r29, r5, 60 + swi r30, r5, 64 + swi r31, r5, 68 + rtsd r15, 8 + ori r3, r0, 0 diff --git a/src/header/setjmp/impl/mips/longjmp.S b/src/header/setjmp/impl/mips/longjmp.S new file mode 100644 index 0000000000..fdb6c95d25 --- /dev/null +++ b/src/header/setjmp/impl/mips/longjmp.S @@ -0,0 +1,40 @@ +.set noreorder + +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + move $2, $5 + bne $2, $0, 1f + nop + addu $2, $2, 1 +1: +#ifndef __mips_soft_float + lwc1 $20, 56($4) + lwc1 $21, 60($4) + lwc1 $22, 64($4) + lwc1 $23, 68($4) + lwc1 $24, 72($4) + lwc1 $25, 76($4) + lwc1 $26, 80($4) + lwc1 $27, 84($4) + lwc1 $28, 88($4) + lwc1 $29, 92($4) + lwc1 $30, 96($4) + lwc1 $31, 100($4) +#endif + lw $ra, 0($4) + lw $sp, 4($4) + lw $16, 8($4) + lw $17, 12($4) + lw $18, 16($4) + lw $19, 20($4) + lw $20, 24($4) + lw $21, 28($4) + lw $22, 32($4) + lw $23, 36($4) + lw $30, 40($4) + jr $ra + lw $28, 44($4) diff --git a/src/header/setjmp/impl/mips/setjmp.S b/src/header/setjmp/impl/mips/setjmp.S new file mode 100644 index 0000000000..501d5264e6 --- /dev/null +++ b/src/header/setjmp/impl/mips/setjmp.S @@ -0,0 +1,39 @@ +.set noreorder + +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +__setjmp: +_setjmp: +setjmp: + sw $ra, 0($4) + sw $sp, 4($4) + sw $16, 8($4) + sw $17, 12($4) + sw $18, 16($4) + sw $19, 20($4) + sw $20, 24($4) + sw $21, 28($4) + sw $22, 32($4) + sw $23, 36($4) + sw $30, 40($4) + sw $28, 44($4) +#ifndef __mips_soft_float + swc1 $20, 56($4) + swc1 $21, 60($4) + swc1 $22, 64($4) + swc1 $23, 68($4) + swc1 $24, 72($4) + swc1 $25, 76($4) + swc1 $26, 80($4) + swc1 $27, 84($4) + swc1 $28, 88($4) + swc1 $29, 92($4) + swc1 $30, 96($4) + swc1 $31, 100($4) +#endif + jr $ra + li $2, 0 diff --git a/src/header/setjmp/impl/mips64/longjmp.S b/src/header/setjmp/impl/mips64/longjmp.S new file mode 100644 index 0000000000..3db8a883c6 --- /dev/null +++ b/src/header/setjmp/impl/mips64/longjmp.S @@ -0,0 +1,37 @@ +.set noreorder +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + move $2, $5 + + bne $2, $0, 1f + nop + daddu $2, $2, 1 +1: +#ifndef __mips_soft_float + ldc1 $24, 96($4) + ldc1 $25, 104($4) + ldc1 $26, 112($4) + ldc1 $27, 120($4) + ldc1 $28, 128($4) + ldc1 $29, 136($4) + ldc1 $30, 144($4) + ldc1 $31, 152($4) +#endif + ld $ra, 0($4) + ld $sp, 8($4) + ld $gp, 16($4) + ld $16, 24($4) + ld $17, 32($4) + ld $18, 40($4) + ld $19, 48($4) + ld $20, 56($4) + ld $21, 64($4) + ld $22, 72($4) + ld $23, 80($4) + ld $30, 88($4) + jr $ra + nop diff --git a/src/header/setjmp/impl/mips64/setjmp.S b/src/header/setjmp/impl/mips64/setjmp.S new file mode 100644 index 0000000000..b9646c2abe --- /dev/null +++ b/src/header/setjmp/impl/mips64/setjmp.S @@ -0,0 +1,34 @@ +.set noreorder +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +__setjmp: +_setjmp: +setjmp: + sd $ra, 0($4) + sd $sp, 8($4) + sd $gp, 16($4) + sd $16, 24($4) + sd $17, 32($4) + sd $18, 40($4) + sd $19, 48($4) + sd $20, 56($4) + sd $21, 64($4) + sd $22, 72($4) + sd $23, 80($4) + sd $30, 88($4) +#ifndef __mips_soft_float + sdc1 $24, 96($4) + sdc1 $25, 104($4) + sdc1 $26, 112($4) + sdc1 $27, 120($4) + sdc1 $28, 128($4) + sdc1 $29, 136($4) + sdc1 $30, 144($4) + sdc1 $31, 152($4) +#endif + jr $ra + li $2, 0 diff --git a/src/header/setjmp/impl/mipsn32/longjmp.S b/src/header/setjmp/impl/mipsn32/longjmp.S new file mode 100644 index 0000000000..30c3ee0b0c --- /dev/null +++ b/src/header/setjmp/impl/mipsn32/longjmp.S @@ -0,0 +1,36 @@ +.set noreorder +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + move $2, $5 + bne $2, $0, 1f + nop + addu $2, $2, 1 +1: +#ifndef __mips_soft_float + ldc1 $24, 96($4) + ldc1 $25, 104($4) + ldc1 $26, 112($4) + ldc1 $27, 120($4) + ldc1 $28, 128($4) + ldc1 $29, 136($4) + ldc1 $30, 144($4) + ldc1 $31, 152($4) +#endif + ld $ra, 0($4) + ld $sp, 8($4) + ld $gp, 16($4) + ld $16, 24($4) + ld $17, 32($4) + ld $18, 40($4) + ld $19, 48($4) + ld $20, 56($4) + ld $21, 64($4) + ld $22, 72($4) + ld $23, 80($4) + ld $30, 88($4) + jr $ra + nop diff --git a/src/header/setjmp/impl/mipsn32/setjmp.S b/src/header/setjmp/impl/mipsn32/setjmp.S new file mode 100644 index 0000000000..b9646c2abe --- /dev/null +++ b/src/header/setjmp/impl/mipsn32/setjmp.S @@ -0,0 +1,34 @@ +.set noreorder +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +__setjmp: +_setjmp: +setjmp: + sd $ra, 0($4) + sd $sp, 8($4) + sd $gp, 16($4) + sd $16, 24($4) + sd $17, 32($4) + sd $18, 40($4) + sd $19, 48($4) + sd $20, 56($4) + sd $21, 64($4) + sd $22, 72($4) + sd $23, 80($4) + sd $30, 88($4) +#ifndef __mips_soft_float + sdc1 $24, 96($4) + sdc1 $25, 104($4) + sdc1 $26, 112($4) + sdc1 $27, 120($4) + sdc1 $28, 128($4) + sdc1 $29, 136($4) + sdc1 $30, 144($4) + sdc1 $31, 152($4) +#endif + jr $ra + li $2, 0 diff --git a/src/header/setjmp/impl/or1k/longjmp.s b/src/header/setjmp/impl/or1k/longjmp.s new file mode 100644 index 0000000000..1db9fd9339 --- /dev/null +++ b/src/header/setjmp/impl/or1k/longjmp.s @@ -0,0 +1,25 @@ +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + l.sfeqi r4, 0 + l.bnf 1f + l.addi r11, r4,0 + l.ori r11, r0, 1 +1: l.lwz r1, 0(r3) + l.lwz r2, 4(r3) + l.lwz r9, 8(r3) + l.lwz r10, 12(r3) + l.lwz r14, 16(r3) + l.lwz r16, 20(r3) + l.lwz r18, 24(r3) + l.lwz r20, 28(r3) + l.lwz r22, 32(r3) + l.lwz r24, 36(r3) + l.lwz r26, 40(r3) + l.lwz r28, 44(r3) + l.lwz r30, 48(r3) + l.jr r9 + l.nop diff --git a/src/header/setjmp/impl/or1k/setjmp.s b/src/header/setjmp/impl/or1k/setjmp.s new file mode 100644 index 0000000000..0677033843 --- /dev/null +++ b/src/header/setjmp/impl/or1k/setjmp.s @@ -0,0 +1,27 @@ +.global ___setjmp +.hidden ___setjmp +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +___setjmp: +__setjmp: +_setjmp: +setjmp: + l.sw 0(r3), r1 + l.sw 4(r3), r2 + l.sw 8(r3), r9 + l.sw 12(r3), r10 + l.sw 16(r3), r14 + l.sw 20(r3), r16 + l.sw 24(r3), r18 + l.sw 28(r3), r20 + l.sw 32(r3), r22 + l.sw 36(r3), r24 + l.sw 40(r3), r26 + l.sw 44(r3), r28 + l.sw 48(r3), r30 + l.jr r9 + l.ori r11,r0,0 diff --git a/src/header/setjmp/impl/powerpc/longjmp.S b/src/header/setjmp/impl/powerpc/longjmp.S new file mode 100644 index 0000000000..e598bd056e --- /dev/null +++ b/src/header/setjmp/impl/powerpc/longjmp.S @@ -0,0 +1,69 @@ + .global _longjmp + .global longjmp + .type _longjmp,@function + .type longjmp,@function +_longjmp: +longjmp: + /* + * void longjmp(jmp_buf env, int val); + * put val into return register and restore the env saved in setjmp + * if val(r4) is 0, put 1 there. + */ + /* 0) move old return address into r0 */ + lwz 0, 0(3) + /* 1) put it into link reg */ + mtlr 0 + /* 2 ) restore stack ptr */ + lwz 1, 4(3) + /* 3) restore control reg */ + lwz 0, 8(3) + mtcr 0 + /* 4) restore r14-r31 */ + lwz 14, 12(3) + lwz 15, 16(3) + lwz 16, 20(3) + lwz 17, 24(3) + lwz 18, 28(3) + lwz 19, 32(3) + lwz 20, 36(3) + lwz 21, 40(3) + lwz 22, 44(3) + lwz 23, 48(3) + lwz 24, 52(3) + lwz 25, 56(3) + lwz 26, 60(3) + lwz 27, 64(3) + lwz 28, 68(3) + lwz 29, 72(3) + lwz 30, 76(3) + lwz 31, 80(3) +#ifndef _SOFT_FLOAT + lfd 14,88(3) + lfd 15,96(3) + lfd 16,104(3) + lfd 17,112(3) + lfd 18,120(3) + lfd 19,128(3) + lfd 20,136(3) + lfd 21,144(3) + lfd 22,152(3) + lfd 23,160(3) + lfd 24,168(3) + lfd 25,176(3) + lfd 26,184(3) + lfd 27,192(3) + lfd 28,200(3) + lfd 29,208(3) + lfd 30,216(3) + lfd 31,224(3) +#endif + /* 5) put val into return reg r3 */ + mr 3, 4 + + /* 6) check if return value is 0, make it 1 in that case */ + cmpwi cr7, 4, 0 + bne cr7, 1f + li 3, 1 +1: + blr + diff --git a/src/header/setjmp/impl/powerpc/setjmp.S b/src/header/setjmp/impl/powerpc/setjmp.S new file mode 100644 index 0000000000..cd91a207f5 --- /dev/null +++ b/src/header/setjmp/impl/powerpc/setjmp.S @@ -0,0 +1,63 @@ + .global ___setjmp + .hidden ___setjmp + .global __setjmp + .global _setjmp + .global setjmp + .type __setjmp,@function + .type _setjmp,@function + .type setjmp,@function +___setjmp: +__setjmp: +_setjmp: +setjmp: + /* 0) store IP int 0, then into the jmpbuf pointed to by r3 (first arg) */ + mflr 0 + stw 0, 0(3) + /* 1) store reg1 (SP) */ + stw 1, 4(3) + /* 2) store cr */ + mfcr 0 + stw 0, 8(3) + /* 3) store r14-31 */ + stw 14, 12(3) + stw 15, 16(3) + stw 16, 20(3) + stw 17, 24(3) + stw 18, 28(3) + stw 19, 32(3) + stw 20, 36(3) + stw 21, 40(3) + stw 22, 44(3) + stw 23, 48(3) + stw 24, 52(3) + stw 25, 56(3) + stw 26, 60(3) + stw 27, 64(3) + stw 28, 68(3) + stw 29, 72(3) + stw 30, 76(3) + stw 31, 80(3) +#ifndef _SOFT_FLOAT + stfd 14,88(3) + stfd 15,96(3) + stfd 16,104(3) + stfd 17,112(3) + stfd 18,120(3) + stfd 19,128(3) + stfd 20,136(3) + stfd 21,144(3) + stfd 22,152(3) + stfd 23,160(3) + stfd 24,168(3) + stfd 25,176(3) + stfd 26,184(3) + stfd 27,192(3) + stfd 28,200(3) + stfd 29,208(3) + stfd 30,216(3) + stfd 31,224(3) +#endif + /* 4) set return value to 0 */ + li 3, 0 + /* 5) return */ + blr diff --git a/src/header/setjmp/impl/powerpc64/longjmp.s b/src/header/setjmp/impl/powerpc64/longjmp.s new file mode 100644 index 0000000000..81d45ff60b --- /dev/null +++ b/src/header/setjmp/impl/powerpc64/longjmp.s @@ -0,0 +1,81 @@ + .global _longjmp + .global longjmp + .type _longjmp,@function + .type longjmp,@function +_longjmp: +longjmp: + # 0) move old return address into the link register + ld 0, 0*8(3) + mtlr 0 + # 1) restore cr + ld 0, 1*8(3) + mtcr 0 + # 2) restore SP + ld 1, 2*8(3) + # 3) restore TOC into both r2 and the caller's stack. + # Which location is required depends on whether setjmp was called + # locally or non-locally, but it's always safe to restore to both. + ld 2, 3*8(3) + std 2, 24(1) + # 4) restore r14-r31 + ld 14, 4*8(3) + ld 15, 5*8(3) + ld 16, 6*8(3) + ld 17, 7*8(3) + ld 18, 8*8(3) + ld 19, 9*8(3) + ld 20, 10*8(3) + ld 21, 11*8(3) + ld 22, 12*8(3) + ld 23, 13*8(3) + ld 24, 14*8(3) + ld 25, 15*8(3) + ld 26, 16*8(3) + ld 27, 17*8(3) + ld 28, 18*8(3) + ld 29, 19*8(3) + ld 30, 20*8(3) + ld 31, 21*8(3) + # 5) restore floating point registers f14-f31 + lfd 14, 22*8(3) + lfd 15, 23*8(3) + lfd 16, 24*8(3) + lfd 17, 25*8(3) + lfd 18, 26*8(3) + lfd 19, 27*8(3) + lfd 20, 28*8(3) + lfd 21, 29*8(3) + lfd 22, 30*8(3) + lfd 23, 31*8(3) + lfd 24, 32*8(3) + lfd 25, 33*8(3) + lfd 26, 34*8(3) + lfd 27, 35*8(3) + lfd 28, 36*8(3) + lfd 29, 37*8(3) + lfd 30, 38*8(3) + lfd 31, 39*8(3) + + # 6) restore vector registers v20-v31 + addi 3, 3, 40*8 + lvx 20, 0, 3 ; addi 3, 3, 16 + lvx 21, 0, 3 ; addi 3, 3, 16 + lvx 22, 0, 3 ; addi 3, 3, 16 + lvx 23, 0, 3 ; addi 3, 3, 16 + lvx 24, 0, 3 ; addi 3, 3, 16 + lvx 25, 0, 3 ; addi 3, 3, 16 + lvx 26, 0, 3 ; addi 3, 3, 16 + lvx 27, 0, 3 ; addi 3, 3, 16 + lvx 28, 0, 3 ; addi 3, 3, 16 + lvx 29, 0, 3 ; addi 3, 3, 16 + lvx 30, 0, 3 ; addi 3, 3, 16 + lvx 31, 0, 3 + + # 7) return r4 ? r4 : 1 + mr 3, 4 + cmpwi cr7, 4, 0 + bne cr7, 1f + li 3, 1 +1: + blr + diff --git a/src/header/setjmp/impl/powerpc64/setjmp.s b/src/header/setjmp/impl/powerpc64/setjmp.s new file mode 100644 index 0000000000..37683fdaf4 --- /dev/null +++ b/src/header/setjmp/impl/powerpc64/setjmp.s @@ -0,0 +1,89 @@ + .global __setjmp + .global _setjmp + .global setjmp + .type __setjmp,@function + .type _setjmp,@function + .type setjmp,@function +__setjmp: +_setjmp: +setjmp: + ld 5, 24(1) # load from the TOC slot in the caller's stack frame + b __setjmp_toc + + .localentry __setjmp,.-__setjmp + .localentry _setjmp,.-_setjmp + .localentry setjmp,.-setjmp + mr 5, 2 + + .global __setjmp_toc + .hidden __setjmp_toc + # same as normal setjmp, except TOC pointer to save is provided in r5. + # r4 would normally be the 2nd parameter, but we're using r5 to simplify calling from sigsetjmp. + # solves the problem of knowing whether to save the TOC pointer from r2 or the caller's stack frame. +__setjmp_toc: + # 0) store IP into 0, then into the jmpbuf pointed to by r3 (first arg) + mflr 0 + std 0, 0*8(3) + # 1) store cr + mfcr 0 + std 0, 1*8(3) + # 2) store SP and TOC + std 1, 2*8(3) + std 5, 3*8(3) + # 3) store r14-31 + std 14, 4*8(3) + std 15, 5*8(3) + std 16, 6*8(3) + std 17, 7*8(3) + std 18, 8*8(3) + std 19, 9*8(3) + std 20, 10*8(3) + std 21, 11*8(3) + std 22, 12*8(3) + std 23, 13*8(3) + std 24, 14*8(3) + std 25, 15*8(3) + std 26, 16*8(3) + std 27, 17*8(3) + std 28, 18*8(3) + std 29, 19*8(3) + std 30, 20*8(3) + std 31, 21*8(3) + # 4) store floating point registers f14-f31 + stfd 14, 22*8(3) + stfd 15, 23*8(3) + stfd 16, 24*8(3) + stfd 17, 25*8(3) + stfd 18, 26*8(3) + stfd 19, 27*8(3) + stfd 20, 28*8(3) + stfd 21, 29*8(3) + stfd 22, 30*8(3) + stfd 23, 31*8(3) + stfd 24, 32*8(3) + stfd 25, 33*8(3) + stfd 26, 34*8(3) + stfd 27, 35*8(3) + stfd 28, 36*8(3) + stfd 29, 37*8(3) + stfd 30, 38*8(3) + stfd 31, 39*8(3) + + # 5) store vector registers v20-v31 + addi 3, 3, 40*8 + stvx 20, 0, 3 ; addi 3, 3, 16 + stvx 21, 0, 3 ; addi 3, 3, 16 + stvx 22, 0, 3 ; addi 3, 3, 16 + stvx 23, 0, 3 ; addi 3, 3, 16 + stvx 24, 0, 3 ; addi 3, 3, 16 + stvx 25, 0, 3 ; addi 3, 3, 16 + stvx 26, 0, 3 ; addi 3, 3, 16 + stvx 27, 0, 3 ; addi 3, 3, 16 + stvx 28, 0, 3 ; addi 3, 3, 16 + stvx 29, 0, 3 ; addi 3, 3, 16 + stvx 30, 0, 3 ; addi 3, 3, 16 + stvx 31, 0, 3 + + # 6) return 0 + li 3, 0 + blr diff --git a/src/header/setjmp/impl/riscv64/longjmp.S b/src/header/setjmp/impl/riscv64/longjmp.S new file mode 100644 index 0000000000..09b36da0a8 --- /dev/null +++ b/src/header/setjmp/impl/riscv64/longjmp.S @@ -0,0 +1,41 @@ +.attribute arch, "rv64gc" # see rust issue #80608 +.global __longjmp +.global _longjmp +.global longjmp +.type __longjmp, %function +.type _longjmp, %function +.type longjmp, %function +__longjmp: +_longjmp: +longjmp: + ld s0, 0(a0) + ld s1, 8(a0) + ld s2, 16(a0) + ld s3, 24(a0) + ld s4, 32(a0) + ld s5, 40(a0) + ld s6, 48(a0) + ld s7, 56(a0) + ld s8, 64(a0) + ld s9, 72(a0) + ld s10, 80(a0) + ld s11, 88(a0) + ld sp, 96(a0) + ld ra, 104(a0) + + fld fs0, 112(a0) + fld fs1, 120(a0) + fld fs2, 128(a0) + fld fs3, 136(a0) + fld fs4, 144(a0) + fld fs5, 152(a0) + fld fs6, 160(a0) + fld fs7, 168(a0) + fld fs8, 176(a0) + fld fs9, 184(a0) + fld fs10, 192(a0) + fld fs11, 200(a0) + + seqz a0, a1 + add a0, a0, a1 + ret diff --git a/src/header/setjmp/impl/riscv64/setjmp.S b/src/header/setjmp/impl/riscv64/setjmp.S new file mode 100644 index 0000000000..243a8f744b --- /dev/null +++ b/src/header/setjmp/impl/riscv64/setjmp.S @@ -0,0 +1,40 @@ +.attribute arch, "rv64gc" # see rust issue #80608 +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp, %function +.type _setjmp, %function +.type setjmp, %function +__setjmp: +_setjmp: +setjmp: + sd s0, 0(a0) + sd s1, 8(a0) + sd s2, 16(a0) + sd s3, 24(a0) + sd s4, 32(a0) + sd s5, 40(a0) + sd s6, 48(a0) + sd s7, 56(a0) + sd s8, 64(a0) + sd s9, 72(a0) + sd s10, 80(a0) + sd s11, 88(a0) + sd sp, 96(a0) + sd ra, 104(a0) + + fsd fs0, 112(a0) + fsd fs1, 120(a0) + fsd fs2, 128(a0) + fsd fs3, 136(a0) + fsd fs4, 144(a0) + fsd fs5, 152(a0) + fsd fs6, 160(a0) + fsd fs7, 168(a0) + fsd fs8, 176(a0) + fsd fs9, 184(a0) + fsd fs10, 192(a0) + fsd fs11, 200(a0) + + li a0, 0 + ret diff --git a/src/header/setjmp/impl/riscv64/sigsetjmp.S b/src/header/setjmp/impl/riscv64/sigsetjmp.S new file mode 100644 index 0000000000..f9bc162a0a --- /dev/null +++ b/src/header/setjmp/impl/riscv64/sigsetjmp.S @@ -0,0 +1,23 @@ +.global sigsetjmp +.global __sigsetjmp +.type sigsetjmp, %function +.type __sigsetjmp, %function +sigsetjmp: +__sigsetjmp: + bnez a1, 1f + tail setjmp +1: + + sd ra, 208(a0) + sd s0, 224(a0) + mv s0, a0 + + call setjmp + + mv a1, a0 + mv a0, s0 + ld s0, 224(a0) + ld ra, 208(a0) + +.hidden __sigsetjmp_tail + tail __sigsetjmp_tail diff --git a/src/header/setjmp/impl/s390x/longjmp.s b/src/header/setjmp/impl/s390x/longjmp.s new file mode 100644 index 0000000000..b2310f8ad1 --- /dev/null +++ b/src/header/setjmp/impl/s390x/longjmp.s @@ -0,0 +1,23 @@ + .global _longjmp + .global longjmp + .type _longjmp,@function + .type longjmp,@function +_longjmp: +longjmp: + +1: + lmg %r6, %r15, 0(%r2) + + ld %f8, 10*8(%r2) + ld %f9, 11*8(%r2) + ld %f10, 12*8(%r2) + ld %f11, 13*8(%r2) + ld %f12, 14*8(%r2) + ld %f13, 15*8(%r2) + ld %f14, 16*8(%r2) + ld %f15, 17*8(%r2) + + ltgr %r2, %r3 + bnzr %r14 + lhi %r2, 1 + br %r14 diff --git a/src/header/setjmp/impl/s390x/setjmp.s b/src/header/setjmp/impl/s390x/setjmp.s new file mode 100644 index 0000000000..afae1b6755 --- /dev/null +++ b/src/header/setjmp/impl/s390x/setjmp.s @@ -0,0 +1,25 @@ + .global ___setjmp + .hidden ___setjmp + .global __setjmp + .global _setjmp + .global setjmp + .type __setjmp,@function + .type _setjmp,@function + .type setjmp,@function +___setjmp: +__setjmp: +_setjmp: +setjmp: + stmg %r6, %r15, 0(%r2) + + std %f8, 10*8(%r2) + std %f9, 11*8(%r2) + std %f10, 12*8(%r2) + std %f11, 13*8(%r2) + std %f12, 14*8(%r2) + std %f13, 15*8(%r2) + std %f14, 16*8(%r2) + std %f15, 17*8(%r2) + + lghi %r2, 0 + br %r14 diff --git a/src/header/setjmp/impl/sh/longjmp.S b/src/header/setjmp/impl/sh/longjmp.S new file mode 100644 index 0000000000..08f668b880 --- /dev/null +++ b/src/header/setjmp/impl/sh/longjmp.S @@ -0,0 +1,28 @@ +.global _longjmp +.global longjmp +.type _longjmp, @function +.type longjmp, @function +_longjmp: +longjmp: + mov.l @r4+, r8 + mov.l @r4+, r9 + mov.l @r4+, r10 + mov.l @r4+, r11 + mov.l @r4+, r12 + mov.l @r4+, r13 + mov.l @r4+, r14 + mov.l @r4+, r15 + lds.l @r4+, pr +#if __SH_FPU_ANY__ || __SH4__ + fmov.s @r4+, fr12 + fmov.s @r4+, fr13 + fmov.s @r4+, fr14 + fmov.s @r4+, fr15 +#endif + + tst r5, r5 + movt r0 + add r5, r0 + + rts + nop diff --git a/src/header/setjmp/impl/sh/setjmp.S b/src/header/setjmp/impl/sh/setjmp.S new file mode 100644 index 0000000000..d476e6395f --- /dev/null +++ b/src/header/setjmp/impl/sh/setjmp.S @@ -0,0 +1,32 @@ +.global ___setjmp +.hidden ___setjmp +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp, @function +.type _setjmp, @function +.type setjmp, @function +___setjmp: +__setjmp: +_setjmp: +setjmp: +#if __SH_FPU_ANY__ || __SH4__ + add #52, r4 + fmov.s fr15, @-r4 + fmov.s fr14, @-r4 + fmov.s fr13, @-r4 + fmov.s fr12, @-r4 +#else + add #36, r4 +#endif + sts.l pr, @-r4 + mov.l r15, @-r4 + mov.l r14, @-r4 + mov.l r13, @-r4 + mov.l r12, @-r4 + mov.l r11, @-r4 + mov.l r10, @-r4 + mov.l r9, @-r4 + mov.l r8, @-r4 + rts + mov #0, r0 diff --git a/src/header/setjmp/impl/x32/longjmp.s b/src/header/setjmp/impl/x32/longjmp.s new file mode 100644 index 0000000000..e175a4b960 --- /dev/null +++ b/src/header/setjmp/impl/x32/longjmp.s @@ -0,0 +1,22 @@ +/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */ +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + mov %rsi,%rax /* val will be longjmp return */ + test %rax,%rax + jnz 1f + inc %rax /* if val==0, val=1 per longjmp semantics */ +1: + mov (%rdi),%rbx /* rdi is the jmp_buf, restore regs from it */ + mov 8(%rdi),%rbp + mov 16(%rdi),%r12 + mov 24(%rdi),%r13 + mov 32(%rdi),%r14 + mov 40(%rdi),%r15 + mov 48(%rdi),%rdx /* this ends up being the stack pointer */ + mov %rdx,%rsp + mov 56(%rdi),%rdx /* this is the instruction pointer */ + jmp *%rdx /* goto saved address without altering rsp */ diff --git a/src/header/setjmp/impl/x32/setjmp.s b/src/header/setjmp/impl/x32/setjmp.s new file mode 100644 index 0000000000..98f58b8d65 --- /dev/null +++ b/src/header/setjmp/impl/x32/setjmp.s @@ -0,0 +1,22 @@ +/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */ +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +__setjmp: +_setjmp: +setjmp: + mov %rbx,(%rdi) /* rdi is jmp_buf, move registers onto it */ + mov %rbp,8(%rdi) + mov %r12,16(%rdi) + mov %r13,24(%rdi) + mov %r14,32(%rdi) + mov %r15,40(%rdi) + lea 8(%rsp),%rdx /* this is our rsp WITHOUT current ret addr */ + mov %rdx,48(%rdi) + mov (%rsp),%rdx /* save return addr ptr for new rip */ + mov %rdx,56(%rdi) + xor %rax,%rax /* always return 0 */ + ret diff --git a/src/header/setjmp/impl/x86_64/longjmp.s b/src/header/setjmp/impl/x86_64/longjmp.s new file mode 100644 index 0000000000..c6b4e0e0f6 --- /dev/null +++ b/src/header/setjmp/impl/x86_64/longjmp.s @@ -0,0 +1,22 @@ +/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */ +.global _longjmp +.global longjmp +.type _longjmp,@function +.type longjmp,@function +_longjmp: +longjmp: + mov rax,rsi /* val will be longjmp return */ + test rax,rax + jnz 1f + inc rax /* if val==0, val=1 per longjmp semantics */ +1: + mov rbx, [rdi] /* rdi is the jmp_buf, restore regs from it */ + mov rbp, [rdi + 8] + mov r12, [rdi + 16] + mov r13, [rdi + 24] + mov r14, [rdi + 32] + mov r15, [rdi + 40] + mov rdx, [rdi + 48] /* this ends up being the stack pointer */ + mov rsp, rdx + mov rdx, [rdi + 56] /* this is the instruction pointer */ + jmp rdx /* goto saved address without altering rsp */ diff --git a/src/header/setjmp/impl/x86_64/setjmp.s b/src/header/setjmp/impl/x86_64/setjmp.s new file mode 100644 index 0000000000..1aa521f997 --- /dev/null +++ b/src/header/setjmp/impl/x86_64/setjmp.s @@ -0,0 +1,22 @@ +/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */ +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,@function +.type _setjmp,@function +.type setjmp,@function +__setjmp: +_setjmp: +setjmp: + mov [rdi], rbx /* rdi is jmp_buf, move registers onto it */ + mov [rdi + 8], rbp + mov [rdi + 16], r12 + mov [rdi + 24], r13 + mov [rdi + 32], r14 + mov [rdi + 40], r15 + lea rdx, [rsp + 8] /* this is our rsp WITHOUT current ret addr */ + mov [rdi + 48], rdx + mov rdx, [rsp] /* save return addr ptr for new rip */ + mov [rdi + 56], rdx + xor rax, rax /* always return 0 */ + ret diff --git a/src/header/setjmp/impl/x86_64/sigsetjmp.s b/src/header/setjmp/impl/x86_64/sigsetjmp.s new file mode 100644 index 0000000000..41bd05b518 --- /dev/null +++ b/src/header/setjmp/impl/x86_64/sigsetjmp.s @@ -0,0 +1,24 @@ +.global sigsetjmp +.global __sigsetjmp +.type sigsetjmp,@function +.type __sigsetjmp,@function +sigsetjmp: +__sigsetjmp: + test esi, esi + jz 1f + + pop [rdi + 64] + mov qword ptr [rdi + 8 + 72], rbx + mov rbx, rdi + + call setjmp + + push [rbx + 64] + mov rdi, rbx + mov esi, eax + mov rbx, qword ptr [rbx + 8 + 72] + +.hidden __sigsetjmp_tail + jmp __sigsetjmp_tail + +1: jmp setjmp diff --git a/src/header/setjmp/mod.rs b/src/header/setjmp/mod.rs new file mode 100644 index 0000000000..3ec61d2b0b --- /dev/null +++ b/src/header/setjmp/mod.rs @@ -0,0 +1,33 @@ +//! `setjmp.h` implementation. +//! +//! See . + +use core::arch::global_asm; + +macro_rules! platform_specific { + ($($rust_arch:expr,$c_arch:expr,$ext:expr;)+) => { + $( + #[cfg(target_arch = $rust_arch)] + global_asm!(include_str!(concat!("impl/", $c_arch, "/setjmp.", $ext))); + #[cfg(target_arch = $rust_arch)] + global_asm!(include_str!(concat!("impl/", $c_arch, "/sigsetjmp.", $ext))); + #[cfg(target_arch = $rust_arch)] + global_asm!(include_str!(concat!("impl/", $c_arch, "/longjmp.", $ext))); + )+ + } +} + +platform_specific! { + "aarch64","aarch64", "s"; + "x86","i386","s"; + "x86_64","x86_64","s"; + "riscv64", "riscv64", "S"; +} + +//Each platform has different sizes for sigjmp_buf, currently only x86_64 is supported +unsafe extern "C" { + /// See . + pub fn setjmp(jb: *mut u64) -> i32; + /// See . + pub fn longjmp(jb: *mut u64, ret: i32); +} diff --git a/src/header/sgtty/cbindgen.toml b/src/header/sgtty/cbindgen.toml new file mode 100644 index 0000000000..e442f18f50 --- /dev/null +++ b/src/header/sgtty/cbindgen.toml @@ -0,0 +1,12 @@ +sys_includes = ["sys/ioctl.h"] +include_guard = "_RELIBC_SGTTY_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[export.rename] +"sgttyb" = "struct sgttyb" + +[enum] +prefix_with_name = true diff --git a/src/header/sgtty/mod.rs b/src/header/sgtty/mod.rs new file mode 100644 index 0000000000..7d63a33a94 --- /dev/null +++ b/src/header/sgtty/mod.rs @@ -0,0 +1,9 @@ +//! sgtty implementation that won't work on redox because no ioctl + +use crate::{header::sys_ioctl::sgttyb, platform::types::c_int}; + +#[unsafe(no_mangle)] +pub extern "C" fn gtty(fd: c_int, out: *mut sgttyb) -> c_int { + todo_skip!(0, "gtty({}, {:p})", fd, out); + -1 +} diff --git a/src/header/shadow/cbindgen.toml b/src/header/shadow/cbindgen.toml new file mode 100644 index 0000000000..8d36c91ba6 --- /dev/null +++ b/src/header/shadow/cbindgen.toml @@ -0,0 +1,12 @@ +sys_includes = ["sys/types.h", "stdint.h", "stdio.h"] +include_guard = "_RELIBC_SHADOW_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export] +include = ["group"] diff --git a/src/header/shadow/mod.rs b/src/header/shadow/mod.rs new file mode 100644 index 0000000000..23492c4e8f --- /dev/null +++ b/src/header/shadow/mod.rs @@ -0,0 +1,275 @@ +//! `shadow.h` implementation. +//! +//! Non-POSIX, see . + +use core::{ + cell::SyncUnsafeCell, + ops::{Deref, DerefMut}, + pin::Pin, + ptr, + str::FromStr, +}; + +use alloc::{boxed::Box, string::String}; + +use crate::{ + c_str::CStr, + fs::File, + header::fcntl, + io::{BufReader, Lines, prelude::*}, + platform, + platform::types::{c_char, c_int, c_long, c_ulong, size_t}, +}; + +use super::errno::*; + +#[cfg(target_os = "linux")] +const SEPARATOR: char = ':'; + +#[cfg(target_os = "redox")] +const SEPARATOR: char = ';'; + +const SHADOW_FILE: &core::ffi::CStr = c"/etc/shadow"; + +#[derive(Clone, Copy, Debug)] +struct DestBuffer { + ptr: *mut u8, + len: usize, +} + +#[derive(Debug)] +enum MaybeAllocated { + Owned(Pin>), + Borrowed(DestBuffer), +} +impl Deref for MaybeAllocated { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts(dst.ptr, dst.len) + }, + } + } +} +impl DerefMut for MaybeAllocated { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + MaybeAllocated::Owned(boxed) => boxed, + MaybeAllocated::Borrowed(dst) => unsafe { + core::slice::from_raw_parts_mut(dst.ptr, dst.len) + }, + } + } +} + +static mut SHADOW_BUF: Option = None; +static mut SHADOW: spwd = spwd { + sp_namp: ptr::null_mut(), + sp_pwdp: ptr::null_mut(), + sp_lstchg: -1, + sp_min: -1, + sp_max: -1, + sp_warn: -1, + sp_inact: -1, + sp_expire: -1, + sp_flag: 0, +}; + +static LINE_READER: SyncUnsafeCell>>> = SyncUnsafeCell::new(None); + +/// Non-POSIX, see . +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct spwd { + pub sp_namp: *mut c_char, + pub sp_pwdp: *mut c_char, + pub sp_lstchg: c_long, + pub sp_min: c_long, + pub sp_max: c_long, + pub sp_warn: c_long, + pub sp_inact: c_long, + pub sp_expire: c_long, + pub sp_flag: c_ulong, +} + +#[derive(Debug)] +enum Error { + EOF, + BufTooSmall, + Syntax, +} + +#[derive(Debug)] +struct OwnedSpwd { + buffer: MaybeAllocated, + reference: spwd, +} + +impl OwnedSpwd { + fn into_global(self) -> *mut spwd { + unsafe { + SHADOW_BUF = Some(self.buffer); + SHADOW = self.reference; + &raw mut SHADOW + } + } +} +fn to_long(s: &str) -> c_long { + c_long::from_str(s).unwrap_or(-1) +} +fn to_ulong(s: &str) -> c_ulong { + c_ulong::from_str(s).unwrap_or(0) +} + +fn parse_spwd(line: String, destbuf: Option) -> Result { + let mut parts = line.split(SEPARATOR); + + let sp_namp_str = parts.next().ok_or(Error::Syntax)?; + let sp_pwdp_str = parts.next().ok_or(Error::Syntax)?; + + //TODO: these are not implemented redox-users crate + let sp_lstchg = to_long(parts.next().unwrap_or("")); + let sp_min = to_long(parts.next().unwrap_or("")); + let sp_max = to_long(parts.next().unwrap_or("")); + let sp_warn = to_long(parts.next().unwrap_or("")); + let sp_inact = to_long(parts.next().unwrap_or("")); + let sp_expire = to_long(parts.next().unwrap_or("")); + let sp_flag = to_ulong(parts.next().unwrap_or("")); + + let string_data_len = sp_namp_str.len() + 1 + sp_pwdp_str.len() + 1; + + let mut buffer = match destbuf { + Some(buf) => { + if buf.len < string_data_len { + platform::ERRNO.set(ERANGE); + return Err(Error::BufTooSmall); + } + MaybeAllocated::Borrowed(buf) + } + None => { + let vec = vec![0; string_data_len]; + MaybeAllocated::Owned(Box::into_pin(vec.into_boxed_slice())) + } + }; + + let (name_slice, rest) = buffer.split_at_mut(sp_namp_str.len() + 1); + name_slice[..sp_namp_str.len()].copy_from_slice(sp_namp_str.as_bytes()); + name_slice[sp_namp_str.len()] = 0; + + let (pwd_slice, _) = rest.split_at_mut(sp_pwdp_str.len() + 1); + pwd_slice[..sp_pwdp_str.len()].copy_from_slice(sp_pwdp_str.as_bytes()); + pwd_slice[sp_pwdp_str.len()] = 0; + + let sp_namp = name_slice.as_mut_ptr().cast::(); + let sp_pwdp = pwd_slice.as_mut_ptr().cast::(); + + let reference = spwd { + sp_namp, + sp_pwdp, + sp_lstchg, + sp_min, + sp_max, + sp_warn, + sp_inact, + sp_expire, + sp_flag, + }; + + Ok(OwnedSpwd { buffer, reference }) +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getspnam(name: *const c_char) -> *mut spwd { + let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else { + return ptr::null_mut(); + }; + let c_name = unsafe { CStr::from_ptr(name) }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { continue }; + if line.starts_with(c_name.to_str().unwrap_or("\0")) + && let Ok(pwd) = parse_spwd(line, None) + { + return pwd.into_global(); + } + } + ptr::null_mut() +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getspnam_r( + name: *const c_char, + result_buf: *mut spwd, + buffer: *mut c_char, + buflen: size_t, + result: *mut *mut spwd, +) -> c_int { + unsafe { *result = ptr::null_mut() }; + + let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) else { + return ENOENT; + }; + let c_name = unsafe { CStr::from_ptr(name).to_str().unwrap_or("\0") }; + + for line in BufReader::new(db).lines() { + let Ok(line) = line else { continue }; + if line.starts_with(c_name) { + let dest_buf = Some(DestBuffer { + ptr: buffer.cast::(), + len: buflen, + }); + return match parse_spwd(line, dest_buf) { + Ok(sp) => { + unsafe { + *result_buf = sp.reference; + *result = result_buf; + } + 0 + } + Err(Error::BufTooSmall) => ERANGE, + _ => ENOENT, + }; + } + } + ENOENT +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setspent() { + let line_reader = unsafe { &mut *LINE_READER.get() }; + if let Ok(db) = File::open(SHADOW_FILE.into(), fcntl::O_RDONLY) { + *line_reader = Some(BufReader::new(db).lines()); + } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn endspent() { + unsafe { + *LINE_READER.get() = None; + } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getspent() -> *mut spwd { + let line_reader = unsafe { &mut *LINE_READER.get() }; + if line_reader.is_none() { + unsafe { + setspent(); + } + } + if let Some(lines) = line_reader + && let Some(Ok(line)) = lines.next() + && let Ok(sp) = parse_spwd(line, None) + { + return sp.into_global(); + } + ptr::null_mut() +} diff --git a/src/header/signal/cbindgen.toml b/src/header/signal/cbindgen.toml new file mode 100644 index 0000000000..7363f32543 --- /dev/null +++ b/src/header/signal/cbindgen.toml @@ -0,0 +1,45 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the pthread_t, size_t, and uid_t types as described in ." +# - "The header shall define the timespec structure as described in ." +# - "pid_t As described in ." +# - "The header shall define the pthread_attr_t type as described in ." +# - "Inclusion of the header may make visible all symbols from the header." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_SIGNAL_H" +after_includes = """ +#include // for sigset_t +#include // for timespec from time.h + +#define SIG_DFL ((void (*)(int))0) +#define SIG_IGN ((void (*)(int))1) +#define SIG_ERR ((void (*)(int))-1) +#define SIG_HOLD ((void (*)(int))2) + +typedef struct siginfo siginfo_t; +typedef struct ucontext ucontext_t; +typedef struct mcontext mcontext_t; +typedef long sig_atomic_t; + +struct sigaction { + union { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t *, void *); + }; + int sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; +}; +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"sigaction" = "struct sigaction" +"timespec" = "struct timespec" diff --git a/src/header/signal/linux.rs b/src/header/signal/linux.rs new file mode 100644 index 0000000000..e933e3054a --- /dev/null +++ b/src/header/signal/linux.rs @@ -0,0 +1,179 @@ +use super::{sigset_t, stack_t}; +use crate::platform::types::{c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort}; +use core::arch::global_asm; + +// Needs to be defined in assembly because it can't have a function prologue +// rax is register, 15 is RT_SIGRETURN +#[cfg(target_arch = "x86_64")] +global_asm!( + " + .global __restore_rt + __restore_rt: + mov rax, 15 + syscall +" +); +// x8 is register, 139 is RT_SIGRETURN +#[cfg(target_arch = "aarch64")] +global_asm!( + " + .global __restore_rt + __restore_rt: + mov x8, #139 + svc 0 +" +); + +#[cfg(target_arch = "riscv64")] +global_asm!( + " + .global __restore_rt + __restore_rt: + li a7, 139 + ecall +" +); + +pub const SIGHUP: usize = 1; +pub const SIGINT: usize = 2; +pub const SIGQUIT: usize = 3; +pub const SIGILL: usize = 4; +pub const SIGTRAP: usize = 5; +pub const SIGABRT: usize = 6; +pub const SIGIOT: usize = SIGABRT; +pub const SIGBUS: usize = 7; +pub const SIGFPE: usize = 8; +pub const SIGKILL: usize = 9; +pub const SIGUSR1: usize = 10; +pub const SIGSEGV: usize = 11; +pub const SIGUSR2: usize = 12; +pub const SIGPIPE: usize = 13; +pub const SIGALRM: usize = 14; +pub const SIGTERM: usize = 15; +pub const SIGSTKFLT: usize = 16; +pub const SIGCHLD: usize = 17; +pub const SIGCONT: usize = 18; +pub const SIGSTOP: usize = 19; +pub const SIGTSTP: usize = 20; +pub const SIGTTIN: usize = 21; +pub const SIGTTOU: usize = 22; +pub const SIGURG: usize = 23; +pub const SIGXCPU: usize = 24; +pub const SIGXFSZ: usize = 25; +pub const SIGVTALRM: usize = 26; +pub const SIGPROF: usize = 27; +pub const SIGWINCH: usize = 28; +pub const SIGIO: usize = 29; +pub const SIGPOLL: usize = SIGIO; +pub const SIGPWR: usize = 30; +pub const SIGSYS: usize = 31; +pub const SIGUNUSED: usize = SIGSYS; +pub const NSIG: usize = 32; + +pub const SIGRTMIN: usize = 35; // TODO: decrease to 34 +pub const SIGRTMAX: usize = 64; + +pub const SA_NOCLDSTOP: usize = 1; +pub const SA_NOCLDWAIT: usize = 2; +pub const SA_SIGINFO: usize = 4; +pub const SA_ONSTACK: usize = 0x0800_0000; +pub const SA_RESTART: usize = 0x1000_0000; +pub const SA_NODEFER: usize = 0x4000_0000; +pub const SA_RESETHAND: usize = 0x8000_0000; +pub const SA_RESTORER: usize = 0x0400_0000; + +pub const SS_ONSTACK: usize = 1; +pub const SS_DISABLE: usize = 2; + +// Those two should be updated from kernel headers +pub const MINSIGSTKSZ: usize = 2048; +pub const SIGSTKSZ: usize = 8096; + +pub const SI_QUEUE: i32 = -1; +pub const SI_USER: i32 = 0; +pub const SI_TIMER: i32 = 1; +pub const SI_ASYNCIO: i32 = 2; +pub const SI_MESGQ: i32 = 3; + +// si_code values (signal-specific) +pub const ILL_ILLOPC: i32 = 1; +pub const ILL_ILLOPN: i32 = 2; +pub const ILL_ILLADR: i32 = 3; +pub const ILL_ILLTRP: i32 = 4; +pub const ILL_PRVOPC: i32 = 5; +pub const ILL_PRVREG: i32 = 6; +pub const ILL_COPROC: i32 = 7; +pub const ILL_BADSTK: i32 = 8; + +pub const FPE_INTDIV: i32 = 1; +pub const FPE_INTOVF: i32 = 2; +pub const FPE_FLTDIV: i32 = 3; +pub const FPE_FLTOVF: i32 = 4; +pub const FPE_FLTUND: i32 = 5; +pub const FPE_FLTRES: i32 = 6; +pub const FPE_FLTINV: i32 = 7; +pub const FPE_FLTSUB: i32 = 8; + +pub const SEGV_MAPERR: i32 = 1; +pub const SEGV_ACCERR: i32 = 2; + +pub const BUS_ADRALN: i32 = 1; +pub const BUS_ADRERR: i32 = 2; +pub const BUS_OBJERR: i32 = 3; + +pub const TRAP_BRKPT: i32 = 1; +pub const TRAP_TRACE: i32 = 2; + +pub const CLD_EXITED: i32 = 1; +pub const CLD_KILLED: i32 = 2; +pub const CLD_DUMPED: i32 = 3; +pub const CLD_TRAPPED: i32 = 4; +pub const CLD_STOPPED: i32 = 5; +pub const CLD_CONTINUED: i32 = 6; + +// Mirrors the ucontext_t struct from the libc crate on Linux. + +pub(crate) type ucontext_t = ucontext; +pub(crate) type mcontext_t = mcontext; + +#[repr(C)] +pub struct ucontext { + pub uc_flags: c_ulong, + pub uc_link: *mut ucontext_t, + pub uc_stack: stack_t, + pub uc_mcontext: mcontext_t, + pub uc_sigmask: sigset_t, + __private: [c_uchar; 512], +} + +#[repr(C)] +pub struct _libc_fpstate { + pub cwd: c_ushort, + pub swd: c_ushort, + pub ftw: c_ushort, + pub fop: c_ushort, + pub rip: c_ulonglong, + pub rdp: c_ulonglong, + pub mxcsr: c_uint, + pub mxcr_mask: c_uint, + pub _st: [_libc_fpxreg; 8], + pub _xmm: [_libc_xmmreg; 16], + __private: [c_ulonglong; 12], +} +#[repr(C)] +pub struct _libc_fpxreg { + pub significand: [c_ushort; 4], + pub exponent: c_ushort, + __private: [c_ushort; 3], +} + +#[repr(C)] +pub struct _libc_xmmreg { + pub element: [c_uint; 4], +} +#[repr(C)] +pub struct mcontext { + pub gregs: [c_longlong; 23], // TODO: greg_t? + pub fpregs: *mut _libc_fpstate, + __private: [c_ulonglong; 8], +} diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs new file mode 100644 index 0000000000..eecab22ae9 --- /dev/null +++ b/src/header/signal/mod.rs @@ -0,0 +1,587 @@ +//! `signal.h` implementation. +//! +//! See . + +use core::{mem, ptr}; + +use cbitset::BitSet; + +use crate::{ + error::{Errno, ResultExt}, + header::{bits_sigset_t::sigset_t, bits_timespec::timespec, errno, setjmp}, + platform::{ + self, ERRNO, Pal, PalSignal, Sys, + types::{ + c_char, c_int, c_ulonglong, c_void, pid_t, pthread_attr_t, pthread_t, size_t, uid_t, + }, + }, +}; + +pub use self::sys::*; + +use super::{ + errno::EFAULT, + stdio::{fprintf, stderr}, +}; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +mod signalfd; +pub use self::signalfd::*; + +type SigSet = BitSet<[u64; 1]>; + +pub(crate) const SIG_DFL: usize = 0; +pub(crate) const SIG_IGN: usize = 1; +pub(crate) const SIG_ERR: isize = -1; +pub(crate) const SIG_HOLD: isize = 2; + +pub const SIG_BLOCK: c_int = 0; +pub const SIG_UNBLOCK: c_int = 1; +pub const SIG_SETMASK: c_int = 2; + +pub const SIGEV_SIGNAL: c_int = 0; +pub const SIGEV_NONE: c_int = 1; +pub const SIGEV_THREAD: c_int = 2; + +/// See . +#[repr(C)] +#[derive(Clone, Debug)] +/// cbindgen:ignore +pub struct sigaction { + pub sa_handler: Option, + pub sa_flags: c_int, + pub sa_restorer: Option, + pub sa_mask: sigset_t, +} + +#[repr(C)] +#[derive(Clone)] +pub struct sigaltstack { + pub ss_sp: *mut c_void, + pub ss_flags: c_int, + pub ss_size: size_t, +} + +/// See . +#[repr(C)] +#[derive(Clone)] +pub struct sigevent { + pub sigev_value: sigval, + pub sigev_signo: c_int, + pub sigev_notify: c_int, + pub sigev_notify_function: Option, + pub sigev_notify_attributes: *mut pthread_attr_t, +} + +// FIXME: This struct is wrong on Linux +#[repr(C)] +#[derive(Clone, Copy)] +pub struct siginfo { + pub si_signo: c_int, + pub si_errno: c_int, + pub si_code: c_int, + pub si_pid: pid_t, + pub si_uid: uid_t, + pub si_addr: *mut c_void, + pub si_status: c_int, + pub si_value: sigval, +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_siginfo(a: siginfo) {} + +/// See . +#[derive(Clone, Copy)] +#[repr(C)] +pub union sigval { + pub sival_int: c_int, + pub sival_ptr: *mut c_void, +} + +/// See . +/// cbindgen:ignore +pub type siginfo_t = siginfo; + +/// See +pub type stack_t = sigaltstack; + +unsafe extern "C" { + pub fn sigsetjmp(jb: *mut c_ulonglong, savemask: c_int) -> c_int; +} + +//NOTE for the following two functions, to see why they're implemented slightly differently from their intended behavior, read +// https://git.musl-libc.org/cgit/musl/commit/?id=583e55122e767b1586286a0d9c35e2a4027998ab +#[unsafe(no_mangle)] +unsafe extern "C" fn __sigsetjmp_tail(jb: *mut c_ulonglong, ret: c_int) -> c_int { + let set = jb.wrapping_add(9); + if ret > 0 { + unsafe { sigprocmask(SIG_SETMASK, set, ptr::null_mut()) }; + } else { + unsafe { sigprocmask(SIG_SETMASK, ptr::null_mut(), set) }; + } + ret +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn siglongjmp(jb: *mut c_ulonglong, ret: c_int) { + unsafe { setjmp::longjmp(jb, ret) }; +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn kill(pid: pid_t, sig: c_int) -> c_int { + Sys::kill(pid, sig).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> c_int { + Sys::sigqueue(pid, sig, val) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int { + Sys::killpg(pgrp, sig).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int { + let os_tid = { + let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) }; + unsafe { pthread.os_tid.get().read() } + }; + crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) }) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pthread_sigmask( + how: c_int, + set: *const sigset_t, + oldset: *mut sigset_t, +) -> c_int { + // On Linux and Redox, pthread_sigmask and sigprocmask are equivalent + if unsafe { sigprocmask(how, set, oldset) } == 0 { + 0 + } else { + //TODO: Fix race + platform::ERRNO.get() + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn raise(sig: c_int) -> c_int { + Sys::raise(sig).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigaction( + sig: c_int, + act: *const sigaction, + oact: *mut sigaction, +) -> c_int { + Sys::sigaction(sig, unsafe { act.as_ref() }, unsafe { oact.as_mut() }) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int { + if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX) + /* TODO */ + { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + if let Some(set) = unsafe { (set.cast::()).as_mut() } { + set.insert(signo as usize - 1); // 0-indexed usize, please! + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int { + unsafe { + Sys::sigaltstack(ss.as_ref(), old_ss.as_mut()) + .map(|()| 0) + .or_minus_one_errno() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int { + if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX) + /* TODO */ + { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + if let Some(set) = unsafe { (set.cast::()).as_mut() } { + set.remove(signo as usize - 1); // 0-indexed usize, please! + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigemptyset(set: *mut sigset_t) -> c_int { + if let Some(set) = unsafe { (set.cast::()).as_mut() } { + set.clear(); + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigfillset(set: *mut sigset_t) -> c_int { + if let Some(set) = unsafe { (set.cast::()).as_mut() } { + set.fill(.., true); + } + 0 +} + +/// See . +/// +/// Present in issue 7. Removed in issue 8. +/// +/// Use of this function is unspecified in a multi-threaded process. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sighold(sig: c_int) -> c_int { + let mut pset = mem::MaybeUninit::::uninit(); + unsafe { sigemptyset(pset.as_mut_ptr()) }; + let mut set = unsafe { pset.assume_init() }; + if unsafe { sigaddset(&raw mut set, sig) } < 0 { + return -1; + } + unsafe { sigprocmask(SIG_BLOCK, &raw const set, ptr::null_mut()) } +} + +/// See . +/// +/// Present in issue 7. Removed in issue 8. +/// +/// Use of this function is unspecified in a multi-threaded process. +#[allow(clippy::missing_transmute_annotations)] +#[unsafe(no_mangle)] +pub extern "C" fn sigignore(sig: c_int) -> c_int { + let mut psa = mem::MaybeUninit::::uninit(); + unsafe { sigemptyset(&raw mut (*psa.as_mut_ptr()).sa_mask) }; + let mut sa = unsafe { psa.assume_init() }; + sa.sa_handler = unsafe { mem::transmute(SIG_IGN) }; + sa.sa_flags = 0; + unsafe { sigaction(sig, &raw const sa, ptr::null_mut()) } +} + +/// See . +/// +/// Marked obsolescent in issue 7. Removed in issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn siginterrupt(sig: c_int, flag: c_int) -> c_int { + let mut psa = mem::MaybeUninit::::uninit(); + unsafe { sigaction(sig, ptr::null_mut(), psa.as_mut_ptr()) }; + let mut sa = unsafe { psa.assume_init() }; + if flag != 0 { + sa.sa_flags &= !SA_RESTART as c_int; + } else { + sa.sa_flags |= SA_RESTART as c_int; + } + + unsafe { sigaction(sig, &raw const sa, ptr::null_mut()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigismember(set: *const sigset_t, signo: c_int) -> c_int { + if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX) + /* TODO */ + { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + if let Some(set) = unsafe { (set as *mut SigSet).as_mut() } + && set.contains(signo as usize - 1) + { + return 1; + } + 0 +} + +/// See . +#[allow(clippy::missing_transmute_annotations)] +#[unsafe(no_mangle)] +pub extern "C" fn signal( + sig: c_int, + func: Option, +) -> Option { + let sa = sigaction { + sa_handler: func, + sa_flags: SA_RESTART as _, + sa_restorer: None, // set by platform if applicable + sa_mask: sigset_t::default(), + }; + let mut old_sa = mem::MaybeUninit::uninit(); + if unsafe { sigaction(sig, &raw const sa, old_sa.as_mut_ptr()) } < 0 { + mem::forget(old_sa); + return unsafe { mem::transmute(SIG_ERR) }; + } + unsafe { old_sa.assume_init() }.sa_handler +} + +/// See . +/// +/// Present in issue 7. Removed in issue 8. +/// +/// Use of this function is unspecified in a multi-threaded process. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int { + let mut pset = mem::MaybeUninit::::uninit(); + unsafe { sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr()) }; + let mut set = unsafe { pset.assume_init() }; + if unsafe { sigdelset(&raw mut set, sig) } == -1 { + return -1; + } + unsafe { sigsuspend(&raw const set) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigpending(set: *mut sigset_t) -> c_int { + (|| Sys::sigpending(unsafe { set.as_mut().ok_or(Errno(EFAULT)) }?))() + .map(|()| 0) + .or_minus_one_errno() +} + +// TODO: Double-check this mask. +// This prevents the application from blocking the two signals SIGRTMIN - 1 and SIGRTMIN - 2 which +// are (at least meant to be) used internally for timers and pthread cancellation. On Linux this is +// 32 and 33 (same as NPTL reserves), whereas this on Redox is 33 and 34 (TODO: could this be +// changed to 32 and 33 for Redox too, since there's currently no support for "sigqueue" targeting +// specific threads). +const RLCT_SIGNAL_MASK: sigset_t = (1 << ((SIGRTMIN - 1) - 1)) | (1 << ((SIGRTMIN - 2) - 1)); + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigprocmask( + how: c_int, + set: *const sigset_t, + oset: *mut sigset_t, +) -> c_int { + (|| { + let set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) }; + let mut oset = unsafe { oset.as_mut() }; + + Sys::sigprocmask( + how, + set.as_ref(), + oset.as_deref_mut(), // as_deref_mut for lifetime reasons + )?; + + if let Some(oset) = oset { + *oset &= !RLCT_SIGNAL_MASK; + } + + Ok(0) + })() + .or_minus_one_errno() +} + +/// See . +/// +/// Present in issue 7. Removed in issue 8. +/// +/// Use of this function is unspecified in a multi-threaded process. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigrelse(sig: c_int) -> c_int { + let mut pset = mem::MaybeUninit::::uninit(); + unsafe { sigemptyset(pset.as_mut_ptr()) }; + let mut set = unsafe { pset.assume_init() }; + if unsafe { sigaddset(&raw mut set, sig) } < 0 { + return -1; + } + unsafe { sigprocmask(SIG_UNBLOCK, &raw const set, ptr::null_mut()) } +} + +/// See . +/// +/// Present in issue 7. Removed in issue 8. +/// +/// Use of this function is unspecified in a multi-threaded process. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigset( + sig: c_int, + func: Option, +) -> Option { + let mut old_sa = mem::MaybeUninit::uninit(); + let mut pset = mem::MaybeUninit::::uninit(); + let sig_hold: Option = unsafe { mem::transmute(SIG_HOLD) }; + let sig_err: Option = unsafe { mem::transmute(SIG_ERR) }; + unsafe { sigemptyset(pset.as_mut_ptr()) }; + let mut set = unsafe { pset.assume_init() }; + if unsafe { sigaddset(&raw mut set, sig) } < 0 { + return sig_err; + } else { + let is_equal = { + match (func, sig_hold) { + (None, None) => true, + (Some(_), None) | (None, Some(_)) => false, + (Some(f), Some(sh)) => ptr::fn_addr_eq(f, sh), + } + }; + if is_equal { + if unsafe { sigaction(sig, ptr::null_mut(), old_sa.as_mut_ptr()) } < 0 + || unsafe { sigprocmask(SIG_BLOCK, &raw const set, &raw mut set) } < 0 + { + mem::forget(old_sa); + return sig_err; + } + } else { + let mut sa = sigaction { + sa_handler: func, + sa_flags: c_int::from(0), + sa_restorer: None, // set by platform if applicable + sa_mask: sigset_t::default(), + }; + unsafe { sigemptyset(&raw mut sa.sa_mask) }; + if unsafe { sigaction(sig, &raw const sa, old_sa.as_mut_ptr()) } < 0 + || unsafe { sigprocmask(SIG_UNBLOCK, &raw const set, &raw mut set) } < 0 + { + mem::forget(old_sa); + return sig_err; + } + } + } + if unsafe { sigismember(&raw const set, sig) } == 1 { + return sig_hold; + } + unsafe { old_sa.assume_init().sa_handler } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigsuspend(sigmask: *const sigset_t) -> c_int { + Err(Sys::sigsuspend(unsafe { &*sigmask })).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int { + let mut pinfo = mem::MaybeUninit::::uninit(); + if unsafe { sigtimedwait(set, pinfo.as_mut_ptr(), ptr::null_mut()) } < 0 { + return -1; + } + let info = unsafe { pinfo.assume_init() }; + unsafe { (*sig) = info.si_signo }; + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigtimedwait( + set: *const sigset_t, + // s/siginfo_t/siginfo due to https://github.com/mozilla/cbindgen/issues/621 + sig: *mut siginfo, + // POSIX leaves behavior unspecified if this is NULL, but on both Linux and Redox, NULL is used + // to differentiate between sigtimedwait and sigwaitinfo internally + tp: *const timespec, +) -> c_int { + Sys::sigtimedwait(unsafe { &*set }, unsafe { sig.as_mut() }, unsafe { + tp.as_ref() + }) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sigwaitinfo(set: *const sigset_t, sig: *mut siginfo_t) -> c_int { + unsafe { sigtimedwait(set, sig, core::ptr::null()) } +} + +pub(crate) const SIGNAL_STRINGS: [&str; 32] = [ + "Unknown signal\0", + "Hangup\0", + "Interrupt\0", + "Quit\0", + "Illegal instruction\0", + "Trace/breakpoint trap\0", + "Aborted\0", + "Bus error\0", + "Arithmetic exception\0", + "Killed\0", + "User defined signal 1\0", + "Segmentation fault\0", + "User defined signal 2\0", + "Broken pipe\0", + "Alarm clock\0", + "Terminated\0", + "Stack fault\0", + "Child process status\0", + "Continued\0", + "Stopped (signal)\0", + "Stopped\0", + "Stopped (tty input)\0", + "Stopped (tty output)\0", + "Urgent I/O condition\0", + "CPU time limit exceeded\0", + "File size limit exceeded\0", + "Virtual timer expired\0", + "Profiling timer expired\0", + "Window changed\0", + "I/O possible\0", + "Power failure\0", + "Bad system call\0", +]; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn psignal(sig: c_int, prefix: *const c_char) { + let c_description = usize::try_from(sig) + .ok() + .and_then(|idx| SIGNAL_STRINGS.get(idx)) + .unwrap_or(&SIGNAL_STRINGS[0]) + .as_ptr(); + // fprintf can affect errno, so we save errno and restore it + let old_errno = ERRNO.get(); + // POSIX says that "prefix" shall be written if it isn't null or an empty string. + // Otherwise, only the signal description should be written + if prefix.is_null() { + unsafe { + fprintf(stderr, c"%s\n".as_ptr(), c_description); + } + } else { + unsafe { + fprintf(stderr, c"%s: %s\n".as_ptr(), prefix, c_description); + } + } + ERRNO.set(old_errno); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn psiginfo(info: *const siginfo_t, prefix: *const c_char) { + unsafe { + psignal((*info).si_signo, prefix); + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_stupid_struct_sigevent_for_timer(_: sigevent) {} diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs new file mode 100644 index 0000000000..0159dbdc5a --- /dev/null +++ b/src/header/signal/redox.rs @@ -0,0 +1,197 @@ +use redox_rt::signal::SiginfoAbi; + +#[cfg(any(target_arch = "x86", target_arch = "aarch64", target_arch = "riscv64"))] +use crate::platform::types::c_uchar; +use crate::platform::types::{c_uint, c_ulong}; + +use super::{siginfo_t, sigset_t, stack_t}; + +pub const SIGHUP: usize = 1; +pub const SIGINT: usize = 2; +pub const SIGQUIT: usize = 3; +pub const SIGILL: usize = 4; +pub const SIGTRAP: usize = 5; +pub const SIGABRT: usize = 6; +pub const SIGBUS: usize = 7; +pub const SIGFPE: usize = 8; +pub const SIGKILL: usize = 9; +pub const SIGUSR1: usize = 10; +pub const SIGSEGV: usize = 11; +pub const SIGUSR2: usize = 12; +pub const SIGPIPE: usize = 13; +pub const SIGALRM: usize = 14; +pub const SIGTERM: usize = 15; +pub const SIGSTKFLT: usize = 16; +pub const SIGCHLD: usize = 17; +pub const SIGCONT: usize = 18; +pub const SIGSTOP: usize = 19; +pub const SIGTSTP: usize = 20; +pub const SIGTTIN: usize = 21; +pub const SIGTTOU: usize = 22; +pub const SIGURG: usize = 23; +pub const SIGXCPU: usize = 24; +pub const SIGXFSZ: usize = 25; +pub const SIGVTALRM: usize = 26; +pub const SIGPROF: usize = 27; +pub const SIGWINCH: usize = 28; +pub const SIGIO: usize = 29; +pub const SIGPWR: usize = 30; +pub const SIGSYS: usize = 31; +pub const NSIG: usize = 32; + +pub const SIGRTMIN: usize = 35; +pub const SIGRTMAX: usize = 64; + +pub const SA_NOCLDWAIT: usize = 0x0000_0002; +pub const SA_RESTORER: usize = 0x0000_0004; // TODO: remove +pub const SA_SIGINFO: usize = 0x0200_0000; +pub const SA_ONSTACK: usize = 0x0400_0000; +pub const SA_RESTART: usize = 0x0800_0000; +pub const SA_NODEFER: usize = 0x1000_0000; +pub const SA_RESETHAND: usize = 0x2000_0000; +pub const SA_NOCLDSTOP: usize = 0x4000_0000; + +pub const SS_ONSTACK: usize = 0x00000001; +pub const SS_DISABLE: usize = 0x00000002; + +const _: () = { + if SS_ONSTACK != redox_rt::signal::SS_ONSTACK { + panic!(); + } + if SS_DISABLE != redox_rt::signal::SS_DISABLE { + panic!(); + } + if MINSIGSTKSZ != redox_rt::signal::MIN_SIGALTSTACK_SIZE { + panic!(); + } +}; + +// should include both SigStack size, and some extra room for the libc handler +pub const MINSIGSTKSZ: usize = 2048; + +pub const SIGSTKSZ: usize = 8096; + +pub const SI_QUEUE: i32 = -1; +pub const SI_USER: i32 = 0; +pub const SI_TIMER: i32 = 1; +pub const SI_ASYNCIO: i32 = 2; +pub const SI_MESGQ: i32 = 3; + +// si_code values (signal-specific) +pub const ILL_ILLOPC: i32 = 1; +pub const ILL_ILLOPN: i32 = 2; +pub const ILL_ILLADR: i32 = 3; +pub const ILL_ILLTRP: i32 = 4; +pub const ILL_PRVOPC: i32 = 5; +pub const ILL_PRVREG: i32 = 6; +pub const ILL_COPROC: i32 = 7; +pub const ILL_BADSTK: i32 = 8; + +pub const FPE_INTDIV: i32 = 1; +pub const FPE_INTOVF: i32 = 2; +pub const FPE_FLTDIV: i32 = 3; +pub const FPE_FLTOVF: i32 = 4; +pub const FPE_FLTUND: i32 = 5; +pub const FPE_FLTRES: i32 = 6; +pub const FPE_FLTINV: i32 = 7; +pub const FPE_FLTSUB: i32 = 8; + +pub const SEGV_MAPERR: i32 = 1; +pub const SEGV_ACCERR: i32 = 2; + +pub const BUS_ADRALN: i32 = 1; +pub const BUS_ADRERR: i32 = 2; +pub const BUS_OBJERR: i32 = 3; + +pub const TRAP_BRKPT: i32 = 1; +pub const TRAP_TRACE: i32 = 2; + +pub const CLD_EXITED: i32 = 1; +pub const CLD_KILLED: i32 = 2; +pub const CLD_DUMPED: i32 = 3; +pub const CLD_TRAPPED: i32 = 4; +pub const CLD_STOPPED: i32 = 5; +pub const CLD_CONTINUED: i32 = 6; + +pub(crate) type ucontext_t = ucontext; +pub(crate) type mcontext_t = mcontext; + +//TODO: share definition with SigStack? +#[repr(C)] +pub struct ucontext { + #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "riscv64" + ))] + _pad: [c_ulong; 1], // pad from 7*8 to 64 + + #[cfg(target_arch = "x86")] + _pad: [c_ulong; 3], // pad from 9*4 to 12*4 + + pub uc_link: *mut ucontext_t, + pub uc_stack: stack_t, + pub uc_sigmask: sigset_t, + _sival: c_ulong, + _sigcode: c_uint, + _signum: c_uint, + pub uc_mcontext: mcontext_t, +} + +#[cfg(target_arch = "x86")] +#[repr(C)] +pub struct mcontext { + _opaque: [c_uchar; 512], +} + +//TODO: share definition with ArchIntRegs? +//TODO: repr(align(16))? +#[cfg(target_arch = "x86_64")] +#[repr(C)] +pub struct mcontext { + pub ymm_upper: [[c_ulong; 2]; 16], + pub fxsave: [[c_ulong; 2]; 29], + pub r15: c_ulong, // fxsave "available" +0 + pub r14: c_ulong, // available +8 + pub r13: c_ulong, // available +16 + pub r12: c_ulong, // available +24 + pub rbp: c_ulong, // available +32 + pub rbx: c_ulong, // available +40 + pub r11: c_ulong, // outside fxsave, and so on + pub r10: c_ulong, + pub r9: c_ulong, + pub r8: c_ulong, + pub rax: c_ulong, + pub rcx: c_ulong, + pub rdx: c_ulong, + pub rsi: c_ulong, + pub rdi: c_ulong, + pub rflags: c_ulong, + pub rip: c_ulong, + pub rsp: c_ulong, +} + +#[cfg(target_arch = "aarch64")] +#[repr(C)] +pub struct mcontext { + _opaque: [c_uchar; 272], +} + +#[cfg(target_arch = "riscv64")] +#[repr(C)] +pub struct mcontext { + _opaque: [c_uchar; 520], +} + +#[unsafe(no_mangle)] +pub extern "C" fn __completely_unused_cbindgen_workaround_fn_ucontext_mcontext( + a: *const ucontext_t, + b: *const mcontext_t, +) { +} + +impl From for siginfo_t { + fn from(value: SiginfoAbi) -> Self { + unsafe { core::mem::transmute(value) } + } +} diff --git a/src/header/signal/signalfd.rs b/src/header/signal/signalfd.rs new file mode 100644 index 0000000000..790b6bdba8 --- /dev/null +++ b/src/header/signal/signalfd.rs @@ -0,0 +1,103 @@ +use core::{mem, ptr}; + +use crate::{ + error::{Errno, ResultExt}, + header::fcntl::{ + FD_CLOEXEC, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC, O_NONBLOCK, O_RDWR, fcntl, + }, + platform::{ + ERRNO, Pal, Sys, + types::{c_int, c_ulonglong}, + }, +}; + +use super::{SIG_BLOCK, sigprocmask, sigset_t}; + +pub const SFD_CLOEXEC: c_int = 0x80000; +pub const SFD_NONBLOCK: c_int = 0x800; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct signalfd_siginfo { + pub ssi_signo: u32, + pub ssi_errno: i32, + pub ssi_code: i32, + pub ssi_pid: u32, + pub ssi_uid: u32, + pub ssi_fd: i32, + pub ssi_tid: u32, + pub ssi_band: u32, + pub ssi_overrun: u32, + pub ssi_trapno: u32, + pub ssi_status: i32, + pub ssi_int: i32, + pub ssi_ptr: u64, + pub ssi_utime: u64, + pub ssi_stime: u64, + pub ssi_addr: u64, + pub ssi_addr_lsb: u16, + pub __pad2: u16, + pub ssi_syscall: i32, + pub ssi_call_addr: u64, + pub ssi_arch: u32, + pub __pad: [u8; 28], +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_signalfd_siginfo(siginfo: signalfd_siginfo) {} + +fn signalfd4_inner(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> Result { + let supported = SFD_CLOEXEC | SFD_NONBLOCK; + if flags & !supported != 0 || masksize != mem::size_of::() { + return Err(Errno(crate::header::errno::EINVAL)); + } + if mask.is_null() { + return Err(Errno(crate::header::errno::EFAULT)); + } + + let new_fd = if fd == -1 { + let mut oflag = O_RDWR; + if flags & SFD_CLOEXEC == SFD_CLOEXEC { + oflag |= O_CLOEXEC; + } + if flags & SFD_NONBLOCK == SFD_NONBLOCK { + oflag |= O_NONBLOCK; + } + Sys::open(c"/scheme/event".into(), oflag, 0)? + } else { + if flags & SFD_CLOEXEC == SFD_CLOEXEC + && unsafe { fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong) } < 0 + { + return Err(Errno(ERRNO.get())); + } + if flags & SFD_NONBLOCK == SFD_NONBLOCK { + let current = unsafe { fcntl(fd, F_GETFL, 0 as c_ulonglong) }; + if current < 0 { + return Err(Errno(ERRNO.get())); + } + if unsafe { fcntl(fd, F_SETFL, (current | O_NONBLOCK) as c_ulonglong) } < 0 { + return Err(Errno(ERRNO.get())); + } + } + fd + }; + + if unsafe { sigprocmask(SIG_BLOCK, mask, ptr::null_mut()) } < 0 { + if fd == -1 { + let _ = Sys::close(new_fd); + } + return Err(Errno(ERRNO.get())); + } + + Ok(new_fd) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn signalfd4(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> c_int { + signalfd4_inner(fd, mask, masksize, flags).or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn signalfd(fd: c_int, mask: *const sigset_t, masksize: usize) -> c_int { + unsafe { signalfd4(fd, mask, masksize, 0) } +} diff --git a/src/header/stdio/cbindgen.toml b/src/header/stdio/cbindgen.toml new file mode 100644 index 0000000000..7a6860140b --- /dev/null +++ b/src/header/stdio/cbindgen.toml @@ -0,0 +1,32 @@ +sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h", "features.h"] +include_guard = "_RELIBC_STDIO_H" +trailer = """ +#ifndef _RELIBC_BITS_STDIO_H +#define _RELIBC_BITS_STDIO_H + +// XXX: this is only here because cbindgen can't handle string constants +#define P_tmpdir "/tmp" + +// TODO: figure out why this duplicate is needed to compile libiconv +typedef struct FILE FILE; + +// A typedef doesn't suffice, because libgmp uses this definition to check if +// STDIO was loaded. +#define FILE FILE +// Likewise, stdin, stdout, and stderr are expected to be macros. +#define stdin stdin +#define stdout stdout +#define stderr stderr + +#endif // _RELIBC_BITS_STDIO_H +""" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"AtomicBool" = "volatile char" diff --git a/src/header/stdio/constants.rs b/src/header/stdio/constants.rs new file mode 100644 index 0000000000..ff19da78e2 --- /dev/null +++ b/src/header/stdio/constants.rs @@ -0,0 +1,50 @@ +use crate::platform::types::{c_int, c_uint, int32_t, off_t}; + +pub const EOF: c_int = -1; +pub const BUFSIZ: c_int = 1024; + +pub const UNGET: c_int = 8; + +pub const FILENAME_MAX: c_int = 4096; + +pub const F_PERM: c_int = 1; +pub const F_NORD: c_int = 4; +pub const F_NOWR: c_int = 8; +pub const F_EOF: c_int = 16; +pub const F_ERR: c_int = 32; +pub const F_SVB: c_int = 64; +pub const F_APP: c_int = 128; +pub const F_BADJ: c_int = 256; + +pub const SEEK_SET: c_int = 0; +pub const SEEK_CUR: c_int = 1; +pub const SEEK_END: c_int = 2; + +pub const _IOFBF: c_int = 0; +pub const _IOLBF: c_int = 1; +pub const _IONBF: c_int = 2; + +// renameat2 +// (uint instead of int is intentional) +/// Rename but don't replace the target if it exists. +pub const RENAME_NOREPLACE: c_uint = 0x01; +/// Atomically swap two files. +#[cfg(target_os = "linux")] +pub const RENAME_EXCHANGE: c_uint = 0x02; +#[cfg(target_os = "linux")] +pub const RENAME_WHITEOUT: c_uint = 0x04; + +// /dev/tty + nul +pub const L_ctermid: usize = 9; +// form of name is /XXXXXX, so 7 +pub const L_tmpnam: c_int = 7; +// 36^6 (26 letters + 10 digits) is larger than i32::MAX, so just set to that +// for now +pub const TMP_MAX: int32_t = 2_147_483_647; +// XXX: defined manually in cbindgen as well because it can't handle +// string constants in any form AFAICT +/// cbindgen:ignore +pub const P_tmpdir: &[u8; 5] = b"/tmp\0"; + +#[allow(non_camel_case_types)] +pub type fpos_t = off_t; diff --git a/src/header/stdio/default.rs b/src/header/stdio/default.rs new file mode 100644 index 0000000000..36ef4bf5d7 --- /dev/null +++ b/src/header/stdio/default.rs @@ -0,0 +1,61 @@ +use super::{BUFSIZ, Buffer, FILE, constants}; +use core::{cell::UnsafeCell, ptr}; + +use crate::{fs::File, header::pthread, io::LineWriter, platform::types::c_int, sync::Once}; +use alloc::{boxed::Box, vec::Vec}; + +// TODO: Change FILE to allow const fn initialization? +pub struct GlobalFile(UnsafeCell); + +impl GlobalFile { + fn new(file: c_int, flags: c_int) -> Self { + let file = File::new(file); + let writer = Box::new(LineWriter::new(unsafe { file.get_ref() })); + let mutex_attr = pthread::RlctMutexAttr { + ty: pthread::PTHREAD_MUTEX_RECURSIVE, + ..Default::default() + }; + GlobalFile(UnsafeCell::new(FILE { + lock: pthread::RlctMutex::new(&mutex_attr).unwrap(), + + file, + flags: constants::F_PERM | flags, + read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]), + read_pos: 0, + read_size: 0, + unget: Vec::new(), + writer, + + pid: None, + + orientation: 0, + })) + } + pub fn get(&self) -> *mut FILE { + self.0.get() + } +} +// statics need to be Sync +unsafe impl Sync for GlobalFile {} + +// TODO: Allow const fn initialization of FILE +static DEFAULT_STDIN: Once = Once::new(); +static DEFAULT_STDOUT: Once = Once::new(); +static DEFAULT_STDERR: Once = Once::new(); + +pub fn default_stdin() -> &'static GlobalFile { + DEFAULT_STDIN.call_once(|| GlobalFile::new(0, constants::F_NOWR)) +} +pub fn default_stdout() -> &'static GlobalFile { + DEFAULT_STDOUT.call_once(|| GlobalFile::new(1, constants::F_NORD)) +} +pub fn default_stderr() -> &'static GlobalFile { + DEFAULT_STDERR.call_once(|| GlobalFile::new(2, constants::F_NORD)) +} + +#[unsafe(no_mangle)] +pub static mut stdin: *mut FILE = ptr::null_mut(); +#[unsafe(no_mangle)] +pub static mut stdout: *mut FILE = ptr::null_mut(); +#[unsafe(no_mangle)] +pub static mut stderr: *mut FILE = ptr::null_mut(); diff --git a/src/header/stdio/ext.rs b/src/header/stdio/ext.rs new file mode 100644 index 0000000000..3156087c2f --- /dev/null +++ b/src/header/stdio/ext.rs @@ -0,0 +1,41 @@ +use crate::{ + header::stdio::{F_NORD, F_NOWR, FILE}, + platform::types::{c_int, size_t}, +}; + +#[unsafe(no_mangle)] +pub extern "C" fn __fpending(stream: *mut FILE) -> size_t { + let stream = unsafe { &mut *stream }.lock(); + + stream.writer.pending() +} + +#[unsafe(no_mangle)] +pub extern "C" fn __freadable(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); + + c_int::from(stream.flags & F_NORD == 0) +} + +#[unsafe(no_mangle)] +pub extern "C" fn __fwritable(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); + + c_int::from(stream.flags & F_NOWR == 0) +} + +//TODO: Check last operation when read-write +#[unsafe(no_mangle)] +pub extern "C" fn __freading(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); + + c_int::from(stream.flags & F_NORD == 0) +} + +//TODO: Check last operation when read-write +#[unsafe(no_mangle)] +pub extern "C" fn __fwriting(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); + + c_int::from(stream.flags & F_NOWR == 0) +} diff --git a/src/header/stdio/getdelim.rs b/src/header/stdio/getdelim.rs new file mode 100644 index 0000000000..145aa25b31 --- /dev/null +++ b/src/header/stdio/getdelim.rs @@ -0,0 +1,145 @@ +// See . + +use alloc::vec::Vec; +use core::{intrinsics::unlikely, ptr}; + +use crate::{ + header::{ + errno::{EINVAL, ENOMEM, EOVERFLOW}, + stdio::FILE, + stdlib, + }, + io::BufRead, + platform::types::{c_char, c_int, c_void, size_t, ssize_t}, +}; + +use crate::{ + header::stdio::{F_EOF, F_ERR, feof, ferror}, + platform::ERRNO, +}; + +/// see getdelim (getline is a special case of getdelim with delim == '\n') +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getline( + lineptr: *mut *mut c_char, + n: *mut size_t, + stream: *mut FILE, +) -> ssize_t { + unsafe { getdelim(lineptr, n, c_int::from(b'\n'), stream) } +} + +// One *could* read the standard as 'getdelim sets the stream error flag on *any* error, though +// since glibc doesn't seem to do this, I won't either + +/// +/// +/// # Safety +/// - `lineptr, *lineptr, `n`, `stream` pointers must be valid and have to be aligned. +/// - `stream` has to be a valid file handle returned by fopen and likes. +/// +/// # Deviation from POSIX +/// - **EINVAL is set on stream being NULL or delim not fitting into char** (POSIX allows UB) +/// - **`*n` can contain invalid data.** The buffer size `n` is not read, instead realloc is called each time. That is in principle +/// inefficent since the buffer is reallocated in memory for every call, but if `n` is by mistake +/// bigger than the number of bytes allocated for the buffer, there can be no out-of-bounds write. +/// - On non-stream-related errors, the error indicator of the stream is *not* set. Posix states +/// "If an error occurs, the error indicator for the stream shall be set, and the function shall +/// return -1 and set errno to indicate the error." but in cases that produce EINVAL even glibc +/// doesn't seem to set the error indicator, so we also don't. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getdelim( + lineptr: *mut *mut c_char, + n: *mut size_t, + delim: c_int, + stream: *mut FILE, +) -> ssize_t { + let (lineptr, n, stream) = if let (Some(ptr), Some(n), Some(file)) = + (unsafe { lineptr.as_mut() }, unsafe { n.as_mut() }, unsafe { + stream.as_mut() + }) { + (ptr, n, file) + } else { + ERRNO.set(EINVAL); + return -1 as ssize_t; + }; + + if unsafe { feof(stream) } != 0 || unsafe { ferror(stream) } != 0 { + return -1 as ssize_t; + } + + // POSIX specifies UB but we test anyway + // returning EINVAL in that case + let delim: u8 = if let Ok(delim) = delim.try_into() { + delim + } else { + ERRNO.set(EINVAL); + return -1; + }; + + //TODO: More efficient algorithm using lineptr and n instead of this vec + let mut buf = Vec::new(); + let count = { + let mut stream = (*stream).lock(); + match stream.read_until(delim, &mut buf) { + Ok(ok) => ok, + Err(err) => { + stream.flags &= F_ERR; + return -1; + } + } + }; + + // "[EOVERFLOW] + // The number of bytes to be written into the buffer, including the delimiter character (if encountered), would exceed {SSIZE_MAX}." + if unlikely(count > ssize_t::MAX as usize) { + ERRNO.set(EOVERFLOW); + return -1; + } + + // we reached EOF if either + // - we have no last elem (because vec is empty), or + // - the last elem doesn't match the delimiter + let eof_reached = if let Some(last) = buf.last() { + *last == delim + } else { + true + }; + + // "If the end-of-file indicator for the stream is set, or if no characters were read and the + // stream is at end-of-file, the end-of-file indicator for the stream shall be set and the + // function shall return -1." + if eof_reached { + stream.flags &= F_EOF; + if count == 0 { + return -1; + } + } + + //TODO: Check errors and improve safety + { + // Allocate lineptr to size of buf plus NUL byte and set n to size of lineptr + *n = count + 1; + // The advantage in always realloc'ing is that even if the user supplies a wrong n, this + // doesn't break + *lineptr = unsafe { stdlib::realloc((*lineptr).cast::(), *n) }.cast::(); + if unlikely(lineptr.is_null() && *n != 0usize) { + // memory error; realloc returns NULL on alloc'ing 0 bytes + ERRNO.set(ENOMEM); + return -1; + } + + // Copy buf to lineptr + unsafe { ptr::copy(buf.as_ptr(), (*lineptr).cast::(), count) }; + + // NUL terminate lineptr + unsafe { *lineptr.add(count) = 0 }; + + // TODO remove + /*eprintln!( + "[DBG]{}: {}, {:?}, {:?}, {:?}", line!(), + String::from_utf8(buf).unwrap(), count, *n, *lineptr + );*/ + // Return allocated size + count as ssize_t + } +} diff --git a/src/header/stdio/helpers.rs b/src/header/stdio/helpers.rs new file mode 100644 index 0000000000..d070673b0a --- /dev/null +++ b/src/header/stdio/helpers.rs @@ -0,0 +1,97 @@ +use alloc::boxed::Box; + +use super::{ + Buffer, FILE, + constants::{BUFSIZ, F_APP, F_NORD, F_NOWR}, +}; +use crate::{ + c_str::CStr, + error::Errno, + fs::File, + header::{ + errno::EINVAL, + fcntl::{ + F_GETFL, F_SETFD, F_SETFL, FD_CLOEXEC, O_APPEND, O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY, + O_RDWR, O_TRUNC, O_WRONLY, fcntl, + }, + pthread, + }, + io::BufWriter, + platform::types::{c_int, c_ulonglong}, +}; +use alloc::vec::Vec; + +/// Parse mode flags as a string and output a mode flags integer +pub fn parse_mode_flags(mode_str: CStr) -> i32 { + let mut flags = if mode_str.contains(b'+') { + O_RDWR + } else if mode_str.first() == b'r' { + O_RDONLY + } else { + O_WRONLY + }; + if mode_str.contains(b'x') { + flags |= O_EXCL; + } + if mode_str.contains(b'e') { + flags |= O_CLOEXEC; + } + if mode_str.first() != b'r' { + flags |= O_CREAT; + } + if mode_str.first() == b'w' { + flags |= O_TRUNC; + } else if mode_str.first() == b'a' { + flags |= O_APPEND; + } + + flags +} + +/// Open a file with the file descriptor `fd` in the mode `mode` +pub fn _fdopen(fd: c_int, mode: CStr) -> Result, Errno> { + if mode.first() != b'r' && mode.first() != b'w' && mode.first() != b'a' { + return Err(Errno(EINVAL)); + } + + let mut flags = 0; + if !mode.contains(b'+') { + flags |= if mode.first() == b'r' { F_NOWR } else { F_NORD }; + } + + if mode.contains(b'e') { + unsafe { + fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong); + } + } + + if mode.first() == b'a' { + let f = unsafe { fcntl(fd, F_GETFL, 0) }; + if (f & O_APPEND) == 0 { + unsafe { fcntl(fd, F_SETFL, (f | O_APPEND) as c_ulonglong) }; + } + flags |= F_APP; + } + + let file = File::new(fd); + let writer = Box::new(BufWriter::new(unsafe { file.get_ref() })); + let mutex_attr = pthread::RlctMutexAttr { + ty: pthread::PTHREAD_MUTEX_RECURSIVE, + ..Default::default() + }; + Ok(Box::new(FILE { + lock: pthread::RlctMutex::new(&mutex_attr).unwrap(), + + file, + flags, + read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]), + read_pos: 0, + read_size: 0, + unget: Vec::new(), + writer, + + pid: None, + + orientation: 0, + })) +} diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs new file mode 100644 index 0000000000..8cb689ec54 --- /dev/null +++ b/src/header/stdio/mod.rs @@ -0,0 +1,1607 @@ +//! `stdio.h` implementation. +//! +//! See . + +use alloc::{ + borrow::{Borrow, BorrowMut}, + boxed::Box, + vec::Vec, +}; +use core::{ + cmp, + ffi::VaList as va_list, + fmt::{self, Write as WriteFmt}, + mem, + ops::{Deref, DerefMut}, + ptr, slice, str, +}; + +use crate::{ + c_str::{CStr, Thin}, + c_vec::CVec, + error::{ResultExt, ResultExtPtrMut}, + fs::File, + header::{ + errno::{self, STR_ERROR}, + fcntl, + pthread::RlctMutex, + pwd, stdlib, + string::{self, strlen, strncpy}, + unistd, + }, + io::{self, BufRead, BufWriter, LineWriter, Read, Write}, + out::Out, + platform::{ + self, ERRNO, Pal, Sys, WriteByte, + types::{c_char, c_int, c_long, c_uint, c_ulonglong, c_void, off_t, size_t}, + }, +}; +use reader::Reader; + +pub use self::constants::*; +mod constants; + +pub use self::default::*; +mod default; + +pub use self::getdelim::*; +mod getdelim; + +pub use self::open_memstream::*; +mod open_memstream; + +mod ext; +mod helpers; +pub mod printf; +pub mod reader; +pub mod scanf; +static mut TMPNAM_BUF: [c_char; L_tmpnam as usize + 1] = [0; L_tmpnam as usize + 1]; + +enum Buffer<'a> { + Borrowed(&'a mut [u8]), + Owned(Vec), +} + +impl<'a> Deref for Buffer<'a> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + match self { + Buffer::Borrowed(inner) => inner, + Buffer::Owned(inner) => inner.borrow(), + } + } +} + +impl<'a> DerefMut for Buffer<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Buffer::Borrowed(inner) => inner, + Buffer::Owned(inner) => inner.borrow_mut(), + } + } +} + +pub trait Pending { + fn pending(&self) -> size_t; +} + +impl Pending for BufWriter { + fn pending(&self) -> size_t { + self.buf.len() as size_t + } +} + +impl Pending for LineWriter { + fn pending(&self) -> size_t { + self.inner.buf.len() as size_t + } +} + +pub trait Writer: Write + Pending { + fn purge(&mut self); +} + +impl Writer for BufWriter { + fn purge(&mut self) { + self.buf.clear(); + } +} + +impl Writer for LineWriter { + fn purge(&mut self) { + self.inner.buf.clear(); + } +} + +/// This struct gets exposed to the C API. +pub struct FILE { + lock: RlctMutex, + + file: File, + // pub for stdio_ext + pub(crate) flags: c_int, + + // TODO: Is the read_buf dropped? + read_buf: Buffer<'static>, + + read_pos: usize, + read_size: usize, + unget: Vec, + // pub for stdio_ext + + // TODO: To support const fn initialization, use static dispatch (perhaps partially)? + pub(crate) writer: Box, + + // Optional pid for use with popen/pclose + pid: Option, + + // wchar support + pub(crate) orientation: c_int, +} + +impl Read for FILE { + fn read(&mut self, out: &mut [u8]) -> io::Result { + let unget_read_size = cmp::min(out.len(), self.unget.len()); + for inner in out.iter_mut().take(unget_read_size) { + *inner = self.unget.pop().unwrap(); + } + if unget_read_size != 0 { + return Ok(unget_read_size); + } + + let len = { + let buf = self.fill_buf()?; + let len = buf.len().min(out.len()); + + out[..len].copy_from_slice(&buf[..len]); + len + }; + self.consume(len); + Ok(len) + } +} + +impl BufRead for FILE { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + if self.read_pos == self.read_size { + self.read_size = match self.file.read(&mut self.read_buf) { + Ok(0) => { + self.flags |= F_EOF; + 0 + } + Ok(n) => n, + Err(err) => { + self.flags |= F_ERR; + return Err(err); + } + }; + self.read_pos = 0; + } + Ok(&self.read_buf[self.read_pos..self.read_size]) + } + fn consume(&mut self, i: usize) { + self.read_pos = (self.read_pos + i).min(self.read_size); + } +} + +impl Write for FILE { + fn write(&mut self, buf: &[u8]) -> io::Result { + match self.writer.write(buf) { + Ok(n) => Ok(n), + Err(err) => { + self.flags |= F_ERR; + Err(err) + } + } + } + fn flush(&mut self) -> io::Result<()> { + match self.writer.flush() { + Ok(()) => Ok(()), + Err(err) => { + self.flags |= F_ERR; + Err(err) + } + } + } +} + +impl WriteFmt for FILE { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()) + .map(|_| ()) + .map_err(|_| fmt::Error) + } +} + +impl WriteByte for FILE { + fn write_u8(&mut self, c: u8) -> fmt::Result { + self.write_all(&[c]).map(|_| ()).map_err(|_| fmt::Error) + } +} + +impl FILE { + pub fn lock(&mut self) -> LockGuard<'_> { + unsafe { + flockfile(self); + } + LockGuard(self) + } + + pub fn try_set_orientation(&mut self, mode: c_int) -> c_int { + let stream = self.lock(); + stream.0.try_set_orientation_unlocked(mode) + } + + pub fn try_set_orientation_unlocked(&mut self, mode: c_int) -> c_int { + if self.orientation == 0 { + self.orientation = match mode { + 1..=i32::MAX => 1, + i32::MIN..=-1 => -1, + 0 => self.orientation, + }; + } + self.orientation + } + + pub fn try_set_byte_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> { + match self.try_set_orientation_unlocked(-1) { + i32::MIN..=-1 => Ok(()), + x => Err(x), + } + } + + pub fn try_set_wide_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> { + match self.try_set_orientation_unlocked(1) { + 1..=i32::MAX => Ok(()), + x => Err(x), + } + } + + pub fn purge(&mut self) { + // Purge read buffer + self.read_pos = 0; + self.read_size = 0; + // Purge unget + self.unget.clear(); + // Purge write buffer + self.writer.purge(); + } +} + +pub struct LockGuard<'a>(&'a mut FILE); + +impl<'a> Deref for LockGuard<'a> { + type Target = FILE; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a> DerefMut for LockGuard<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +impl<'a> Drop for LockGuard<'a> { + fn drop(&mut self) { + unsafe { + funlockfile(self.0); + } + } +} + +/// See . +/// +/// Clears EOF and ERR indicators on a stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn clearerr(stream: *mut FILE) { + let mut stream = unsafe { (*stream).lock() }; + stream.flags &= !(F_EOF | F_ERR); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ctermid(s: *mut c_char) -> *mut c_char { + static mut TERMID: [u8; L_ctermid] = *b"/dev/tty\0"; + + if s.is_null() { + return (&raw mut TERMID).cast::(); + } + + unsafe { strncpy(s, (&raw mut TERMID).cast::(), L_ctermid) } +} + +/// See +/// +/// Marked legacy in SUS Version 2. +// #[unsafe(no_mangle)] +#[deprecated] +pub unsafe extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { + let mut buf: Vec = vec![0; 256]; + let mut pwd: pwd::passwd = unsafe { mem::zeroed() }; + let mut pwdbuf: *mut pwd::passwd = unsafe { mem::zeroed() }; + if !s.is_null() { + unsafe { + *s.add(0) = 0; + } + } + unsafe { + pwd::getpwuid_r( + unistd::geteuid(), + &raw mut pwd, + buf.as_mut_ptr(), + buf.len(), + &raw mut pwdbuf, + ) + }; + if pwdbuf.is_null() { + return s; + } + + if !s.is_null() { + unsafe { strncpy(s, (*pwdbuf).pw_name, unistd::L_cuserid) }; + return s; + } + + unsafe { (*pwdbuf).pw_name } +} + +/// See . +/// +/// Close a file +/// This function does not guarentee that the file buffer will be flushed or that the file +/// descriptor will be closed, so if it is important that the file be written to, use `fflush()` +/// prior to using this function. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }; + unsafe { flockfile(stream) }; + + let mut r = stream.flush().is_err(); + // TODO: better error handling + let close = Sys::close(*stream.file).map(|()| 0).or_minus_one_errno() == -1; + r = r || close; + + if stream.flags & constants::F_PERM == 0 { + // Not one of stdin, stdout or stderr + let mut stream = unsafe { Box::from_raw(stream) }; + // Reference files aren't closed on drop, so pretend to be a reference + stream.file.reference = true; + } else { + unsafe { funlockfile(stream) }; + } + + c_int::from(r) +} + +/// See . +/// +/// Open a file from a file descriptor +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { + helpers::_fdopen(fildes, unsafe { CStr::from_ptr(mode) }) + .map(Box::into_raw) + .or_errno_null_mut() +} + +/// See . +/// +/// Check for EOF +#[unsafe(no_mangle)] +pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int { + let stream = unsafe { (*stream).lock() }; + stream.flags & F_EOF +} + +/// See . +/// +/// Check for ERR +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int { + let stream = unsafe { (*stream).lock() }; + stream.flags & F_ERR +} + +/// See . +/// +/// Flush output to stream, or sync read position +/// Ensure the file is unlocked before calling this function, as it will attempt to lock the file +/// itself. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int { + if stream.is_null() { + //TODO: flush all files! + + if unsafe { fflush(stdout) } != 0 { + return EOF; + } + + if unsafe { fflush(stderr) } != 0 { + return EOF; + } + } else { + let mut stream = unsafe { (*stream).lock() }; + if stream.flush().is_err() { + return EOF; + } + } + + 0 +} + +/// See . +/// +/// Get a single char from a stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + unsafe { getc_unlocked(&raw mut *stream) } +} + +/// See . +/// +/// Get the position of the stream and store it in pos +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int { + let off = unsafe { ftello(stream) }; + if off < 0 { + return -1; + } + unsafe { *pos = off }; + 0 +} + +/// See . +/// +/// Get a string from the stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fgets( + original: *mut c_char, + max: c_int, + stream: *mut FILE, +) -> *mut c_char { + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return ptr::null_mut(); + } + + let mut out = original; + let max = max as usize; + let mut left = max.saturating_sub(1); // Make space for the terminating NUL-byte + let mut wrote = false; + + if left >= 1 { + let unget_read_size = cmp::min(left, stream.unget.len()); + for _ in 0..unget_read_size { + unsafe { *out = stream.unget.pop().unwrap() as c_char }; + out = unsafe { out.offset(1) }; + } + left -= unget_read_size; + } + + loop { + if left == 0 { + break; + } + + // TODO: When NLL is a thing, this block can be flattened out + let (read, exit) = { + let buf = match stream.fill_buf() { + Ok(buf) => buf, + Err(_) => return ptr::null_mut(), + }; + if buf.is_empty() { + break; + } + wrote = true; + let len = buf.len().min(left); + + let newline = buf[..len].iter().position(|&c| c == b'\n'); + let len = newline.map(|i| i + 1).unwrap_or(len); + + unsafe { ptr::copy_nonoverlapping(buf.as_ptr(), out.cast::(), len) }; + + (len, newline.is_some()) + }; + + stream.consume(read); + + out = unsafe { out.add(read) }; + left -= read; + + if exit { + break; + } + } + + if max >= 1 { + // Write the NUL byte + unsafe { *out = 0 }; + } + if wrote { original } else { ptr::null_mut() } +} + +/// See . +/// +/// Get the underlying file descriptor +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int { + let stream = unsafe { (*stream).lock() }; + *stream.file +} + +/// See . +/// +/// Lock the file +/// Do not call any functions other than those with the `_unlocked` postfix while the file is +/// locked +#[unsafe(no_mangle)] +pub unsafe extern "C" fn flockfile(file: *mut FILE) { + if let Err(e) = unsafe { (*file).lock.lock() } { + todo_error!(0, e, "flockfile error") + } +} + +/// See . +/// +/// Open the file in mode `mode` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { + let initial_mode = unsafe { *mode }; + if initial_mode != b'r' as c_char + && initial_mode != b'w' as c_char + && initial_mode != b'a' as c_char + { + platform::ERRNO.set(errno::EINVAL); + return ptr::null_mut(); + } + + let flags = helpers::parse_mode_flags(unsafe { CStr::from_ptr(mode) }); + + let new_mode = if flags & fcntl::O_CREAT == fcntl::O_CREAT { + 0o666 + } else { + 0 + }; + + let fd = unsafe { fcntl::open(filename, flags, new_mode) }; + if fd < 0 { + return ptr::null_mut(); + } + + if flags & fcntl::O_CLOEXEC > 0 { + unsafe { fcntl::fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong) }; + } + + helpers::_fdopen(fd, unsafe { CStr::from_ptr(mode) }) + .map(Box::into_raw) + .inspect_err(|err| { + // TODO: guard type + if let Ok(()) = Sys::close(fd) {}; // TODO handle error + }) + .or_errno_null_mut() +} + +/// See . +/// +/// Non-POSIX. From Solaris. +/// +/// Clear the buffers of a stream +/// Ensure the file is unlocked before calling this function, as it will attempt to lock the file +/// itself. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __fpurge(stream: *mut FILE) { + if !stream.is_null() { + let mut stream = unsafe { (*stream).lock() }; + stream.purge(); + } +} + +/// See . +/// +/// Insert a character into the stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + unsafe { putc_unlocked(c, &raw mut *stream) } +} + +/// See . +/// +/// Insert a string into a stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + let buf = unsafe { slice::from_raw_parts(s as *mut u8, strlen(s)) }; + + if stream.write_all(buf).is_ok() { 0 } else { -1 } +} + +/// See . +/// +/// Read `nitems` of size `size` into `ptr` from `stream` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fread( + ptr: *mut c_void, + size: size_t, + nitems: size_t, + stream: *mut FILE, +) -> size_t { + if size == 0 || nitems == 0 { + return 0; + } + + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return 0; + } + + let buf = unsafe { slice::from_raw_parts_mut(ptr.cast::(), size * nitems) }; + let mut read = 0; + while read < buf.len() { + match stream.read(&mut buf[read..]) { + Ok(0) | Err(_) => break, + Ok(n) => read += n, + } + } + (read / size) as size_t +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn freopen( + filename: *const c_char, + mode: *const c_char, + stream: &mut FILE, +) -> *mut FILE { + let mut flags = helpers::parse_mode_flags(unsafe { CStr::from_ptr(mode) }); + unsafe { flockfile(stream) }; + + let _ = stream.flush(); + if filename.is_null() { + // Reopen stream in new mode + if flags & fcntl::O_CLOEXEC > 0 { + unsafe { + fcntl::fcntl( + *stream.file, + fcntl::F_SETFD, + fcntl::FD_CLOEXEC as c_ulonglong, + ) + }; + } + flags &= !(fcntl::O_CREAT | fcntl::O_EXCL | fcntl::O_CLOEXEC); + if unsafe { fcntl::fcntl(*stream.file, fcntl::F_SETFL, flags as c_ulonglong) } < 0 { + unsafe { funlockfile(stream) }; + unsafe { fclose(stream) }; + return ptr::null_mut(); + } + } else { + let new = unsafe { fopen(filename, mode) }; + if new.is_null() { + unsafe { funlockfile(stream) }; + unsafe { fclose(stream) }; + return ptr::null_mut(); + } + let new = unsafe { &mut *new }; // Should be safe, new is not null + if *new.file == *stream.file { + new.file.fd = -1; + } else if Sys::dup2(*new.file, *stream.file).or_minus_one_errno() == -1 + || unsafe { + fcntl::fcntl( + *stream.file, + fcntl::F_SETFL, + (flags & fcntl::O_CLOEXEC) as c_ulonglong, + ) + } < 0 + { + unsafe { funlockfile(stream) }; + unsafe { fclose(new) }; + unsafe { fclose(stream) }; + return ptr::null_mut(); + } + stream.flags = (stream.flags & constants::F_PERM) | new.flags; + unsafe { fclose(new) }; + } + stream.orientation = 0; + unsafe { funlockfile(stream) }; + stream +} + +/// See . +/// +/// Seek to an offset `offset` from `whence` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int { + unsafe { fseeko(stream, offset as off_t, whence) } +} + +/// See . +/// +/// Seek to an offset `offset` from `whence` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fseeko(stream: *mut FILE, off: off_t, whence: c_int) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + unsafe { fseek_locked(&mut stream, off, whence) } +} + +pub unsafe fn fseek_locked(stream: &mut FILE, mut off: off_t, whence: c_int) -> c_int { + if whence == SEEK_CUR { + // Since it's a buffered writer, our actual cursor isn't where the user + // thinks + off -= (stream.read_size - stream.read_pos) as off_t; + } + + // Flush write buffer before seek + if stream.flush().is_err() { + return -1; + } + + let err = Sys::lseek(*stream.file, off, whence).or_minus_one_errno(); + if err < 0 { + return err as c_int; + } + + stream.flags &= !(F_EOF | F_ERR); + stream.read_pos = 0; + stream.read_size = 0; + stream.unget = Vec::new(); + 0 +} + +/// See . +/// +/// Seek to a position `pos` in the file from the beginning of the file +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int { + unsafe { fseeko(stream, *pos, SEEK_SET) } +} + +/// See . +/// +/// Get the current position of the cursor in the file +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long { + unsafe { ftello(stream) as c_long } +} + +/// See . +/// +/// Get the current position of the cursor in the file +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t { + let mut stream = unsafe { (*stream).lock() }; + unsafe { ftell_locked(&mut stream) } +} + +pub unsafe extern "C" fn ftell_locked(stream: &mut FILE) -> off_t { + let pos = Sys::lseek(*stream.file, 0, SEEK_CUR).or_minus_one_errno(); + if pos < 0 { + return -1; + } + + // Adjust for read buffer, ungetc, and write buffer + pos - (stream.read_size - stream.read_pos) as off_t - stream.unget.len() as off_t + + stream.writer.pending() as off_t +} + +/// See . +/// +/// Try to lock the file. Returns 0 for success, 1 for failure +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> c_int { + if unsafe { (*file).lock.try_lock() }.is_ok() { + 0 + } else { + 1 + } +} + +/// See . +/// +/// Unlock the file +#[unsafe(no_mangle)] +pub unsafe extern "C" fn funlockfile(file: *mut FILE) { + if let Err(e) = unsafe { (*file).lock.unlock() } { + todo_error!(0, e, "RELIBC: funlockfile error") + } +} + +/// See . +/// +/// Write `nitems` of size `size` from `ptr` to `stream` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fwrite( + ptr: *const c_void, + size: size_t, + nitems: size_t, + stream: *mut FILE, +) -> size_t { + if size == 0 || nitems == 0 { + return 0; + } + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return 0; + } + + let buf = unsafe { slice::from_raw_parts(ptr.cast::(), size * nitems) }; + let mut written = 0; + while written < buf.len() { + match stream.write(&buf[written..]) { + Ok(0) | Err(_) => break, + Ok(n) => written += n, + } + } + (written / size) as size_t +} + +/// See . +/// +/// Get a single char from a stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + unsafe { getc_unlocked(&raw mut *stream) } +} + +/// See . +/// +/// Get a single char from `stdin` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getchar() -> c_int { + unsafe { fgetc(&raw mut *stdin) } +} + +/// See . +/// +/// Get a char from a stream without locking the stream +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { + if unsafe { (*stream).try_set_byte_orientation_unlocked() }.is_err() { + return -1; + } + + let mut buf = [0]; + + match unsafe { (*stream).read(&mut buf) } { + Ok(0) | Err(_) => EOF, + Ok(_) => c_int::from(buf[0]), + } +} + +/// See . +/// +/// Get a char from `stdin` without locking `stdin` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getchar_unlocked() -> c_int { + unsafe { getc_unlocked(&raw mut *stdin) } +} + +/// See . +/// +/// Marked obsolescent in issue 7. +/// `fgets` is recommended instead, which is what this implementation calls. +/// +/// Get a string from `stdin` +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { + unsafe { fgets(s, c_int::MAX, &raw mut *stdin) } +} + +/// See . +/// +/// Was marked legacy and removed in issue 6. +/// +/// Get an integer from `stream` +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int { + let mut ret: c_int = 0; + if unsafe { + fread( + ptr::from_mut(&mut ret).cast::(), + mem::size_of_val(&ret), + 1, + stream, + ) + } > 0 + { + ret + } else { + -1 + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pclose(stream: *mut FILE) -> c_int { + // TODO: rusty error handling? + let pid = { + let mut stream = unsafe { (*stream).lock() }; + + if let Some(pid) = stream.pid.take() { + pid + } else { + ERRNO.set(errno::ECHILD); + return -1; + } + }; + + unsafe { fclose(stream) }; + + let mut wstatus = 0; + if Sys::waitpid(pid, Some(Out::from_mut(&mut wstatus)), 0).or_minus_one_errno() == -1 { + return -1; + } + + wstatus +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn perror(s: *const c_char) { + let err = ERRNO.get(); + let err_str = if err >= 0 && err < STR_ERROR.len() as c_int { + STR_ERROR[err as usize] + } else { + "Unknown error" + }; + let mut w = platform::FileWriter::new(2); + + // The prefix, `s`, is optional (empty or NULL) according to the spec + match unsafe { CStr::from_nullable_ptr(s) } + .and_then(|s_cstr| str::from_utf8(s_cstr.to_bytes()).ok()) + { + Some(s_str) if !s_str.is_empty() => w + .write_fmt(format_args!("{}: {}\n", s_str, err_str)) + .unwrap(), + _ => w.write_fmt(format_args!("{}\n", err_str)).unwrap(), + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE { + //TODO: share code with system + + let mode = unsafe { CStr::from_ptr(mode) }; + + let mut cloexec = false; + let mut write_opt = None; + for b in mode.to_bytes().iter() { + match b { + b'e' => cloexec = true, + b'r' if write_opt.is_none() => write_opt = Some(false), + b'w' if write_opt.is_none() => write_opt = Some(true), + _ => { + ERRNO.set(errno::EINVAL); + return ptr::null_mut(); + } + } + } + + let write = match write_opt { + Some(some) => some, + None => { + ERRNO.set(errno::EINVAL); + return ptr::null_mut(); + } + }; + + let mut pipes = [-1, -1]; + if unsafe { unistd::pipe(pipes.as_mut_ptr()) } != 0 { + return ptr::null_mut(); + } + + let child_pid = unsafe { unistd::fork() }; + if child_pid == 0 { + let command_nonnull = if command.is_null() { + c"exit 0".as_ptr() + } else { + command.cast::() + }; + + let shell = c"/bin/sh".as_ptr(); + + let args = [c"sh".as_ptr(), c"-c".as_ptr(), command_nonnull, ptr::null()]; + + // Setup up stdin or stdout + //TODO: dup errors are ignored, should they be? + { + if write { + match unistd::dup2(pipes[0], 0) { + 0 => {} + e => unsafe { stdlib::exit(127) }, + } + } else { + match unistd::dup2(pipes[1], 1) { + 1 => {} + e => unsafe { stdlib::exit(127) }, + } + } + + unistd::close(pipes[0]); + unistd::close(pipes[1]); + } + + unsafe { unistd::execv(shell.cast::(), args.as_ptr().cast::<*mut c_char>()) }; + + unsafe { stdlib::exit(127) }; + + unreachable!(); + } else if child_pid > 0 { + let (fd, fd_mode): (_, CStr) = if write { + unistd::close(pipes[0]); + (pipes[1], if cloexec { c"we".into() } else { c"w".into() }) + } else { + unistd::close(pipes[1]); + (pipes[0], if cloexec { c"re".into() } else { c"r".into() }) + }; + + helpers::_fdopen(fd, fd_mode) + .map(|mut f| { + f.pid = Some(child_pid); + Box::into_raw(f) + }) + .or_errno_null_mut() + } else { + ptr::null_mut() + } +} + +/// See . +/// +/// Put a character `c` into `stream` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + unsafe { putc_unlocked(c, &raw mut *stream) } +} + +/// See . +/// +/// Put a character `c` into `stdout` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putchar(c: c_int) -> c_int { + unsafe { fputc(c, &raw mut *stdout) } +} + +/// See . +/// +/// Put a character `c` into `stream` without locking `stream` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { + if unsafe { (*stream).try_set_byte_orientation_unlocked() }.is_err() { + return -1; + } + + match unsafe { (*stream).write(&[c as u8]) } { + Ok(0) | Err(_) => EOF, + Ok(_) => c, + } +} + +/// See . +/// +/// Put a character `c` into `stdout` without locking `stdout` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int { + unsafe { putc_unlocked(c, stdout) } +} + +/// See . +/// +/// Put a string `s` into `stdout` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn puts(s: *const c_char) -> c_int { + let mut stream = unsafe { (&mut *stdout).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + let buf = unsafe { slice::from_raw_parts(s as *mut u8, strlen(s)) }; + + if stream.write_all(buf).is_err() { + return -1; + } + if stream.write(b"\n").is_err() { + return -1; + } + 0 +} + +/// See . +/// +/// Marked legacy in SUS Version 2. +/// +/// Put an integer `w` into `stream` +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int { + (unsafe { + fwrite( + ptr::from_ref::(&w).cast(), + mem::size_of_val(&w), + 1, + stream, + ) + }) as i32 + - 1 +} + +/// See . +/// +/// Delete file or directory `path` +#[unsafe(no_mangle)] +pub unsafe extern "C" fn remove(path: *const c_char) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::unlink(path) + .or_else(|_err| Sys::rmdir(path)) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rename(oldpath: *const c_char, newpath: *const c_char) -> c_int { + let oldpath = unsafe { CStr::from_ptr(oldpath) }; + let newpath = unsafe { CStr::from_ptr(newpath) }; + Sys::rename(oldpath, newpath) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn renameat( + old_dir: c_int, + old_path: *const c_char, + new_dir: c_int, + new_path: *const c_char, +) -> c_int { + let old_path = unsafe { CStr::from_ptr(old_path) }; + let new_path = unsafe { CStr::from_ptr(new_path) }; + Sys::renameat(old_dir, old_path, new_dir, new_path) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// Non-POSIX. Seems to be a GNU extension. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn renameat2( + old_dir: c_int, + old_path: *const c_char, + new_dir: c_int, + new_path: *const c_char, + flags: c_uint, +) -> c_int { + let old_path = unsafe { CStr::from_ptr(old_path) }; + let new_path = unsafe { CStr::from_ptr(new_path) }; + Sys::renameat2(old_dir, old_path, new_dir, new_path, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// Rewind `stream` back to the beginning of it +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rewind(stream: *mut FILE) { + unsafe { fseeko(stream, 0, SEEK_SET) }; +} + +/// See . +/// +/// Reset `stream` to use buffer `buf`. Buffer must be `BUFSIZ` in length +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) { + unsafe { + setvbuf( + stream, + buf, + if buf.is_null() { _IONBF } else { _IOFBF }, + BUFSIZ as usize, + ) + }; +} + +/// See . +/// +/// Non-POSIX. +/// +/// Set buffering of `stream` to line buffered +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setlinebuf(stream: *mut FILE) { + unsafe { setvbuf(stream, ptr::null_mut(), _IOLBF, 0) }; +} + +/// See . +/// +/// Reset `stream` to use buffer `buf` of size `size` +/// If this isn't the meaning of unsafe, idk what is +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setvbuf( + stream: *mut FILE, + buf: *mut c_char, + mode: c_int, + mut size: size_t, +) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + // Set a buffer of size `size` if no buffer is given + stream.read_buf = if buf.is_null() || size == 0 { + if size == 0 { + size = BUFSIZ as usize; + } + // TODO: Make it unbuffered if _IONBF + // if mode == _IONBF { + // } else { + Buffer::Owned(vec![0; size]) + // } + } else { + Buffer::Borrowed(unsafe { slice::from_raw_parts_mut(buf.cast::(), size) }) + }; + stream.flags |= F_SVB; + 0 +} + +/// See . +/// +/// Marked obsolescent in issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char { + unsafe fn is_appropriate(pos_dir: *const c_char) -> bool { + !pos_dir.is_null() && unsafe { unistd::access(pos_dir, unistd::W_OK) } == 0 + } + + // directory search order is env!(TMPDIR), dir, P_tmpdir, "/tmp" + let dirname = { + let tmpdir = unsafe { stdlib::getenv(c"TMPDIR".as_ptr().cast()) }; + [tmpdir, dir, P_tmpdir.as_ptr().cast()] + .iter() + .copied() + .skip_while(|&d| !unsafe { is_appropriate(d) }) + .next() + .unwrap_or(c"/tmp".as_ptr().cast()) + }; + let dirname_len = unsafe { string::strlen(dirname) }; + + let prefix_len = unsafe { string::strnlen_s(pfx, 5) }; + + // allocate enough for dirname "/" prefix "XXXXXX\0" + let mut out_buf = + unsafe { platform::alloc(dirname_len + 1 + prefix_len + L_tmpnam as usize + 1) } + .cast::(); + + if !out_buf.is_null() { + // copy the directory name and prefix into the allocated buffer + unsafe { out_buf.copy_from_nonoverlapping(dirname, dirname_len) }; + unsafe { *out_buf.add(dirname_len) = b'/' as _ }; + unsafe { + out_buf + .add(dirname_len + 1) + .copy_from_nonoverlapping(pfx, prefix_len) + }; + + // use the same mechanism as tmpnam to get the file name + if unsafe { + #[allow(deprecated)] + tmpnam_inner(out_buf, dirname_len + 1 + prefix_len) + } + .is_null() + { + // failed to find a valid file name, so we need to free the buffer + unsafe { platform::free(out_buf.cast()) }; + out_buf = ptr::null_mut(); + } + } + + out_buf +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tmpfile() -> *mut FILE { + let mut file_name = *b"/tmp/tmpfileXXXXXX\0"; + let file_name = file_name.as_mut_ptr().cast::(); + let fd = unsafe { stdlib::mkstemp(file_name) }; + + if fd < 0 { + return ptr::null_mut(); + } + + let fp = unsafe { fdopen(fd, c"w+".as_ptr()) }; + { + let file_name = unsafe { CStr::from_ptr(file_name) }; + if let Ok(()) = Sys::unlink(file_name) {}; // TODO handle error + } + + if fp.is_null() + && let Ok(()) = Sys::close(fd) + {}; // TODO handle error + + fp +} + +/// See . +/// +/// Marked obsolescent in issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { + let buf = if s.is_null() { + (&raw mut TMPNAM_BUF).cast() + } else { + s + }; + + unsafe { *buf = b'/' as _ }; + unsafe { + #[allow(deprecated)] + tmpnam_inner(buf, 1) + } +} + +#[deprecated] +unsafe extern "C" fn tmpnam_inner(buf: *mut c_char, offset: usize) -> *mut c_char { + const TEMPLATE: &[u8] = b"XXXXXX\0"; + + unsafe { + buf.add(offset) + .copy_from_nonoverlapping(TEMPLATE.as_ptr().cast(), TEMPLATE.len()) + }; + + let err = platform::ERRNO.get(); + unsafe { + #[allow(deprecated)] + stdlib::mktemp(buf) + }; + platform::ERRNO.set(err); + + if unsafe { *buf } == 0 { + ptr::null_mut() + } else { + buf + } +} + +/// See . +/// +/// Push character `c` back onto `stream` so it'll be read next +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + stream.unget.push(c as u8); + c +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { + let mut file = unsafe { (*file).lock() }; + if file.try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + unsafe { printf::printf(&mut *file, CStr::from_ptr(format), ap) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fprintf( + file: *mut FILE, + format: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { vfprintf(file, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vdprintf(fd: c_int, format: *const c_char, ap: va_list) -> c_int { + let mut f = File::new(fd); + + // We don't want to close the file on drop; we're merely + // borrowing the file descriptor here + f.reference = true; + + unsafe { printf::printf(f, CStr::from_ptr(format), ap) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn dprintf(fd: c_int, format: *const c_char, mut __valist: ...) -> c_int { + unsafe { vdprintf(fd, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int { + unsafe { vfprintf(&raw mut *stdout, format, ap) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn printf(format: *const c_char, mut __valist: ...) -> c_int { + unsafe { vfprintf(&raw mut *stdout, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vasprintf( + strp: *mut *mut c_char, + format: *const c_char, + ap: va_list, +) -> c_int { + let mut alloc_writer = CVec::new(); + let ret = unsafe { printf::printf(&mut alloc_writer, CStr::from_ptr(format), ap) }; + alloc_writer.push(0).unwrap(); + alloc_writer.shrink_to_fit().unwrap(); + unsafe { *strp = alloc_writer.leak().cast::() }; + ret +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asprintf( + strp: *mut *mut c_char, + format: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { vasprintf(strp, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vsnprintf( + s: *mut c_char, + n: size_t, + format: *const c_char, + ap: va_list, +) -> c_int { + unsafe { + printf::printf( + &mut platform::StringWriter(s.cast::(), n), + CStr::from_ptr(format), + ap, + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn snprintf( + s: *mut c_char, + n: size_t, + format: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { + printf::printf( + &mut platform::StringWriter(s.cast::(), n), + CStr::from_ptr(format), + __valist.as_va_list(), + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_list) -> c_int { + unsafe { + printf::printf( + &mut platform::UnsafeStringWriter(s.cast::()), + CStr::from_ptr(format), + ap, + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sprintf( + s: *mut c_char, + format: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { + printf::printf( + &mut platform::UnsafeStringWriter(s.cast::()), + CStr::from_ptr(format), + __valist.as_va_list(), + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vfscanf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { + let mut file = unsafe { (*file).lock() }; + if file.try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + let f: &mut FILE = &mut file; + let reader: Reader = f.into(); + unsafe { + let format = CStr::from_ptr(format); + scanf::scanf(reader, format.into(), ap) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fscanf( + file: *mut FILE, + format: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { vfscanf(file, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vscanf(format: *const c_char, ap: va_list) -> c_int { + unsafe { vfscanf(&raw mut *stdin, format, ap) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn scanf(format: *const c_char, mut __valist: ...) -> c_int { + unsafe { vfscanf(&raw mut *stdin, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vsscanf(s: *const c_char, format: *const c_char, ap: va_list) -> c_int { + unsafe { + let format = CStr::from_ptr(format); + let s = CStr::from_ptr(s); + scanf::scanf(s.into(), format.into(), ap) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sscanf( + s: *const c_char, + format: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { + let format = CStr::from_ptr(format); + let s = CStr::from_ptr(s); + scanf::scanf(s.into(), format.into(), __valist.as_va_list()) + } +} + +pub unsafe fn flush_io_streams() { + let flush = |stream: *mut FILE| { + let stream = unsafe { &mut *stream }; + let _ = stream.flush(); + }; + flush(unsafe { stdout }); + flush(unsafe { stderr }); +} diff --git a/src/header/stdio/open_memstream.rs b/src/header/stdio/open_memstream.rs new file mode 100644 index 0000000000..60ee508e7c --- /dev/null +++ b/src/header/stdio/open_memstream.rs @@ -0,0 +1,124 @@ +use alloc::{boxed::Box, vec, vec::Vec}; +use core::ptr; + +use super::{ + Buffer, FILE, + constants::{BUFSIZ, F_NORD}, +}; +use crate::{ + error::{Errno, ResultExtPtrMut}, + fs::File, + header::{ + errno::{EFAULT, ENOMEM}, + fcntl, pthread, stdlib, unistd, + }, + io::{self, BufWriter, Write}, + platform::{ + ERRNO, + types::{c_char, size_t}, + }, +}; + +struct MemstreamWriter { + bufp: *mut *mut c_char, + sizep: *mut size_t, + current: *mut c_char, + buffer: Vec, +} + +unsafe impl Send for MemstreamWriter {} + +impl MemstreamWriter { + fn new(bufp: *mut *mut c_char, sizep: *mut size_t) -> Self { + Self { + bufp, + sizep, + current: ptr::null_mut(), + buffer: Vec::new(), + } + } + + fn sync_output(&mut self) -> io::Result<()> { + let size = self.buffer.len(); + let alloc_size = size + .checked_add(1) + .ok_or_else(|| io::Error::from_raw_os_error(ENOMEM))?; + + let raw = if self.current.is_null() { + unsafe { stdlib::malloc(alloc_size) } + } else { + unsafe { stdlib::realloc(self.current.cast(), alloc_size) } + }; + if raw.is_null() { + return Err(io::Error::from_raw_os_error(ENOMEM)); + } + + let raw = raw.cast::(); + if size != 0 { + unsafe { ptr::copy_nonoverlapping(self.buffer.as_ptr(), raw.cast::(), size) }; + } + unsafe { + *raw.add(size) = 0; + *self.bufp = raw; + *self.sizep = size; + } + self.current = raw; + Ok(()) + } +} + +impl Write for MemstreamWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buffer + .try_reserve(buf.len()) + .map_err(|_| io::Error::from_raw_os_error(ENOMEM))?; + self.buffer.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + self.sync_output() + } +} + +fn create_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> Result, Errno> { + if bufp.is_null() || sizep.is_null() { + return Err(Errno(EFAULT)); + } + + unsafe { + *bufp = ptr::null_mut(); + *sizep = 0; + } + + let mut fds = [0; 2]; + if unsafe { unistd::pipe2(fds.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 { + return Err(Errno(ERRNO.get())); + } + let _ = unistd::close(fds[0]); + + let file = File::new(fds[1]); + let writer = Box::new(BufWriter::new(MemstreamWriter::new(bufp, sizep))); + let mutex_attr = pthread::RlctMutexAttr { + ty: pthread::PTHREAD_MUTEX_RECURSIVE, + ..Default::default() + }; + + Ok(Box::new(FILE { + lock: pthread::RlctMutex::new(&mutex_attr).unwrap(), + file, + flags: F_NORD, + read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]), + read_pos: 0, + read_size: 0, + unget: Vec::new(), + writer, + pid: None, + orientation: 0, + })) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> *mut FILE { + create_memstream(bufp, sizep).or_errno_null_mut() +} diff --git a/src/header/stdio/printf.rs b/src/header/stdio/printf.rs new file mode 100644 index 0000000000..2f63b420ed --- /dev/null +++ b/src/header/stdio/printf.rs @@ -0,0 +1,1447 @@ +// TODO: reuse more code with the wide printf impl +use crate::{ + c_str::{self, CStr, NulStr}, + io::{self, Write}, +}; +use alloc::{ + collections::BTreeMap, + string::{String, ToString}, + vec::Vec, +}; +use core::{cmp, ffi::VaList, fmt, num::FpCategory, ops::Range, slice}; + +use crate::{ + header::errno::{self, EILSEQ}, + platform::{ + self, + types::{ + c_char, c_double, c_int, c_long, c_longdouble, c_longlong, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, c_void, intmax_t, ptrdiff_t, size_t, ssize_t, + uintmax_t, wchar_t, wint_t, + }, + }, +}; + +#[allow(unused_doc_comments)] +/// cbindgen:ignore +unsafe extern "C" { + pub unsafe fn relibc_ldtod(x: *const c_longdouble) -> c_double; + pub unsafe fn relibc_dtold(x: c_double, out: *mut c_longdouble); +} + +// ____ _ _ _ _ +// | __ ) ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _ +// | _ \ / _ \| | |/ _ \ '__| '_ \| |/ _` | __/ _ (_) +// | |_) | (_) | | | __/ | | |_) | | (_| | || __/_ +// |____/ \___/|_|_|\___|_| | .__/|_|\__,_|\__\___(_) +// |_| + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(crate) enum IntKind { + Byte, + Short, + Int, + Long, + LongLong, + IntMax, + PtrDiff, + Size, +} +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(crate) enum FmtKind { + Percent, + + Signed, + Unsigned, + + Scientific, + Decimal, + AnyNotation, + + String, + Char, + Pointer, + GetWritten, +} +#[derive(Clone, Copy, Debug)] +pub(crate) enum Number { + Static(usize), + Index(usize), + Next, +} +impl Number { + pub(crate) unsafe fn resolve(self, varargs: &mut VaListCache, ap: &mut VaList) -> usize { + let arg = match self { + Number::Static(num) => return num, + Number::Index(i) => unsafe { varargs.get(i - 1, ap, None) }, + Number::Next => { + let i = varargs.i; + varargs.i += 1; + unsafe { varargs.get(i, ap, None) } + } + }; + match arg { + VaArg::c_char(i) => i as usize, + VaArg::c_double(i) => i as usize, + #[cfg(target_pointer_width = "32")] + VaArg::c_longdouble(_) => 0 as usize, + #[cfg(target_pointer_width = "64")] + VaArg::c_longdouble(i) => i as usize, + VaArg::c_int(i) => i as usize, + VaArg::c_long(i) => i as usize, + VaArg::c_longlong(i) => i as usize, + VaArg::c_short(i) => i as usize, + VaArg::intmax_t(i) => i as usize, + VaArg::pointer(i) => i as usize, + VaArg::ptrdiff_t(i) => i as usize, + VaArg::ssize_t(i) => i as usize, + VaArg::wint_t(i) => i as usize, + } + } +} +#[derive(Clone, Copy, Debug)] +pub(crate) enum VaArg { + c_char(c_char), + c_double(c_double), + c_longdouble(c_longdouble), + c_int(c_int), + c_long(c_long), + c_longlong(c_longlong), + c_short(c_short), + intmax_t(intmax_t), + pointer(*const c_void), + ptrdiff_t(ptrdiff_t), + ssize_t(ssize_t), + wint_t(wint_t), +} +impl VaArg { + pub(crate) unsafe fn arg_from(fmtkind: FmtKind, intkind: IntKind, ap: &mut VaList) -> VaArg { + // Per the C standard using va_arg with a type with a size + // less than that of an int for integers and double for floats + // is invalid. As a result any arguments smaller than an int or + // double passed to a function will be promoted to the smallest + // possible size. The VaList::arg function will handle this + // automagically. + + match (fmtkind, intkind) { + (FmtKind::Percent, _) => panic!("Can't call arg_from on %"), + + (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => { + VaArg::wint_t(unsafe { ap.arg::() }) + } + + (FmtKind::Char, _) + | (FmtKind::Unsigned, IntKind::Byte) + | (FmtKind::Signed, IntKind::Byte) => { + // c_int is passed but truncated to c_char + VaArg::c_char(unsafe { ap.arg::() } as c_char) + } + (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => { + // c_int is passed but truncated to c_short + VaArg::c_short(unsafe { ap.arg::() } as c_short) + } + (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => { + VaArg::c_int(unsafe { ap.arg::() }) + } + (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => { + VaArg::c_long(unsafe { ap.arg::() }) + } + (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => { + VaArg::c_longlong(unsafe { ap.arg::() }) + } + (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => { + VaArg::intmax_t(unsafe { ap.arg::() }) + } + (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => { + VaArg::ptrdiff_t(unsafe { ap.arg::() }) + } + (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => { + VaArg::ssize_t(unsafe { ap.arg::() }) + } + + (FmtKind::AnyNotation, IntKind::LongLong) + | (FmtKind::Decimal, IntKind::LongLong) + | (FmtKind::Scientific, IntKind::LongLong) => { + VaArg::c_longdouble(unsafe { VaArg::extract_longdouble(ap) }) + } + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => { + VaArg::c_double(unsafe { ap.arg::() }) + } + + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => { + VaArg::pointer(unsafe { ap.arg::<*const c_void>() }) + } + } + } + #[cfg(target_arch = "x86")] + unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble { + todo_skip!(0, "long double in variadic printf is not supported"); + [0, 0, 0] + } + #[cfg(target_arch = "x86_64")] + unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble { + // https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf (long double) + + // exactly same as core::ffi::VaListImpl but all variables exposed + #[repr(C)] + struct VaListImpl { + gp_offset: i32, + fp_offset: i32, + overflow_arg_area: *mut u8, + reg_save_area: *mut u8, + } + + let ap_impl = unsafe { + // The double deconstruct is intended + let ptr_to_struct = *(ap as *mut core::ffi::VaList as *mut *mut VaListImpl); + &mut *ptr_to_struct + }; + + let ptr = ap_impl.overflow_arg_area as *const c_longdouble; + let val = unsafe { ptr.read() }; + + ap_impl.overflow_arg_area = unsafe { ap_impl.overflow_arg_area.add(16) }; + + val + } + #[cfg(target_arch = "aarch64")] + unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble { + // https://c9x.me/compile/bib/abi-arm64.pdf (quad precision) + + // exactly same as core::ffi::VaListImpl but all variables exposed + #[repr(C)] + struct VaListImpl { + stack: *mut u8, + gr_top: *mut u8, + vr_top: *mut u8, + gr_offs: i32, + vr_offs: i32, + } + + let ap_impl: &mut VaListImpl = unsafe { + // The double deconstruct is intended + let ptr_to_struct = *(ap as *mut core::ffi::VaList as *mut *mut VaListImpl); + &mut *ptr_to_struct + }; + + let ptr = unsafe { ap_impl.vr_top.offset(ap_impl.vr_offs as isize) as *const c_longdouble }; + + ap_impl.vr_offs += 16; + + unsafe { ptr.read() } + } + + #[cfg(target_arch = "riscv64")] + unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble { + todo_skip!(0, "long double in variadic printf is not supported"); + 0u128 + } + unsafe fn transmute(&self, fmtkind: FmtKind, intkind: IntKind) -> VaArg { + // At this point, there are conflicting printf arguments. An + // example of this is: + // ```c + // printf("%1$d %1$lf\n", 5, 0.1); + // ``` + // We handle it just like glibc: We read it from the VaList + // using the *last* argument type, but we transmute it when we + // try to access the other ones. + union Untyped { + c_char: c_char, + c_double: c_double, + c_longdouble: c_longdouble, + c_int: c_int, + c_long: c_long, + c_longlong: c_longlong, + c_short: c_short, + intmax_t: intmax_t, + pointer: *const c_void, + ptrdiff_t: ptrdiff_t, + ssize_t: ssize_t, + wint_t: wint_t, + } + let untyped = match *self { + VaArg::c_char(i) => Untyped { c_char: i }, + VaArg::c_double(i) => Untyped { c_double: i }, + VaArg::c_longdouble(i) => Untyped { c_longdouble: i }, + VaArg::c_int(i) => Untyped { c_int: i }, + VaArg::c_long(i) => Untyped { c_long: i }, + VaArg::c_longlong(i) => Untyped { c_longlong: i }, + VaArg::c_short(i) => Untyped { c_short: i }, + VaArg::intmax_t(i) => Untyped { intmax_t: i }, + VaArg::pointer(i) => Untyped { pointer: i }, + VaArg::ptrdiff_t(i) => Untyped { ptrdiff_t: i }, + VaArg::ssize_t(i) => Untyped { ssize_t: i }, + VaArg::wint_t(i) => Untyped { wint_t: i }, + }; + match (fmtkind, intkind) { + (FmtKind::Percent, _) => panic!("Can't call transmute on %"), + + (FmtKind::Char, IntKind::Long) | (FmtKind::Char, IntKind::LongLong) => { + VaArg::wint_t(unsafe { untyped.wint_t }) + } + + (FmtKind::Char, _) + | (FmtKind::Unsigned, IntKind::Byte) + | (FmtKind::Signed, IntKind::Byte) => VaArg::c_char(unsafe { untyped.c_char }), + (FmtKind::Unsigned, IntKind::Short) | (FmtKind::Signed, IntKind::Short) => { + VaArg::c_short(unsafe { untyped.c_short }) + } + (FmtKind::Unsigned, IntKind::Int) | (FmtKind::Signed, IntKind::Int) => { + VaArg::c_int(unsafe { untyped.c_int }) + } + (FmtKind::Unsigned, IntKind::Long) | (FmtKind::Signed, IntKind::Long) => { + VaArg::c_long(unsafe { untyped.c_long }) + } + (FmtKind::Unsigned, IntKind::LongLong) | (FmtKind::Signed, IntKind::LongLong) => { + VaArg::c_longlong(unsafe { untyped.c_longlong }) + } + (FmtKind::Unsigned, IntKind::IntMax) | (FmtKind::Signed, IntKind::IntMax) => { + VaArg::intmax_t(unsafe { untyped.intmax_t }) + } + (FmtKind::Unsigned, IntKind::PtrDiff) | (FmtKind::Signed, IntKind::PtrDiff) => { + VaArg::ptrdiff_t(unsafe { untyped.ptrdiff_t }) + } + (FmtKind::Unsigned, IntKind::Size) | (FmtKind::Signed, IntKind::Size) => { + VaArg::ssize_t(unsafe { untyped.ssize_t }) + } + + (FmtKind::AnyNotation, IntKind::LongLong) + | (FmtKind::Decimal, IntKind::LongLong) + | (FmtKind::Scientific, IntKind::LongLong) => { + VaArg::c_longdouble(unsafe { untyped.c_longdouble }) + } + (FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => { + VaArg::c_double(unsafe { untyped.c_double }) + } + + (FmtKind::GetWritten, _) | (FmtKind::Pointer, _) | (FmtKind::String, _) => { + VaArg::pointer(unsafe { untyped.pointer }) + } + } + } +} +#[derive(Default)] +pub(crate) struct VaListCache { + pub(crate) args: Vec, + pub(crate) i: usize, +} +impl VaListCache { + pub(crate) unsafe fn get( + &mut self, + i: usize, + ap: &mut VaList, + default: Option<(FmtKind, IntKind)>, + ) -> VaArg { + if let Some(&arg) = self.args.get(i) { + // This value is already cached + let mut arg = arg; + if let Some((fmtkind, intkind)) = default { + // ...but as a different type + arg = unsafe { arg.transmute(fmtkind, intkind) }; + } + return arg; + } + + // Get all values before this value + while self.args.len() < i { + // We can't POSSIBLY know the type if we reach this + // point. Reaching here means there are unused gaps in the + // arguments. Ultimately we'll have to settle down with + // defaulting to c_int. + self.args.push(VaArg::c_int(unsafe { ap.arg::() })) + } + + // Add the value to the cache + self.args.push(match default { + Some((fmtkind, intkind)) => unsafe { VaArg::arg_from(fmtkind, intkind, ap) }, + None => VaArg::c_int(unsafe { ap.arg::() }), + }); + + // Return the value + self.args[i] + } +} + +// ___ _ _ _ _ +// |_ _|_ __ ___ _ __ | | ___ _ __ ___ ___ _ __ | |_ __ _| |_(_) ___ _ __ _ +// | || '_ ` _ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \(_) +// | || | | | | | |_) | | __/ | | | | | __/ | | | || (_| | |_| | (_) | | | |_ +// |___|_| |_| |_| .__/|_|\___|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_(_) +// |_| + +enum FmtCase { + Lower, + Upper, +} + +// The spelled-out "infinity"/"INFINITY" is also permitted by the standard +static INF_STR_LOWER: &str = "inf"; +static INF_STR_UPPER: &str = "INF"; + +static NAN_STR_LOWER: &str = "nan"; +static NAN_STR_UPPER: &str = "NAN"; + +fn pop_int_raw(format: &mut NulStr) -> Option { + let mut int = None; + while let Some((digit, rest)) = format + .split_first_char() + .and_then(|(d, r)| Some((d.to_digit(10)?, r))) + { + *format = rest; + if int.is_none() { + int = Some(0); + } + *int.as_mut().unwrap() *= 10; + *int.as_mut().unwrap() += digit as usize; + } + int +} +fn pop_index(format: &mut NulStr) -> Option { + // Peek ahead for a positional argument: + let mut format2 = *format; + if let Some(i) = pop_int_raw(&mut format2) + && let Some(('$', format2)) = format2.split_first_char() + { + *format = format2; + return Some(i); + } + None +} +fn pop_int(format: &mut NulStr) -> Option { + if let Some(('*', rest)) = format.split_first_char() { + *format = rest; + Some(pop_index(format).map(Number::Index).unwrap_or(Number::Next)) + } else { + pop_int_raw(format).map(Number::Static) + } +} + +fn fmt_int(fmt: char, i: I) -> String +where + I: fmt::Display + fmt::Octal + fmt::LowerHex + fmt::UpperHex + fmt::Binary, +{ + match fmt { + 'o' => format!("{:o}", i), + 'u' => i.to_string(), + 'x' => format!("{:x}", i), + 'X' => format!("{:X}", i), + 'b' | 'B' if T::IS_THIN_NOT_WIDE => format!("{:b}", i), + _ => panic!("fmt_int should never be called with the fmt {:?}", fmt,), + } +} + +fn pad( + w: &mut W, + current_side: bool, + pad_char: u8, + range: Range, +) -> io::Result<()> { + if current_side { + for _ in range { + w.write_all(&[pad_char])?; + } + } + Ok(()) +} + +fn float_string(float: c_double, precision: usize, trim: bool, alternate: bool) -> String { + // The Rust format! macro doesn't keep the dot on precision = 0 and alternate = true, + // so we have to perform a fix-up + // + // POSIX.1-2024 says "... if the precision is zero and no '#' flag is present, + // no radix character shall appear." + // + // This case is covered here. + let mut string = format!("{:.p$}", float, p = precision); + // + // Additionally, it says "For a, A, e, E, f, F, g, and G conversion specifiers, + // the result shall always contain a radix character, even if no digits follow + // the radix character." + // + if alternate && precision == 0 { + string.push('.'); + } else if trim && string.contains('.') { + let truncate = { + let slice = string.trim_end_matches('0'); + let mut truncate = slice.len(); + if slice.ends_with('.') { + truncate -= 1; + } + truncate + }; + string.truncate(truncate); + } + string +} + +fn float_exp(mut float: c_double) -> (c_double, isize) { + let mut exp: isize = 0; + while float.abs() >= 10.0 { + float /= 10.0; + exp += 1; + } + while f64::EPSILON < float.abs() && float.abs() < 1.0 { + float *= 10.0; + exp -= 1; + } + (float, exp) +} + +fn fmt_float_exp( + w: &mut W, + exp_fmt: char, + trim: bool, + alternate: bool, + precision: usize, + float: c_double, + exp: isize, + left: bool, + pad_space: usize, + pad_zero: usize, +) -> io::Result<()> { + let mut exp2 = exp; + let mut exp_len = 1; + while exp2 >= 10 { + exp2 /= 10; + exp_len += 1; + } + + let string = float_string(float, precision, trim, alternate); + let len = string.len() + 2 + 2.max(exp_len); + + pad(w, !left, b' ', len..pad_space)?; + let bytes = if string.starts_with('-') { + w.write_all(b"-")?; + &string.as_bytes()[1..] + } else { + string.as_bytes() + }; + pad(w, !left, b'0', len..pad_zero)?; + w.write_all(bytes)?; + write!(w, "{}{:+03}", exp_fmt, exp)?; + pad(w, left, b' ', len..pad_space)?; + + Ok(()) +} + +fn fmt_float_normal( + w: &mut W, + trim: bool, + alternate: bool, + precision: usize, + float: c_double, + left: bool, + pad_space: usize, + pad_zero: usize, +) -> io::Result { + let string = float_string(float, precision, trim, alternate); + + pad(w, !left, b' ', string.len()..pad_space)?; + let bytes = if string.starts_with('-') { + w.write_all(b"-")?; + &string.as_bytes()[1..] + } else { + string.as_bytes() + }; + pad(w, true, b'0', string.len()..pad_zero)?; + w.write_all(bytes)?; + pad(w, left, b' ', string.len()..pad_space)?; + + Ok(string.len()) +} + +/// Write ±infinity or ±NaN representation for any floating-point style +fn fmt_float_nonfinite( + w: &mut W, + float: c_double, + case: FmtCase, + left: bool, + pad_space: usize, + pad_zero: usize, +) -> io::Result<()> { + let string = match float.classify() { + FpCategory::Infinite => match case { + FmtCase::Lower => INF_STR_LOWER, + FmtCase::Upper => INF_STR_UPPER, + }, + FpCategory::Nan => match case { + FmtCase::Lower => NAN_STR_LOWER, + FmtCase::Upper => NAN_STR_UPPER, + }, + _ => { + // This function should only be called with infinite or NaN value. + panic!("fmt_float_nonfinite called with finite float") + } + }; + + // Infinity is always padded with spaces, rather than zeroes + pad(w, !left, b' ', string.len()..pad_space + pad_zero)?; + if float.is_sign_negative() { + w.write_all(b"-")?; + } + w.write_all(string.as_bytes())?; + pad(w, left, b' ', string.len()..pad_space + pad_zero)?; + + Ok(()) +} + +#[derive(Clone, Copy)] +pub(crate) struct PrintfIter<'a, T: c_str::Kind> { + pub(crate) format: NulStr<'a, T>, +} +#[derive(Clone, Copy, Debug)] +pub(crate) struct PrintfArg { + pub(crate) index: Option, + pub(crate) alternate: bool, + pub(crate) zero: bool, + pub(crate) left: bool, + pub(crate) sign_reserve: bool, + pub(crate) sign_always: bool, + pub(crate) min_width: Number, + pub(crate) precision: Option, + pub(crate) intkind: IntKind, + pub(crate) fmt: char, + pub(crate) fmtkind: FmtKind, +} +#[derive(Debug)] +pub(crate) enum PrintfFmt<'a, U> { + Plain(&'a [U]), + Arg(PrintfArg), +} +impl<'a, T: c_str::Kind> Iterator for PrintfIter<'a, T> { + type Item = Result, ()>; + + fn next(&mut self) -> Option { + // Send PrintfFmt::Plain until the next % + let first_percent = match self.format.find_get_subslice_or_all(b'%') { + Err(([], _)) => return None, + Ok((chunk @ [_, ..], rest)) | Err((chunk @ [_, ..], rest)) => { + self.format = rest; + return Some(Ok(PrintfFmt::Plain(chunk))); + } + Ok(([], rest)) => rest, + }; + + // at this point the next char must be % + self.format = first_percent.split_first().expect("must be %").1; + + let mut peekahead = self.format; + let index = pop_index(&mut peekahead).inspect(|i| { + self.format = peekahead; + }); + + // Flags: + let mut alternate = false; + let mut zero = false; + let mut left = false; + let mut sign_reserve = false; + let mut sign_always = false; + + while let Some((c, rest)) = self.format.split_first_char() { + match c { + '#' => alternate = true, + '0' => zero = true, + '-' => left = true, + ' ' => sign_reserve = true, + '+' => sign_always = true, + _ => break, + } + self.format = rest; + } + + // Width and precision: + let min_width = pop_int(&mut self.format).unwrap_or(Number::Static(0)); + let precision = if let Some(('.', rest)) = self.format.split_first_char() { + self.format = rest; + match pop_int(&mut self.format) { + int @ Some(_) => int, + None => return Some(Err(())), + } + } else { + None + }; + + // Integer size: + let mut intkind = IntKind::Int; + while let Some((byte, rest)) = self.format.split_first_char() { + intkind = match byte { + 'h' => { + if intkind == IntKind::Short || intkind == IntKind::Byte { + IntKind::Byte + } else { + IntKind::Short + } + } + 'j' => IntKind::IntMax, + 'l' => { + if intkind == IntKind::Long || intkind == IntKind::LongLong { + IntKind::LongLong + } else { + IntKind::Long + } + } + 'q' | 'L' => IntKind::LongLong, + 't' => IntKind::PtrDiff, + 'z' => IntKind::Size, + _ => break, + }; + + self.format = rest; + } + let Some((fmt, rest)) = self.format.split_first_char() else { + return Some(Err(())); + }; + self.format = rest; + let fmtkind = match fmt { + '%' => FmtKind::Percent, + 'd' | 'i' => FmtKind::Signed, + 'o' | 'u' | 'x' | 'X' => FmtKind::Unsigned, + 'b' | 'B' if T::IS_THIN_NOT_WIDE => FmtKind::Unsigned, + 'e' | 'E' => FmtKind::Scientific, + 'f' | 'F' | 'L' => FmtKind::Decimal, + 'g' | 'G' => FmtKind::AnyNotation, + 's' => FmtKind::String, + 'c' => FmtKind::Char, + 'p' => FmtKind::Pointer, + 'n' => FmtKind::GetWritten, + 'm' if T::IS_THIN_NOT_WIDE => { + // %m is technically for syslog only, but musl and glibc implement it for + // printf because it is difficult and error prone to implement a format + // specifier for just *one* function. + return Some(Ok(PrintfFmt::Plain( + T::chars_from_bytes( + errno::STR_ERROR + .get(platform::ERRNO.get() as usize) + .map(|e| e.as_bytes()) + .unwrap_or(b"unknown error"), + ) + .expect("string must be thin"), + ))); + } + _ => return Some(Err(())), + }; + // "For b, B, d, i, o, u, x, and X conversions, + // if a precision is specified, the 0 flag is ignored." + match fmt { + 'b' | 'B' | 'd' | 'i' | 'o' | 'u' | 'x' | 'X' => { + if precision.is_some() { + zero = false; + } + } + _ => (), + } + + Some(Ok(PrintfFmt::Arg(PrintfArg { + index, + alternate, + zero, + left, + sign_reserve, + sign_always, + min_width, + precision, + intkind, + fmt, + fmtkind, + }))) + } +} + +pub(crate) unsafe fn inner_printf( + w: impl Write, + format: NulStr, + mut ap: VaList, +) -> io::Result { + let w = &mut platform::CountingWriter::new(w); + + let iterator = PrintfIter { format }; + + // Pre-fetch vararg types + let mut varargs = VaListCache::default(); + let mut positional = BTreeMap::new(); + // ^ NOTE: This depends on the sorted order, do not change to HashMap or whatever + + for section in iterator { + let arg = match section { + Ok(PrintfFmt::Plain(text)) => continue, + Ok(PrintfFmt::Arg(arg)) => arg, + Err(()) => return Ok(-1), + }; + if arg.fmtkind == FmtKind::Percent { + continue; + } + for num in &[arg.min_width, arg.precision.unwrap_or(Number::Static(0))] { + match num { + Number::Next => varargs + .args + .push(VaArg::c_int(unsafe { ap.arg::() })), + Number::Index(i) => { + positional.insert(i - 1, (FmtKind::Signed, IntKind::Int)); + } + Number::Static(_) => (), + } + } + match arg.index { + Some(i) => { + positional.insert(i - 1, (arg.fmtkind, arg.intkind)); + } + None => varargs + .args + .push(unsafe { VaArg::arg_from(arg.fmtkind, arg.intkind, &mut ap) }), + } + } + + // Make sure, in order, the positional arguments exist with the specified type + for (i, arg) in positional { + unsafe { varargs.get(i, &mut ap, Some(arg)) }; + } + + // Main loop + for section in iterator { + let arg = match section { + Ok(PrintfFmt::Plain(text)) => { + if T::IS_THIN_NOT_WIDE { + let bytes = T::chars_to_bytes(text).expect("is thin"); + w.write_all(bytes)?; + } else { + // TODO: wcsrtombs wrapper + for c in text.iter().filter_map(|u| char::from_u32((*u).into())) { + if let Ok(()) = write!(w, "{}", c) {}; // TODO handle error + } + } + continue; + } + Ok(PrintfFmt::Arg(arg)) => arg, + Err(()) => return Ok(-1), + }; + let alternate = arg.alternate; + let zero = arg.zero; + let mut left = arg.left; + let sign_reserve = arg.sign_reserve; + let sign_always = arg.sign_always; + let min_width = unsafe { arg.min_width.resolve(&mut varargs, &mut ap) }; + let precision = arg + .precision + .map(|n| unsafe { n.resolve(&mut varargs, &mut ap) }) + .filter(|&n| (n as c_int) >= 0); + let pad_zero = if zero { min_width } else { 0 }; + let signed_space = match pad_zero { + 0 => min_width as isize, + _ => 0, + }; + let pad_space = if signed_space < 0 { + left = true; + -signed_space as usize + } else { + signed_space as usize + }; + let intkind = arg.intkind; + let fmt = arg.fmt; + let fmtkind = arg.fmtkind; + let fmtcase = match fmt { + 'b' if T::IS_THIN_NOT_WIDE => Some(FmtCase::Lower), + 'B' if T::IS_THIN_NOT_WIDE => Some(FmtCase::Upper), + 'x' | 'f' | 'e' | 'g' => Some(FmtCase::Lower), + 'X' | 'F' | 'E' | 'G' => Some(FmtCase::Upper), + _ => None, + }; + + let index = arg.index.map(|i| i - 1).unwrap_or_else(|| { + if fmtkind == FmtKind::Percent { + 0 + } else { + let i = varargs.i; + varargs.i += 1; + i + } + }); + + match fmtkind { + FmtKind::Percent => w.write_all(b"%")?, + FmtKind::Signed => { + let string = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::c_char(i) => i.to_string(), + VaArg::c_double(i) => panic!("this should not be possible"), + VaArg::c_longdouble(i) => panic!("this should not be possible"), + VaArg::c_int(i) => i.to_string(), + VaArg::c_long(i) => i.to_string(), + VaArg::c_longlong(i) => i.to_string(), + VaArg::c_short(i) => i.to_string(), + VaArg::intmax_t(i) => i.to_string(), + VaArg::pointer(i) => (i as usize).to_string(), + VaArg::ptrdiff_t(i) => i.to_string(), + VaArg::ssize_t(i) => i.to_string(), + VaArg::wint_t(_) => unreachable!("this should not be possible"), + }; + let positive = !string.starts_with('-'); + let zero = precision == Some(0) && string == "0"; + + let mut len = string.len(); + let mut final_len = string.len().max(precision.unwrap_or(0)); + if positive && (sign_reserve || sign_always) { + final_len += 1; + } + if zero { + len = 0; + final_len = 0; + } + + pad(w, !left, b' ', final_len..pad_space)?; + + let bytes = if positive { + if sign_reserve { + w.write_all(b" ")?; + } else if sign_always { + w.write_all(b"+")?; + } + string.as_bytes() + } else { + w.write_all(b"-")?; + &string.as_bytes()[1..] + }; + pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; + + if !zero { + w.write_all(bytes)?; + } + + pad(w, left, b' ', final_len..pad_space)?; + } + FmtKind::Unsigned => { + let string = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::c_char(i) => fmt_int::<_, T>(fmt, i as c_uchar), + VaArg::c_double(i) => panic!("this should not be possible"), + VaArg::c_longdouble(i) => panic!("this should not be possible"), + VaArg::c_int(i) => fmt_int::<_, T>(fmt, i as c_uint), + VaArg::c_long(i) => fmt_int::<_, T>(fmt, i as c_ulong), + VaArg::c_longlong(i) => fmt_int::<_, T>(fmt, i as c_ulonglong), + VaArg::c_short(i) => fmt_int::<_, T>(fmt, i as c_ushort), + VaArg::intmax_t(i) => fmt_int::<_, T>(fmt, i as uintmax_t), + VaArg::pointer(i) => fmt_int::<_, T>(fmt, i as usize), + VaArg::ptrdiff_t(i) => fmt_int::<_, T>(fmt, i as size_t), + VaArg::ssize_t(i) => fmt_int::<_, T>(fmt, i as size_t), + VaArg::wint_t(_) => unreachable!("this should not be possible"), + }; + let zero = precision == Some(0) && string == "0"; + + // If this int is padded out to be larger than it is, don't + // add an extra zero if octal. + let no_precision = precision.map(|pad| pad < string.len()).unwrap_or(true); + + let len; + let final_len = if zero { + len = 0; + 0 + } else { + len = string.len(); + len.max(precision.unwrap_or(0)) + + if alternate && string != "0" { + match fmt { + 'o' if no_precision => 1, + 'x' | 'X' => 2, + 'b' | 'B' if T::IS_THIN_NOT_WIDE => 2, + _ => 0, + } + } else { + 0 + } + }; + + pad(w, !left, b' ', final_len..pad_space)?; + + if alternate && string != "0" { + match fmt { + 'o' if no_precision => w.write_all(b"0")?, + 'x' => w.write_all(b"0x")?, + 'X' => w.write_all(b"0X")?, + 'b' if T::IS_THIN_NOT_WIDE => w.write_all(b"0b")?, + 'B' if T::IS_THIN_NOT_WIDE => w.write_all(b"0B")?, + _ => (), + } + } + pad(w, true, b'0', len..precision.unwrap_or(pad_zero))?; + + if !zero { + w.write_all(string.as_bytes())?; + } + + pad(w, left, b' ', final_len..pad_space)?; + } + FmtKind::Scientific => { + let float = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::c_double(i) => i, + VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&raw const i) }, + _ => panic!("this should not be possible"), + }; + if float.is_finite() { + let (float, exp) = float_exp(float); + let precision = precision.unwrap_or(6); + + fmt_float_exp( + w, fmt, false, alternate, precision, float, exp, left, pad_space, pad_zero, + )?; + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?; + } + } + FmtKind::Decimal => { + let float = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::c_double(i) => i, + VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&raw const i) }, + _ => panic!("this should not be possible"), + }; + if float.is_finite() { + let precision = precision.unwrap_or(6); + + fmt_float_normal( + w, false, alternate, precision, float, left, pad_space, pad_zero, + )?; + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?; + } + } + FmtKind::AnyNotation => { + let float = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::c_double(i) => i, + VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&raw const i) }, + _ => panic!("this should not be possible"), + }; + if float.is_finite() { + let (log, exp) = float_exp(float); + // TODO: .is_uppercase()? + let exp_fmt = if fmt as u32 & 32 == 32 { 'e' } else { 'E' }; + let precision = precision.unwrap_or(6); + let use_exp_format = exp < -4 || exp >= precision as isize; + + if use_exp_format { + // Length of integral part will always be 1 here, + // because that's how x/floor(log10(x)) works + let precision = precision.saturating_sub(1); + fmt_float_exp( + w, exp_fmt, !alternate, alternate, precision, log, exp, left, + pad_space, pad_zero, + )?; + } else { + // Length of integral part will be the exponent of + // the unused logarithm, unless the exponent is + // negative which in case the integral part must + // of course be 0, 1 in length + let len = 1 + cmp::max(0, exp) as usize; + let precision = precision.saturating_sub(len); + fmt_float_normal( + w, !alternate, alternate, precision, float, left, pad_space, pad_zero, + )?; + } + } else { + fmt_float_nonfinite(w, float, fmtcase.unwrap(), left, pad_space, pad_zero)?; + } + } + FmtKind::String => { + let ptr = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible"), + } + .cast::(); + + if ptr.is_null() { + w.write_all(b"(null)")?; + } else { + let max = precision.unwrap_or(usize::MAX); + + if intkind == IntKind::Long || intkind == IntKind::LongLong { + // Handle wchar_t + let mut ptr = ptr.cast::(); + let mut string = String::new(); + + while unsafe { *ptr } != 0 { + let c = match char::from_u32(unsafe { *ptr } as _) { + Some(c) => c, + None => { + platform::ERRNO.set(EILSEQ); + return Err(io::last_os_error()); + } + }; + if string.len() + c.len_utf8() >= max { + break; + } + string.push(c); + ptr = unsafe { ptr.add(1) }; + } + + pad(w, !left, b' ', string.len()..pad_space)?; + w.write_all(string.as_bytes())?; + pad(w, left, b' ', string.len()..pad_space)?; + } else { + let mut len = 0; + while unsafe { *ptr.add(len) } != 0 && len < max { + len += 1; + } + + pad(w, !left, b' ', len..pad_space)?; + w.write_all(unsafe { slice::from_raw_parts(ptr.cast::(), len) })?; + pad(w, left, b' ', len..pad_space)?; + } + } + } + FmtKind::Char => { + match unsafe { varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) } { + VaArg::c_char(c) => { + pad(w, !left, b' ', 1..pad_space)?; + w.write_all(&[c as u8])?; + pad(w, left, b' ', 1..pad_space)?; + } + VaArg::wint_t(c) => { + let c = match char::from_u32(c as _) { + Some(c) => c, + None => { + platform::ERRNO.set(EILSEQ); + return Err(io::last_os_error()); + } + }; + let mut buf = [0; 4]; + + pad(w, !left, b' ', 1..pad_space)?; + w.write_all(c.encode_utf8(&mut buf).as_bytes())?; + pad(w, left, b' ', 1..pad_space)?; + } + _ => unreachable!("this should not be possible"), + } + } + FmtKind::Pointer => { + let ptr = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible"), + }; + + let mut len = 1; + if ptr.is_null() { + len = "(nil)".len(); + } else { + let mut ptr = ptr as usize; + while ptr >= 10 { + ptr /= 10; + len += 1; + } + } + + pad(w, !left, b' ', len..pad_space)?; + if ptr.is_null() { + write!(w, "(nil)")?; + } else { + write!(w, "0x{:x}", ptr as usize)?; + } + pad(w, left, b' ', len..pad_space)?; + } + FmtKind::GetWritten => { + let ptr = match unsafe { + varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind))) + } { + VaArg::pointer(p) => p, + _ => panic!("this should not be possible"), + }; + + match intkind { + IntKind::Byte => unsafe { *(ptr as *mut c_char) = w.written as c_char }, + IntKind::Short => unsafe { *(ptr as *mut c_short) = w.written as c_short }, + IntKind::Int => unsafe { *(ptr as *mut c_int) = w.written as c_int }, + IntKind::Long => unsafe { *(ptr as *mut c_long) = w.written as c_long }, + IntKind::LongLong => unsafe { + *(ptr as *mut c_longlong) = w.written as c_longlong + }, + IntKind::IntMax => unsafe { *(ptr as *mut intmax_t) = w.written as intmax_t }, + IntKind::PtrDiff => unsafe { + *(ptr as *mut ptrdiff_t) = w.written as ptrdiff_t + }, + IntKind::Size => unsafe { *(ptr as *mut size_t) = w.written as size_t }, + } + } + } + } + Ok(w.written as c_int) +} + +/// Implementation of `printf` formatting function, generic over a `writer` +/// +/// This implementation in currently compliant over C17 specification (lacking a few one from C23) +/// and contains extensions as well. +/// +/// # The Format Specification +/// ```text +/// %[conversion-flags][field-width][precision][length-modifier] +/// ``` +/// +///
+/// ※ : This symbol means it is not implemented yet, but it is defined in the C standard +///
+/// +/// ## Conversion Flags +/// Conversion flags are flags that modify the behavior of the [conversion +/// format]. Each one can happen only once per format specifier. They are: +/// +/// - `-`: The result of the conversion is left-justified within the field (by default it is +/// right-justified). +/// - `+`: The sign of signed conversions is always prepended to the result of the conversion (by +/// default the result is preceded by minus **only** when it is negative). +/// - ` `(space): If the result of a signed conversion does not start with a sign character, or is +/// empty, space is prepended to the result. +/// - It is ignored if `+` flag is present. +/// - `#`: Alternative form of the conversion is performed. See the documentation for each +/// [conversion format] for details. +/// - `0`: For integer and floating-point number conversions, leading zeros are used to pad the +/// field instead of space characters. +/// - For integer numbers it is ignored if the precision is explicitly specified. +/// - For other conversions using this flag results in undefined behavior. +/// - It is ignored if `-` flag is present. +/// +/// ## Field Width +/// Specifies minimum field width. This makes the result to be padded (with spaces by default, with +/// zeroes if `0` conversion flag is specified) if the converted value has fewer characters than the +/// specified width. It can take three forms: +/// +/// - `N` where N is a positive integer: Specifies the field width value of `N`. +/// - `*`: The width is specified by an extra argument of type [`int`], which has to appear before +/// the argument to be converted and the [precision] (if specified with `.*`). +/// - If the value of e extra argument is negative, it is interpreted as with `-` [conversion +/// flag], i.e. left-justified result. +/// - `*P$` where P is a positive integer: The width is specified by an extra argument of type +/// [`int`], which has to appear exactly at the position specified by `P`. +/// - This is a popular extension of the C and POSIX standards. +/// - If the value of e extra argument is negative, it is interpreted as with `-` [conversion +/// flag], i.e. left-justified result. +/// +/// ## Precision +/// Specifies the precision of the conversion. +/// +/// For integer [conversion formats], this specifies the number of digits to appear in the result. +/// +/// For float point [conversion formats], this specifies the number of digits to appear after the +/// decimal-point character. +/// +/// It can take three forms: +/// +/// - `.N` where N is a positive integer: Specifies the precision value of `N`. +/// - `.*`: The precision is specified by an extra argument of type [`int`], which has to appear +/// before the argument to be converted and after the the [field width] (if specified with `*`). +/// - If the value of the extra argument is negative, it is interpreted as if the precision were +/// omitted. +/// - `.*P$` where P is a positive integer: The precision is specified by an extra argument of type +/// [`int`], which has to appear exactly at the position specified by `P`. +/// - This is an popular extension of the C and POSIX standards. +/// - If the value of e extra argument is negative, it is interpreted as if the precision were +/// omitted. +/// +/// ## Length Modifier +/// Specifies the size of the argument. In combination with the [conversion format], it specifies +/// the type of the corresponding argument. +/// +/// - `hh`: Byte size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `h`: Short size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `l`: Long size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with character conversion format (`c`) +/// - Works with string conversion format (`s`) +/// - Works with written number conversion format (`n`) +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) (C99) +/// - `ll`: Long long size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `j`: Maximum width +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `z`: Pointer width size +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `t`: Pointer diff width +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - Works with written number conversion format (`n`) +/// - `wN` (C23 ※): Specifies that the size should be N bits width version of the supported +/// conversion format. +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - The supported values of `N` must be the same as the widths specified in `stdint.h` +/// - `wfN` (C23 ※): Specifies that the size should be the fast N bits width version of the +/// supported conversion format. +/// - Works with integer conversion formats (`d`, `i`, `o`, `x`, `X`, `b`, `B`) +/// - The supported values of `N` must be the same as the widths specified in `stdint.h` +/// - `L`: Long double size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// - `H` (C23 ※): _Decimal32 size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// - `D` (C23 ※): _Decimal64 size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// - `DD` (C23 ※): _Decimal128 size +/// - Works with float conversion formats (`f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`) +/// +/// ## Conversion Format +/// Specifies the conversion format as one of the following: +/// +/// - `%`: Writes a percent symbol. The full conversion format must be `%%`. +/// - `c`: Writes as single character +/// - Without length modifier: +/// - The argument is first converted to [`unsigned char`] +/// - With `l` length modifier: +/// - The argument is first converted to a character string as if by `%ls` with a array of 2 +/// [`wchar_t`] argument. +/// - `s`: Writes a character string +/// - The argument is a pointer to the first character +/// - The [precision] specifies the maximum number of bytes to be written. If not specified, +/// writes up to the first null character found. +/// - `d` and `i`: Writes a decimal representation of a signed integer +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - `u`: Writes the decimal representation of a unsigned integer +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - `o`: Writes the octal representation of a unsigned integer. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0`. +/// - The types are the same as `u` +/// - `x`: Writes the hexadecimal representation of a unsigned integer with lowercase characters. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0x`. +/// - The types are the same as `u` +/// - `X`: Writes the hexadecimal representation of a unsigned integer with uppercase characters. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0X`. +/// - The types are the same as `u`. +/// - `b` | `B` (C23): Writes the binary representation of a unsigned integer. +/// - The [precision] specifies the minimal number to appear (defaults to `1`). +/// - If the precision is zero and the value to be written is also zero, the result is no +/// characters written. +/// - The alternative representation includes a leading `0b` and `0B`, respectively. +/// - The types are the same as `u`. +/// - `f` | `F`: Writes the decimal representation of a float point number. +/// - The [precision] specifies the exact number of digits to appear after the decimal point +/// character (defaults to `6`). +/// - The alternative representation, the decimal point character is written even if no digits +/// follow it. +/// - `e` | `E`: Writes the float point number with the decimal exponential notation (\[-\]d.ddd +/// **e**±dd | \[-\]d.ddd **E**±dd) +/// - The [precision] specifies the exact number of digits to appear after the decimal point +/// character (defaults to `6`). +/// - The exponent contains at least two digits, more digits are used only if necessary. +/// - If the value is ​zero, the exponent is also ​zero​. +/// - The alternative representation: decimal point character is written even if no digits follow +/// it. +/// - `a` | `A`: Writes the float point number with the hexadecimal exponential notation (\[-\] +/// **0x**h.hhh **p**±d | \[-\] **0X**h.hhh **P**±d) +/// - The [precision] specifies the exact number of digits to appear after the hexadecimal point +/// character (defaults to `6`). +/// - If the value is ​zero, the exponent is also ​zero​. +/// - The alternative representation: decimal point character is written even if no digits follow +/// it. +/// - `g` | `G`: Writes the float point number to decimal or decimal exponent notation depending on +/// the value and the [precision]. +/// - Let `P` equal the precision if nonzero, `6` if the precision is not specified, or `1` if the +/// precision is `​0`​. Then, if a conversion with style `E` would have an exponent of `X`: +/// - If `P > X ≥ −4`, the conversion is with the format `f` and precision `P − 1 − X`. +/// - Otherwise, the conversion is with the format `e` or `E` and precision `P − 1`. +/// - Unless alternative representation is requested, the trailing zeros are removed. Also the +/// decimal point character is removed if no fractional part is left. +/// - `n`: Writes the number of characters written in the call into the argument pointer +/// - It can not contain any [conversion flag], [field width], or [precision]. +/// - `p`: Writes an implementation defined character sequence defining a pointer. +/// +/// ### Types +/// The types expected by the format string can change with the [length modifier]. +/// +/// For the `c`: +/// - Without length modifier: [`int`] +/// - With `l` length modifier: [`wint_t`] +/// +/// For the `s`: +/// - Without length modifier: pointer to [`char`] (`char*`, `const char*`) +/// - With `l` length modifier: pointer to [`wchar_t`] (`wchar_t*`, `const wchar_t*`) +/// +/// For the `d` and `i`: +/// - Without length modifier: [`int`] +/// - With `hh` length modifier: [`signed char`] +/// - With `h` length modifier: [`short`] +/// - With `l` length modifier: [`long`] +/// - With `ll` length modifier: [`long long`] +/// - With `j` length modifier: [`intmax_t`] +/// - With `z` length modifier: [`ssize_t`] +/// - With `t` length modifier: [`ptrdiff_t`] +/// +/// For the `u`, `o`, `x`, `X`, `b`, `B`: +/// - Without length modifier: [`unsigned int`] +/// - With `hh` length modifier: [`unsigned char`] +/// - With `h` length modifier: [`unsigned short`] +/// - With `l` length modifier: [`unsigned long`] +/// - With `ll` length modifier: [`unsigned long long`] +/// - With `j` length modifier: [`uintmax_t`] +/// - With `z` length modifier: [`size_t`] +/// - With `t` length modifier: [`unsigned ptrdiff_t`] +/// +/// For the `f`, `F`, `e`, `E`, `a`, `A`, `g`, `G`: +/// - Without length modifier: [`double`] +/// - With `l` length modifier: [`double`] +/// - With `L` length modifier: `long double` +/// - With `H` length modifier (C23 ※): `_Decimal32` +/// - With `D` length modifier (C23 ※): `_Decimal64` +/// - With `DD` length modifier (C23 ※): `_Decimal128` +/// +/// For the `n` +/// - Without length modifier: pointer to [`int`] (`int*`) +/// - With `hh` length modifier: pointer to [`signed char`] (`signed char*`) +/// - With `h` length modifier: pointer to [`short`] (`short*`) +/// - With `l` length modifier: pointer to [`long`] (`long*`) +/// - With `ll` length modifier: pointer to [`long long`] (`long long*`) +/// - With `j` length modifier: pointer to [`intmax_t`] (`intmax_t*`) +/// - With `z` length modifier: pointer to [`ssize_t`] (`ssize_t*`) +/// - With `t` length modifier: pointer to [`ptrdiff_t`] (`ptrdiff_t*`) +/// +/// For the `p`, it must always be a pointer to [`void`] (`void*` | `const void*`) +/// +/// [precision]: #precision +/// [field width]: #field-width +/// [length modifier]: #length-modifier +/// [conversion format]: #conversion-format +/// [`int`]: c_int +/// [`unsigned char`]: c_uchar +/// [`unsigned short`]: c_ushort +/// [`unsigned int`]: c_uint +/// [`unsigned long`]: c_ulong +/// [`unsigned long long`]: c_ulonglong +/// [`unsigned ptrdiff_t`]: ptrdiff_t +/// [`wchar_t`]: wchar_t +/// [`char`]: c_char +/// [`signed char`]: c_schar +/// [`short`]: c_short +/// [`long`]: c_long +/// [`long long`]: c_longlong +/// [`double`]: c_double +/// [`long double`]: c_longdouble +/// [`void`]: c_void +/// +/// # Safety +/// Behavior is undefined if any of the following conditions are violated: +/// - `ap` must follow the safety contract of variable arguments of C. +pub unsafe fn printf(w: impl Write, format: CStr, ap: VaList) -> c_int { + unsafe { inner_printf::(w, format, ap).unwrap_or(-1) } +} diff --git a/src/header/stdio/reader.rs b/src/header/stdio/reader.rs new file mode 100644 index 0000000000..aaf6ea05e2 --- /dev/null +++ b/src/header/stdio/reader.rs @@ -0,0 +1,180 @@ +use super::{FILE, SEEK_SET, fseek_locked, ftell_locked}; +use crate::{ + c_str::{CStr, Kind, NulStr, Thin, WStr, Wide}, + header::{ + errno::EILSEQ, + stdlib::MB_CUR_MAX, + wchar::{get_char_encoded_length, mbrtowc}, + wctype::WEOF, + }, + io::Read, + platform::{ + ERRNO, + types::{c_char, off_t, wchar_t, wint_t}, + }, +}; +use core::{ + iter::Iterator, + marker::PhantomData, + ptr::{self}, +}; + +pub struct BufferReader<'a, T: Kind> { + buf: NulStr<'a, T>, +} + +impl<'a> From> for BufferReader<'a, Wide> { + fn from(buff: WStr<'a>) -> Self { + BufferReader { buf: buff } + } +} + +impl<'a> From> for BufferReader<'a, Thin> { + fn from(buff: CStr<'a>) -> Self { + BufferReader { buf: buff } + } +} + +impl<'a, T: Kind> Iterator for BufferReader<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + self.buf.split_first().map(|(c, r)| { + self.buf = r; + Ok(c) + }) + } +} + +pub struct FileReader<'a, T: Kind> { + f: &'a mut FILE, + position: off_t, + phantom: PhantomData, +} + +impl<'a, T: Kind> FileReader<'a, T> { + // Gets the wchar at the current position + #[inline] + fn get_curret_char(&mut self) -> Result, i32> { + if T::IS_THIN_NOT_WIDE { + let mut buf: [u8; 1] = [0]; + match self.f.read(&mut buf) { + Ok(0) => Ok(None), + Ok(n) => Ok(Some((T::Char::from(buf[0]), n))), + Err(_) => Err(-1), + } + } else { + let buf = &mut [0; MB_CUR_MAX as usize]; + let mut encoded_length = 0; + let mut bytes_read = 0; + + loop { + match self.f.read(&mut buf[bytes_read..bytes_read + 1]) { + Ok(0) => return Ok(None), + Ok(_) => {} + Err(_) => return Err(-1), + } + + bytes_read += 1; + + if bytes_read == 1 { + encoded_length = if let Some(el) = get_char_encoded_length(buf[0]) { + el + } else { + ERRNO.set(EILSEQ); + return Self::get_char_from_wint(WEOF).map(|c| Some((c, 0))); + }; + } + + if bytes_read >= encoded_length { + break; + } + } + + let mut wc: wchar_t = 0; + unsafe { + mbrtowc( + &raw mut wc, + buf.as_ptr().cast::(), + encoded_length, + ptr::null_mut(), + ); + } + + Self::get_char_from_wint(wc as wint_t).map(|c| Some((c, encoded_length))) + } + } + + fn get_char_from_wint(wc: wint_t) -> Result { + if let Some(wc_char) = T::chars_from_bytes(&wc.to_be_bytes()) + && wc_char.len() == 1 + { + Ok(wc_char[0]) + } else { + Err(-1) + } + } +} + +impl<'a, T: Kind> Iterator for FileReader<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + unsafe { fseek_locked(self.f, self.position, SEEK_SET) }; + + match self.get_curret_char() { + Ok(Some((wc, encoded_length))) => { + unsafe { fseek_locked(self.f, self.position, SEEK_SET) }; + self.position += encoded_length as off_t; + Some(Ok(wc)) + } + Ok(None) => None, + Err(e) => Some(Err(e)), + } + } +} + +impl<'a, T: Kind> From<&'a mut FILE> for FileReader<'a, T> { + fn from(f: &'a mut FILE) -> Self { + let position = unsafe { ftell_locked(f) } as off_t; + FileReader { + f, + position, + phantom: PhantomData::, + } + } +} + +pub enum Reader<'a, T: Kind> { + FILE(FileReader<'a, T>, PhantomData), + BUFFER(BufferReader<'a, T>), +} + +impl<'a, T: Kind> Iterator for Reader<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + match self { + Self::FILE(r, _) => r.next(), + Self::BUFFER(r) => r.next(), + } + } +} + +impl<'a, T: Kind> From<&'a mut FILE> for Reader<'a, T> { + fn from(f: &'a mut FILE) -> Self { + Self::FILE(f.into(), PhantomData::) + } +} + +impl<'a> From> for Reader<'a, Wide> { + fn from(buff: WStr<'a>) -> Self { + Self::BUFFER(buff.into()) + } +} + +impl<'a> From> for Reader<'a, Thin> { + fn from(buff: CStr<'a>) -> Self { + Self::BUFFER(buff.into()) + } +} diff --git a/src/header/stdio/scanf.rs b/src/header/stdio/scanf.rs new file mode 100644 index 0000000000..1d89cc54e6 --- /dev/null +++ b/src/header/stdio/scanf.rs @@ -0,0 +1,514 @@ +use super::reader::Reader; +use crate::{ + c_str::Kind, + header::stdio::printf::IntKind, + platform::types::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong, + c_ulonglong, c_ushort, c_void, intmax_t, ptrdiff_t, size_t, ssize_t, uintmax_t, wchar_t, + }, +}; +use alloc::{string::String, vec::Vec}; +use core::{ffi::VaList as va_list, iter::Peekable}; + +#[derive(PartialEq, Eq)] +enum CharKind { + Ascii, + Wide, +} + +fn next_char(lar: &mut Peekable>) -> Result { + if let Some(c) = lar.next().transpose()? { + char::try_from(c.into()).map_err(|_| -1) + } else { + Ok('\0') + } +} + +macro_rules! wc_as_char { + ($c:ident) => { + char::try_from($c.into()).map_err(|_| -1)? + }; +} + +pub unsafe fn inner_scanf( + mut r: Reader, + format: Reader, + mut ap: va_list, +) -> Result { + let mut matched = 0; + let mut character: char = '\0'; + let mut skip_read = false; + let mut count = 0; + let mut format = format.peekable(); + + macro_rules! read { + () => {{ + match r.next() { + None => false, + Some(Ok(b)) => { + character = wc_as_char!(b); + count += 1; + true + } + Some(Err(x)) => return Err(x), + } + }}; + } + + macro_rules! maybe_read { + () => { + maybe_read!(inner false); + }; + (noreset) => { + maybe_read!(inner); + }; + (inner $($placeholder:expr)*) => { + if !skip_read && !read!() { + match matched { + 0 => return Ok(-1), + a => return Ok(a), + } + } + $(else { + // Hacky way of having this optional + skip_read = $placeholder; + })* + } + } + + while format.peek().is_some() { + let mut c = next_char(&mut format)?; + + if c == ' ' { + maybe_read!(noreset); + + while (character).is_whitespace() { + if !read!() { + return Ok(matched); + } + } + + skip_read = true; + } else if c != '%' { + maybe_read!(); + if c != character { + return Ok(matched); + } + } else { + c = next_char(&mut format)?; + + let mut ignore = false; + if c == '*' { + ignore = true; + c = next_char(&mut format)?; + } + + let mut width = String::new(); + while c.is_ascii_digit() { + width.push(c); + c = next_char(&mut format)?; + } + let mut width = if width.is_empty() { + None + } else { + match width.parse::() { + Ok(n) => Some(n), + Err(_) => return Err(-1), + } + }; + + // When an EOF occurs, eof is set, stuff is marked matched + // as usual, and finally it is returned + let mut eof = false; + + let mut kind = IntKind::Int; + let mut c_kind = CharKind::Ascii; + loop { + match c { + 'h' => { + if kind == IntKind::Short || kind == IntKind::Byte { + kind = IntKind::Byte; + } else { + kind = IntKind::Short; + } + } + 'j' => kind = IntKind::IntMax, + 'l' => { + if kind == IntKind::Long || kind == IntKind::LongLong { + kind = IntKind::LongLong; + } else { + kind = IntKind::Long; + } + } + 'q' | 'L' => kind = IntKind::LongLong, + 't' => kind = IntKind::PtrDiff, + 'z' => kind = IntKind::Size, + // If kind is Long, means we found a 'l' before finding 'c' or 's'. In this + // case the format corresponds to a wide char/string + 'c' | 's' if kind == IntKind::Long && !T::IS_THIN_NOT_WIDE => { + c_kind = CharKind::Wide; + break; + } + _ => break, + } + + c = next_char(&mut format)?; + } + + if c != 'n' { + maybe_read!(noreset); + } + match c { + '%' => { + while (character).is_whitespace() { + if !read!() { + return Ok(matched); + } + } + + if character != '%' { + return Err(matched); + } else if !read!() { + return Ok(matched); + } + } + + 'd' | 'i' | 'o' | 'u' | 'x' | 'X' | 'f' | 'e' | 'g' | 'E' | 'a' | 'p' => { + while character.is_whitespace() { + if !read!() { + return Ok(matched); + } + } + + let pointer = c == 'p'; + // Pointers aren't automatic, but we do want to parse "0x" + let auto = c == 'i' || pointer; + let float = c == 'f' || c == 'e' || c == 'g' || c == 'E' || c == 'a'; + + let mut radix = match c { + 'o' => 8, + 'x' | 'X' | 'p' => 16, + _ => 10, + }; + + let mut n = String::new(); + let mut dot = false; + + while width.map(|w| w > 0).unwrap_or(true) + && (('0'..='7').contains(&character) + || (radix >= 10 && ('8'..='9').contains(&character)) + || (float && !dot && character == '.') + || (radix == 16 + && (('a'..='f').contains(&character) + || ('A'..='F').contains(&character)))) + { + if auto + && n.is_empty() + && character == '0' + && width.map(|w| w > 0).unwrap_or(true) + { + if !pointer { + radix = 8; + } + width = width.map(|w| w - 1); + if !read!() { + return Ok(matched); + } + if width.map(|w| w > 0).unwrap_or(true) + && (character == 'x' || character == 'X') + { + radix = 16; + width = width.map(|w| w - 1); + if width.map(|w| w > 0).unwrap_or(true) && !read!() { + return Ok(matched); + } + } + continue; + } + if character == '.' { + // Don't allow another dot + dot = true; + } + n.push(character); + width = width.map(|w| w - 1); + if width.map(|w| w > 0).unwrap_or(true) && !read!() { + break; + } + } + + macro_rules! parse_type { + (noformat $type:ident) => {{ + let n = if n.is_empty() { + 0 as $type + } else { + n.parse::<$type>().map_err(|_| 0)? + }; + if !ignore { + unsafe { *ap.arg::<*mut $type>() = n }; + matched += 1; + } + }}; + (c_double) => { + parse_type!(noformat c_double) + }; + (c_float) => { + parse_type!(noformat c_float) + }; + ($type:ident) => { + parse_type!($type, $type) + }; + ($type:ident, $final:ty) => {{ + let n = if n.is_empty() { + 0 as $type + } else { + $type::from_str_radix(&n, radix).map_err(|_| 0)? + }; + if !ignore { + unsafe { *ap.arg::<*mut $final>() = n as $final }; + matched += 1; + } + }}; + } + + if float { + if kind == IntKind::Long || kind == IntKind::LongLong { + parse_type!(c_double); + } else { + parse_type!(c_float); + } + } else if c == 'p' { + parse_type!(size_t, *mut c_void); + } else { + let unsigned = c == 'o' || c == 'u' || c == 'x' || c == 'X'; + + match kind { + IntKind::Byte => { + if unsigned { + parse_type!(c_uchar); + } else { + parse_type!(c_char); + } + } + IntKind::Short => { + if unsigned { + parse_type!(c_ushort) + } else { + parse_type!(c_short) + } + } + IntKind::Int => { + if unsigned { + parse_type!(c_uint) + } else { + parse_type!(c_int) + } + } + IntKind::Long => { + if unsigned { + parse_type!(c_ulong) + } else { + parse_type!(c_long) + } + } + IntKind::LongLong => { + if unsigned { + parse_type!(c_ulonglong) + } else { + parse_type!(c_longlong) + } + } + IntKind::IntMax => { + if unsigned { + parse_type!(uintmax_t) + } else { + parse_type!(intmax_t) + } + } + IntKind::PtrDiff => parse_type!(ptrdiff_t), + IntKind::Size => { + if unsigned { + parse_type!(size_t) + } else { + parse_type!(ssize_t) + } + } + } + } + } + + 's' => { + macro_rules! parse_string_type { + ($type:ident) => { + while character.is_whitespace() { + if !read!() { + return Ok(matched); + } + } + + let mut ptr: Option<*mut $type> = + if ignore { None } else { Some(ap.arg()) }; + + while width.map(|w| w > 0).unwrap_or(true) && !character.is_whitespace() + { + if let Some(ref mut ptr) = ptr { + **ptr = character as $type; + *ptr = ptr.offset(1); + } + width = width.map(|w| w - 1); + if width.map(|w| w > 0).unwrap_or(true) && !read!() { + eof = true; + break; + } + } + + // If we read the width, we end up in the last character of was + // intended to be read, so we advance one + if let Some(0) = width { + read!(); + } + + if let Some(ptr) = ptr { + *ptr = 0; + matched += 1; + } + }; + } + + if c_kind == CharKind::Ascii { + unsafe { + parse_string_type!(c_char); + } + } else { + unsafe { + parse_string_type!(wchar_t); + } + } + } + + 'c' => { + macro_rules! parse_char_type { + ($type:ident) => { + let ptr: Option<*mut $type> = if ignore { + None + } else { + Some(unsafe { ap.arg() }) + }; + + for i in 0..width.unwrap_or(1) { + if let Some(ptr) = ptr { + unsafe { *ptr.add(i) = character as $type }; + } + width = width.map(|w| w - 1); + if width.map(|w| w > 0).unwrap_or(true) && !read!() { + eof = true; + break; + } + } + + if ptr.is_some() { + matched += 1; + } + }; + } + + if c_kind == CharKind::Ascii { + parse_char_type!(c_char); + } else { + parse_char_type!(wchar_t); + } + } + + '[' => { + c = next_char(&mut format)?; + + let mut matches = Vec::new(); + let invert = if c == '^' { + c = next_char(&mut format)?; + true + } else { + false + }; + + let mut prev: u32; + loop { + matches.push(c); + prev = c.into(); + c = next_char(&mut format)?; + if c == '-' { + if prev as u8 == b']' { + continue; + } + c = next_char(&mut format)?; + if c == ']' { + matches.push('-'); + break; + } + prev += 1; + while prev < c.into() { + matches.push(char::try_from(prev).map_err(|_| -1)?); + prev += 1; + } + } else if c == ']' { + break; + } + } + + let mut ptr: Option<*mut c_char> = if ignore { + None + } else { + Some(unsafe { ap.arg() }) + }; + + // While we haven't used up all the width, and it matches + let mut data_stored = false; + while width.map(|w| w > 0).unwrap_or(true) + && invert != matches.contains(&character) + { + if let Some(ref mut ptr) = ptr { + unsafe { **ptr = character as c_char }; + *ptr = unsafe { ptr.offset(1) }; + data_stored = true; + } + // Decrease the width, and read a new character unless the width is 0 + width = width.map(|w| w - 1); + if width.map(|w| w > 0).unwrap_or(true) && !read!() { + // Reading a new character has failed, return after + // actually marking this as matched + eof = true; + break; + } + } + + if data_stored { + unsafe { *ptr.unwrap() = 0 }; + matched += 1; + } + } + 'n' => { + if !ignore { + unsafe { *ap.arg::<*mut c_int>() = count as c_int }; + } + } + _ => return Err(-1), + } + + if eof { + return Ok(matched); + } + + if width != Some(0) && c != 'n' { + // It didn't hit the width, so an extra character was read and matched. + // But this character did not match so let's reuse it. + skip_read = true; + } + } + } + Ok(matched) +} + +pub unsafe fn scanf(r: Reader, format: Reader, ap: va_list) -> c_int { + match unsafe { inner_scanf(r, format, ap) } { + Ok(n) => n, + Err(n) => n, + } +} diff --git a/src/header/stdlib/cbindgen.toml b/src/header/stdlib/cbindgen.toml new file mode 100644 index 0000000000..c105da5486 --- /dev/null +++ b/src/header/stdlib/cbindgen.toml @@ -0,0 +1,25 @@ +sys_includes = ["stddef.h", "alloca.h", "wchar.h", "features.h"] +include_guard = "_RELIBC_STDLIB_H" +trailer = """ +#ifndef _RELIBC_STDLIB_EXTRA_H +#define _RELIBC_STDLIB_EXTRA_H + +#ifdef __cplusplus +extern "C" { +#endif + +long double strtold(const char *nptr, char **endptr); + +#ifdef __cplusplus +} +#endif + +#endif +""" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs new file mode 100644 index 0000000000..96f9611aa9 --- /dev/null +++ b/src/header/stdlib/mod.rs @@ -0,0 +1,1705 @@ +//! `stdlib.h` implementation. +//! +//! See . + +use core::{convert::TryFrom, intrinsics, iter, mem, ptr, slice}; +use rand::{ + RngExt, SeedableRng, + distr::{Alphanumeric, Distribution, Uniform}, +}; +use rand_jitter::JitterRng; +use rand_xorshift::XorShiftRng; + +use crate::{ + c_str::CStr, + error::{Errno, ResultExt}, + fs::File, + header::{ + ctype, + errno::{self, *}, + fcntl::*, + limits, + stdio::flush_io_streams, + string::*, + sys_ioctl::*, + time::constants::CLOCK_MONOTONIC, + unistd::{self, _SC_PAGESIZE, sysconf}, + wchar::*, + }, + ld_so, + out::Out, + platform::{ + self, Pal, Sys, + types::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong, + c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t, + }, + }, + raw_cell::RawCell, + sync::Once, +}; + +mod rand48; +mod random; +mod sort; + +/// See . +pub const EXIT_FAILURE: c_int = 1; +/// See . +pub const EXIT_SUCCESS: c_int = 0; +/// See . +pub const RAND_MAX: c_int = 2_147_483_647; + +/// See . +//Maximum number of bytes in a multibyte character for the current locale +pub const MB_CUR_MAX: c_int = 4; +/// Actually specified for `limits.h`? +//Maximum number of bytes in a multibyte characters for any locale +pub const MB_LEN_MAX: c_int = 4; + +static ATEXIT_FUNCS: RawCell<[Option; 32]> = RawCell::new([None; 32]); +static AT_QUICK_EXIT_FUNCS: RawCell<[Option; 32]> = RawCell::new([None; 32]); +static L64A_BUFFER: RawCell<[c_char; 7]> = RawCell::new([0; 7]); // up to 6 digits plus null terminator +static mut RNG: Option = None; + +// TODO: This could be const fn, but the trait system won't allow that. +static RNG_SAMPLER: Once> = Once::new(); + +fn rng_sampler() -> &'static Uniform { + RNG_SAMPLER.call_once(|| Uniform::new_inclusive(0, RAND_MAX).expect("within bounds")) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn _Exit(status: c_int) -> ! { + unistd::_exit(status); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn a64l(s: *const c_char) -> c_long { + // Early return upon null pointer argument + if s.is_null() { + return 0; + } + + // POSIX says only the low-order 32 bits are used. + let mut l: i32 = 0; + + // Handle up to 6 input characters (excl. null terminator) + for i in 0..6 { + let digit_char = unsafe { *s.offset(i) }; + + let digit_value = match digit_char { + 0 => break, // Null terminator encountered + 46..=57 => { + // ./0123456789 represents values 0 to 11. b'.' == 46 + digit_char - 46 + } + 65..=90 => { + // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53 + digit_char - 53 + } + 97..=122 => { + // a-z for values 38 to 63. b'a' == 97, 97-38 == 59 + digit_char - 59 + } + _ => return 0, // Early return for anything else + }; + + l |= i32::from(digit_value) << (6 * i); + } + + c_long::from(l) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn abort() -> ! { + log::error!("Abort"); + intrinsics::abort(); +} + +#[cfg(not(target_pointer_width = "64"))] +#[unsafe(no_mangle)] +static __stack_chk_guard: uintptr_t = 0x19fcadfe; + +#[cfg(target_pointer_width = "64")] +#[unsafe(no_mangle)] +static __stack_chk_guard: uintptr_t = 0xd048c37519fcadfe; + +#[unsafe(no_mangle)] +unsafe extern "C" fn __stack_chk_fail() -> ! { + unsafe { abort() }; +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn abs(i: c_int) -> c_int { + i.abs() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void { + if alignment == 0 || !size.is_multiple_of(alignment) { + platform::ERRNO.set(EINVAL); + return ptr::null_mut(); + } + let mut pointer = ptr::null_mut(); + unsafe { posix_memalign(ptr::from_mut(&mut pointer), alignment, size) }; + pointer +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn at_quick_exit(func: Option) -> c_int { + for i in 0..unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref().len() } { + if unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }[i].is_none() { + (unsafe { AT_QUICK_EXIT_FUNCS.unsafe_mut() })[i] = func; + return 0; + } + } + + 1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atexit(func: Option) -> c_int { + for i in 0..unsafe { ATEXIT_FUNCS.unsafe_ref().len() } { + if unsafe { ATEXIT_FUNCS.unsafe_ref() }[i].is_none() { + (unsafe { ATEXIT_FUNCS.unsafe_mut() })[i] = func; + return 0; + } + } + + 1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atof(s: *const c_char) -> c_double { + unsafe { strtod(s, ptr::null_mut()) } +} + +macro_rules! dec_num_from_ascii { + ($s:expr, $t:ty) => {{ + let mut s = $s; + // Iterate past whitespace + while ctype::isspace(c_int::from(unsafe { *s })) != 0 { + s = unsafe { s.offset(1) }; + } + + // Find out if there is a - sign + let neg_sign = match unsafe { *s } { + 0x2d => { + s = unsafe { s.offset(1) }; + true + } + // '+' increment s and continue parsing + 0x2b => { + s = unsafe { s.offset(1) }; + false + } + _ => false, + }; + + let mut n: $t = 0; + while ctype::isdigit(c_int::from(unsafe { *s })) != 0 { + n = 10 * n - (unsafe { *s } as $t - 0x30); + s = unsafe { s.offset(1) }; + } + + if neg_sign { n } else { -n } + }}; +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atoi(s: *const c_char) -> c_int { + dec_num_from_ascii!(s, c_int) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atol(s: *const c_char) -> c_long { + dec_num_from_ascii!(s, c_long) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong { + dec_num_from_ascii!(s, c_longlong) +} + +unsafe extern "C" fn void_cmp(a: *const c_void, b: *const c_void) -> c_int { + (unsafe { *(a.cast::()) }) - unsafe { *(b.cast::()) } as c_int +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn bsearch( + key: *const c_void, + base: *const c_void, + nel: size_t, + width: size_t, + compar: Option c_int>, +) -> *mut c_void { + let mut start = base; + let mut len = nel; + let cmp_fn = compar.unwrap_or(void_cmp); + while len > 0 { + let med = (start as size_t + (len >> 1) * width) as *const c_void; + let diff = unsafe { cmp_fn(key, med) }; + if diff == 0 { + return med.cast_mut(); + } else if diff > 0 { + start = (med as usize + width) as *const c_void; + len -= 1; + } + len >>= 1; + } + ptr::null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn calloc(nelem: size_t, elsize: size_t) -> *mut c_void { + //Handle possible integer overflow in size calculation + match nelem.checked_mul(elsize) { + Some(size) => { + /* If allocation fails here, errno setting will be handled + * by malloc() */ + let ptr = unsafe { malloc(size) }; + if !ptr.is_null() { + unsafe { ptr.write_bytes(0, size) }; + } + ptr + } + None => { + // For overflowing multiplication, we have to set errno here + platform::ERRNO.set(ENOMEM); + ptr::null_mut() + } + } +} + +/// See . +#[repr(C)] +pub struct div_t { + quot: c_int, + rem: c_int, +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn div(numer: c_int, denom: c_int) -> div_t { + div_t { + quot: numer / denom, + rem: numer % denom, + } +} + +/// See . +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub extern "C" fn drand48() -> c_double { + let params = rand48::params(); + let mut xsubi = rand48::xsubi_lock(); + *xsubi = params.step(*xsubi); + xsubi.get_f64() +} + +/// See . +/// +/// # Deprecation +/// The `ecvt()` function was marked as legacy in the Open Group Base +/// Specifications Issue 6, and the function was removed in Issue 7. +#[deprecated] +// #[unsafe(no_mangle)] +pub extern "C" fn ecvt( + value: c_double, + ndigit: c_int, + decpt: *mut c_int, + sign: *mut c_int, +) -> *mut c_char { + unimplemented!(); +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `xsubi` is convertible to a +/// `&mut [c_ushort; 3]`. +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn erand48(xsubi: *mut c_ushort) -> c_double { + let params = rand48::params(); + let xsubi_mut: &mut [c_ushort; 3] = + unsafe { slice::from_raw_parts_mut(xsubi, 3).try_into().unwrap() }; + let new_xsubi_value = params.step(xsubi_mut.into()); + *xsubi_mut = new_xsubi_value.into(); + new_xsubi_value.get_f64() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn exit(status: c_int) -> ! { + unsafe extern "C" { + static __fini_array_start: extern "C" fn(); + static __fini_array_end: extern "C" fn(); + } + + for i in (0..unsafe { ATEXIT_FUNCS.unsafe_ref().len() }).rev() { + if let Some(func) = unsafe { ATEXIT_FUNCS.unsafe_ref() }[i] { + (func)(); + } + } + + // Look for the neighbor functions in memory until the end + let mut f = unsafe { &__fini_array_end } as *const _; + #[allow(clippy::op_ref)] + while f > &raw const __fini_array_start { + f = unsafe { f.offset(-1) }; + (unsafe { *f })(); + } + + unsafe { ld_so::fini() }; + + unsafe { crate::pthread::terminate_from_main_thread() }; + + unsafe { flush_io_streams() }; + + Sys::exit(status); +} + +/// See . +/// +/// # Deprecation +/// The `fcvt()` function was marked as legacy in the Open Group Base +/// Specifications Issue 6, and the function was removed in Issue 7. +#[deprecated] +// #[unsafe(no_mangle)] +pub extern "C" fn fcvt( + value: c_double, + ndigit: c_int, + decpt: *mut c_int, + sign: *mut c_int, +) -> *mut c_char { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn free(ptr: *mut c_void) { + unsafe { platform::free(ptr) }; +} + +/// See . +/// +/// # Deprecation +/// The `gcvt()` function was marked as legacy in the Open Group Base +/// Specifications Issue 6, and the function was removed in Issue 7. +#[deprecated] +// #[unsafe(no_mangle)] +pub extern "C" fn gcvt(value: c_double, ndigit: c_int, buf: *mut c_char) -> *mut c_char { + unimplemented!(); +} + +unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { + for (i, mut item) in platform::environ_iter().enumerate() { + let mut search = search; + loop { + let end_of_query = unsafe { *search } == 0 || unsafe { *search } == b'=' as c_char; + if unsafe { *item } == 0 { + //TODO: environ has an item without value, is this a problem? + break; + } + if unsafe { *item } == b'=' as c_char || end_of_query { + if unsafe { *item } == b'=' as c_char && end_of_query { + // Both keys env here + return Some((i, unsafe { item.add(1) })); + } else { + break; + } + } + + if unsafe { *item } != unsafe { *search } { + break; + } + + item = unsafe { item.add(1) }; + search = unsafe { search.add(1) }; + } + } + + None +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char { + unsafe { find_env(name) } + .map(|val| val.1) + .unwrap_or(ptr::null_mut()) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getsubopt( + optionp: *mut *mut c_char, + keylistp: *const *mut c_char, + valuep: *mut *mut c_char, +) -> c_int { + if optionp.is_null() + || (unsafe { *optionp }).is_null() + || keylistp.is_null() + || valuep.is_null() + { + return -1; + } + + let start = unsafe { *optionp }; + let mut cursor = start; + let mut found_comma = false; + + while unsafe { *cursor } != 0 { + if unsafe { *cursor } == b',' as c_char { + unsafe { *cursor = 0 }; + unsafe { *optionp = cursor.add(1) }; + found_comma = true; + break; + } + cursor = unsafe { cursor.add(1) }; + } + + if !found_comma { + unsafe { *optionp = cursor }; + } + + let mut i = 0; + while !(unsafe { *keylistp.add(i) }).is_null() { + let token = unsafe { *keylistp.add(i) }; + let token_len = unsafe { strlen(token) }; + + if unsafe { strncmp(start, token, token_len) } == 0 { + let suffix_char = unsafe { *start.add(token_len) }; + + if suffix_char == b'=' as c_char { + unsafe { *valuep = start.add(token_len + 1) }; + return i as c_int; + } else if suffix_char == 0 { + unsafe { *valuep = ptr::null_mut() }; + return i as c_int; + } + } + i += 1; + } + + unsafe { *valuep = start }; + -1 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn grantpt(fildes: c_int) -> c_int { + // No-op on Linux and Redox + 0 +} + +/// See . +// Ported from musl +#[unsafe(no_mangle)] +pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_t) -> *mut c_char { + if size < 8 { + ptr::null_mut() + } else { + let mut random_state = random::state_lock(); + let old_state = unsafe { random_state.save() }; + random_state.n = match size { + 0..=7 => unreachable!(), // ensured above + 8..=31 => 0, + 32..=63 => 7, + 64..=127 => 15, + 128..=255 => 31, + _ => 63, + }; + + random_state.x_ptr = unsafe { (state.cast::<[u8; 4]>()).offset(1) }; + unsafe { random_state.seed(seed) }; + unsafe { random_state.save() }; + + old_state.cast::<_>() + } +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `xsubi` is convertible to a +/// `&mut [c_ushort; 3]`. +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn jrand48(xsubi: *mut c_ushort) -> c_long { + let params = rand48::params(); + let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3) } + .try_into() + .unwrap(); + let new_xsubi_value = params.step(xsubi_mut.into()); + *xsubi_mut = new_xsubi_value.into(); + new_xsubi_value.get_i32() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn l64a(value: c_long) -> *mut c_char { + // POSIX says we should only consider the lower 32 bits of value. + let value_as_i32 = value as i32; + + /* If we pretend to extend the 32-bit value with 4 binary zeros, we + * would get a 36-bit integer. The number of base-64 digits to be + * left unused can then be found by taking the number of leading + * zeros, dividing by 6 and rounding down (i.e. using integer + * division). */ + let num_output_digits = usize::try_from(6 - (value_as_i32.leading_zeros() + 4) / 6).unwrap(); + + // Reset buffer (and have null terminator in place for any result) + unsafe { L64A_BUFFER.unsafe_set([0; 7]) }; + + for i in 0..num_output_digits { + // Conversion to c_char always succeeds for the range 0..=63 + let digit_value = c_char::try_from((value_as_i32 >> (6 * i)) & 63).unwrap(); + + (unsafe { L64A_BUFFER.unsafe_mut() })[i] = match digit_value { + 0..=11 => { + // ./0123456789 for values 0 to 11. b'.' == 46 + 46 + digit_value + } + 12..=37 => { + // A-Z for values 12 to 37. b'A' == 65, 65-12 == 53 + 53 + digit_value + } + 38..=63 => { + // a-z for values 38 to 63. b'a' == 97, 97-38 == 59 + 59 + digit_value + } + _ => unreachable!(), // Guaranteed by taking "& 63" above + }; + } + + unsafe { L64A_BUFFER.unsafe_mut().as_mut_ptr() } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn labs(i: c_long) -> c_long { + i.abs() +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `param` is convertible to a +/// `&mut [c_ushort; 7]`. +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lcong48(param: *mut c_ushort) { + let mut xsubi = rand48::xsubi_lock(); + let mut params = rand48::params_mut(); + + let param_slice = unsafe { slice::from_raw_parts(param, 7) }; + + let xsubi_ref: &[c_ushort; 3] = param_slice[0..3].try_into().unwrap(); + let a_ref: &[c_ushort; 3] = param_slice[3..6].try_into().unwrap(); + let c = param_slice[6]; + + *xsubi = xsubi_ref.into(); + params.set(a_ref, c); +} + +/// See . +#[repr(C)] +pub struct ldiv_t { + quot: c_long, + rem: c_long, +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ldiv(numer: c_long, denom: c_long) -> ldiv_t { + ldiv_t { + quot: numer / denom, + rem: numer % denom, + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn llabs(i: c_longlong) -> c_longlong { + i.abs() +} + +/// See . +#[repr(C)] +pub struct lldiv_t { + quot: c_longlong, + rem: c_longlong, +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn lldiv(numer: c_longlong, denom: c_longlong) -> lldiv_t { + lldiv_t { + quot: numer / denom, + rem: numer % denom, + } +} + +/// See . +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub extern "C" fn lrand48() -> c_long { + let params = rand48::params(); + let mut xsubi = rand48::xsubi_lock(); + *xsubi = params.step(*xsubi); + xsubi.get_u31() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn malloc(size: size_t) -> *mut c_void { + let ptr = unsafe { platform::alloc(size) }; + if ptr.is_null() { + platform::ERRNO.set(ENOMEM); + } + ptr +} + +/// Non-POSIX, see . +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void { + if alignment.is_power_of_two() { + let ptr = unsafe { platform::alloc_align(size, alignment) }; + if ptr.is_null() { + platform::ERRNO.set(ENOMEM); + } + ptr + } else { + platform::ERRNO.set(EINVAL); + ptr::null_mut() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mblen(s: *const c_char, n: size_t) -> c_int { + let mut wc: wchar_t = 0; + let mut state: mbstate_t = mbstate_t {}; + let result: usize = unsafe { mbrtowc(&raw mut wc, s, n, &raw mut state) }; + + if result == -1isize as usize { + return -1; + } + if result == -2isize as usize { + return -1; + } + + result as i32 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbstowcs(pwcs: *mut wchar_t, mut s: *const c_char, n: size_t) -> size_t { + let mut state: mbstate_t = mbstate_t {}; + unsafe { mbsrtowcs(pwcs, &raw mut s, n, &raw mut state) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbtowc(pwc: *mut wchar_t, s: *const c_char, n: size_t) -> c_int { + let mut state: mbstate_t = mbstate_t {}; + (unsafe { mbrtowc(pwc, s, n, &raw mut state) }) as c_int +} + +fn inner_mktemp(name: *mut c_char, suffix_len: c_int, mut attempt: F) -> Option +where + F: FnMut() -> Option, +{ + let len = unsafe { strlen(name) as c_int }; + + if len < 6 || suffix_len > len - 6 { + platform::ERRNO.set(errno::EINVAL); + return None; + } + + for i in (len - suffix_len - 6)..(len - suffix_len) { + if unsafe { *name.offset(i as isize) } != b'X' as c_char { + platform::ERRNO.set(errno::EINVAL); + return None; + } + } + + let mut rng = JitterRng::new_with_timer(get_nstime); + let _ = rng.test_timer(); + + for _ in 0..100 { + let char_iter = iter::repeat(()) + .map(|()| rng.sample(Alphanumeric)) + .take(6) + .enumerate(); + unsafe { + for (i, c) in char_iter { + *name.offset((len as isize) - (suffix_len as isize) - (i as isize) - 1) = + c as c_char + } + } + + if let result @ Some(_) = attempt() { + return result; + } + } + + platform::ERRNO.set(errno::EEXIST); + + None +} + +fn get_nstime() -> u64 { + unsafe { + let mut ts = mem::MaybeUninit::uninit(); + if Sys::clock_gettime(CLOCK_MONOTONIC, Out::from_uninit_mut(&mut ts)).is_ok() {}; // TODO what to do if Err? + ts.assume_init().tv_nsec as u64 + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkdtemp(name: *mut c_char) -> *mut c_char { + inner_mktemp(name, 0, || { + let name_c = unsafe { CStr::from_ptr(name) }; + match Sys::mkdir(name_c, 0o700) { + Ok(()) => Some(name), + Err(_) => None, + } + }) + .unwrap_or(ptr::null_mut()) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int { + unsafe { mkostemps(name, 0, flags) } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkostemps( + name: *mut c_char, + suffix_len: c_int, + mut flags: c_int, +) -> c_int { + // TODO: Rustify impl + + flags &= !O_ACCMODE; + flags |= O_RDWR | O_CREAT | O_EXCL; + + inner_mktemp(name, suffix_len, || { + let name = unsafe { CStr::from_ptr(name) }; + let fd = Sys::open(name, flags, 0o600).or_minus_one_errno(); + + if fd >= 0 { Some(fd) } else { None } + }) + .unwrap_or(-1) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkstemp(name: *mut c_char) -> c_int { + unsafe { mkostemps(name, 0, 0) } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int { + unsafe { mkostemps(name, suffix_len, 0) } +} + +/// See . +/// +/// # Deprecation +/// The `mktemp()` function was marked as legacy in the Open Group Base +/// Specifications Issue 6, and the function was removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mktemp(name: *mut c_char) -> *mut c_char { + if inner_mktemp(name, 0, || { + let name = unsafe { CStr::from_ptr(name) }; + if Sys::access(name, 0) == Err(Errno(ENOENT)) { + Some(()) + } else { + None + } + }) + .is_none() + { + unsafe { *name = 0 }; + } + name +} + +/// See . +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub extern "C" fn mrand48() -> c_long { + let params = rand48::params(); + let mut xsubi = rand48::xsubi_lock(); + *xsubi = params.step(*xsubi); + xsubi.get_i32() +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `xsubi` is convertible to a +/// `&mut [c_ushort; 3]`. +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn nrand48(xsubi: *mut c_ushort) -> c_long { + let params = rand48::params(); + let xsubi_mut: &mut [c_ushort; 3] = unsafe { slice::from_raw_parts_mut(xsubi, 3) } + .try_into() + .unwrap(); + let new_xsubi_value = params.step(xsubi_mut.into()); + *xsubi_mut = new_xsubi_value.into(); + new_xsubi_value.get_u31() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_memalign( + memptr: *mut *mut c_void, + alignment: size_t, + size: size_t, +) -> c_int { + const VOID_PTR_SIZE: usize = mem::size_of::<*mut c_void>(); + + if alignment.is_multiple_of(VOID_PTR_SIZE) && alignment.is_power_of_two() { + let ptr = unsafe { platform::alloc_align(size, alignment) }; + unsafe { *memptr = ptr }; + if ptr.is_null() { ENOMEM } else { 0 } + } else { + unsafe { *memptr = ptr::null_mut() }; + EINVAL + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { + #[cfg(target_os = "redox")] + let r = unsafe { open((b"/scheme/pty\0" as *const u8).cast(), O_CREAT) }; + + #[cfg(target_os = "linux")] + let r = unsafe { open((b"/dev/ptmx\0" as *const u8).cast(), flags) }; + + if r < 0 && platform::ERRNO.get() == ENOSPC { + platform::ERRNO.set(EAGAIN); + } + + r +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ptsname(fd: c_int) -> *mut c_char { + const PTS_BUFFER_LEN: usize = 9 + mem::size_of::() * 3 + 1; + static mut PTS_BUFFER: [c_char; PTS_BUFFER_LEN] = [0; PTS_BUFFER_LEN]; + if unsafe { ptsname_r(fd, (&raw mut PTS_BUFFER).cast(), PTS_BUFFER_LEN) } != 0 { + ptr::null_mut() + } else { + (&raw mut PTS_BUFFER).cast() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + if buf.is_null() { + platform::ERRNO.set(EINVAL); + EINVAL + } else { + unsafe { __ptsname_r(fd, buf, buflen) } + } +} + +#[cfg(target_os = "redox")] +#[inline(always)] +unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + let tty_ptr = unsafe { unistd::ttyname(fd) }; + + if !tty_ptr.is_null() { + if let Ok(name) = unsafe { CStr::from_ptr(tty_ptr) }.to_str() { + let len = name.len(); + if len > buflen { + platform::ERRNO.set(ERANGE); + return ERANGE; + } else { + // we have checked the string will fit in the buffer + // so can use strcpy safely + let s = name.as_ptr().cast(); + unsafe { + ptr::copy_nonoverlapping(s, buf, len); + } + return 0; + } + } + } + platform::ERRNO.get() +} + +#[cfg(target_os = "linux")] +#[inline(always)] +unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + let mut pty = 0; + let err = platform::ERRNO.get(); + + if unsafe { ioctl(fd, TIOCGPTN, ptr::from_mut(&mut pty).cast::()) } == 0 { + let name = format!("/dev/pts/{}", pty); + let len = name.len(); + if len > buflen { + platform::ERRNO.set(ERANGE); + ERANGE + } else { + // we have checked the string will fit in the buffer + // so can use strcpy safely + let s = name.as_ptr().cast(); + unsafe { ptr::copy_nonoverlapping(s, buf, len) }; + platform::ERRNO.set(err); + 0 + } + } else { + platform::ERRNO.get() + } +} + +unsafe fn put_new_env(insert: *mut c_char) { + // XXX: Another problem is that `environ` can be set to any pointer, which means there is a + // chance of a memory leak. But we can check if it was the same as before, like musl does. + if unsafe { platform::environ } == unsafe { platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() } { + { + let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; + *our_environ.last_mut().unwrap() = insert; + our_environ.push(core::ptr::null_mut()); + } + // Likely a no-op but is needed due to Stacked Borrows. + unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; + } else { + { + let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; + our_environ.clear(); + our_environ.extend(platform::environ_iter()); + our_environ.push(insert); + our_environ.push(core::ptr::null_mut()); + } + unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int { + assert_ne!(insert, ptr::null_mut(), "putenv(NULL)"); + if let Some((i, _)) = unsafe { find_env(insert) } { + // XXX: The POSIX manual states that environment variables can be *set* via the `environ` + // global variable. While we can check if a pointer belongs to our allocator, or check + // `environ` against a vector which we control, it is likely not worth the effort. + unsafe { platform::environ.add(i).write(insert) }; + } else { + unsafe { put_new_env(insert) }; + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn qsort( + base: *mut c_void, + nel: size_t, + width: size_t, + compar: Option c_int>, +) { + if let Some(comp) = compar { + // XXX: check width too? not specified + if nel > 0 { + // XXX: maybe try to do mergesort/timsort first and fallback to introsort if memory + // allocation fails? not sure what is ideal + unsafe { sort::introsort(base.cast::(), nel, width, comp) }; + } + } +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn qsort_r( + base: *mut c_void, + nel: size_t, + width: size_t, + compar: Option c_int>, + arg: *mut c_void, +) { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn quick_exit(status: c_int) -> ! { + for i in (0..unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }.len()).rev() { + if let Some(func) = unsafe { AT_QUICK_EXIT_FUNCS.unsafe_ref() }[i] { + (func)(); + } + } + + Sys::exit(status); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rand() -> c_int { + unsafe { + match RNG { + Some(ref mut rng) => rng_sampler().sample(rng), + None => { + let mut rng = XorShiftRng::from_seed([1; 16]); + let ret = rng_sampler().sample(&mut rng); + RNG = Some(rng); + ret + } + } + } +} + +/// See . +/// +/// # Deprecation +/// The `rand_r()` function was marked as obsolescent in the Open Group Base +/// Specifications Issue 7, and the function was removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int { + if seed.is_null() { + errno::EINVAL + } else { + // set the type explicitly so this will fail if the array size for XorShiftRng changes + let seed_arr: [u8; 16] = unsafe { mem::transmute([*seed; 16 / mem::size_of::()]) }; + + let mut rng = XorShiftRng::from_seed(seed_arr); + let ret = rng_sampler().sample(&mut rng); + + unsafe { *seed = ret as _ }; + + ret + } +} + +/// See . +// Ported from musl +#[unsafe(no_mangle)] +pub unsafe extern "C" fn random() -> c_long { + let mut random_state = random::state_lock(); + + let k: u32; + + unsafe { random_state.ensure_x_ptr_init() }; + + if random_state.n == 0 { + let x_old = u32::from_ne_bytes(unsafe { *random_state.x_ptr }); + let x_new = random::lcg31_step(x_old); + unsafe { *random_state.x_ptr = x_new.to_ne_bytes() }; + k = x_new; + } else { + // The non-u32-aligned way of saying x[i] += x[j]... + let x_i_old = + u32::from_ne_bytes(unsafe { *random_state.x_ptr.add(usize::from(random_state.i)) }); + let x_j = + u32::from_ne_bytes(unsafe { *random_state.x_ptr.add(usize::from(random_state.j)) }); + let x_i_new = x_i_old.wrapping_add(x_j); + unsafe { *random_state.x_ptr.add(usize::from(random_state.i)) = x_i_new.to_ne_bytes() }; + + k = x_i_new >> 1; + + random_state.i += 1; + if random_state.i == random_state.n { + random_state.i = 0; + } + + random_state.j += 1; + if random_state.j == random_state.n { + random_state.j = 0; + } + } + + /* Both branches of this function result in a "u31", which will + * always fit in a c_long. */ + #[cfg(not(target_arch = "x86"))] + return c_long::from(k); + #[cfg(target_arch = "x86")] // c_long on x86 is i32 so not infallible cast + c_long::try_from(k).unwrap() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void { + let new_ptr = unsafe { platform::realloc(ptr, size) }; + if new_ptr.is_null() { + platform::ERRNO.set(ENOMEM); + } + new_ptr +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn reallocarray(ptr: *mut c_void, m: size_t, n: size_t) -> *mut c_void { + //Handle possible integer overflow in size calculation + match m.checked_mul(n) { + Some(size) => unsafe { realloc(ptr, size) }, + None => { + // For overflowing multiplication, we have to set errno here + platform::ERRNO.set(ENOMEM); + ptr::null_mut() + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char) -> *mut c_char { + let ptr = if resolved.is_null() { + (unsafe { malloc(limits::PATH_MAX) }).cast::() + } else { + resolved + }; + + let out = unsafe { slice::from_raw_parts_mut(ptr.cast::(), limits::PATH_MAX) }; + { + let file = match File::open(unsafe { CStr::from_ptr(pathname) }, O_PATH | O_CLOEXEC) { + Ok(file) => file, + Err(_) => return ptr::null_mut(), + }; + + let len = out.len(); + // TODO: better error handling + let read = Sys::fpath(*file, &mut out[..len - 1]) + .map(|read| read as ssize_t) + .or_minus_one_errno(); + if read < 0 { + return ptr::null_mut(); + } + out[read as usize] = 0; + } + + ptr +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char { + // Redox does not support setuid/setgid binaries, so there is no + // elevated-privilege case to guard against. Always delegate to getenv. + // If setuid support is ever added, this must check real vs effective + // uid/gid and return NULL when they differ. + unsafe { getenv(name) } +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `seed16v` is convertible to a `&[c_ushort; 3]`. +/// Additionally, the caller must ensure that the function has exclusive access +/// to the static buffer it returns; this includes avoiding simultaneous calls +/// to this function. +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn seed48(seed16v: *mut c_ushort) -> *mut c_ushort { + static RETURN_BUFFER: RawCell<[c_ushort; 3]> = RawCell::new([0; 3]); + + let mut params = rand48::params_mut(); + let mut xsubi = rand48::xsubi_lock(); + + // SAFETY: the caller is required to ensure seed16v is convertible to + // &[c_ushort; 3]. + let seed16v_ref: &[c_ushort; 3] = unsafe { slice::from_raw_parts(seed16v, 3) } + .try_into() + .unwrap(); + + // SAFETY: the caller is required to ensure exclusive access to + // RETURN_BUFFER. + let return_buffer_mut = unsafe { RETURN_BUFFER.unsafe_mut() }; + *return_buffer_mut = (*xsubi).into(); + *xsubi = seed16v_ref.into(); + params.reset(); + RETURN_BUFFER.as_mut_ptr().cast() +} + +unsafe fn copy_kv( + existing: *mut c_char, + key: *const c_char, + value: *const c_char, + key_len: usize, + value_len: usize, +) { + unsafe { core::ptr::copy_nonoverlapping(key, existing, key_len) }; + unsafe { core::ptr::write(existing.add(key_len), b'=' as c_char) }; + unsafe { core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len) }; + unsafe { core::ptr::write(existing.add(key_len + 1 + value_len), 0) }; +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setenv( + key: *const c_char, + value: *const c_char, + overwrite: c_int, +) -> c_int { + let key_len = unsafe { strlen(key) }; + let value_len = unsafe { strlen(value) }; + + if let Some((i, existing)) = unsafe { find_env(key) } { + if overwrite == 0 { + return 0; + } + + let existing_len = unsafe { strlen(existing) }; + + if existing_len >= value_len { + // Reuse existing element's allocation + unsafe { core::ptr::copy_nonoverlapping(value, existing, value_len) }; + //TODO: fill to end with zeroes + unsafe { core::ptr::write(existing.add(value_len), 0) }; + } else { + // Reuse platform::environ slot, but allocate a new pointer. + let ptr = unsafe { platform::alloc(key_len as usize + 1 + value_len as usize + 1) } + .cast::(); + unsafe { copy_kv(ptr, key, value, key_len, value_len) }; + unsafe { platform::environ.add(i).write(ptr) }; + } + } else { + // Expand platform::environ and allocate a new pointer. + let ptr = unsafe { platform::alloc(key_len as usize + 1 + value_len as usize + 1) } + .cast::(); + unsafe { copy_kv(ptr, key, value, key_len, value_len) }; + unsafe { put_new_env(ptr) }; + } + + //platform::free(platform::inner_environ[index] as *mut c_void); + + 0 +} + +/// See . +/// +/// # Deprecation +/// The `setkey()` function was marked as obsolescent in the Open Group Base +/// Specifications Issue 8. +#[deprecated] +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn setkey(key: *const c_char) { + unimplemented!(); +} + +/// See . +// Ported from musl. The state parameter is no longer const in newer versions of POSIX. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char { + let mut random_state = random::state_lock(); + + let old_state = unsafe { random_state.save() }; + unsafe { random_state.load(state.cast::<_>()) }; + + old_state.cast::<_>() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn srand(seed: c_uint) { + unsafe { RNG = Some(XorShiftRng::from_seed([seed as u8; 16])) }; +} + +/// See . +/// +/// # Panics +/// Panics if the function is unable to obtain a lock on the generator's global +/// state. +#[unsafe(no_mangle)] +pub extern "C" fn srand48(seedval: c_long) { + let mut params = rand48::params_mut(); + let mut xsubi = rand48::xsubi_lock(); + + params.reset(); + /* Set the high 32 bits of the 48-bit X_i value to the lower 32 bits + * of the input argument, and the lower 16 bits to 0x330e, as + * specified in POSIX. */ + *xsubi = ((u64::from(seedval as u32) << 16) | 0x330e) + .try_into() + .unwrap(); +} + +/// See . +// Ported from musl +#[unsafe(no_mangle)] +pub unsafe extern "C" fn srandom(seed: c_uint) { + let mut random_state = random::state_lock(); + + unsafe { random_state.seed(seed) }; +} + +pub fn is_positive(ch: c_char) -> Option<(bool, isize)> { + match ch { + 0 => None, + ch if ch == b'+' as c_char => Some((true, 1)), + ch if ch == b'-' as c_char => Some((false, 1)), + _ => Some((true, 0)), + } +} + +pub unsafe fn detect_base(s: *const c_char) -> Option<(c_int, isize)> { + let first = unsafe { *s } as u8; + match first { + 0 => None, + b'0' => { + let second = unsafe { *s.offset(1) } as u8; + if second == b'X' || second == b'x' { + Some((16, 2)) + } else if (b'0'..=b'7').contains(&second) { + Some((8, 1)) + } else { + // in this case, the prefix (0) is going to be the number + Some((8, 0)) + } + } + _ => Some((10, 0)), + } +} + +pub unsafe fn convert_octal(s: *const c_char) -> Option<(c_ulong, isize, bool)> { + if unsafe { *s } != 0 && unsafe { *s } == b'0' as c_char { + if let Some((val, idx, overflow)) = unsafe { convert_integer(s.offset(1), 8) } { + Some((val, idx + 1, overflow)) + } else { + // in case the prefix is not actually a prefix + Some((0, 1, false)) + } + } else { + None + } +} + +pub unsafe fn convert_hex(s: *const c_char) -> Option<(c_ulong, isize, bool)> { + if (unsafe { *s } != 0 && unsafe { *s } == b'0' as c_char) + && (unsafe { *s.offset(1) } != 0 + && (unsafe { *s.offset(1) } == b'x' as c_char + || unsafe { *s.offset(1) } == b'X' as c_char)) + { + unsafe { convert_integer(s.offset(2), 16) } + .map(|(val, idx, overflow)| (val, idx + 2, overflow)) + } else { + unsafe { convert_integer(s, 16) } + } +} + +pub unsafe fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, bool)> { + // -1 means the character is invalid + #[rustfmt::skip] + const LOOKUP_TABLE: [c_long; 256] = [ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + ]; + + let mut num: c_ulong = 0; + let mut idx = 0; + let mut overflowed = false; + + loop { + // `-1 as usize` is usize::MAX + // `-1 as u8 as usize` is u8::MAX + // It extends by the sign bit unless we cast it to unsigned first. + let val = LOOKUP_TABLE[unsafe { *s.offset(idx) } as u8 as usize]; + if val == -1 || val as c_int >= base { + break; + } else { + if let Some(res) = num + .checked_mul(base as c_ulong) + .and_then(|num| num.checked_add(val as c_ulong)) + { + num = res; + } else { + platform::ERRNO.set(ERANGE); + num = c_ulong::MAX; + overflowed = true; + } + + idx += 1; + } + } + + if idx > 0 { + Some((num, idx, overflowed)) + } else { + None + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { + strto_float_impl!(c_double, s, endptr) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float { + strto_float_impl!(c_float, s, endptr) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long { + strto_impl!(c_long, true, c_long::MAX, c_long::MIN, s, endptr, base) +} + +// TODO: strtold(), when long double is available + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtoll( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> c_longlong { + strto_impl!( + c_longlong, + true, + c_longlong::MAX, + c_longlong::MIN, + s, + endptr, + base + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtoul( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> c_ulong { + strto_impl!(c_ulong, false, c_ulong::MAX, c_ulong::MIN, s, endptr, base) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtoull( + s: *const c_char, + endptr: *mut *mut c_char, + base: c_int, +) -> c_ulonglong { + strto_impl!( + c_ulonglong, + false, + c_ulonglong::MAX, + c_ulonglong::MIN, + s, + endptr, + base + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn system(command: *const c_char) -> c_int { + // TODO: rusty error handling? + //TODO: share code with popen + + // handle shell detection on command == NULL + if command.is_null() { + let status = unsafe { system(c"exit 0".as_ptr().cast::()) }; + if status == 0 { + return 1; + } else { + return 0; + } + } + + let child_pid = unsafe { unistd::fork() }; + if child_pid == 0 { + let command_nonnull = command.cast::(); + + let shell = c"/bin/sh".as_ptr(); + + let args = [c"sh".as_ptr(), c"-c".as_ptr(), command_nonnull, ptr::null()]; + + unsafe { unistd::execv(shell.cast::(), args.as_ptr().cast::<*mut c_char>()) }; + + unsafe { exit(127) }; + + unreachable!(); + } else if child_pid > 0 { + let mut wstatus = 0; + if Sys::waitpid(child_pid, Some(Out::from_mut(&mut wstatus)), 0).or_minus_one_errno() == -1 + { + return -1; + } + + wstatus + } else { + -1 + } +} + +/// See . +/// +/// # Deprecation +/// The `ttyslot()` function was marked as obsolescent in the Open Group Base +/// Specifications Issue 5, and the function was removed in Issue 6. +#[deprecated] +// #[unsafe(no_mangle)] +pub extern "C" fn ttyslot() -> c_int { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn unlockpt(fildes: c_int) -> c_int { + let mut u: c_int = 0; + unsafe { + ioctl( + fildes, + TIOCSPTLCK, + ptr::from_mut::(&mut u).cast::(), + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int { + if let Some((i, _)) = unsafe { find_env(key) } { + if unsafe { platform::environ } + == unsafe { platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() } + { + // No need to worry about updating the pointer, this does not + // reallocate in any way. And the final null is already shifted back. + { + let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; + our_environ.remove(i); + } + + // My UB paranoia. + unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; + } else { + { + let our_environ = unsafe { &mut *platform::OUR_ENVIRON.as_mut_ptr() }; + our_environ.clear(); + our_environ.extend( + platform::environ_iter() + .enumerate() + .filter(|&(j, _)| j != i) + .map(|(_, v)| v), + ); + our_environ.push(core::ptr::null_mut()); + } + unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; + } + } + 0 +} + +/// See . +/// +/// # Deprecation +/// The `valloc()` function was marked as obsolescent in the Open Group Base +/// Specifications Issue 5, and the function was removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn valloc(size: size_t) -> *mut c_void { + /* sysconf(_SC_PAGESIZE) is a c_long and may in principle not + * convert correctly to a size_t. */ + match size_t::try_from(unsafe { sysconf(_SC_PAGESIZE) }) { + Ok(page_size) => { + /* valloc() is not supposed to be able to set errno to + * EINVAL, hence no call to memalign(). */ + let ptr = unsafe { platform::alloc_align(size, page_size) }; + if ptr.is_null() { + platform::ERRNO.set(ENOMEM); + } + ptr + } + Err(_) => { + // A corner case. No errno setting. + ptr::null_mut() + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstombs(s: *mut c_char, mut pwcs: *const wchar_t, n: size_t) -> size_t { + let mut state: mbstate_t = mbstate_t {}; + unsafe { wcsrtombs(s, &raw mut pwcs, n, &raw mut state) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wctomb(s: *mut c_char, wc: wchar_t) -> c_int { + let mut state: mbstate_t = mbstate_t {}; + let result: usize = unsafe { wcrtomb(s, wc, &raw mut state) }; + + if result == -1isize as usize { + return -1; + } + if result == -2isize as usize { + return -1; + } + + result as c_int +} diff --git a/src/header/stdlib/rand48.rs b/src/header/stdlib/rand48.rs new file mode 100644 index 0000000000..f8b3b4e35a --- /dev/null +++ b/src/header/stdlib/rand48.rs @@ -0,0 +1,141 @@ +//! Helper functions for pseudorandom number generation using LCG, see . + +use crate::{ + platform::types::{c_double, c_long, c_ushort}, + sync::{ + Mutex, MutexGuard, + rwlock::{self, RwLock}, + }, +}; + +/// A 48-bit integer, used for the 48-bit arithmetic in these functions. +#[derive(Clone, Copy, Default)] +#[repr(C)] +pub struct U48(u64); + +impl From<&[c_ushort; 3]> for U48 { + fn from(value: &[c_ushort; 3]) -> Self { + /* c_ushort is u16 so we get only the lower 16 bits of each + * element, as specified by POSIX. */ + Self(u64::from(value[0]) | (u64::from(value[1]) << 16) | (u64::from(value[2]) << 32)) + } +} + +impl From<&mut [c_ushort; 3]> for U48 { + fn from(value: &mut [c_ushort; 3]) -> Self { + Self::from(&*value) + } +} + +impl TryFrom for U48 { + type Error = u64; + + fn try_from(value: u64) -> Result { + if value < 0x1_0000_0000_0000 { + Ok(Self(value)) + } else { + Err(value) + } + } +} + +impl From for u64 { + fn from(value: U48) -> Self { + value.0 + } +} + +impl From for [c_ushort; 3] { + fn from(value: U48) -> Self { + [ + (value.0 as u16), + ((value.0 >> 16) as u16), + ((value.0 >> 32) as u16), + ] + } +} + +impl U48 { + /// Get a C `double` in the interval [0.0, 1.0) (for `drand48()` and `erand48()`). + pub fn get_f64(self) -> c_double { + /* We set the exponent to 0, and the 48-bit integer is copied into the high + * 48 of the 52 significand bits. The value then lies in the range + * [1.0, 2.0), from which we simply subtract 1.0. */ + f64::from_bits(0x3ff0_0000_0000_0000_u64 | (self.0 << 4)) - 1.0 + } + + /// Get the high 31 bits (for `lrand48()` and `nrand48()`). + pub fn get_u31(self) -> c_long { + (self.0 >> 17).try_into().unwrap() + } + + /// Get the high 32 bits, signed (for `mrand48()` and `jrand48()`). + pub fn get_i32(self) -> c_long { + // Cast via i32 to ensure we get the sign correct + ((self.0 >> 16) as i32).into() + } +} + +/// The a and c parameters of an LCG. +#[derive(Default)] +#[repr(C)] +pub struct Params { + pub a: U48, + pub c: u16, +} + +impl Params { + pub const fn new() -> Self { + // Default values as specified in POSIX + Params { + a: U48(0x5deece66d), + c: 0xb, + } + } + + pub fn reset(&mut self) { + *self = Self::new(); + } + + /// For use in lcong48(). + pub fn set(&mut self, a: &[c_ushort; 3], c: c_ushort) { + self.a = a.into(); + self.c = c; + } + + pub fn step(&self, xsubi: U48) -> U48 { + /* The recurrence relation of the linear congruential generator, + * X_(n+1) = (a * X_n + c) % m, + * with m = 2**48. The multiplication and addition can overflow a u64, but + * we just let it wrap since we take mod 2**48 anyway. */ + (u64::from(self.a) + .wrapping_mul(u64::from(xsubi)) + .wrapping_add(u64::from(self.c)) + & 0xffff_ffff_ffff) + .try_into() + .unwrap() + } +} + +static PARAMS: RwLock = RwLock::::new(Params::new()); + +/// Immediately get the global [`Params`] lock for reading, or panic if unsuccessful. +pub fn params<'a>() -> rwlock::ReadGuard<'a, Params> { + PARAMS + .try_read() + .expect("unable to acquire LCG parameter lock") +} + +/// Immediately get the global [`Params`] lock for writing, or panic if unsuccessful. +pub fn params_mut<'a>() -> rwlock::WriteGuard<'a, Params> { + PARAMS + .try_write() + .expect("unable to acquire LCG parameter lock") +} + +/// Immediately get the global X_i lock, or panic if unsuccessful. +pub fn xsubi_lock<'a>() -> MutexGuard<'a, U48> { + static XSUBI: Mutex = Mutex::::new(U48(0)); + + XSUBI.try_lock().expect("unable to acquire LCG X_i lock") +} diff --git a/src/header/stdlib/random.rs b/src/header/stdlib/random.rs new file mode 100644 index 0000000000..86aec6f3db --- /dev/null +++ b/src/header/stdlib/random.rs @@ -0,0 +1,118 @@ +//! Helper functions for random() and friends, see . +// Ported from musl's implementation (src/prng/random.c) + +use crate::{ + platform::types::c_uint, + sync::{Mutex, MutexGuard}, +}; +use core::{cell::SyncUnsafeCell, convert::TryFrom, ptr}; + +// This is the default buffer for X. Not guarded by mutex as a mutable pointer to this is available to the user through initstate() and setstate(). +#[rustfmt::skip] +pub static DEFAULT_X: SyncUnsafeCell<[u32; 32]> = SyncUnsafeCell::new([ + 0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646, + 0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78, + 0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff, + 0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc, + 0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09, + 0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74, + 0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115, + 0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657, +]); + +pub struct State { + pub x_ptr: *mut [u8; 4], + pub n: u8, + pub i: u8, + pub j: u8, +} + +// Necessary because raw pointers are not Send +unsafe impl Send for State {} + +impl State { + /// To be called in any function that may read from X_PTR + pub unsafe fn ensure_x_ptr_init(&mut self) { + if self.x_ptr.is_null() { + let x_u32_ptr: *mut u32 = unsafe { DEFAULT_X.get().cast::().add(1) }; + self.x_ptr = x_u32_ptr.cast::<[u8; 4]>(); + } + } + + pub unsafe fn save(&mut self) -> *mut [u8; 4] { + unsafe { self.ensure_x_ptr_init() }; + + let stash_value: u32 = + (u32::from(self.n) << 16) | (u32::from(self.i) << 8) | u32::from(self.j); + unsafe { *self.x_ptr.offset(-1) = stash_value.to_ne_bytes() }; + unsafe { self.x_ptr.offset(-1) } + } + + pub unsafe fn load(&mut self, state_ptr: *mut [u8; 4]) { + let stash_value = u32::from_ne_bytes(unsafe { *state_ptr }); + self.x_ptr = unsafe { state_ptr.offset(1) }; + + /* This calculation of n does not have a bit mask in the musl + * original, in principle resulting in a u16, but obtaining a value + * larger than 63 can probably be dismissed as pathological. */ + self.n = u8::try_from((stash_value >> 16) & 0xff).unwrap(); + + // i and j calculations are straight from musl + self.i = u8::try_from((stash_value >> 8) & 0xff).unwrap(); + self.j = u8::try_from(stash_value & 0xff).unwrap(); + } + + pub unsafe fn seed(&mut self, seed: c_uint) { + unsafe { self.ensure_x_ptr_init() }; + + let mut s = u64::from(seed); + + if self.n == 0 { + unsafe { *self.x_ptr = (s as u32).to_ne_bytes() }; + } else { + self.i = if self.n == 31 || self.n == 7 { 3 } else { 1 }; + + self.j = 0; + + for k in 0..usize::from(self.n) { + s = lcg64_step(s); + + // Conversion will always succeed (value is a 32-bit right- + // shift of a 64-bit integer). + unsafe { *self.x_ptr.add(k) = u32::try_from(s >> 32).unwrap().to_ne_bytes() }; + } + + // ensure X contains at least one odd number + unsafe { *self.x_ptr = (u32::from_ne_bytes(*self.x_ptr) | 1).to_ne_bytes() }; + } + } +} + +pub fn state_lock<'a>() -> MutexGuard<'a, State> { + static STATE: Mutex = Mutex::new(State { + /* As such, random() and related functions work on u32 values, but POSIX + * allows the user to supply a custom state data array as a `char *` + * with no requirements on alignment. Thus, we must assume the worst in + * terms of alignment and convert back and forth from [u8; 4]. + * + * Also, unlike in C, we can't take the address of the initializing + * array outside of a function. */ + x_ptr: ptr::null_mut(), + /* N needs to accommodate values up to 63, corresponding to the maximum + * state array size of 256 bytes. I and J must be able to accommodate + * values less than or equal to N. */ + n: 31, + i: 3, + j: 0, + }); + + STATE.try_lock().expect("unable to acquire PRNG lock") +} + +pub fn lcg31_step(x: u32) -> u32 { + 1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff +} + +pub fn lcg64_step(x: u64) -> u64 { + 6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64) +} diff --git a/src/header/stdlib/sort.rs b/src/header/stdlib/sort.rs new file mode 100644 index 0000000000..943707f0d0 --- /dev/null +++ b/src/header/stdlib/sort.rs @@ -0,0 +1,245 @@ +use crate::platform::types::{c_char, c_int, c_void, size_t}; + +pub unsafe fn introsort( + base: *mut c_char, + nel: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + /*TODO: introsort is much faster than insertion sort, but is currently broken + let maxdepth = 2 * log2(nel); + introsort_helper(base, nel, width, maxdepth, comp); + */ + unsafe { insertion_sort(base, nel, width, comp) }; +} + +// NOTE: if num is 0, the result should be considered undefined +fn log2(num: size_t) -> size_t { + const IS_32_BIT: bool = size_t::MAX as u32 as size_t == size_t::MAX; + + let max_bits = if IS_32_BIT { + 31 + } else { + // assuming we are 64-bit (this may or may not need to be updated in the future) + 63 + }; + + max_bits - num.to_le().leading_zeros() as size_t +} + +unsafe fn introsort_helper( + mut base: *mut c_char, + mut nel: size_t, + width: size_t, + mut maxdepth: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + const THRESHOLD: size_t = 8; + + // this loop is a trick to save stack space because TCO is not a thing in Rustland + // basically, we just change the arguments and loop rather than recursing for the second call + // to introsort_helper() + loop { + if nel < THRESHOLD { + unsafe { insertion_sort(base, nel, width, comp) }; + break; + } else if nel > 1 { + if maxdepth == 0 { + unsafe { heapsort(base, nel, width, comp) }; + break; + } else { + let (left, right) = unsafe { partition(base, nel, width, comp) }; + let right_base = unsafe { base.add((right + 1) * width) }; + let right_nel = nel - (right + 1); + maxdepth -= 1; + if left < nel - right { + unsafe { introsort_helper(base, left, width, maxdepth, comp) }; + base = right_base; + nel = right_nel; + } else { + unsafe { introsort_helper(right_base, right_nel, width, maxdepth, comp) }; + nel = left; + } + } + } + } +} + +unsafe fn insertion_sort( + base: *mut c_char, + nel: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + for i in 0..nel { + for j in (0..i).rev() { + let current = unsafe { base.add(j * width) }; + let prev = unsafe { base.add((j + 1) * width) }; + if comp(current as *const c_void, prev as *const c_void) > 0 { + unsafe { swap(current, prev, width) }; + } else { + break; + } + } + } +} + +unsafe fn heapsort( + base: *mut c_char, + nel: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + unsafe { heapify(base, nel, width, comp) }; + + let mut end = nel - 1; + while end > 0 { + let end_ptr = unsafe { base.add(end * width) }; + unsafe { swap(end_ptr, base, width) }; + end -= 1; + unsafe { heap_sift_down(base, 0, end, width, comp) }; + } +} + +unsafe fn heapify( + base: *mut c_char, + nel: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + // we start at the last parent in the heap (the parent of the last child) + let last_parent = (nel - 2) / 2; + + for start in (0..=last_parent).rev() { + unsafe { heap_sift_down(base, start, nel - 1, width, comp) }; + } +} + +unsafe fn heap_sift_down( + base: *mut c_char, + start: size_t, + end: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + // get the left child of the node at the given index + let left_child = |idx| 2 * idx + 1; + + let mut root = start; + + while left_child(root) <= end { + let child = left_child(root); + let mut swap_idx = root; + + let root_ptr = unsafe { base.add(root * width) }; + let mut swap_ptr = unsafe { base.add(swap_idx * width) }; + let first_child_ptr = unsafe { base.add(child * width) }; + let second_child_ptr = unsafe { base.add((child + 1) * width) }; + + if comp(swap_ptr as *const c_void, first_child_ptr as *const c_void) < 0 { + swap_idx = child; + swap_ptr = first_child_ptr; + } + if child < end && comp(swap_ptr as *const c_void, second_child_ptr as *const c_void) < 0 { + swap_idx = child + 1; + swap_ptr = second_child_ptr; + } + + if swap_idx == root { + break; + } else { + unsafe { swap(root_ptr, swap_ptr, width) }; + root = swap_idx; + } + } +} + +#[inline] +unsafe fn partition( + base: *mut c_char, + nel: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) -> (size_t, size_t) { + // calculate the median of the first, middle, and last elements and use it as the pivot + // to do fewer comparisons, also swap the elements into their correct positions + let mut pivot = unsafe { median_of_three(base, nel, width, comp) }; + + let mut i = 1; + let mut j = 1; + let mut n = nel - 2; + + // use this to deal with the Dutch national flag problem + while j <= n { + let i_ptr = unsafe { base.add(i * width) }; + let j_ptr = unsafe { base.add(j * width) }; + let n_ptr = unsafe { base.add(n * width) }; + let pivot_ptr = unsafe { base.add(pivot * width) }; + + let comparison = comp(j_ptr as *const c_void, pivot_ptr as *const c_void); + if comparison < 0 { + unsafe { swap(i_ptr, j_ptr, width) }; + if i == pivot { + pivot = j; + } + i += 1; + j += 1; + } else if comparison > 0 { + unsafe { swap(j_ptr, n_ptr, width) }; + if n == pivot { + pivot = j; + } + n -= 1; + } else { + j += 1; + } + } + + (i, n) +} + +unsafe fn median_of_three( + base: *mut c_char, + nel: size_t, + width: size_t, + comp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) -> size_t { + let pivot = nel / 2; + + let mid = unsafe { base.add(pivot * width) }; + let last = unsafe { base.add((nel - 1) * width) }; + if comp(mid as *const c_void, base as *const c_void) < 0 { + unsafe { swap(mid, base, width) }; + } + if comp(last as *const c_void, mid as *const c_void) < 0 { + unsafe { swap(mid, last, width) }; + if comp(mid as *const c_void, base as *const c_void) < 0 { + unsafe { swap(mid, base, width) }; + } + } + + pivot +} + +#[inline] +unsafe fn swap(mut ptr1: *mut c_char, mut ptr2: *mut c_char, mut width: size_t) { + use core::mem; + + const BUFSIZE: usize = 128; + + let mut buffer = mem::MaybeUninit::<[c_char; BUFSIZE]>::uninit(); + while width > 0 { + let copy_size = BUFSIZE.min(width); + let buf = buffer.as_mut_ptr().cast::(); + + unsafe { + buf.copy_from_nonoverlapping(ptr1, copy_size); + ptr1.copy_from_nonoverlapping(ptr2, copy_size); + ptr2.copy_from_nonoverlapping(buf, copy_size); + + ptr1 = ptr1.add(copy_size); + ptr2 = ptr2.add(copy_size); + } + width -= copy_size as size_t; + } +} diff --git a/src/header/string/cbindgen.toml b/src/header/string/cbindgen.toml new file mode 100644 index 0000000000..f5b8a9ad9a --- /dev/null +++ b/src/header/string/cbindgen.toml @@ -0,0 +1,25 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/string.h.html +# +# Spec quotations relating to includes: +# - "The header shall define NULL and size_t as described in ." +# - "The header shall define the locale_t type as described in ." +# - "Inclusion of the header may also make visible all symbols from ." +sys_includes = ["stddef.h"] +include_guard = "_RELIBC_STRING_H" +after_includes = """ +#include // for locale_t from locale.h + +// POSIX doesn't require importing strings.h but mesa needs it +#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) +#include +#endif +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true +# make size_t actually size_t instead of uintptr_t (which would require stdint.h) +usize_is_size_t = true + +[enum] +prefix_with_name = true diff --git a/src/header/string/mod.rs b/src/header/string/mod.rs new file mode 100644 index 0000000000..45621c2dcc --- /dev/null +++ b/src/header/string/mod.rs @@ -0,0 +1,670 @@ +//! `string.h` implementation. +//! +//! See . + +use core::{iter::once, mem, ptr, slice}; + +use cbitset::BitSet256; + +use crate::{ + header::{errno::*, signal}, + iter::{NulTerminated, NulTerminatedInclusive, SrcDstPtrIter}, + platform::{ + self, + types::{c_char, c_int, c_void, size_t}, + }, + raw_cell::RawCell, +}; + +use super::{bits_locale_t::locale_t, locale::THREAD_LOCALE}; + +/// See . +/// +/// # Safety +/// The caller must ensure that: +/// - `n` is not longer than the memory area pointed to by `s1`, and +/// - `n` is not longer than the memory area pointed to by `s2`. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memccpy( + s1: *mut c_void, + s2: *const c_void, + c: c_int, + n: size_t, +) -> *mut c_void { + let to = unsafe { memchr(s2, c, n) }; + let dist = if to.is_null() { + n + } else { + ((to as usize) - (s2 as usize)) + 1 + }; + unsafe { memcpy(s1, s2, dist) }; + if to.is_null() { + ptr::null_mut() + } else { + unsafe { s1.cast::().add(dist).cast::() } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memchr( + haystack: *const c_void, + needle: c_int, + len: size_t, +) -> *mut c_void { + let haystack = unsafe { slice::from_raw_parts(haystack.cast::(), len) }; + + match memchr::memchr(needle as u8, haystack) { + Some(index) => haystack[index..].as_ptr() as *mut c_void, + None => ptr::null_mut(), + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memcmp(s1: *const c_void, s2: *const c_void, n: size_t) -> c_int { + let (div, rem) = (n / mem::size_of::(), n % mem::size_of::()); + let mut a = s1.cast::(); + let mut b = s2.cast::(); + for _ in 0..div { + // SAFETY: `s1` and `s2` are `*const c_void`, which only guarantees byte + // alignment. Hence `a` and `b` may be unaligned. + if unsafe { a.read_unaligned() } != unsafe { b.read_unaligned() } { + for i in 0..mem::size_of::() { + let c = unsafe { *(a.cast::()).add(i) }; + let d = unsafe { *(b.cast::()).add(i) }; + if c != d { + return c_int::from(c) - c_int::from(d); + } + } + unreachable!() + } + a = unsafe { a.offset(1) }; + b = unsafe { b.offset(1) }; + } + + let mut a = a.cast::(); + let mut b = b.cast::(); + for _ in 0..rem { + if unsafe { *a } != unsafe { *b } { + return c_int::from(unsafe { *a }) - c_int::from(unsafe { *b }); + } + a = unsafe { a.offset(1) }; + b = unsafe { b.offset(1) }; + } + 0 +} + +/// See . +/// +/// # Safety +/// The caller must ensure that *either*: +/// - `n` is 0, *or* +/// - `s1` is convertible to a `&mut [MaybeUninit]` with length `n`, +/// and +/// - `s2` is convertible to a `&[MaybeUninit]` with length `n`. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memcpy(s1: *mut c_void, s2: *const c_void, n: size_t) -> *mut c_void { + for i in 0..n { + unsafe { *s1.cast::().add(i) = *s2.cast::().add(i) }; + } + s1 +} + +/// See . +/// +/// # Safety +/// The caller must ensure that: +/// - `haystack` is convertible to a `&[u8]` with length `haystacklen`, and +/// - `needle` is convertible to a `&[u8]` with length `needlelen`. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memmem( + haystack: *const c_void, + haystacklen: size_t, + needle: *const c_void, + needlelen: size_t, +) -> *mut c_void { + match needlelen { + // Required to satisfy spec (would otherwise cause .windows() to panic) + 0 => haystack, + _ => { + // SAFETY: the caller is required to ensure that the provided + // pointers are valid. + let haystack_slice = + unsafe { slice::from_raw_parts(haystack.cast::(), haystacklen) }; + let needle_slice = unsafe { slice::from_raw_parts(needle.cast::(), needlelen) }; + + // At this point, .windows() will receive a nonzero `needlelen` and + // thus not panic. + match haystack_slice + .windows(needlelen) + .find(|&haystack_window| haystack_window == needle_slice) + { + Some(match_slice) => match_slice.as_ptr().cast(), + None => ptr::null(), + } + } + } + .cast_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memmove(s1: *mut c_void, s2: *const c_void, n: size_t) -> *mut c_void { + if s2 < s1.cast_const() { + // copy from end + let mut i = n; + while i != 0 { + i -= 1; + unsafe { *s1.cast::().add(i) = *s2.cast::().add(i) }; + } + } else { + // copy from beginning + let mut i = 0; + while i < n { + unsafe { *s1.cast::().add(i) = *s2.cast::().add(i) }; + i += 1; + } + } + s1 +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memrchr( + haystack: *const c_void, + needle: c_int, + len: size_t, +) -> *mut c_void { + let haystack = unsafe { slice::from_raw_parts(haystack.cast::(), len) }; + + match memchr::memrchr(needle as u8, haystack) { + Some(index) => haystack[index..].as_ptr() as *mut c_void, + None => ptr::null_mut(), + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn memset(s: *mut c_void, c: c_int, n: size_t) -> *mut c_void { + for i in 0..n { + unsafe { *s.cast::().add(i) = c as u8 }; + } + s +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn stpcpy(mut s1: *mut c_char, mut s2: *const c_char) -> *mut c_char { + loop { + unsafe { *s1 = *s2 }; + + if unsafe { *s1 } == 0 { + break; + } + + s1 = unsafe { s1.add(1) }; + s2 = unsafe { s2.add(1) }; + } + + s1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn stpncpy( + mut s1: *mut c_char, + mut s2: *const c_char, + mut n: size_t, +) -> *mut c_char { + while n > 0 { + unsafe { *s1 = *s2 }; + + if unsafe { *s1 } == 0 { + break; + } + + n -= 1; + s1 = unsafe { s1.add(1) }; + s2 = unsafe { s2.add(1) }; + } + + unsafe { memset(s1.cast(), 0, n) }; + + s1 +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcasestr(haystack: *const c_char, needle: *const c_char) -> *mut c_char { + unsafe { inner_strstr(haystack, needle, !32) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcat(s1: *mut c_char, s2: *const c_char) -> *mut c_char { + unsafe { strncat(s1, s2, usize::MAX) } +} + +/// See . +/// +/// # Safety +/// The caller is required to ensure that `s` is a valid pointer to a buffer +/// containing at least one nul value. The pointed-to buffer must not be +/// modified for the duration of the call. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strchr(s: *const c_char, c: c_int) -> *mut c_char { + let c_as_c_char = c as c_char; + + // We iterate over non-mut references and thus need to coerce the + // resulting reference via a *const pointer before we can get our *mut. + // SAFETY: the caller is required to ensure that s points to a valid + // nul-terminated buffer. + let ptr: *const c_char = + match unsafe { NulTerminatedInclusive::new(s) }.find(|&&sc| sc == c_as_c_char) { + Some(sc_ref) => sc_ref, + None => ptr::null(), + }; + ptr.cast_mut() +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strchrnul(s: *const c_char, c: c_int) -> *mut c_char { + let mut s = s.cast_mut(); + loop { + if unsafe { *s } == c as _ { + break; + } + if unsafe { *s } == 0 { + break; + } + s = unsafe { s.add(1) }; + } + s +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int { + unsafe { strncmp(s1, s2, usize::MAX) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcoll_l(s1: *const c_char, s2: *const c_char, _loc: locale_t) -> c_int { + // relibc has no locale stuff (yet) + unsafe { strcmp(s1, s2) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcoll(s1: *const c_char, s2: *const c_char) -> c_int { + unsafe { strcoll_l(s1, s2, THREAD_LOCALE as locale_t) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char { + let src_iter = unsafe { NulTerminated::new(src).unwrap() }; + let src_dest_iter = unsafe { SrcDstPtrIter::new(src_iter.chain(once(&0)), dst) }; + for (src_item, dst_item) in src_dest_iter { + dst_item.write(*src_item); + } + + dst +} + +pub unsafe fn inner_strspn(s1: *const c_char, s2: *const c_char, cmp: bool) -> size_t { + let mut s1 = s1.cast::(); + let mut s2 = s2.cast::(); + + // The below logic is effectively ripped from the musl implementation. It + // works by placing each byte as it's own bit in an array of numbers. Each + // number can hold up to 8 * mem::size_of::() bits. We need 256 bits + // in total, to fit one byte. + + let mut set = BitSet256::new(); + + while unsafe { *s2 } != 0 { + set.insert(unsafe { *s2 } as usize); + s2 = unsafe { s2.offset(1) }; + } + + let mut i = 0; + while unsafe { *s1 } != 0 { + if set.contains(unsafe { *s1 } as usize) != cmp { + break; + } + i += 1; + s1 = unsafe { s1.offset(1) }; + } + i +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcspn(s1: *const c_char, s2: *const c_char) -> size_t { + unsafe { inner_strspn(s1, s2, false) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strdup(s1: *const c_char) -> *mut c_char { + unsafe { strndup(s1, usize::MAX) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strerror_l(errnum: c_int, _loc: locale_t) -> *mut c_char { + use core::fmt::Write; + + static strerror_buf: RawCell<[u8; STRERROR_MAX]> = RawCell::new([0; STRERROR_MAX]); + let mut w = platform::StringWriter(unsafe { strerror_buf.unsafe_mut().as_mut_ptr() }, unsafe { + strerror_buf.unsafe_ref().len() + }); + + let _ = match STR_ERROR.get(errnum as usize) { + Some(e) => w.write_str(e), + None => w.write_fmt(format_args!("Unknown error {}", errnum)), + }; + + (unsafe { strerror_buf.unsafe_mut().as_mut_ptr() }).cast::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char { + unsafe { strerror_l(errnum, THREAD_LOCALE as locale_t) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + let msg = unsafe { strerror(errnum) }; + let len = unsafe { strlen(msg) }; + + if len >= buflen { + if buflen != 0 { + unsafe { memcpy(buf.cast::(), msg as *const c_void, buflen - 1) }; + unsafe { *buf.add(buflen - 1) = 0 }; + } + return ERANGE as c_int; + } + unsafe { memcpy(buf.cast::(), msg as *const c_void, len + 1) }; + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strlcat(dst: *mut c_char, src: *const c_char, dstsize: size_t) -> size_t { + let dst_len = unsafe { strnlen(dst, dstsize) }; + let d = unsafe { dst.add(dst_len) }; + let src_len = unsafe { strlcpy(d, src, dstsize - dst_len) }; + src_len + if dst_len > dstsize { dstsize } else { dst_len } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strsep(str_: *mut *mut c_char, sep: *const c_char) -> *mut c_char { + let s = unsafe { *str_ }; + if s.is_null() { + return ptr::null_mut(); + } + let mut end = unsafe { s.add(strcspn(s, sep)) }; + if unsafe { *end } != 0 { + unsafe { *end = 0 }; + end = unsafe { end.add(1) }; + } else { + end = ptr::null_mut(); + } + unsafe { *str_ = end }; + s +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strlcpy(dst: *mut c_char, src: *const c_char, dstsize: size_t) -> size_t { + let mut i = 0; + + if dstsize != 0 { + while unsafe { *src.add(i) } != 0 && i < dstsize - 1 { + unsafe { + *dst.add(i) = *src.add(i); + } + i += 1; + } + unsafe { + *dst.add(i) = 0; + } + } + + while unsafe { *src.add(i) } != 0 { + i += 1; + } + + i as size_t +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strlen(s: *const c_char) -> size_t { + unsafe { NulTerminated::new(s) } + .map(|s| s.count()) + .unwrap_or(0) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strncat(s1: *mut c_char, s2: *const c_char, n: size_t) -> *mut c_char { + let len = unsafe { strlen(s1.cast()) }; + let mut i = 0; + while i < n { + let b = unsafe { *s2.add(i) }; + if b == 0 { + break; + } + + unsafe { *s1.add(len + i) = b }; + i += 1; + } + unsafe { *s1.add(len + i) = 0 }; + + s1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strncmp(s1: *const c_char, s2: *const c_char, n: size_t) -> c_int { + for i in 0..n { + // These must be cast as u8 to have correct comparisons + let a = unsafe { *s1.add(i) } as u8; + let b = unsafe { *s2.add(i) } as u8; + if a != b || a == 0 { + return c_int::from(a) - c_int::from(b); + } + } + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strncpy(s1: *mut c_char, s2: *const c_char, n: size_t) -> *mut c_char { + unsafe { stpncpy(s1, s2, n) }; + s1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strndup(s1: *const c_char, size: size_t) -> *mut c_char { + let len = unsafe { strnlen(s1, size) }; + + // the "+ 1" is to account for the NUL byte + let buffer = unsafe { platform::alloc(len + 1) }.cast::(); + if buffer.is_null() { + platform::ERRNO.set(ENOMEM as c_int); + } else { + //memcpy(buffer, s1, len) + for i in 0..len { + unsafe { *buffer.add(i) = *s1.add(i) }; + } + unsafe { *buffer.add(len) = 0 }; + } + + buffer +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strnlen(s: *const c_char, size: size_t) -> size_t { + unsafe { NulTerminated::new(s).unwrap() }.take(size).count() +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strnlen_s(s: *const c_char, size: size_t) -> size_t { + if s.is_null() { + 0 + } else { + unsafe { strnlen(s, size) } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strpbrk(s1: *const c_char, s2: *const c_char) -> *mut c_char { + let p = unsafe { s1.add(strcspn(s1, s2)) }; + if unsafe { *p } != 0 { + p.cast_mut() + } else { + ptr::null_mut() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strrchr(s: *const c_char, c: c_int) -> *mut c_char { + let len = unsafe { strlen(s) } as isize; + let c = c as c_char; + let mut i = len - 1; + while i >= 0 { + if unsafe { *s.offset(i) } == c { + return unsafe { s.offset(i) }.cast_mut(); + } + i -= 1; + } + ptr::null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strsignal(sig: c_int) -> *mut c_char { + signal::SIGNAL_STRINGS + .get(sig as usize) + .unwrap_or(&signal::SIGNAL_STRINGS[0]) // Unknown signal message + .as_ptr() as *mut c_char +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strspn(s1: *const c_char, s2: *const c_char) -> size_t { + unsafe { inner_strspn(s1, s2, true) } +} + +unsafe fn inner_strstr( + mut haystack: *const c_char, + needle: *const c_char, + mask: c_char, +) -> *mut c_char { + while unsafe { *haystack } != 0 { + let mut i = 0; + loop { + if unsafe { *needle.offset(i) } == 0 { + // We reached the end of the needle, everything matches this far + return haystack.cast_mut(); + } + if unsafe { *haystack.offset(i) } & mask != unsafe { *needle.offset(i) } & mask { + break; + } + + i += 1; + } + + haystack = unsafe { haystack.offset(1) }; + } + ptr::null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strstr(haystack: *const c_char, needle: *const c_char) -> *mut c_char { + unsafe { inner_strstr(haystack, needle, !0) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtok(s1: *mut c_char, delimiter: *const c_char) -> *mut c_char { + static mut HAYSTACK: *mut c_char = ptr::null_mut(); + unsafe { strtok_r(s1, delimiter, &raw mut HAYSTACK) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strtok_r( + s: *mut c_char, + delimiter: *const c_char, + lasts: *mut *mut c_char, +) -> *mut c_char { + // musl returns null if both s and lasts are null, it sets s to lasts otherwise + let mut haystack = s; + if haystack.is_null() { + if (unsafe { *lasts }).is_null() { + return ptr::null_mut(); + } + haystack = unsafe { *lasts }; + } + + // Skip past any extra delimiter left over from previous call + haystack = unsafe { haystack.add(strspn(haystack, delimiter)) }; + if unsafe { *haystack } == 0 { + unsafe { *lasts = haystack }; + return ptr::null_mut(); + } + + // Build token by injecting null byte into delimiter + let token = haystack; + haystack = unsafe { strpbrk(token, delimiter) }; + if !haystack.is_null() { + unsafe { haystack.write(0) }; + haystack = unsafe { haystack.add(1) }; + unsafe { *lasts = haystack }; + } else { + unsafe { *lasts = token.add(strlen(token)) }; + } + + token +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strxfrm_l( + s1: *mut c_char, + s2: *const c_char, + n: size_t, + _loc: locale_t, +) -> size_t { + // relibc has no locale stuff (yet) + let len = unsafe { strlen(s2) }; + if len < n { + unsafe { strcpy(s1, s2) }; + } + len +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strxfrm(s1: *mut c_char, s2: *const c_char, n: size_t) -> size_t { + unsafe { strxfrm_l(s1, s2, n, THREAD_LOCALE as locale_t) } +} diff --git a/src/header/strings/cbindgen.toml b/src/header/strings/cbindgen.toml new file mode 100644 index 0000000000..4479f685d6 --- /dev/null +++ b/src/header/strings/cbindgen.toml @@ -0,0 +1,22 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/strings.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the locale_t type as described in ." +# - "The header shall define the size_t type as described in ." +# +# sys/types.h gets size_t from stddef.h (just include stddef.h here instead of importing all of sys/types.h) +# features.h required for deprecated annotations +sys_includes = ["features.h", "stddef.h"] +include_guard = "_RELIBC_STRINGS_H" +after_includes = """ +#include // for locale_t from locale.h +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true +# make size_t actually size_t instead of uintptr_t (which would require stdint.h) +usize_is_size_t = true + +[enum] +prefix_with_name = true diff --git a/src/header/strings/mod.rs b/src/header/strings/mod.rs new file mode 100644 index 0000000000..9f33d65f54 --- /dev/null +++ b/src/header/strings/mod.rs @@ -0,0 +1,157 @@ +//! `strings.h` implementation. +//! +//! See . + +use core::{ + arch, + iter::{once, zip}, + ptr, +}; + +use crate::{ + header::{ctype, string}, + iter::NulTerminated, + platform::types::{c_char, c_int, c_long, c_longlong, c_void, size_t}, +}; + +/// See . +/// +/// # Deprecation +/// The `bcmp()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn bcmp(first: *const c_void, second: *const c_void, n: size_t) -> c_int { + unsafe { string::memcmp(first, second, n) } +} + +/// See . +/// +/// # Deprecation +/// The `bcopy()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn bcopy(src: *const c_void, dst: *mut c_void, n: size_t) { + unsafe { + ptr::copy(src.cast::(), dst.cast::(), n); + } +} + +/// See . +/// +/// # Deprecation +/// The `bzero()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn bzero(dst: *mut c_void, n: size_t) { + unsafe { + ptr::write_bytes(dst.cast::(), 0, n); + } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn explicit_bzero(s: *mut c_void, n: size_t) { + for i in 0..n { + unsafe { + *s.cast::().add(i) = 0_u8; + } + } + unsafe { + arch::asm!(""); + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ffs(i: c_int) -> c_int { + if i == 0 { + return 0; + } + 1 + i.trailing_zeros() as c_int +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ffsl(i: c_long) -> c_int { + if i == 0 { + return 0; + } + 1 + i.trailing_zeros() as c_int +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ffsll(i: c_longlong) -> c_int { + if i == 0 { + return 0; + } + 1 + i.trailing_zeros() as c_int +} + +/// See . +/// +/// # Deprecation +/// The `index()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn index(s: *const c_char, c: c_int) -> *mut c_char { + unsafe { string::strchr(s, c) } +} + +/// See . +/// +/// # Deprecation +/// The `rindex()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rindex(s: *const c_char, c: c_int) -> *mut c_char { + unsafe { string::strrchr(s, c) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strcasecmp(s1: *const c_char, s2: *const c_char) -> c_int { + // SAFETY: the caller must ensure that s1 and s2 point to nul-terminated buffers. + let s1_iter = unsafe { NulTerminated::new(s1).unwrap() }.chain(once(&0)); + let s2_iter = unsafe { NulTerminated::new(s2).unwrap() }.chain(once(&0)); + + let zipped = zip(s1_iter, s2_iter); + inner_casecmp(zipped) +} + +// TODO: needs locale_t +// See . +// #[unsafe(no_mangle)] +/*pub extern "C" fn strcasecmp_l(s1: *const c_char, s2: *const c_char, locale: locale_t) -> c_int { + unimplemented!(); +}*/ + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strncasecmp(s1: *const c_char, s2: *const c_char, n: size_t) -> c_int { + // SAFETY: the caller must ensure that s1 and s2 point to nul-terminated buffers. + let s1_iter = unsafe { NulTerminated::new(s1).unwrap() }.chain(once(&0)); + let s2_iter = unsafe { NulTerminated::new(s2).unwrap() }.chain(once(&0)); + + let zipped = zip(s1_iter, s2_iter).take(n); + inner_casecmp(zipped) +} + +// TODO: needs locale_t +// See . +// #[unsafe(no_mangle)] +/*pub extern "C" fn strncasecmp_l(s1: *const c_char, s2: *const c_char, n: size_t, locale: locale_t) -> c_int { + unimplemented!(); +}*/ + +/// Given two zipped `&c_char` iterators, either find the first comparison != 0, or return 0. +fn inner_casecmp<'a>(iterator: impl Iterator) -> c_int { + let cmp_iter = iterator.map(|(&c1, &c2)| ctype::tolower(c1.into()) - ctype::tolower(c2.into())); + let mut skip_iter = cmp_iter.skip_while(|&cmp| cmp == 0); + skip_iter.next().unwrap_or(0) +} diff --git a/src/header/sys_auxv/cbindgen.toml b/src/header/sys_auxv/cbindgen.toml new file mode 100644 index 0000000000..44924e2e2c --- /dev/null +++ b/src/header/sys_auxv/cbindgen.toml @@ -0,0 +1,8 @@ +include_guard = "_SYS_AUXV_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_auxv/mod.rs b/src/header/sys_auxv/mod.rs new file mode 100644 index 0000000000..dbd26a8c75 --- /dev/null +++ b/src/header/sys_auxv/mod.rs @@ -0,0 +1,13 @@ +//! `sys/auxv.h` implementation. +//! +//! Non-POSIX, see . + +use crate::platform::types::c_ulong; + +pub use crate::platform::auxv_defs::*; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getauxval(_t: c_ulong) -> c_ulong { + 0 +} diff --git a/src/header/sys_epoll/cbindgen.toml b/src/header/sys_epoll/cbindgen.toml new file mode 100644 index 0000000000..64ce6c75b7 --- /dev/null +++ b/src/header/sys_epoll/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["signal.h"] +include_guard = "_SYS_EPOLL_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_epoll/linux.rs b/src/header/sys_epoll/linux.rs new file mode 100644 index 0000000000..89b5365017 --- /dev/null +++ b/src/header/sys_epoll/linux.rs @@ -0,0 +1,20 @@ +use crate::platform::types::{c_int, c_uint}; + +pub const EPOLL_CLOEXEC: c_int = 0x8_0000; + +pub const EPOLLIN: c_uint = 0x001; +pub const EPOLLPRI: c_uint = 0x002; +pub const EPOLLOUT: c_uint = 0x004; +pub const EPOLLERR: c_uint = 0x008; +pub const EPOLLHUP: c_uint = 0x010; +pub const EPOLLNVAL: c_uint = 0x020; +pub const EPOLLRDNORM: c_uint = 0x040; +pub const EPOLLRDBAND: c_uint = 0x080; +pub const EPOLLWRNORM: c_uint = 0x100; +pub const EPOLLWRBAND: c_uint = 0x200; +pub const EPOLLMSG: c_uint = 0x400; +pub const EPOLLRDHUP: c_uint = 0x2000; +pub const EPOLLEXCLUSIVE: c_uint = 1 << 28; +pub const EPOLLWAKEUP: c_uint = 1 << 29; +pub const EPOLLONESHOT: c_uint = 1 << 30; +pub const EPOLLET: c_uint = 1 << 31; diff --git a/src/header/sys_epoll/mod.rs b/src/header/sys_epoll/mod.rs new file mode 100644 index 0000000000..715d7b9d63 --- /dev/null +++ b/src/header/sys_epoll/mod.rs @@ -0,0 +1,136 @@ +//! `sys/epoll.h` implementation. +//! +//! Non-POSIX, see . + +use core::ptr; + +use crate::{ + error::ResultExt, + header::bits_sigset_t::sigset_t, + platform::{ + PalEpoll, Sys, + types::{c_int, c_void}, + }, +}; + +pub use self::sys::*; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +pub const EPOLL_CTL_ADD: c_int = 1; +pub const EPOLL_CTL_DEL: c_int = 2; +pub const EPOLL_CTL_MOD: c_int = 3; + +/// Non-POSIX, see . +#[repr(C)] +#[derive(Clone, Copy)] +pub union epoll_data { + pub ptr: *mut c_void, + pub fd: c_int, + pub u32: u32, + pub u64: u64, +} +impl Default for epoll_data { + fn default() -> Self { + Self { u64: 0 } + } +} + +/// Non-POSIX, see . +#[cfg(all(target_os = "redox", target_pointer_width = "64"))] +#[repr(C)] +#[derive(Clone, Copy, Default)] +// This will match in size with syscall::Event (24 bytes on 64-bit +// systems) on redox. The `Default` trait is here so we don't need to +// worry about the padding when using this type. +pub struct epoll_event { + pub events: u32, // 4 bytes + // 4 automatic alignment bytes + pub data: epoll_data, // 8 bytes + + pub _pad: u64, // 8 bytes +} + +/// Non-POSIX, see . +#[cfg(not(all(target_os = "redox", target_pointer_width = "64")))] +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct epoll_event { + pub events: u32, + pub data: epoll_data, +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub extern "C" fn epoll_create(_size: c_int) -> c_int { + epoll_create1(0) +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub extern "C" fn epoll_create1(flags: c_int) -> c_int { + trace_expr!( + Sys::epoll_create1(flags).or_minus_one_errno(), + "epoll_create1({:#x})", + flags + ) +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn epoll_ctl( + epfd: c_int, + op: c_int, + fd: c_int, + event: *mut epoll_event, +) -> c_int { + trace_expr!( + unsafe { Sys::epoll_ctl(epfd, op, fd, event) } + .map(|()| 0) + .or_minus_one_errno(), + "epoll_ctl({}, {}, {}, {:p})", + epfd, + op, + fd, + event + ) +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn epoll_wait( + epfd: c_int, + events: *mut epoll_event, + maxevents: c_int, + timeout: c_int, +) -> c_int { + unsafe { epoll_pwait(epfd, events, maxevents, timeout, ptr::null()) } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn epoll_pwait( + epfd: c_int, + events: *mut epoll_event, + maxevents: c_int, + timeout: c_int, + sigmask: *const sigset_t, +) -> c_int { + trace_expr!( + unsafe { Sys::epoll_pwait(epfd, events, maxevents, timeout, sigmask) } + .map(|e| e as c_int) + .or_minus_one_errno(), + "epoll_pwait({}, {:p}, {}, {}, {:p})", + epfd, + events, + maxevents, + timeout, + sigmask + ) +} diff --git a/src/header/sys_epoll/redox.rs b/src/header/sys_epoll/redox.rs new file mode 100644 index 0000000000..dfaf15fd6d --- /dev/null +++ b/src/header/sys_epoll/redox.rs @@ -0,0 +1,20 @@ +use crate::platform::types::{c_int, c_uint}; + +pub const EPOLL_CLOEXEC: c_int = 0x0100_0000; + +pub const EPOLLIN: c_uint = 0x001; +pub const EPOLLPRI: c_uint = 0x002; +pub const EPOLLOUT: c_uint = 0x004; +pub const EPOLLERR: c_uint = 0x008; +pub const EPOLLHUP: c_uint = 0x010; +pub const EPOLLNVAL: c_uint = 0x020; +pub const EPOLLRDNORM: c_uint = 0x040; +pub const EPOLLRDBAND: c_uint = 0x080; +pub const EPOLLWRNORM: c_uint = 0x100; +pub const EPOLLWRBAND: c_uint = 0x200; +pub const EPOLLMSG: c_uint = 0x400; +pub const EPOLLRDHUP: c_uint = 0x2000; +pub const EPOLLEXCLUSIVE: c_uint = 1 << 28; +pub const EPOLLWAKEUP: c_uint = 1 << 29; +pub const EPOLLONESHOT: c_uint = 1 << 30; +pub const EPOLLET: c_uint = 1 << 31; diff --git a/src/header/sys_eventfd/mod.rs b/src/header/sys_eventfd/mod.rs new file mode 100644 index 0000000000..5689a66590 --- /dev/null +++ b/src/header/sys_eventfd/mod.rs @@ -0,0 +1,8 @@ +//! `sys/eventfd.h` implementation — constants only. +//! libwayland provides its own eventfd() at the caller level. + +use crate::platform::types::c_int; + +pub const EFD_SEMAPHORE: c_int = 1; +pub const EFD_CLOEXEC: c_int = 0x80000; +pub const EFD_NONBLOCK: c_int = 0x800; diff --git a/src/header/sys_file/cbindgen.toml b/src/header/sys_file/cbindgen.toml new file mode 100644 index 0000000000..d5d26dfa5b --- /dev/null +++ b/src/header/sys_file/cbindgen.toml @@ -0,0 +1,8 @@ +include_guard = "_SYS_FILE_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_file/mod.rs b/src/header/sys_file/mod.rs new file mode 100644 index 0000000000..810540213d --- /dev/null +++ b/src/header/sys_file/mod.rs @@ -0,0 +1,23 @@ +//! `sys/file.h` implementation. +//! +//! Non-POSIX, see . + +use crate::{ + error::ResultExt, + platform::{Pal, Sys, types::c_int}, +}; + +pub const LOCK_SH: c_int = 1; +pub const LOCK_EX: c_int = 2; +pub const LOCK_NB: c_int = 4; +pub const LOCK_UN: c_int = 8; + +pub const L_SET: c_int = 0; +pub const L_INCR: c_int = 1; +pub const L_XTND: c_int = 2; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn flock(fd: c_int, operation: c_int) -> c_int { + Sys::flock(fd, operation).map(|()| 0).or_minus_one_errno() +} diff --git a/src/header/sys_ioctl/cbindgen.toml b/src/header/sys_ioctl/cbindgen.toml new file mode 100644 index 0000000000..64117641a7 --- /dev/null +++ b/src/header/sys_ioctl/cbindgen.toml @@ -0,0 +1,24 @@ +include_guard = "_SYS_IOCTL_H" +language = "C" +style = "Tag" +trailer = """ +// Shamelessly copy-pasted from musl + +#define _IOC(a,b,c,d) ( ((a)<<30) | ((b)<<8) | (c) | ((d)<<16) ) +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IO(a,b) _IOC(_IOC_NONE,(a),(b),0) +#define _IOW(a,b,c) _IOC(_IOC_WRITE,(a),(b),sizeof(c)) +#define _IOR(a,b,c) _IOC(_IOC_READ,(a),(b),sizeof(c)) +#define _IOWR(a,b,c) _IOC(_IOC_READ|_IOC_WRITE,(a),(b),sizeof(c)) +""" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export] +include = ["sgttyb", "winsize"] diff --git a/src/header/sys_ioctl/linux.rs b/src/header/sys_ioctl/linux.rs new file mode 100644 index 0000000000..62d5946c0a --- /dev/null +++ b/src/header/sys_ioctl/linux.rs @@ -0,0 +1,201 @@ +use crate::{ + error::ResultExt, + platform::{ + Sys, + types::{c_int, c_ulong, c_void}, + }, +}; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int { + // TODO: Somehow support varargs to syscall?? + unsafe { Sys::ioctl(fd, request, out).or_minus_one_errno() } +} + +pub const TCGETS: c_ulong = 0x5401; +pub const TCSETS: c_ulong = 0x5402; +pub const TCSETSW: c_ulong = 0x5403; +pub const TCSETSF: c_ulong = 0x5404; +pub const TCGETA: c_ulong = 0x5405; +pub const TCSETA: c_ulong = 0x5406; +pub const TCSETAW: c_ulong = 0x5407; +pub const TCSETAF: c_ulong = 0x5408; +pub const TCSBRK: c_ulong = 0x5409; +pub const TCXONC: c_ulong = 0x540A; +pub const TCFLSH: c_ulong = 0x540B; +pub const TIOCEXCL: c_ulong = 0x540C; +pub const TIOCNXCL: c_ulong = 0x540D; +pub const TIOCSCTTY: c_ulong = 0x540E; +pub const TIOCGPGRP: c_ulong = 0x540F; +pub const TIOCSPGRP: c_ulong = 0x5410; +pub const TIOCOUTQ: c_ulong = 0x5411; +pub const TIOCSTI: c_ulong = 0x5412; +pub const TIOCGWINSZ: c_ulong = 0x5413; +pub const TIOCSWINSZ: c_ulong = 0x5414; +pub const TIOCMGET: c_ulong = 0x5415; +pub const TIOCMBIS: c_ulong = 0x5416; +pub const TIOCMBIC: c_ulong = 0x5417; +pub const TIOCMSET: c_ulong = 0x5418; +pub const TIOCGSOFTCAR: c_ulong = 0x5419; +pub const TIOCSSOFTCAR: c_ulong = 0x541A; +pub const FIONREAD: c_ulong = 0x541B; +pub const TIOCINQ: c_ulong = FIONREAD; +pub const TIOCLINUX: c_ulong = 0x541C; +pub const TIOCCONS: c_ulong = 0x541D; +pub const TIOCGSERIAL: c_ulong = 0x541E; +pub const TIOCSSERIAL: c_ulong = 0x541F; +pub const TIOCPKT: c_ulong = 0x5420; +pub const FIONBIO: c_ulong = 0x5421; +pub const TIOCNOTTY: c_ulong = 0x5422; +pub const TIOCSETD: c_ulong = 0x5423; +pub const TIOCGETD: c_ulong = 0x5424; +pub const TCSBRKP: c_ulong = 0x5425; +pub const TIOCSBRK: c_ulong = 0x5427; +pub const TIOCCBRK: c_ulong = 0x5428; +pub const TIOCGSID: c_ulong = 0x5429; +pub const TIOCGRS485: c_ulong = 0x542E; +pub const TIOCSRS485: c_ulong = 0x542F; +pub const TIOCGPTN: c_ulong = 0x8004_5430; +pub const TIOCSPTLCK: c_ulong = 0x4004_5431; +pub const TIOCGDEV: c_ulong = 0x8004_5432; +pub const TCGETX: c_ulong = 0x5432; +pub const TCSETX: c_ulong = 0x5433; +pub const TCSETXF: c_ulong = 0x5434; +pub const TCSETXW: c_ulong = 0x5435; +pub const TIOCSIG: c_ulong = 0x4004_5436; +pub const TIOCVHANGUP: c_ulong = 0x5437; +pub const TIOCGPKT: c_ulong = 0x8004_5438; +pub const TIOCGPTLCK: c_ulong = 0x8004_5439; +pub const TIOCGEXCL: c_ulong = 0x8004_5440; +pub const TIOCGPTPEER: c_ulong = 0x5441; + +pub const FIONCLEX: c_ulong = 0x5450; +pub const FIOCLEX: c_ulong = 0x5451; +pub const FIOASYNC: c_ulong = 0x5452; +pub const TIOCSERCONFIG: c_ulong = 0x5453; +pub const TIOCSERGWILD: c_ulong = 0x5454; +pub const TIOCSERSWILD: c_ulong = 0x5455; +pub const TIOCGLCKTRMIOS: c_ulong = 0x5456; +pub const TIOCSLCKTRMIOS: c_ulong = 0x5457; +pub const TIOCSERGSTRUCT: c_ulong = 0x5458; +pub const TIOCSERGETLSR: c_ulong = 0x5459; +pub const TIOCSERGETMULTI: c_ulong = 0x545A; +pub const TIOCSERSETMULTI: c_ulong = 0x545B; + +pub const TIOCMIWAIT: c_ulong = 0x545C; +pub const TIOCGICOUNT: c_ulong = 0x545D; +pub const FIOQSIZE: c_ulong = 0x5460; + +pub const TIOCPKT_DATA: c_ulong = 0; +pub const TIOCPKT_FLUSHREAD: c_ulong = 1; +pub const TIOCPKT_FLUSHWRITE: c_ulong = 2; +pub const TIOCPKT_STOP: c_ulong = 4; +pub const TIOCPKT_START: c_ulong = 8; +pub const TIOCPKT_NOSTOP: c_ulong = 16; +pub const TIOCPKT_DOSTOP: c_ulong = 32; +pub const TIOCPKT_IOCTL: c_ulong = 64; + +pub const TIOCSER_TEMT: c_ulong = 0x01; + +pub const TIOCM_LE: c_ulong = 0x001; +pub const TIOCM_DTR: c_ulong = 0x002; +pub const TIOCM_RTS: c_ulong = 0x004; +pub const TIOCM_ST: c_ulong = 0x008; +pub const TIOCM_SR: c_ulong = 0x010; +pub const TIOCM_CTS: c_ulong = 0x020; +pub const TIOCM_CAR: c_ulong = 0x040; +pub const TIOCM_RNG: c_ulong = 0x080; +pub const TIOCM_DSR: c_ulong = 0x100; +pub const TIOCM_CD: c_ulong = TIOCM_CAR; +pub const TIOCM_RI: c_ulong = TIOCM_RNG; +pub const TIOCM_OUT1: c_ulong = 0x2000; +pub const TIOCM_OUT2: c_ulong = 0x4000; +pub const TIOCM_LOOP: c_ulong = 0x8000; + +pub const N_TTY: c_ulong = 0; +pub const N_SLIP: c_ulong = 1; +pub const N_MOUSE: c_ulong = 2; +pub const N_PPP: c_ulong = 3; +pub const N_STRIP: c_ulong = 4; +pub const N_AX25: c_ulong = 5; +pub const N_X25: c_ulong = 6; +pub const N_6PACK: c_ulong = 7; +pub const N_MASC: c_ulong = 8; +pub const N_R3964: c_ulong = 9; +pub const N_PROFIBUS_FDL: c_ulong = 10; +pub const N_IRDA: c_ulong = 11; +pub const N_SMSBLOCK: c_ulong = 12; +pub const N_HDLC: c_ulong = 13; +pub const N_SYNC_PPP: c_ulong = 14; +pub const N_HCI: c_ulong = 15; + +pub const FIOSETOWN: c_ulong = 0x8901; +pub const SIOCSPGRP: c_ulong = 0x8902; +pub const FIOGETOWN: c_ulong = 0x8903; +pub const SIOCGPGRP: c_ulong = 0x8904; +pub const SIOCATMARK: c_ulong = 0x8905; +pub const SIOCGSTAMP: c_ulong = 0x8906; +pub const SIOCGSTAMPNS: c_ulong = 0x8907; + +pub const SIOCADDRT: c_ulong = 0x890B; +pub const SIOCDELRT: c_ulong = 0x890C; +pub const SIOCRTMSG: c_ulong = 0x890D; + +pub const SIOCGIFNAME: c_ulong = 0x8910; +pub const SIOCSIFLINK: c_ulong = 0x8911; +pub const SIOCGIFCONF: c_ulong = 0x8912; +pub const SIOCGIFFLAGS: c_ulong = 0x8913; +pub const SIOCSIFFLAGS: c_ulong = 0x8914; +pub const SIOCGIFADDR: c_ulong = 0x8915; +pub const SIOCSIFADDR: c_ulong = 0x8916; +pub const SIOCGIFDSTADDR: c_ulong = 0x8917; +pub const SIOCSIFDSTADDR: c_ulong = 0x8918; +pub const SIOCGIFBRDADDR: c_ulong = 0x8919; +pub const SIOCSIFBRDADDR: c_ulong = 0x891a; +pub const SIOCGIFNETMASK: c_ulong = 0x891b; +pub const SIOCSIFNETMASK: c_ulong = 0x891c; +pub const SIOCGIFMETRIC: c_ulong = 0x891d; +pub const SIOCSIFMETRIC: c_ulong = 0x891e; +pub const SIOCGIFMEM: c_ulong = 0x891f; +pub const SIOCSIFMEM: c_ulong = 0x8920; +pub const SIOCGIFMTU: c_ulong = 0x8921; +pub const SIOCSIFMTU: c_ulong = 0x8922; +pub const SIOCSIFNAME: c_ulong = 0x8923; +pub const SIOCSIFHWADDR: c_ulong = 0x8924; +pub const SIOCGIFENCAP: c_ulong = 0x8925; +pub const SIOCSIFENCAP: c_ulong = 0x8926; +pub const SIOCGIFHWADDR: c_ulong = 0x8927; +pub const SIOCGIFSLAVE: c_ulong = 0x8929; +pub const SIOCSIFSLAVE: c_ulong = 0x8930; +pub const SIOCADDMULTI: c_ulong = 0x8931; +pub const SIOCDELMULTI: c_ulong = 0x8932; +pub const SIOCGIFINDEX: c_ulong = 0x8933; +pub const SIOGIFINDEX: c_ulong = SIOCGIFINDEX; +pub const SIOCSIFPFLAGS: c_ulong = 0x8934; +pub const SIOCGIFPFLAGS: c_ulong = 0x8935; +pub const SIOCDIFADDR: c_ulong = 0x8936; +pub const SIOCSIFHWBROADCAST: c_ulong = 0x8937; +pub const SIOCGIFCOUNT: c_ulong = 0x8938; + +pub const SIOCGIFBR: c_ulong = 0x8940; +pub const SIOCSIFBR: c_ulong = 0x8941; + +pub const SIOCGIFTXQLEN: c_ulong = 0x8942; +pub const SIOCSIFTXQLEN: c_ulong = 0x8943; + +pub const SIOCDARP: c_ulong = 0x8953; +pub const SIOCGARP: c_ulong = 0x8954; +pub const SIOCSARP: c_ulong = 0x8955; + +pub const SIOCDRARP: c_ulong = 0x8960; +pub const SIOCGRARP: c_ulong = 0x8961; +pub const SIOCSRARP: c_ulong = 0x8962; + +pub const SIOCGIFMAP: c_ulong = 0x8970; +pub const SIOCSIFMAP: c_ulong = 0x8971; + +pub const SIOCADDDLCI: c_ulong = 0x8980; +pub const SIOCDELDLCI: c_ulong = 0x8981; + +pub const SIOCDEVPRIVATE: c_ulong = 0x89F0; +pub const SIOCPROTOPRIVATE: c_ulong = 0x89E0; diff --git a/src/header/sys_ioctl/mod.rs b/src/header/sys_ioctl/mod.rs new file mode 100644 index 0000000000..2b80f7536f --- /dev/null +++ b/src/header/sys_ioctl/mod.rs @@ -0,0 +1,40 @@ +//! ioctl implementation for linux + +use crate::platform::types::{c_char, c_ushort}; + +// This is used from sgtty +#[repr(C)] +pub struct sgttyb { + sg_ispeed: c_char, + sg_ospeed: c_char, + sg_erase: c_char, + sg_kill: c_char, + sg_flags: c_ushort, +} + +#[repr(C)] +#[derive(Default)] +pub struct winsize { + ws_row: c_ushort, + ws_col: c_ushort, + ws_xpixel: c_ushort, + ws_ypixel: c_ushort, +} + +impl winsize { + pub fn get_row_col(&self) -> (c_ushort, c_ushort) { + (self.ws_row, self.ws_col) + } +} + +#[cfg(target_os = "linux")] +pub use self::linux::*; + +#[cfg(target_os = "linux")] +pub mod linux; + +#[cfg(target_os = "redox")] +pub use self::redox::*; + +#[cfg(target_os = "redox")] +pub mod redox; diff --git a/src/header/sys_ioctl/redox/drm.rs b/src/header/sys_ioctl/redox/drm.rs new file mode 100644 index 0000000000..0557178ee8 --- /dev/null +++ b/src/header/sys_ioctl/redox/drm.rs @@ -0,0 +1,135 @@ +use core::slice; + +use redox_ioctl::{IoctlData, drm::*}; + +use crate::{ + error::{Errno, Result}, + header::errno::EINVAL, + platform::types::c_int, +}; + +use super::IoctlBuffer; + +const DRM_FORMAT_ARGB8888: u32 = 0x34325241; // 'AR24' fourcc code, for ARGB8888 + +fn id_index(id: u32) -> u32 { + id & 0xFF +} + +fn conn_id(i: u32) -> u32 { + id_index(i) | (1 << 8) +} + +fn crtc_id(i: u32) -> u32 { + id_index(i) | (1 << 9) +} + +fn enc_id(i: u32) -> u32 { + id_index(i) | (1 << 10) +} + +fn fb_id(i: u32) -> u32 { + id_index(i) | (1 << 11) +} + +fn fb_handle_id(i: u32) -> u32 { + id_index(i) | (1 << 12) +} + +fn plane_id(i: u32) -> u32 { + id_index(i) | (1 << 13) +} + +unsafe fn copy_array(src: &[T], dst_ptr: *mut T, dst_len: usize) -> usize { + let dst = unsafe { slice::from_raw_parts_mut(dst_ptr, dst_len) }; + dst.copy_from_slice(&src[..src.len().min(dst_len)]); + src.len() +} + +struct Dev { + fd: c_int, +} + +impl Dev { + fn new(fd: c_int) -> Result { + //TODO: check display scheme using fpath? + Ok(Self { fd }) + } + + unsafe fn read_write_ioctl( + &self, + mut buf: IoctlBuffer, + func: u64, + ) -> Result { + let mut data = unsafe { buf.read::() }?; + let mut wire = unsafe { data.write() }; + let res = redox_rt::sys::sys_call_rw( + self.fd as usize, + &mut wire, + syscall::CallFlags::empty(), + &[func], + )?; + unsafe { data.read_from(&wire) }; + (unsafe { buf.write(data) })?; + Ok(res as c_int) + } + + unsafe fn write_ioctl(&self, buf: IoctlBuffer, func: u64) -> Result { + let data = unsafe { buf.read::() }?; + let wire = unsafe { data.write() }; + let res = redox_rt::sys::sys_call_wo( + self.fd as usize, + &wire, + syscall::CallFlags::empty(), + &[func], + )?; + Ok(res as c_int) + } +} + +pub(super) unsafe fn ioctl(fd: c_int, func: u8, buf: IoctlBuffer) -> Result { + let dev = Dev::new(fd)?; + match func { + 0x00 => unsafe { dev.read_write_ioctl::(buf, VERSION) }, + 0x0C => unsafe { dev.read_write_ioctl::(buf, GET_CAP) }, + 0x0D => unsafe { dev.write_ioctl::(buf, SET_CLIENT_CAP) }, + 0xA0 => unsafe { dev.read_write_ioctl::(buf, MODE_CARD_RES) }, + 0xA1 => unsafe { dev.read_write_ioctl::(buf, MODE_GET_CRTC) }, + 0xA2 => unsafe { dev.read_write_ioctl::(buf, MODE_SET_CRTC) }, + 0xA3 => unsafe { dev.write_ioctl::(buf, MODE_CURSOR) }, + 0xA6 => unsafe { dev.read_write_ioctl::(buf, MODE_GET_ENCODER) }, + 0xA7 => unsafe { dev.read_write_ioctl::(buf, MODE_GET_CONNECTOR) }, + 0xAA => unsafe { dev.read_write_ioctl::(buf, MODE_GET_PROPERTY) }, + 0xAB => unsafe { + dev.read_write_ioctl::(buf, MODE_SET_PROPERTY) + }, + 0xAC => unsafe { dev.read_write_ioctl::(buf, MODE_GET_PROP_BLOB) }, + 0xAD => unsafe { dev.read_write_ioctl::(buf, MODE_GET_FB) }, + 0xAE => unsafe { dev.read_write_ioctl::(buf, MODE_ADD_FB) }, + 0xAF => unsafe { dev.read_write_ioctl::(buf, MODE_RM_FB) }, + 0xB0 => unsafe { + dev.read_write_ioctl::(buf, MODE_PAGE_FLIP) + }, + 0xB1 => unsafe { dev.read_write_ioctl::(buf, MODE_DIRTYFB) }, + 0xB2 => unsafe { dev.read_write_ioctl::(buf, MODE_CREATE_DUMB) }, + 0xB3 => unsafe { dev.read_write_ioctl::(buf, MODE_MAP_DUMB) }, + 0xB4 => unsafe { dev.read_write_ioctl::(buf, MODE_DESTROY_DUMB) }, + 0xB5 => unsafe { dev.read_write_ioctl::(buf, MODE_GET_PLANE_RES) }, + 0xB6 => unsafe { dev.read_write_ioctl::(buf, MODE_GET_PLANE) }, + 0xB9 => unsafe { + dev.read_write_ioctl::(buf, MODE_OBJ_GET_PROPERTIES) + }, + 0xBB => unsafe { dev.write_ioctl::(buf, MODE_CURSOR2) }, + 0xCE => unsafe { dev.read_write_ioctl::(buf, MODE_GET_FB2) }, + _ => { + todo_skip!( + 0, + "unimplemented DRM ioctl({}, 0x{:02x}, {:?})", + fd, + func, + buf + ); + Err(Errno(EINVAL)) + } + } +} diff --git a/src/header/sys_ioctl/redox/mod.rs b/src/header/sys_ioctl/redox/mod.rs new file mode 100644 index 0000000000..8b3cf8fc23 --- /dev/null +++ b/src/header/sys_ioctl/redox/mod.rs @@ -0,0 +1,196 @@ +use core::{mem, ptr, slice}; +use redox_rt::proc::FdGuard; +use syscall; + +use crate::{ + error::{Errno, Result, ResultExt}, + header::{errno::EINVAL, fcntl, termios}, + platform::{ + Pal, Sys, + types::{c_int, c_ulong, c_ulonglong, c_void, pid_t}, + }, +}; + +use super::winsize; + +mod drm; + +pub const TCGETS: c_ulong = 0x5401; +pub const TCSETS: c_ulong = 0x5402; +pub const TCSETSW: c_ulong = 0x5403; +pub const TCSETSF: c_ulong = 0x5404; + +pub const TCSBRK: c_ulong = 0x5409; +pub const TCXONC: c_ulong = 0x540A; +pub const TCFLSH: c_ulong = 0x540B; + +pub const TIOCSCTTY: c_ulong = 0x540E; +pub const TIOCGPGRP: c_ulong = 0x540F; +pub const TIOCSPGRP: c_ulong = 0x5410; + +pub const TIOCGWINSZ: c_ulong = 0x5413; +pub const TIOCSWINSZ: c_ulong = 0x5414; + +//TODO: used by tcgetsid, not implemented yet on redox +pub const TIOCGSID: c_ulong = 0x5429; + +pub const FIONREAD: c_ulong = 0x541B; + +pub const FIONBIO: c_ulong = 0x5421; + +pub const TIOCSPTLCK: c_ulong = 0x4004_5431; +pub const TIOCGPTLCK: c_ulong = 0x8004_5439; + +pub const SIOCATMARK: c_ulong = 0x8905; + +// TODO: some of the structs passed as T have padding bytes, so casting to a byte slice is UB + +fn dup_read(fd: c_int, name: &str, t: &mut T) -> syscall::Result { + let dup = FdGuard::new(syscall::dup(fd as usize, name.as_bytes())?); + + let size = mem::size_of::(); + + let bytes = dup.read(unsafe { slice::from_raw_parts_mut(t as *mut T as *mut u8, size) })?; + + Ok(bytes / size) +} + +// FIXME: unsound +fn dup_write(fd: c_int, name: &str, t: &T) -> Result { + let dup = FdGuard::new(syscall::dup(fd as usize, name.as_bytes())?); + + let size = mem::size_of::(); + + let bytes = dup.write(unsafe { slice::from_raw_parts(t as *const T as *const u8, size) })?; + + Ok(bytes / size) +} + +#[derive(Debug)] +enum IoctlBuffer { + None, + Read(*mut c_void, usize), // read (write to userspace) + Write(*const c_void, usize), // write (read from userspace) + ReadWrite(*mut c_void, usize), +} + +impl IoctlBuffer { + unsafe fn read(&self) -> Result { + let (ptr, size) = match *self { + Self::Write(ptr, size) => (ptr, size), + Self::ReadWrite(ptr, size) => (ptr as *const c_void, size), + _ => { + return Err(Errno(EINVAL)); + } + }; + if size == mem::size_of::() { + let value = unsafe { ptr::read(ptr as *const T) }; + Ok(value) + } else { + Err(Errno(EINVAL)) + } + } + + unsafe fn write(&mut self, value: T) -> Result<()> { + let (ptr, size) = match *self { + Self::Read(ptr, size) | Self::ReadWrite(ptr, size) => (ptr, size), + _ => { + return Err(Errno(EINVAL)); + } + }; + if size == mem::size_of::() { + unsafe { ptr::write(ptr as *mut T, value) }; + Ok(()) + } else { + Err(Errno(EINVAL)) + } + } +} + +unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Result { + match request { + FIONBIO => { + let mut flags = Sys::fcntl(fd, fcntl::F_GETFL, 0)?; + flags = if unsafe { *(out as *mut c_int) } == 0 { + flags & !fcntl::O_NONBLOCK + } else { + flags | fcntl::O_NONBLOCK + }; + Sys::fcntl(fd, fcntl::F_SETFL, flags as c_ulonglong)?; + } + TCGETS => { + let termios = unsafe { &mut *(out as *mut termios::termios) }; + dup_read(fd, "termios", termios)?; + } + // TODO: give these different behaviors + TCSETS | TCSETSW | TCSETSF => { + let termios = unsafe { &*(out as *const termios::termios) }; + dup_write(fd, "termios", termios)?; + } + TCFLSH => { + let queue = out as c_int; + dup_write(fd, "flush", &queue)?; + } + TIOCSCTTY => { + todo_skip!(0, "ioctl TIOCSCTTY"); + } + TIOCGPGRP => { + let pgrp = unsafe { &mut *(out as *mut pid_t) }; + dup_read(fd, "pgrp", pgrp)?; + } + TIOCSPGRP => { + let pgrp = unsafe { &*(out as *const pid_t) }; + dup_write(fd, "pgrp", pgrp)?; + } + TIOCGWINSZ => { + let winsize = unsafe { &mut *(out as *mut winsize) }; + dup_read(fd, "winsize", winsize)?; + } + TIOCSWINSZ => { + let winsize = unsafe { &*(out as *const winsize) }; + dup_write(fd, "winsize", winsize)?; + } + TIOCGPTLCK => { + todo_skip!(0, "ioctl TIOCGPTLCK"); + } + TIOCSPTLCK => { + todo_skip!(0, "ioctl TIOCSPTLCK"); + } + TCSBRK => { + todo_skip!(0, "ioctl TCSBRK"); + } + TCXONC => { + todo_skip!(0, "ioctl TCXONC"); + } + SIOCATMARK => { + todo_skip!(0, "ioctl SIOCATMARK"); + } + _ => { + // See https://docs.kernel.org/userspace-api/ioctl/ioctl-decoding.html for details + let dir = (request >> 30) & 0b11; + let size = ((request >> 16) & 0x3FFF) as usize; + let name = (((request >> 8) & 0xFF) as u8) as char; + let func = (request & 0xFF) as u8; + match name { + 'd' => { + let buf = match dir { + 0b10 => IoctlBuffer::Read(out, size), + 0b01 => IoctlBuffer::Write(out, size), + 0b11 => IoctlBuffer::ReadWrite(out, size), + _ => IoctlBuffer::None, + }; + return unsafe { drm::ioctl(fd, func, buf) }; + } + _ => { + return Err(Errno(EINVAL)); + } + } + } + } + Ok(0) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int { + unsafe { ioctl_inner(fd, request, out) }.or_minus_one_errno() +} diff --git a/src/header/sys_mman/cbindgen.toml b/src/header/sys_mman/cbindgen.toml new file mode 100644 index 0000000000..63f6299c7e --- /dev/null +++ b/src/header/sys_mman/cbindgen.toml @@ -0,0 +1,21 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_mman.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the mode_t, off_t, and size_t types as described in ." +# - "The header shall define the following symbolic constants as described in " +# - "Inclusion of the header may make visible all symbols from the header." +sys_includes = ["sys/types.h", "fcntl.h"] +include_guard = "_RELIBC_SYS_MMAN_H" +after_includes = """ + +#define MAP_FAILED ((void *) -1) +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true +# make size_t actually size_t instead of uintptr_t (which would require stdint.h) +usize_is_size_t = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_mman/linux.rs b/src/header/sys_mman/linux.rs new file mode 100644 index 0000000000..5fc27ce4d7 --- /dev/null +++ b/src/header/sys_mman/linux.rs @@ -0,0 +1,17 @@ +use crate::platform::types::c_int; + +pub const PROT_READ: c_int = 0x0001; +pub const PROT_WRITE: c_int = 0x0002; +pub const PROT_EXEC: c_int = 0x0004; +pub const PROT_NONE: c_int = 0x0000; + +pub const MAP_FIXED: c_int = 0x0010; +pub const MAP_FIXED_NOREPLACE: c_int = 0x100000; +pub const MAP_POPULATE: c_int = 0x008000; +pub const MAP_HUGETLB: c_int = 0x40000; +pub const MAP_NORESERVE: c_int = 0x4000; + +pub const MADV_HUGEPAGE: c_int = 14; +pub const MADV_NOHUGEPAGE: c_int = 15; +pub const MADV_DONTDUMP: c_int = 16; +pub const MADV_DODUMP: c_int = 17; diff --git a/src/header/sys_mman/mod.rs b/src/header/sys_mman/mod.rs new file mode 100644 index 0000000000..0930c9660c --- /dev/null +++ b/src/header/sys_mman/mod.rs @@ -0,0 +1,195 @@ +//! `sys/mman.h` implementation. +//! +//! See . + +use crate::{ + c_str::{CStr, CString}, + error::{Errno, ResultExt}, + header::{fcntl, unistd}, + platform::{ + ERRNO, Pal, Sys, + types::{c_char, c_int, c_void, mode_t, off_t, size_t}, + }, +}; + +pub use self::sys::*; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +pub const MADV_NORMAL: c_int = 0; +pub const MADV_RANDOM: c_int = 1; +pub const MADV_SEQUENTIAL: c_int = 2; +pub const MADV_WILLNEED: c_int = 3; +pub const MADV_DONTNEED: c_int = 4; + +pub const MAP_SHARED: c_int = 0x0001; +pub const MAP_PRIVATE: c_int = 0x0002; +pub const MAP_TYPE: c_int = 0x000F; +pub const MAP_ANON: c_int = 0x0020; +pub const MAP_ANONYMOUS: c_int = MAP_ANON; +pub const MAP_STACK: c_int = 0x20000; +/// cbindgen:ignore +pub const MAP_FAILED: *mut c_void = usize::wrapping_neg(1) as *mut c_void; + +pub const MREMAP_MAYMOVE: c_int = 1; + +pub const MS_ASYNC: c_int = 0x0001; +pub const MS_INVALIDATE: c_int = 0x0002; +pub const MS_SYNC: c_int = 0x0004; + +pub const MCL_CURRENT: c_int = 1; +pub const MCL_FUTURE: c_int = 2; + +pub const POSIX_MADV_NORMAL: c_int = 0; +pub const POSIX_MADV_RANDOM: c_int = 1; +pub const POSIX_MADV_SEQUENTIAL: c_int = 2; +pub const POSIX_MADV_WILLNEED: c_int = 3; +pub const POSIX_MADV_WONTNEED: c_int = 4; + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn madvise(addr: *mut c_void, len: size_t, flags: c_int) -> c_int { + unsafe { Sys::madvise(addr, len, flags) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mlock(addr: *const c_void, len: size_t) -> c_int { + unsafe { Sys::mlock(addr, len) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mlockall(flags: c_int) -> c_int { + unsafe { Sys::mlockall(flags) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mmap( + addr: *mut c_void, + len: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, +) -> *mut c_void { + match unsafe { Sys::mmap(addr, len, prot, flags, fildes, off) } { + Ok(ptr) => ptr, + Err(Errno(errno)) => { + ERRNO.set(errno); + MAP_FAILED + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mprotect(addr: *mut c_void, len: size_t, prot: c_int) -> c_int { + unsafe { Sys::mprotect(addr, len, prot) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mremap( + old_address: *mut c_void, + old_size: size_t, + new_size: size_t, + flags: c_int, + mut __valist: ... +) -> *mut c_void { + let new_address = unsafe { __valist.arg::<*mut c_void>() }; + match unsafe { Sys::mremap(old_address, old_size, new_size, flags, new_address) } { + Ok(ptr) => ptr, + Err(Errno(errno)) => { + ERRNO.set(errno); + MAP_FAILED + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn msync(addr: *mut c_void, len: size_t, flags: c_int) -> c_int { + unsafe { Sys::msync(addr, len, flags) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn munlock(addr: *const c_void, len: size_t) -> c_int { + unsafe { Sys::munlock(addr, len) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn munlockall() -> c_int { + unsafe { Sys::munlockall() } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn munmap(addr: *mut c_void, len: size_t) -> c_int { + unsafe { Sys::munmap(addr, len) } + .map(|()| 0) + .or_minus_one_errno() +} + +#[cfg(target_os = "linux")] +static SHM_PATH: &[u8] = b"/dev/shm/"; + +#[cfg(target_os = "redox")] +static SHM_PATH: &[u8] = b"/scheme/shm/"; + +unsafe fn shm_path(name: *const c_char) -> CString { + let name_c = unsafe { CStr::from_ptr(name) }; + + let mut path = SHM_PATH.to_vec(); + + let mut skip_slash = true; + for &b in name_c.to_bytes() { + if skip_slash { + if b == b'/' { + continue; + } else { + skip_slash = false; + } + } + path.push(b); + } + + unsafe { CString::from_vec_unchecked(path) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shm_open(name: *const c_char, oflag: c_int, mode: mode_t) -> c_int { + let path = unsafe { shm_path(name) }; + unsafe { fcntl::open(path.as_ptr(), oflag, mode) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shm_unlink(name: *const c_char) -> c_int { + let path = unsafe { shm_path(name) }; + unsafe { unistd::unlink(path.as_ptr()) } +} diff --git a/src/header/sys_mman/redox.rs b/src/header/sys_mman/redox.rs new file mode 100644 index 0000000000..ea0ee2215d --- /dev/null +++ b/src/header/sys_mman/redox.rs @@ -0,0 +1,14 @@ +use crate::platform::types::c_int; + +pub const PROT_NONE: c_int = 0x0000; +pub const PROT_EXEC: c_int = 0x0001; +pub const PROT_WRITE: c_int = 0x0002; +pub const PROT_READ: c_int = 0x0004; + +pub const MAP_FIXED: c_int = 0x0004; +pub const MAP_FIXED_NOREPLACE: c_int = 0x000C; +pub const MAP_POPULATE: c_int = 0x0001F; +pub const MAP_HUGETLB: c_int = 0x2E; + +pub const MADV_DONTDUMP: c_int = 5; +pub const MADV_DODUMP: c_int = 6; diff --git a/src/header/sys_procfs/cbindgen.toml b/src/header/sys_procfs/cbindgen.toml new file mode 100644 index 0000000000..9328ff99d3 --- /dev/null +++ b/src/header/sys_procfs/cbindgen.toml @@ -0,0 +1,7 @@ +sys_includes = ["sys/user.h"] +include_guard = "_SYS_PROCFS_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/header/sys_procfs/mod.rs b/src/header/sys_procfs/mod.rs new file mode 100644 index 0000000000..b1534b33f2 --- /dev/null +++ b/src/header/sys_procfs/mod.rs @@ -0,0 +1,77 @@ +#[cfg(target_arch = "aarch64")] +use crate::header::arch_aarch64_user::{elf_fpregset_t, elf_gregset_t}; +#[cfg(target_arch = "riscv64")] +use crate::header::arch_riscv64_user::{elf_fpregset_t, elf_gregset_t}; +#[cfg(target_arch = "x86_64")] +use crate::header::arch_x64_user::{elf_fpregset_t, elf_gregset_t}; +use crate::platform::types::{ + c_char, c_int, c_long, c_short, c_uint, c_ulong, c_void, pid_t, size_t, +}; + +pub const ELF_PRARGSZ: size_t = 80; + +#[repr(C)] +pub struct elf_siginfo { + pub si_signo: c_int, + pub si_code: c_int, + pub si_errno: c_int, +} + +#[repr(C)] +pub struct time { + pub tv_sec: c_long, + pub tv_usec: c_long, +} + +#[repr(C)] +pub struct elf_prstatus { + pub pr_info: elf_siginfo, + pub pr_cursig: c_short, + pub pr_sigpend: c_ulong, + pub pr_sighold: c_ulong, + pub pr_pid: pid_t, + pub pr_ppid: pid_t, + pub pr_pgrp: pid_t, + pub pr_sid: pid_t, + pub pr_utime: time, + pub pr_stime: time, + pub pr_cutime: time, + pub pr_cstime: time, + pub pr_reg: elf_gregset_t, + pub pr_fpvalid: c_int, +} + +#[repr(C)] +pub struct elf_prpsinfo { + pub pr_state: c_char, + pub pr_sname: c_char, + pub pr_zomb: c_char, + pub pr_nice: c_char, + pub pr_flag: c_uint, + pub pr_uid: c_uint, + pub pr_gid: c_uint, + pub pr_pid: c_int, + pub pr_ppid: c_int, + pub pr_pgrp: c_int, + pub pr_sid: c_int, + pub pr_fname: [c_char; 16], + pub pr_psargs: [c_char; ELF_PRARGSZ], +} + +pub type psaddr_t = *mut c_void; +pub type prgregset_t = *mut elf_gregset_t; +pub type prfpregset_t = elf_fpregset_t; +pub type lwpid_t = pid_t; +pub type prstatus_t = elf_prstatus; +pub type prpsinfo_t = elf_prpsinfo; + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_procfs( + a: psaddr_t, + b: prgregset_t, + c: prfpregset_t, + d: lwpid_t, + e: prstatus_t, + f: prpsinfo_t, +) { +} diff --git a/src/header/sys_ptrace/cbindgen.toml b/src/header/sys_ptrace/cbindgen.toml new file mode 100644 index 0000000000..3ecacd0534 --- /dev/null +++ b/src/header/sys_ptrace/cbindgen.toml @@ -0,0 +1,7 @@ +sys_includes = [] +include_guard = "_SYS_PTRACE_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/header/sys_ptrace/mod.rs b/src/header/sys_ptrace/mod.rs new file mode 100644 index 0000000000..772182d9cc --- /dev/null +++ b/src/header/sys_ptrace/mod.rs @@ -0,0 +1,35 @@ +//! `sys/ptrace.h` implementation. +//! +//! Non-POSIX, see . + +use crate::{ + error::ResultExt, + platform::{PalPtrace, Sys, types::c_int}, +}; + +pub const PTRACE_TRACEME: c_int = 0; +pub const PTRACE_PEEKTEXT: c_int = 1; +pub const PTRACE_PEEKDATA: c_int = 2; +pub const PTRACE_POKETEXT: c_int = 4; +pub const PTRACE_POKEDATA: c_int = 5; +pub const PTRACE_CONT: c_int = 7; +pub const PTRACE_KILL: c_int = 8; +pub const PTRACE_SINGLESTEP: c_int = 9; +pub const PTRACE_GETREGS: c_int = 12; +pub const PTRACE_SETREGS: c_int = 13; +pub const PTRACE_GETFPREGS: c_int = 14; +pub const PTRACE_SETFPREGS: c_int = 15; +pub const PTRACE_ATTACH: c_int = 16; +pub const PTRACE_DETACH: c_int = 17; +pub const PTRACE_SYSCALL: c_int = 24; +pub const PTRACE_SYSEMU: c_int = 31; +pub const PTRACE_SYSEMU_SINGLESTEP: c_int = 32; + +// Can't use "params: ..." syntax, because... guess what? Cbingen again :( +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ptrace(request: c_int, mut __valist: ...) -> c_int { + // Musl also just grabs the arguments from the varargs... + unsafe { Sys::ptrace(request, __valist.arg(), __valist.arg(), __valist.arg()) } + .or_minus_one_errno() +} diff --git a/src/header/sys_random/cbindgen.toml b/src/header/sys_random/cbindgen.toml new file mode 100644 index 0000000000..d83dd8b5eb --- /dev/null +++ b/src/header/sys_random/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["sys/types.h"] +include_guard = "_SYS_RANDOM_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_random/mod.rs b/src/header/sys_random/mod.rs new file mode 100644 index 0000000000..57040d469a --- /dev/null +++ b/src/header/sys_random/mod.rs @@ -0,0 +1,29 @@ +//! `sys/random.h` implementation. +//! +//! Non-POSIX, see . + +use core::slice; + +use crate::{ + error::ResultExt, + platform::{ + Pal, Sys, + types::{c_uint, c_void, size_t, ssize_t}, + }, +}; + +/// See . +pub const GRND_NONBLOCK: c_uint = 1; +/// See . +pub const GRND_RANDOM: c_uint = 2; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getrandom(buf: *mut c_void, buflen: size_t, flags: c_uint) -> ssize_t { + Sys::getrandom( + unsafe { slice::from_raw_parts_mut(buf.cast::(), buflen) }, + flags, + ) + .map(|read| read as ssize_t) + .or_minus_one_errno() +} diff --git a/src/header/sys_resource/cbindgen.toml b/src/header/sys_resource/cbindgen.toml new file mode 100644 index 0000000000..42e748a803 --- /dev/null +++ b/src/header/sys_resource/cbindgen.toml @@ -0,0 +1,19 @@ +sys_includes = ["sys/types.h", "stdint.h", "sys/time.h"] +include_guard = "_RELIBC_SYS_RESOURCE_H" +after_includes = """ + +#define RUSAGE_SELF 0 +#define RUSAGE_CHILDREN (-1) +#define RUSAGE_BOTH (-2) +#define RUSAGE_THREAD 1 +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"timeval" = "struct timeval" diff --git a/src/header/sys_resource/mod.rs b/src/header/sys_resource/mod.rs new file mode 100644 index 0000000000..c645e8eb67 --- /dev/null +++ b/src/header/sys_resource/mod.rs @@ -0,0 +1,124 @@ +//! `sys/resource.h` implementation. +//! +//! See . + +use crate::{ + error::ResultExt, + header::sys_select::timeval, + out::Out, + platform::{ + Pal, Sys, + types::{c_int, c_long, id_t}, + }, +}; + +// Exported from cbindgen +// const RUSAGE_SELF: c_int = 0; +// const RUSAGE_CHILDREN: c_int = -1; +// const RUSAGE_BOTH: c_int = -2; +// const RUSAGE_THREAD: c_int = 1; + +pub const RLIM_INFINITY: u64 = 0xFFFF_FFFF_FFFF_FFFF; +pub const RLIM_SAVED_CUR: u64 = RLIM_INFINITY; +pub const RLIM_SAVED_MAX: u64 = RLIM_INFINITY; + +pub const RLIMIT_CPU: c_int = 0; +pub const RLIMIT_FSIZE: c_int = 1; +pub const RLIMIT_DATA: c_int = 2; +pub const RLIMIT_STACK: c_int = 3; +pub const RLIMIT_CORE: c_int = 4; +pub const RLIMIT_RSS: c_int = 5; +pub const RLIMIT_NPROC: c_int = 6; +pub const RLIMIT_NOFILE: c_int = 7; +pub const RLIMIT_MEMLOCK: c_int = 8; +pub const RLIMIT_AS: c_int = 9; +pub const RLIMIT_LOCKS: c_int = 10; +pub const RLIMIT_SIGPENDING: c_int = 11; +pub const RLIMIT_MSGQUEUE: c_int = 12; +pub const RLIMIT_NICE: c_int = 13; +pub const RLIMIT_RTPRIO: c_int = 14; +pub const RLIMIT_NLIMITS: c_int = 15; + +pub type rlim_t = u64; + +#[repr(C)] +pub struct rlimit { + pub rlim_cur: rlim_t, + pub rlim_max: rlim_t, +} + +#[repr(C)] +pub struct rusage { + pub ru_utime: timeval, + pub ru_stime: timeval, + pub ru_maxrss: c_long, + pub ru_ixrss: c_long, + pub ru_idrss: c_long, + pub ru_isrss: c_long, + pub ru_minflt: c_long, + pub ru_majflt: c_long, + pub ru_nswap: c_long, + pub ru_inblock: c_long, + pub ru_oublock: c_long, + pub ru_msgsnd: c_long, + pub ru_msgrcv: c_long, + pub ru_nsignals: c_long, + pub ru_nvcsw: c_long, + pub ru_nivcsw: c_long, +} + +pub const PRIO_PROCESS: c_int = 0; +pub const PRIO_PGRP: c_int = 1; +pub const PRIO_USER: c_int = 2; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getpriority(which: c_int, who: id_t) -> c_int { + let r = Sys::getpriority(which, who).or_minus_one_errno(); + if r < 0 { + return r; + } + 20 - r +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setpriority(which: c_int, who: id_t, nice: c_int) -> c_int { + Sys::setpriority(which, who, nice) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getrlimit(resource: c_int, rlp: *mut rlimit) -> c_int { + let Some(rlp) = (unsafe { Out::nullable(rlp) }) else { + crate::platform::ERRNO.set(crate::header::errno::EFAULT); + return -1; + }; + + Sys::getrlimit(resource, rlp) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setrlimit(resource: c_int, rlp: *const rlimit) -> c_int { + unsafe { Sys::setrlimit(resource, rlp) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getrusage(who: c_int, r_usage: *mut rusage) -> c_int { + let Some(r_usage) = (unsafe { Out::nullable(r_usage) }) else { + crate::platform::ERRNO.set(crate::header::errno::EFAULT); + return -1; + }; + + Sys::getrusage(who, r_usage) + .map(|()| 0) + .or_minus_one_errno() +} diff --git a/src/header/sys_select/cbindgen.toml b/src/header/sys_select/cbindgen.toml new file mode 100644 index 0000000000..36bfa1fcab --- /dev/null +++ b/src/header/sys_select/cbindgen.toml @@ -0,0 +1,33 @@ +sys_includes = ["time.h", "signal.h"] +after_includes = """ +// from musl license MIT { +#define FD_SETSIZE 1024 + +typedef unsigned long fd_mask; + +typedef struct { + unsigned long fds_bits[FD_SETSIZE / 8 / sizeof(long)]; +} fd_set; + +#define FD_ZERO(s) do { int __i; unsigned long *__b=(s)->fds_bits; for(__i=sizeof (fd_set)/sizeof (long); __i; __i--) *__b++=0; } while(0) +#define FD_SET(d, s) ((s)->fds_bits[(d)/(8*sizeof(long))] |= (1UL<<((d)%(8*sizeof(long))))) +#define FD_CLR(d, s) ((s)->fds_bits[(d)/(8*sizeof(long))] &= ~(1UL<<((d)%(8*sizeof(long))))) +#define FD_ISSET(d, s) !!((s)->fds_bits[(d)/(8*sizeof(long))] & (1UL<<((d)%(8*sizeof(long))))) + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +#define NFDBITS (8*(int)sizeof(long)) +#endif +// } from musl license MIT +""" +include_guard = "_SYS_SELECT_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export] +# fd_set is also defined in C (above) because cbindgen is incompatible with mem::size_of +exclude = ["FD_SETSIZE", "fd_set"] diff --git a/src/header/sys_select/mod.rs b/src/header/sys_select/mod.rs new file mode 100644 index 0000000000..45e7a2381b --- /dev/null +++ b/src/header/sys_select/mod.rs @@ -0,0 +1,228 @@ +//! `sys/select.h` implementation. +//! +//! See . + +use core::mem; + +use cbitset::BitSet; + +use crate::{ + fs::File, + header::{ + errno, + sys_epoll::{ + EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLIN, EPOLLOUT, epoll_create1, epoll_ctl, + epoll_data, epoll_event, epoll_wait, + }, + }, + platform::{ + self, + types::{c_int, suseconds_t, time_t}, + }, +}; + +/// See . +/// +/// Note that the `timeval` struct was specified for +/// [`sys/time.h`](crate::header::sys_time) in the Open Group Base +/// Specifications Issue 7 and prior, see +/// . +#[repr(C)] +#[derive(Default)] +pub struct timeval { + pub tv_sec: time_t, + pub tv_usec: suseconds_t, +} + +// fd_set is also defined in C because cbindgen is incompatible with mem::size_of booo + +/// See . +pub const FD_SETSIZE: usize = 1024; +type FdBitSet = BitSet<[u64; FD_SETSIZE / (8 * mem::size_of::())]>; + +/// See . +#[repr(C)] +pub struct fd_set { + pub fds_bits: FdBitSet, +} + +#[allow(clippy::needless_update)] +pub fn select_epoll( + nfds: c_int, + readfds: Option<&mut fd_set>, + writefds: Option<&mut fd_set>, + exceptfds: Option<&mut fd_set>, + timeout: Option<&mut timeval>, +) -> c_int { + if nfds < 0 || nfds > FD_SETSIZE as i32 { + platform::ERRNO.set(errno::EINVAL); + return -1; + }; + + let ep = { + let epfd = epoll_create1(EPOLL_CLOEXEC); + if epfd < 0 { + return -1; + } + File::new(epfd) + }; + + let mut read_bitset: Option<&mut FdBitSet> = readfds.map(|fd_set| &mut fd_set.fds_bits); + let mut write_bitset: Option<&mut FdBitSet> = writefds.map(|fd_set| &mut fd_set.fds_bits); + let mut except_bitset: Option<&mut FdBitSet> = exceptfds.map(|fd_set| &mut fd_set.fds_bits); + + // Keep track of the number of file descriptors that do not support epoll + let mut not_epoll = 0; + for fd in 0..nfds { + let mut events = 0; + + if let Some(ref fd_set) = read_bitset + && fd_set.contains(fd as usize) + { + events |= EPOLLIN; + } + + if let Some(ref fd_set) = write_bitset + && fd_set.contains(fd as usize) + { + events |= EPOLLOUT; + } + + if let Some(ref fd_set) = except_bitset + && fd_set.contains(fd as usize) + { + events |= EPOLLERR; + } + + if events > 0 { + let mut event = epoll_event { + events, + data: epoll_data { fd }, + ..Default::default() // clippy lint, _pad field on redox but not linux + }; + if unsafe { epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &raw mut event) } < 0 { + if platform::ERRNO.get() == errno::EPERM { + not_epoll += 1; + } else { + return -1; + } + } else { + if let Some(ref mut fd_set) = read_bitset + && fd_set.contains(fd as usize) + { + fd_set.remove(fd as usize); + } + + if let Some(ref mut fd_set) = write_bitset + && fd_set.contains(fd as usize) + { + fd_set.remove(fd as usize); + } + + if let Some(ref mut fd_set) = except_bitset + && fd_set.contains(fd as usize) + { + fd_set.remove(fd as usize); + } + } + } + } + + let mut events: [epoll_event; 32] = unsafe { mem::zeroed() }; + let epoll_timeout = if not_epoll > 0 { + // Do not wait if any non-epoll file descriptors were found + 0 + } else { + match timeout { + Some(timeout) => { + let sec_ms = (timeout.tv_sec as c_int).checked_mul(1000); + let usec_ms = (timeout.tv_usec as c_int) / 1000; + match sec_ms.and_then(|s| s.checked_add(usec_ms)) { + Some(s) => s as c_int, + None => c_int::MAX, + } + } + None => -1, + } + }; + let res = unsafe { + epoll_wait( + *ep, + events.as_mut_ptr(), + events.len() as c_int, + epoll_timeout, + ) + }; + if res < 0 { + return -1; + } + + let mut count = not_epoll; + for event in events.iter().take(res as usize) { + let fd = unsafe { event.data.fd }; + // TODO: Error status when fd does not match? + if fd >= 0 && fd < FD_SETSIZE as c_int { + if event.events & EPOLLIN > 0 + && let Some(ref mut fd_set) = read_bitset + { + fd_set.insert(fd as usize); + count += 1; + } + if event.events & EPOLLOUT > 0 + && let Some(ref mut fd_set) = write_bitset + { + fd_set.insert(fd as usize); + count += 1; + } + if event.events & EPOLLERR > 0 + && let Some(ref mut fd_set) = except_bitset + { + fd_set.insert(fd as usize); + count += 1; + } + } + } + count +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn select( + nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *mut timeval, +) -> c_int { + trace_expr!( + select_epoll( + nfds, + if readfds.is_null() { + None + } else { + Some(unsafe { &mut *readfds }) + }, + if writefds.is_null() { + None + } else { + Some(unsafe { &mut *writefds }) + }, + if exceptfds.is_null() { + None + } else { + Some(unsafe { &mut *exceptfds }) + }, + if timeout.is_null() { + None + } else { + Some(unsafe { &mut *timeout }) + } + ), + "select({}, {:p}, {:p}, {:p}, {:p})", + nfds, + readfds, + writefds, + exceptfds, + timeout + ) +} diff --git a/src/header/sys_signalfd/cbindgen.toml b/src/header/sys_signalfd/cbindgen.toml new file mode 100644 index 0000000000..acd5ddd02d --- /dev/null +++ b/src/header/sys_signalfd/cbindgen.toml @@ -0,0 +1,24 @@ +sys_includes = ["signal.h", "stdint.h", "stddef.h"] +include_guard = "_SYS_SIGNALFD_H" +trailer = """ +#ifndef SFD_CLOEXEC +#define SFD_CLOEXEC 0x80000 +#endif + +#ifndef SFD_NONBLOCK +#define SFD_NONBLOCK 0x800 +#endif + +int signalfd(int fd, const sigset_t *mask, size_t masksize); +int signalfd4(int fd, const sigset_t *mask, size_t masksize, int flags); +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"signalfd_siginfo" = "struct signalfd_siginfo" diff --git a/src/header/sys_signalfd/mod.rs b/src/header/sys_signalfd/mod.rs new file mode 100644 index 0000000000..a7a7d85e4a --- /dev/null +++ b/src/header/sys_signalfd/mod.rs @@ -0,0 +1,14 @@ +//! `sys/signalfd.h` implementation. + +use crate::{ + header::signal::{self, signalfd_siginfo}, + platform::types::c_int, +}; + +pub const SFD_CLOEXEC: c_int = signal::SFD_CLOEXEC; +pub const SFD_NONBLOCK: c_int = signal::SFD_NONBLOCK; + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_sys_signalfd_siginfo(siginfo: signalfd_siginfo) { + let _ = siginfo; +} diff --git a/src/header/sys_socket/cbindgen.toml b/src/header/sys_socket/cbindgen.toml new file mode 100644 index 0000000000..6372c60a0f --- /dev/null +++ b/src/header/sys_socket/cbindgen.toml @@ -0,0 +1,45 @@ +sys_includes = ["stddef.h", "stdint.h", "sys/types.h"] +include_guard = "_RELIBC_SYS_SOCKET_H" +after_includes = """ +#include // for iovec from sys/uio.h +#include // for sa_family_t +#include // for socklen_t +""" +trailer = """ +#ifndef _RELIBC_BITS_SYS_SOCKET_H +#define _RELIBC_BITS_SYS_SOCKET_H + +struct sockaddr_storage { + sa_family_t ss_family; + char __ss_padding[128-sizeof(long)-sizeof(sa_family_t)]; + unsigned long __ss_align; +}; + +// These definitions were taken from musl, license MIT { +#define __CMSG_LEN(cmsg) (((cmsg)->cmsg_len + sizeof(long) - 1) & ~(long)(sizeof(long) - 1)) +#define __CMSG_NEXT(cmsg) ((unsigned char *)(cmsg) + __CMSG_LEN(cmsg)) +#define __MHDR_END(mhdr) ((unsigned char *)(mhdr)->msg_control + (mhdr)->msg_controllen) + +#define CMSG_DATA(cmsg) ((unsigned char *) (((struct cmsghdr *)(cmsg)) + 1)) +#define CMSG_NXTHDR(mhdr, cmsg) ((cmsg)->cmsg_len < sizeof (struct cmsghdr) || \ + __CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \ + ? 0 : (struct cmsghdr *)__CMSG_NEXT(cmsg)) +#define CMSG_FIRSTHDR(mhdr) ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr) ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) 0) + +#define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) +#define CMSG_SPACE(len) (CMSG_ALIGN (len) + CMSG_ALIGN (sizeof (struct cmsghdr))) +#define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +// } from musl, license MIT + +#endif // _RELIBC_BITS_SYS_SOCKET_H +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"iovec" = "struct iovec" diff --git a/src/header/sys_socket/constants.rs b/src/header/sys_socket/constants.rs new file mode 100644 index 0000000000..c91ffb1af5 --- /dev/null +++ b/src/header/sys_socket/constants.rs @@ -0,0 +1,80 @@ +use crate::platform::types::c_int; + +pub const SOCK_STREAM: c_int = 1; +pub const SOCK_DGRAM: c_int = 2; +pub const SOCK_RAW: c_int = 3; +pub const SOCK_NONBLOCK: c_int = 0o4_000; +pub const SOCK_CLOEXEC: c_int = 0o2_000_000; + +// Other constants +pub const SOCK_RDM: c_int = 4; +pub const SOCK_SEQPACKET: c_int = 5; + +pub const SOL_SOCKET: c_int = 1; + +pub const SO_DEBUG: c_int = 1; +pub const SO_REUSEADDR: c_int = 2; +pub const SO_TYPE: c_int = 3; +pub const SO_ERROR: c_int = 4; +pub const SO_DONTROUTE: c_int = 5; +pub const SO_BROADCAST: c_int = 6; +pub const SO_SNDBUF: c_int = 7; +pub const SO_RCVBUF: c_int = 8; +pub const SO_KEEPALIVE: c_int = 9; +pub const SO_OOBINLINE: c_int = 10; +pub const SO_NO_CHECK: c_int = 11; +pub const SO_PRIORITY: c_int = 12; +pub const SO_LINGER: c_int = 13; +pub const SO_BSDCOMPAT: c_int = 14; +pub const SO_REUSEPORT: c_int = 15; +pub const SO_PASSCRED: c_int = 16; +pub const SO_PEERCRED: c_int = 17; +pub const SO_RCVLOWAT: c_int = 18; +pub const SO_SNDLOWAT: c_int = 19; +pub const SO_RCVTIMEO: c_int = 20; +pub const SO_SNDTIMEO: c_int = 21; +pub const SO_ACCEPTCONN: c_int = 30; +pub const SO_PEERSEC: c_int = 31; +pub const SO_SNDBUFFORCE: c_int = 32; +pub const SO_RCVBUFFORCE: c_int = 33; +pub const SO_PROTOCOL: c_int = 38; +pub const SO_DOMAIN: c_int = 39; + +pub const SOMAXCONN: c_int = 128; + +pub const MSG_CTRUNC: c_int = 8; +pub const MSG_DONTROUTE: c_int = 4; +pub const MSG_EOR: c_int = 128; +pub const MSG_OOB: c_int = 1; +pub const MSG_PEEK: c_int = 2; +pub const MSG_TRUNC: c_int = 32; +pub const MSG_DONTWAIT: c_int = 64; +pub const MSG_WAITALL: c_int = 256; +pub const MSG_CMSG_CLOEXEC: c_int = 0x40000000; + +pub const IP_ADD_SOURCE_MEMBERSHIP: c_int = 70; +pub const IP_DROP_SOURCE_MEMBERSHIP: c_int = 71; +pub const MCAST_JOIN_SOURCE_GROUP: c_int = 46; +pub const MCAST_LEAVE_SOURCE_GROUP: c_int = 47; + +pub const AF_INET: c_int = 2; +pub const AF_INET6: c_int = 10; +pub const AF_LOCAL: c_int = AF_UNIX; +pub const AF_UNIX: c_int = 1; +pub const AF_UNSPEC: c_int = 0; + +pub const PF_INET: c_int = 2; +pub const PF_INET6: c_int = 10; +pub const PF_LOCAL: c_int = PF_UNIX; +pub const PF_UNIX: c_int = 1; +pub const PF_UNSPEC: c_int = 0; + +pub const SHUT_RD: c_int = 0; +pub const SHUT_RDWR: c_int = 2; +pub const SHUT_WR: c_int = 1; + +pub const SCM_RIGHTS: c_int = 1; +pub const SCM_CREDENTIALS: c_int = 2; + +pub const IPPROTO_TCP: c_int = 6; +pub const TCP_NODELAY: c_int = 1; diff --git a/src/header/sys_socket/mod.rs b/src/header/sys_socket/mod.rs new file mode 100644 index 0000000000..1c0c6d7493 --- /dev/null +++ b/src/header/sys_socket/mod.rs @@ -0,0 +1,445 @@ +//! `sys/socket.h` implementation. +//! +//! See . + +use core::{mem, ptr}; + +use crate::{ + error::ResultExt, + header::{bits_iovec::iovec, bits_safamily_t::sa_family_t, bits_socklen_t::socklen_t}, + platform::{ + PalSocket, Sys, + types::{ + c_char, c_int, c_long, c_uchar, c_uint, c_void, gid_t, pid_t, size_t, ssize_t, uid_t, + }, + }, +}; + +pub mod constants; + +/// See . +#[repr(C)] +#[derive(Default, CheckVsLibcCrate)] +pub struct linger { + pub l_onoff: c_int, + pub l_linger: c_int, +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_linger(linger: linger) {} + +/// See . +#[repr(C)] +#[derive(Debug, CheckVsLibcCrate)] +pub struct msghdr { + pub msg_name: *mut c_void, + pub msg_namelen: socklen_t, + pub msg_iov: *mut iovec, + pub msg_iovlen: size_t, + pub msg_control: *mut c_void, + pub msg_controllen: size_t, + pub msg_flags: c_int, +} + +/// See . +#[repr(C)] +#[derive(Debug, CheckVsLibcCrate)] +pub struct cmsghdr { + pub cmsg_len: size_t, + pub cmsg_level: c_int, + pub cmsg_type: c_int, +} + +#[repr(C)] +#[derive(Clone, Debug)] +// FIXME: CheckVsLibcCrate +pub struct ucred { + pub pid: pid_t, + pub uid: uid_t, + pub gid: gid_t, +} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_cmsghdr(cmsghdr: cmsghdr) {} + +#[unsafe(no_mangle)] +pub extern "C" fn _cbindgen_export_ucred(ucred: ucred) {} + +/// See . +#[repr(C)] +#[derive(Default, CheckVsLibcCrate)] +pub struct sockaddr { + pub sa_family: sa_family_t, + pub sa_data: [c_char; 14], +} + +// Max size of [`sockaddr_storage`] +/// cbindgen:ignore +const _SS_MAXSIZE: usize = 128; +// Align to pointer width +/// cbindgen:ignore +const _SS_PADDING: usize = _SS_MAXSIZE - mem::size_of::() - mem::size_of::(); + +/// See . +/// Opaque storage large enough to hold any protocol specific address structure. +/// +/// ## Implementation notes +/// * The total size of this struct is 128 bytes which is based off of `musl` and `glibc` +/// * The underscore fields are implementation specific details for padding that may change +/// * [`usize`] is used because it's the width of a pointer for a given platform +/// * The order of the fields is important because the bytes in the padding will be cast to and +/// from protocol structs in C +#[repr(C)] +//#[derive(CheckVsLibcCrate)] FIXME: can't ignore private fields yet +/// cbindgen:ignore +pub struct sockaddr_storage { + pub ss_family: sa_family_t, + __ss_pad2: [u8; _SS_PADDING], + __ss_align: usize, +} + +// These must match C macros in sys_socket/cbindgen.toml { +/// cbindgen:ignore +pub unsafe extern "C" fn __CMSG_LEN(cmsg: *const cmsghdr) -> ssize_t { + ((unsafe { (*cmsg).cmsg_len as size_t } + mem::size_of::() - 1) + & !(mem::size_of::() - 1)) as ssize_t +} + +/// cbindgen:ignore +pub unsafe extern "C" fn __CMSG_NEXT(cmsg: *const cmsghdr) -> *mut c_uchar { + unsafe { (cmsg as *mut c_uchar).offset(__CMSG_LEN(cmsg)) } +} + +/// cbindgen:ignore +pub unsafe extern "C" fn __MHDR_END(mhdr: *const msghdr) -> *mut c_uchar { + unsafe { ((*mhdr).msg_control.cast::()).add((*mhdr).msg_controllen) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut c_uchar { + unsafe { (cmsg as *mut c_uchar).add(CMSG_ALIGN(mem::size_of::())) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn CMSG_NXTHDR(mhdr: *const msghdr, cmsg: *const cmsghdr) -> *mut cmsghdr { + if cmsg.is_null() { + return unsafe { CMSG_FIRSTHDR(mhdr) }; + }; + + unsafe { + let next = + cmsg as usize + CMSG_ALIGN((*cmsg).cmsg_len) + CMSG_ALIGN(mem::size_of::()); + let max = (*mhdr).msg_control as usize + (*mhdr).msg_controllen; + if next > max { + ptr::null_mut::() + } else { + (cmsg as usize + CMSG_ALIGN((*cmsg).cmsg_len)) as *mut cmsghdr + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr { + unsafe { + if (*mhdr).msg_controllen >= mem::size_of::() { + (*mhdr).msg_control.cast::() + } else { + ptr::null_mut::() + } + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn CMSG_ALIGN(len: size_t) -> size_t { + (len + mem::size_of::() - 1) & !(mem::size_of::() - 1) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn CMSG_SPACE(len: c_uint) -> c_uint { + (unsafe { CMSG_ALIGN(len as size_t) } + unsafe { CMSG_ALIGN(mem::size_of::()) }) + as c_uint +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn CMSG_LEN(length: c_uint) -> c_uint { + (unsafe { CMSG_ALIGN(mem::size_of::()) } + length as usize) as c_uint +} +// } These must match C macros in sys_socket/cbindgen.toml + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn accept( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::accept(socket, address, address_len) }.or_minus_one_errno(), + "accept({}, {:p}, {:p})", + socket, + address, + address_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn bind( + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::bind(socket, address, address_len) } + .map(|()| 0) + .or_minus_one_errno(), + "bind({}, {:p}, {})", + socket, + address, + address_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn connect( + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::connect(socket, address, address_len) }.or_minus_one_errno(), + "connect({}, {:p}, {})", + socket, + address, + address_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getpeername( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::getpeername(socket, address, address_len) } + .map(|()| 0) + .or_minus_one_errno(), + "getpeername({}, {:p}, {:p})", + socket, + address, + address_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getsockname( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::getsockname(socket, address, address_len) } + .map(|()| 0) + .or_minus_one_errno(), + "getsockname({}, {:p}, {:p})", + socket, + address, + address_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *mut c_void, + option_len: *mut socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::getsockopt(socket, level, option_name, option_value, option_len) } + .map(|()| 0) + .or_minus_one_errno(), + "getsockopt({}, {}, {}, {:p}, {:p})", + socket, + level, + option_name, + option_value, + option_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn listen(socket: c_int, backlog: c_int) -> c_int { + Sys::listen(socket, backlog) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn recv( + socket: c_int, + buffer: *mut c_void, + length: size_t, + flags: c_int, +) -> ssize_t { + unsafe { + recvfrom( + socket, + buffer, + length, + flags, + ptr::null_mut(), + ptr::null_mut(), + ) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn recvfrom( + socket: c_int, + buffer: *mut c_void, + length: size_t, + flags: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, +) -> ssize_t { + trace_expr!( + unsafe { Sys::recvfrom(socket, buffer, length, flags, address, address_len) } + .map(|r| r as ssize_t) + .or_minus_one_errno(), + "recvfrom({}, {:p}, {}, {:#x}, {:p}, {:p})", + socket, + buffer, + length, + flags, + address, + address_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> ssize_t { + unsafe { Sys::recvmsg(socket, msg, flags) } + .map(|r| r as ssize_t) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn send( + socket: c_int, + message: *const c_void, + length: size_t, + flags: c_int, +) -> ssize_t { + unsafe { sendto(socket, message, length, flags, ptr::null(), 0) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> ssize_t { + unsafe { Sys::sendmsg(socket, msg, flags) } + .map(|w| w as ssize_t) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sendto( + socket: c_int, + message: *const c_void, + length: size_t, + flags: c_int, + dest_addr: *const sockaddr, + dest_len: socklen_t, +) -> ssize_t { + trace_expr!( + unsafe { Sys::sendto(socket, message, length, flags, dest_addr, dest_len) } + .map(|w| w as ssize_t) + .or_minus_one_errno(), + "sendto({}, {:p}, {}, {:#x}, {:p}, {})", + socket, + message, + length, + flags, + dest_addr, + dest_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *const c_void, + option_len: socklen_t, +) -> c_int { + trace_expr!( + unsafe { Sys::setsockopt(socket, level, option_name, option_value, option_len) } + .map(|()| 0) + .or_minus_one_errno(), + "setsockopt({}, {}, {}, {:p}, {})", + socket, + level, + option_name, + option_value, + option_len + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn shutdown(socket: c_int, how: c_int) -> c_int { + Sys::shutdown(socket, how).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn socket(domain: c_int, kind: c_int, protocol: c_int) -> c_int { + trace_expr!( + unsafe { Sys::socket(domain, kind, protocol) }.or_minus_one_errno(), + "socket({}, {}, {})", + domain, + kind, + protocol, + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn socketpair( + domain: c_int, + kind: c_int, + protocol: c_int, + sv: *mut c_int, +) -> c_int { + trace_expr!( + Sys::socketpair(domain, kind, protocol, unsafe { + &mut *sv.cast::<[c_int; 2]>() + }) + .map(|()| 0) + .or_minus_one_errno(), + "socketpair({}, {}, {}, {:p})", + domain, + kind, + protocol, + sv + ) +} diff --git a/src/header/sys_stat/cbindgen.toml b/src/header/sys_stat/cbindgen.toml new file mode 100644 index 0000000000..61ca0da6b4 --- /dev/null +++ b/src/header/sys_stat/cbindgen.toml @@ -0,0 +1,33 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_stat.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, uid_t, gid_t, off_t, and time_t types as described in ." +# - "The header shall define the timespec structure as described in . Times shall be given in seconds since the Epoch." +# - "Inclusion of the header may make visible all symbols from the header." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_SYS_STAT_H" +after_includes = """ +#include // for timespec from time.h + +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) + +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"timespec" = "struct timespec" diff --git a/src/header/sys_stat/mod.rs b/src/header/sys_stat/mod.rs new file mode 100644 index 0000000000..0975a20dec --- /dev/null +++ b/src/header/sys_stat/mod.rs @@ -0,0 +1,242 @@ +//! `sys/stat.h` implementation. +//! +//! See . + +use crate::{ + c_str::CStr, + error::ResultExt, + header::{ + bits_timespec::timespec, + fcntl::{O_NOFOLLOW, O_PATH}, + }, + out::Out, + platform::{ + Pal, Sys, + types::{ + blkcnt_t, blksize_t, c_char, c_int, c_long, dev_t, gid_t, ino_t, mode_t, nlink_t, + off_t, uid_t, + }, + }, +}; + +pub const S_IFMT: c_int = 0o0_170_000; + +pub const S_IFDIR: c_int = 0o040_000; +pub const S_IFCHR: c_int = 0o020_000; +pub const S_IFBLK: c_int = 0o060_000; +pub const S_IFREG: c_int = 0o100_000; +pub const S_IFIFO: c_int = 0o010_000; +pub const S_IFLNK: c_int = 0o120_000; +pub const S_IFSOCK: c_int = 0o140_000; + +pub const S_IRWXU: c_int = 0o0_700; +pub const S_IRUSR: c_int = 0o0_400; +pub const S_IWUSR: c_int = 0o0_200; +pub const S_IXUSR: c_int = 0o0_100; + +// Defined for compatibility +pub const S_IREAD: c_int = S_IRUSR; +pub const S_IWRITE: c_int = S_IWUSR; +pub const S_IEXEC: c_int = S_IXUSR; + +pub const S_IRWXG: c_int = 0o0_070; +pub const S_IRGRP: c_int = 0o0_040; +pub const S_IWGRP: c_int = 0o0_020; +pub const S_IXGRP: c_int = 0o0_010; + +pub const S_IRWXO: c_int = 0o0_007; +pub const S_IROTH: c_int = 0o0_004; +pub const S_IWOTH: c_int = 0o0_002; +pub const S_IXOTH: c_int = 0o0_001; +pub const S_ISUID: c_int = 0o4_000; +pub const S_ISGID: c_int = 0o2_000; +pub const S_ISVTX: c_int = 0o1_000; + +pub const UTIME_NOW: c_long = (1 << 30) - 1; +pub const UTIME_OMIT: c_long = (1 << 30) - 2; + +/// See . +#[repr(C)] +#[derive(Default)] +pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_nlink: nlink_t, + pub st_mode: mode_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + + pub st_atim: timespec, + pub st_mtim: timespec, + pub st_ctim: timespec, + + // Compared to glibc, our struct is for some reason 24 bytes too small. + // Accessing atime works, so clearly the struct isn't incorrect... + // This works. + pub _pad: [c_char; 24], +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn chmod(path: *const c_char, mode: mode_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::chmod(path, mode).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fchmod(fildes: c_int, mode: mode_t) -> c_int { + Sys::fchmod(fildes, mode).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fchmodat( + dirfd: c_int, + path: *const c_char, + mode: mode_t, + flags: c_int, +) -> c_int { + let path = unsafe { CStr::from_nullable_ptr(path) }; + Sys::fchmodat(dirfd, path, mode, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fstat(fildes: c_int, buf: *mut stat) -> c_int { + let buf = unsafe { Out::nonnull(buf) }; + Sys::fstat(fildes, buf).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fstatat( + fildes: c_int, + path: *const c_char, + buf: *mut stat, + flags: c_int, +) -> c_int { + let path = unsafe { CStr::from_nullable_ptr(path) }; + let buf = unsafe { Out::nonnull(buf) }; + Sys::fstatat(fildes, path, buf, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn __fxstat(_ver: c_int, fildes: c_int, buf: *mut stat) -> c_int { + unsafe { fstat(fildes, buf) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn futimens(fd: c_int, times: *const timespec) -> c_int { + unsafe { Sys::futimens(fd, times) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lstat(path: *const c_char, buf: *mut stat) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + let buf = unsafe { Out::nonnull(buf) }; + + // TODO: Rustify + let fd = Sys::open(path, O_PATH | O_NOFOLLOW, 0).or_minus_one_errno(); + if fd < 0 { + return -1; + } + + // TODO: Rustify + let res = Sys::fstat(fd, buf).map(|()| 0).or_minus_one_errno(); + + if let Ok(()) = Sys::close(fd) {}; // TODO handle error + + res +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkdirat(dirfd: c_int, path: *const c_char, mode: mode_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::mkdirat(dirfd, path, mode) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkdir(path: *const c_char, mode: mode_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::mkdir(path, mode).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkfifoat(dirfd: c_int, path: *const c_char, mode: mode_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::mkfifoat(dirfd, path, mode) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mkfifo(path: *const c_char, mode: mode_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::mkfifo(path, mode).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mknod(path: *const c_char, mode: mode_t, dev: dev_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::mknod(path, mode, dev).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mknodat( + dirfd: c_int, + path: *const c_char, + mode: mode_t, + dev: dev_t, +) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::mknodat(dirfd, path, mode, dev) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn stat(file: *const c_char, buf: *mut stat) -> c_int { + let file = unsafe { CStr::from_ptr(file) }; + let buf = unsafe { Out::nonnull(buf) }; + + // TODO: Rustify + let fd = Sys::open(file, O_PATH, 0).or_minus_one_errno(); + if fd < 0 { + return -1; + } + + // TODO: Rustify + let res = Sys::fstat(fd, buf).map(|()| 0).or_minus_one_errno(); + + if let Ok(()) = Sys::close(fd) {}; // TODO handle error + + res +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn umask(mask: mode_t) -> mode_t { + Sys::umask(mask) +} diff --git a/src/header/sys_statvfs/cbindgen.toml b/src/header/sys_statvfs/cbindgen.toml new file mode 100644 index 0000000000..fa324209cd --- /dev/null +++ b/src/header/sys_statvfs/cbindgen.toml @@ -0,0 +1,13 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_statvfs.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the fsblkcnt_t and fsfilcnt_t types as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_SYS_STATVFS_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_statvfs/mod.rs b/src/header/sys_statvfs/mod.rs new file mode 100644 index 0000000000..866768b885 --- /dev/null +++ b/src/header/sys_statvfs/mod.rs @@ -0,0 +1,58 @@ +//! `sys_statvfs.h` implementation. +//! +//! See . + +use crate::{ + c_str::CStr, + error::ResultExt, + header::fcntl::O_PATH, + out::Out, + platform::{ + Pal, Sys, + types::{c_char, c_int, c_ulong, fsblkcnt_t, fsfilcnt_t}, + }, +}; + +//pub const ST_RDONLY +//pub const ST_NOSUID + +#[repr(C)] +#[derive(Default)] +pub struct statvfs { + pub f_bsize: c_ulong, + pub f_frsize: c_ulong, + pub f_blocks: fsblkcnt_t, + pub f_bfree: fsblkcnt_t, + pub f_bavail: fsblkcnt_t, + pub f_files: fsfilcnt_t, + pub f_ffree: fsfilcnt_t, + pub f_favail: fsfilcnt_t, + pub f_fsid: c_ulong, + pub f_flag: c_ulong, + pub f_namemax: c_ulong, +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fstatvfs(fildes: c_int, buf: *mut statvfs) -> c_int { + let buf = unsafe { Out::nonnull(buf) }; + Sys::fstatvfs(fildes, buf).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn statvfs(file: *const c_char, buf: *mut statvfs) -> c_int { + let file = unsafe { CStr::from_ptr(file) }; + let buf = unsafe { Out::nonnull(buf) }; + // TODO: Rustify + let fd = Sys::open(file, O_PATH, 0).or_minus_one_errno(); + if fd < 0 { + return -1; + } + + let res = Sys::fstatvfs(fd, buf).map(|()| 0).or_minus_one_errno(); + + if let Ok(()) = Sys::close(fd) {}; // TODO handle error + + res +} diff --git a/src/header/sys_syscall/aarch64.rs b/src/header/sys_syscall/aarch64.rs new file mode 100644 index 0000000000..369a4b045a --- /dev/null +++ b/src/header/sys_syscall/aarch64.rs @@ -0,0 +1,303 @@ +// Adapted from sc crate: https://github.com/japaric/syscall.rs/blob/master/src/platform/linux-aarch64/nr.rs +// const ([A-Z_\d]+): -> const __NR_\L$1: + +pub const __NR_accept: usize = 202; +pub const __NR_accept4: usize = 242; +pub const __NR_acct: usize = 89; +pub const __NR_add_key: usize = 217; +pub const __NR_adjtimex: usize = 171; +pub const __NR_arch_specific_syscall: usize = 244; +pub const __NR_bind: usize = 200; +pub const __NR_bpf: usize = 280; +pub const __NR_brk: usize = 214; +pub const __NR_capget: usize = 90; +pub const __NR_capset: usize = 91; +pub const __NR_chdir: usize = 49; +pub const __NR_chroot: usize = 51; +pub const __NR_clock_adjtime: usize = 266; +pub const __NR_clock_getres: usize = 114; +pub const __NR_clock_gettime: usize = 113; +pub const __NR_clock_nanosleep: usize = 115; +pub const __NR_clock_settime: usize = 112; +pub const __NR_clone: usize = 220; +pub const __NR_clone3: usize = 435; +pub const __NR_close: usize = 57; +pub const __NR_close_range: usize = 436; +pub const __NR_connect: usize = 203; +pub const __NR_copy_file_range: usize = 285; +pub const __NR_delete_module: usize = 106; +pub const __NR_dup: usize = 23; +pub const __NR_dup3: usize = 24; +pub const __NR_epoll_create1: usize = 20; +pub const __NR_epoll_ctl: usize = 21; +pub const __NR_epoll_pwait: usize = 22; +pub const __NR_epoll_pwait2: usize = 441; +pub const __NR_eventfd2: usize = 19; +pub const __NR_execve: usize = 221; +pub const __NR_execveat: usize = 281; +pub const __NR_exit: usize = 93; +pub const __NR_exit_group: usize = 94; +pub const __NR_faccessat: usize = 48; +pub const __NR_faccessat2: usize = 439; +pub const __NR_fadvise64: usize = 223; +pub const __NR_fallocate: usize = 47; +pub const __NR_fanotify_init: usize = 262; +pub const __NR_fanotify_mark: usize = 263; +pub const __NR_fchdir: usize = 50; +pub const __NR_fchmod: usize = 52; +pub const __NR_fchmodat: usize = 53; +pub const __NR_fchown: usize = 55; +pub const __NR_fchownat: usize = 54; +pub const __NR_fcntl: usize = 25; +pub const __NR_fdatasync: usize = 83; +pub const __NR_fgetxattr: usize = 10; +pub const __NR_finit_module: usize = 273; +pub const __NR_flistxattr: usize = 13; +pub const __NR_flock: usize = 32; +pub const __NR_fremovexattr: usize = 16; +pub const __NR_fsconfig: usize = 431; +pub const __NR_fsetxattr: usize = 7; +pub const __NR_fsmount: usize = 432; +pub const __NR_fsopen: usize = 430; +pub const __NR_fspick: usize = 433; +pub const __NR_fstat: usize = 80; +pub const __NR_fstatfs: usize = 44; +pub const __NR_fsync: usize = 82; +pub const __NR_ftruncate: usize = 46; +pub const __NR_futex: usize = 98; +pub const __NR_get_mempolicy: usize = 236; +pub const __NR_get_robust_list: usize = 100; +pub const __NR_getcpu: usize = 168; +pub const __NR_getcwd: usize = 17; +pub const __NR_getdents64: usize = 61; +pub const __NR_getegid: usize = 177; +pub const __NR_geteuid: usize = 175; +pub const __NR_getgid: usize = 176; +pub const __NR_getgroups: usize = 158; +pub const __NR_getitimer: usize = 102; +pub const __NR_getpeername: usize = 205; +pub const __NR_getpgid: usize = 155; +pub const __NR_getpid: usize = 172; +pub const __NR_getppid: usize = 173; +pub const __NR_getpriority: usize = 141; +pub const __NR_getrandom: usize = 278; +pub const __NR_getresgid: usize = 150; +pub const __NR_getresuid: usize = 148; +pub const __NR_getrlimit: usize = 163; +pub const __NR_getrusage: usize = 165; +pub const __NR_getsid: usize = 156; +pub const __NR_getsockname: usize = 204; +pub const __NR_getsockopt: usize = 209; +pub const __NR_gettid: usize = 178; +pub const __NR_gettimeofday: usize = 169; +pub const __NR_getuid: usize = 174; +pub const __NR_getxattr: usize = 8; +pub const __NR_init_module: usize = 105; +pub const __NR_inotify_add_watch: usize = 27; +pub const __NR_inotify_init1: usize = 26; +pub const __NR_inotify_rm_watch: usize = 28; +pub const __NR_io_cancel: usize = 3; +pub const __NR_io_destroy: usize = 1; +pub const __NR_io_getevents: usize = 4; +pub const __NR_io_pgetevents: usize = 292; +pub const __NR_io_setup: usize = 0; +pub const __NR_io_submit: usize = 2; +pub const __NR_io_uring_enter: usize = 426; +pub const __NR_io_uring_register: usize = 427; +pub const __NR_io_uring_setup: usize = 425; +pub const __NR_ioctl: usize = 29; +pub const __NR_ioprio_get: usize = 31; +pub const __NR_ioprio_set: usize = 30; +pub const __NR_kcmp: usize = 272; +pub const __NR_kexec_file_load: usize = 294; +pub const __NR_kexec_load: usize = 104; +pub const __NR_keyctl: usize = 219; +pub const __NR_kill: usize = 129; +pub const __NR_lgetxattr: usize = 9; +pub const __NR_linkat: usize = 37; +pub const __NR_listen: usize = 201; +pub const __NR_listxattr: usize = 11; +pub const __NR_llistxattr: usize = 12; +pub const __NR_lookup_dcookie: usize = 18; +pub const __NR_lremovexattr: usize = 15; +pub const __NR_lseek: usize = 62; +pub const __NR_lsetxattr: usize = 6; +pub const __NR_madvise: usize = 233; +pub const __NR_mbind: usize = 235; +pub const __NR_membarrier: usize = 283; +pub const __NR_memfd_create: usize = 279; +pub const __NR_migrate_pages: usize = 238; +pub const __NR_mincore: usize = 232; +pub const __NR_mkdirat: usize = 34; +pub const __NR_mknodat: usize = 33; +pub const __NR_mlock: usize = 228; +pub const __NR_mlock2: usize = 284; +pub const __NR_mlockall: usize = 230; +pub const __NR_mmap: usize = 222; +pub const __NR_mount: usize = 40; +pub const __NR_mount_setattr: usize = 442; +pub const __NR_move_mount: usize = 429; +pub const __NR_move_pages: usize = 239; +pub const __NR_mprotect: usize = 226; +pub const __NR_mq_getsetattr: usize = 185; +pub const __NR_mq_notify: usize = 184; +pub const __NR_mq_open: usize = 180; +pub const __NR_mq_timedreceive: usize = 183; +pub const __NR_mq_timedsend: usize = 182; +pub const __NR_mq_unlink: usize = 181; +pub const __NR_mremap: usize = 216; +pub const __NR_msgctl: usize = 187; +pub const __NR_msgget: usize = 186; +pub const __NR_msgrcv: usize = 188; +pub const __NR_msgsnd: usize = 189; +pub const __NR_msync: usize = 227; +pub const __NR_munlock: usize = 229; +pub const __NR_munlockall: usize = 231; +pub const __NR_munmap: usize = 215; +pub const __NR_name_to_handle_at: usize = 264; +pub const __NR_nanosleep: usize = 101; +pub const __NR_newfstatat: usize = 79; +pub const __NR_nfsservctl: usize = 42; +pub const __NR_open_by_handle_at: usize = 265; +pub const __NR_open_tree: usize = 428; +pub const __NR_openat: usize = 56; +pub const __NR_openat2: usize = 437; +pub const __NR_perf_event_open: usize = 241; +pub const __NR_personality: usize = 92; +pub const __NR_pidfd_getfd: usize = 438; +pub const __NR_pidfd_open: usize = 434; +pub const __NR_pidfd_send_signal: usize = 424; +pub const __NR_pipe2: usize = 59; +pub const __NR_pivot_root: usize = 41; +pub const __NR_pkey_alloc: usize = 289; +pub const __NR_pkey_free: usize = 290; +pub const __NR_pkey_mprotect: usize = 288; +pub const __NR_ppoll: usize = 73; +pub const __NR_prctl: usize = 167; +pub const __NR_pread64: usize = 67; +pub const __NR_preadv: usize = 69; +pub const __NR_preadv2: usize = 286; +pub const __NR_prlimit64: usize = 261; +pub const __NR_process_madvise: usize = 440; +pub const __NR_process_vm_readv: usize = 270; +pub const __NR_process_vm_writev: usize = 271; +pub const __NR_pselect6: usize = 72; +pub const __NR_ptrace: usize = 117; +pub const __NR_pwrite64: usize = 68; +pub const __NR_pwritev: usize = 70; +pub const __NR_pwritev2: usize = 287; +pub const __NR_quotactl: usize = 60; +pub const __NR_read: usize = 63; +pub const __NR_readahead: usize = 213; +pub const __NR_readlinkat: usize = 78; +pub const __NR_readv: usize = 65; +pub const __NR_reboot: usize = 142; +pub const __NR_recvfrom: usize = 207; +pub const __NR_recvmmsg: usize = 243; +pub const __NR_recvmsg: usize = 212; +pub const __NR_remap_file_pages: usize = 234; +pub const __NR_removexattr: usize = 14; +pub const __NR_renameat: usize = 38; +pub const __NR_renameat2: usize = 276; +pub const __NR_request_key: usize = 218; +pub const __NR_restart_syscall: usize = 128; +pub const __NR_rseq: usize = 293; +pub const __NR_rt_sigaction: usize = 134; +pub const __NR_rt_sigpending: usize = 136; +pub const __NR_rt_sigprocmask: usize = 135; +pub const __NR_rt_sigqueueinfo: usize = 138; +pub const __NR_rt_sigreturn: usize = 139; +pub const __NR_rt_sigsuspend: usize = 133; +pub const __NR_rt_sigtimedwait: usize = 137; +pub const __NR_rt_tgsigqueueinfo: usize = 240; +pub const __NR_sched_get_priority_max: usize = 125; +pub const __NR_sched_get_priority_min: usize = 126; +pub const __NR_sched_getaffinity: usize = 123; +pub const __NR_sched_getattr: usize = 275; +pub const __NR_sched_getparam: usize = 121; +pub const __NR_sched_getscheduler: usize = 120; +pub const __NR_sched_rr_get_interval: usize = 127; +pub const __NR_sched_setaffinity: usize = 122; +pub const __NR_sched_setattr: usize = 274; +pub const __NR_sched_setparam: usize = 118; +pub const __NR_sched_setscheduler: usize = 119; +pub const __NR_sched_yield: usize = 124; +pub const __NR_seccomp: usize = 277; +pub const __NR_semctl: usize = 191; +pub const __NR_semget: usize = 190; +pub const __NR_semop: usize = 193; +pub const __NR_semtimedop: usize = 192; +pub const __NR_sendfile: usize = 71; +pub const __NR_sendmmsg: usize = 269; +pub const __NR_sendmsg: usize = 211; +pub const __NR_sendto: usize = 206; +pub const __NR_set_mempolicy: usize = 237; +pub const __NR_set_robust_list: usize = 99; +pub const __NR_set_tid_address: usize = 96; +pub const __NR_setdomainname: usize = 162; +pub const __NR_setfsgid: usize = 152; +pub const __NR_setfsuid: usize = 151; +pub const __NR_setgid: usize = 144; +pub const __NR_setgroups: usize = 159; +pub const __NR_sethostname: usize = 161; +pub const __NR_setitimer: usize = 103; +pub const __NR_setns: usize = 268; +pub const __NR_setpgid: usize = 154; +pub const __NR_setpriority: usize = 140; +pub const __NR_setregid: usize = 143; +pub const __NR_setresgid: usize = 149; +pub const __NR_setresuid: usize = 147; +pub const __NR_setreuid: usize = 145; +pub const __NR_setrlimit: usize = 164; +pub const __NR_setsid: usize = 157; +pub const __NR_setsockopt: usize = 208; +pub const __NR_settimeofday: usize = 170; +pub const __NR_setuid: usize = 146; +pub const __NR_setxattr: usize = 5; +pub const __NR_shmat: usize = 196; +pub const __NR_shmctl: usize = 195; +pub const __NR_shmdt: usize = 197; +pub const __NR_shmget: usize = 194; +pub const __NR_shutdown: usize = 210; +pub const __NR_sigaltstack: usize = 132; +pub const __NR_signalfd4: usize = 74; +pub const __NR_socket: usize = 198; +pub const __NR_socketpair: usize = 199; +pub const __NR_splice: usize = 76; +pub const __NR_statfs: usize = 43; +pub const __NR_statx: usize = 291; +pub const __NR_swapoff: usize = 225; +pub const __NR_swapon: usize = 224; +pub const __NR_symlinkat: usize = 36; +pub const __NR_sync: usize = 81; +pub const __NR_sync_file_range: usize = 84; +pub const __NR_syncfs: usize = 267; +pub const __NR_syscalls: usize = 443; +pub const __NR_sysinfo: usize = 179; +pub const __NR_syslog: usize = 116; +pub const __NR_tee: usize = 77; +pub const __NR_tgkill: usize = 131; +pub const __NR_timer_create: usize = 107; +pub const __NR_timer_delete: usize = 111; +pub const __NR_timer_getoverrun: usize = 109; +pub const __NR_timer_gettime: usize = 108; +pub const __NR_timer_settime: usize = 110; +pub const __NR_timerfd_create: usize = 85; +pub const __NR_timerfd_gettime: usize = 87; +pub const __NR_timerfd_settime: usize = 86; +pub const __NR_times: usize = 153; +pub const __NR_tkill: usize = 130; +pub const __NR_truncate: usize = 45; +pub const __NR_umask: usize = 166; +pub const __NR_umount2: usize = 39; +pub const __NR_uname: usize = 160; +pub const __NR_unlinkat: usize = 35; +pub const __NR_unshare: usize = 97; +pub const __NR_userfaultfd: usize = 282; +pub const __NR_utimensat: usize = 88; +pub const __NR_vhangup: usize = 58; +pub const __NR_vmsplice: usize = 75; +pub const __NR_wait4: usize = 260; +pub const __NR_waitid: usize = 95; +pub const __NR_write: usize = 64; +pub const __NR_writev: usize = 66; diff --git a/src/header/sys_syscall/cbindgen.toml b/src/header/sys_syscall/cbindgen.toml new file mode 100644 index 0000000000..6976288d42 --- /dev/null +++ b/src/header/sys_syscall/cbindgen.toml @@ -0,0 +1,8 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_statvfs.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the fsblkcnt_t and fsfilcnt_t types as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_SYS_SYSCALL_H" +language = "C" +no_includes = true diff --git a/src/header/sys_syscall/mod.rs b/src/header/sys_syscall/mod.rs new file mode 100644 index 0000000000..cb8c1350e7 --- /dev/null +++ b/src/header/sys_syscall/mod.rs @@ -0,0 +1,5 @@ +// copied from sc crate +#[cfg(all(target_os = "linux", target_arch = "aarch64"))] +pub mod aarch64; +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +pub mod x86_64; diff --git a/src/header/sys_syscall/x86_64.rs b/src/header/sys_syscall/x86_64.rs new file mode 100644 index 0000000000..d5fd94ae8a --- /dev/null +++ b/src/header/sys_syscall/x86_64.rs @@ -0,0 +1,356 @@ +// Adapted from sc crate: https://github.com/japaric/syscall.rs/blob/master/src/platform/linux-x86_64/nr.rs +// const ([A-Z_\d]+): -> const __NR_\L$1: + +pub const __NR_accept: usize = 43; +pub const __NR_accept4: usize = 288; +pub const __NR_access: usize = 21; +pub const __NR_acct: usize = 163; +pub const __NR_add_key: usize = 248; +pub const __NR_adjtimex: usize = 159; +pub const __NR_afs_syscall: usize = 183; +pub const __NR_alarm: usize = 37; +pub const __NR_arch_prctl: usize = 158; +pub const __NR_bind: usize = 49; +pub const __NR_bpf: usize = 321; +pub const __NR_brk: usize = 12; +pub const __NR_capget: usize = 125; +pub const __NR_capset: usize = 126; +pub const __NR_chdir: usize = 80; +pub const __NR_chmod: usize = 90; +pub const __NR_chown: usize = 92; +pub const __NR_chroot: usize = 161; +pub const __NR_clock_adjtime: usize = 305; +pub const __NR_clock_getres: usize = 229; +pub const __NR_clock_gettime: usize = 228; +pub const __NR_clock_nanosleep: usize = 230; +pub const __NR_clock_settime: usize = 227; +pub const __NR_clone: usize = 56; +pub const __NR_clone3: usize = 435; +pub const __NR_close: usize = 3; +pub const __NR_close_range: usize = 436; +pub const __NR_connect: usize = 42; +pub const __NR_copy_file_range: usize = 326; +pub const __NR_creat: usize = 85; +pub const __NR_create_module: usize = 174; +pub const __NR_delete_module: usize = 176; +pub const __NR_dup: usize = 32; +pub const __NR_dup2: usize = 33; +pub const __NR_dup3: usize = 292; +pub const __NR_epoll_create: usize = 213; +pub const __NR_epoll_create1: usize = 291; +pub const __NR_epoll_ctl: usize = 233; +pub const __NR_epoll_ctl_old: usize = 214; +pub const __NR_epoll_pwait: usize = 281; +pub const __NR_epoll_pwait2: usize = 441; +pub const __NR_epoll_wait: usize = 232; +pub const __NR_epoll_wait_old: usize = 215; +pub const __NR_eventfd: usize = 284; +pub const __NR_eventfd2: usize = 290; +pub const __NR_execve: usize = 59; +pub const __NR_execveat: usize = 322; +pub const __NR_exit: usize = 60; +pub const __NR_exit_group: usize = 231; +pub const __NR_faccessat: usize = 269; +pub const __NR_faccessat2: usize = 439; +pub const __NR_fadvise64: usize = 221; +pub const __NR_fallocate: usize = 285; +pub const __NR_fanotify_init: usize = 300; +pub const __NR_fanotify_mark: usize = 301; +pub const __NR_fchdir: usize = 81; +pub const __NR_fchmod: usize = 91; +pub const __NR_fchmodat: usize = 268; +pub const __NR_fchown: usize = 93; +pub const __NR_fchownat: usize = 260; +pub const __NR_fcntl: usize = 72; +pub const __NR_fdatasync: usize = 75; +pub const __NR_fgetxattr: usize = 193; +pub const __NR_finit_module: usize = 313; +pub const __NR_flistxattr: usize = 196; +pub const __NR_flock: usize = 73; +pub const __NR_fork: usize = 57; +pub const __NR_fremovexattr: usize = 199; +pub const __NR_fsconfig: usize = 431; +pub const __NR_fsetxattr: usize = 190; +pub const __NR_fsmount: usize = 432; +pub const __NR_fsopen: usize = 430; +pub const __NR_fspick: usize = 433; +pub const __NR_fstat: usize = 5; +pub const __NR_fstatfs: usize = 138; +pub const __NR_fsync: usize = 74; +pub const __NR_ftruncate: usize = 77; +pub const __NR_futex: usize = 202; +pub const __NR_futimesat: usize = 261; +pub const __NR_get_kernel_syms: usize = 177; +pub const __NR_get_mempolicy: usize = 239; +pub const __NR_get_robust_list: usize = 274; +pub const __NR_get_thread_area: usize = 211; +pub const __NR_getcpu: usize = 309; +pub const __NR_getcwd: usize = 79; +pub const __NR_getdents: usize = 78; +pub const __NR_getdents64: usize = 217; +pub const __NR_getegid: usize = 108; +pub const __NR_geteuid: usize = 107; +pub const __NR_getgid: usize = 104; +pub const __NR_getgroups: usize = 115; +pub const __NR_getitimer: usize = 36; +pub const __NR_getpeername: usize = 52; +pub const __NR_getpgid: usize = 121; +pub const __NR_getpgrp: usize = 111; +pub const __NR_getpid: usize = 39; +pub const __NR_getpmsg: usize = 181; +pub const __NR_getppid: usize = 110; +pub const __NR_getpriority: usize = 140; +pub const __NR_getrandom: usize = 318; +pub const __NR_getresgid: usize = 120; +pub const __NR_getresuid: usize = 118; +pub const __NR_getrlimit: usize = 97; +pub const __NR_getrusage: usize = 98; +pub const __NR_getsid: usize = 124; +pub const __NR_getsockname: usize = 51; +pub const __NR_getsockopt: usize = 55; +pub const __NR_gettid: usize = 186; +pub const __NR_gettimeofday: usize = 96; +pub const __NR_getuid: usize = 102; +pub const __NR_getxattr: usize = 191; +pub const __NR_init_module: usize = 175; +pub const __NR_inotify_add_watch: usize = 254; +pub const __NR_inotify_init: usize = 253; +pub const __NR_inotify_init1: usize = 294; +pub const __NR_inotify_rm_watch: usize = 255; +pub const __NR_io_cancel: usize = 210; +pub const __NR_io_destroy: usize = 207; +pub const __NR_io_getevents: usize = 208; +pub const __NR_io_pgetevents: usize = 333; +pub const __NR_io_setup: usize = 206; +pub const __NR_io_submit: usize = 209; +pub const __NR_io_uring_enter: usize = 426; +pub const __NR_io_uring_register: usize = 427; +pub const __NR_io_uring_setup: usize = 425; +pub const __NR_ioctl: usize = 16; +pub const __NR_ioperm: usize = 173; +pub const __NR_iopl: usize = 172; +pub const __NR_ioprio_get: usize = 252; +pub const __NR_ioprio_set: usize = 251; +pub const __NR_kcmp: usize = 312; +pub const __NR_kexec_file_load: usize = 320; +pub const __NR_kexec_load: usize = 246; +pub const __NR_keyctl: usize = 250; +pub const __NR_kill: usize = 62; +pub const __NR_lchown: usize = 94; +pub const __NR_lgetxattr: usize = 192; +pub const __NR_link: usize = 86; +pub const __NR_linkat: usize = 265; +pub const __NR_listen: usize = 50; +pub const __NR_listxattr: usize = 194; +pub const __NR_llistxattr: usize = 195; +pub const __NR_lookup_dcookie: usize = 212; +pub const __NR_lremovexattr: usize = 198; +pub const __NR_lseek: usize = 8; +pub const __NR_lsetxattr: usize = 189; +pub const __NR_lstat: usize = 6; +pub const __NR_madvise: usize = 28; +pub const __NR_mbind: usize = 237; +pub const __NR_membarrier: usize = 324; +pub const __NR_memfd_create: usize = 319; +pub const __NR_migrate_pages: usize = 256; +pub const __NR_mincore: usize = 27; +pub const __NR_mkdir: usize = 83; +pub const __NR_mkdirat: usize = 258; +pub const __NR_mknod: usize = 133; +pub const __NR_mknodat: usize = 259; +pub const __NR_mlock: usize = 149; +pub const __NR_mlock2: usize = 325; +pub const __NR_mlockall: usize = 151; +pub const __NR_mmap: usize = 9; +pub const __NR_modify_ldt: usize = 154; +pub const __NR_mount: usize = 165; +pub const __NR_mount_setattr: usize = 442; +pub const __NR_move_mount: usize = 429; +pub const __NR_move_pages: usize = 279; +pub const __NR_mprotect: usize = 10; +pub const __NR_mq_getsetattr: usize = 245; +pub const __NR_mq_notify: usize = 244; +pub const __NR_mq_open: usize = 240; +pub const __NR_mq_timedreceive: usize = 243; +pub const __NR_mq_timedsend: usize = 242; +pub const __NR_mq_unlink: usize = 241; +pub const __NR_mremap: usize = 25; +pub const __NR_msgctl: usize = 71; +pub const __NR_msgget: usize = 68; +pub const __NR_msgrcv: usize = 70; +pub const __NR_msgsnd: usize = 69; +pub const __NR_msync: usize = 26; +pub const __NR_munlock: usize = 150; +pub const __NR_munlockall: usize = 152; +pub const __NR_munmap: usize = 11; +pub const __NR_name_to_handle_at: usize = 303; +pub const __NR_nanosleep: usize = 35; +pub const __NR_newfstatat: usize = 262; +pub const __NR_nfsservctl: usize = 180; +pub const __NR_open: usize = 2; +pub const __NR_open_by_handle_at: usize = 304; +pub const __NR_open_tree: usize = 428; +pub const __NR_openat: usize = 257; +pub const __NR_openat2: usize = 437; +pub const __NR_pause: usize = 34; +pub const __NR_perf_event_open: usize = 298; +pub const __NR_personality: usize = 135; +pub const __NR_pidfd_getfd: usize = 438; +pub const __NR_pidfd_open: usize = 434; +pub const __NR_pidfd_send_signal: usize = 424; +pub const __NR_pipe: usize = 22; +pub const __NR_pipe2: usize = 293; +pub const __NR_pivot_root: usize = 155; +pub const __NR_pkey_alloc: usize = 330; +pub const __NR_pkey_free: usize = 331; +pub const __NR_pkey_mprotect: usize = 329; +pub const __NR_poll: usize = 7; +pub const __NR_ppoll: usize = 271; +pub const __NR_prctl: usize = 157; +pub const __NR_pread64: usize = 17; +pub const __NR_preadv: usize = 295; +pub const __NR_preadv2: usize = 327; +pub const __NR_prlimit64: usize = 302; +pub const __NR_process_madvise: usize = 440; +pub const __NR_process_vm_readv: usize = 310; +pub const __NR_process_vm_writev: usize = 311; +pub const __NR_pselect6: usize = 270; +pub const __NR_ptrace: usize = 101; +pub const __NR_putpmsg: usize = 182; +pub const __NR_pwrite64: usize = 18; +pub const __NR_pwritev: usize = 296; +pub const __NR_pwritev2: usize = 328; +pub const __NR_query_module: usize = 178; +pub const __NR_quotactl: usize = 179; +pub const __NR_read: usize = 0; +pub const __NR_readahead: usize = 187; +pub const __NR_readlink: usize = 89; +pub const __NR_readlinkat: usize = 267; +pub const __NR_readv: usize = 19; +pub const __NR_reboot: usize = 169; +pub const __NR_recvfrom: usize = 45; +pub const __NR_recvmmsg: usize = 299; +pub const __NR_recvmsg: usize = 47; +pub const __NR_remap_file_pages: usize = 216; +pub const __NR_removexattr: usize = 197; +pub const __NR_rename: usize = 82; +pub const __NR_renameat: usize = 264; +pub const __NR_renameat2: usize = 316; +pub const __NR_request_key: usize = 249; +pub const __NR_restart_syscall: usize = 219; +pub const __NR_rmdir: usize = 84; +pub const __NR_rseq: usize = 334; +pub const __NR_rt_sigaction: usize = 13; +pub const __NR_rt_sigpending: usize = 127; +pub const __NR_rt_sigprocmask: usize = 14; +pub const __NR_rt_sigqueueinfo: usize = 129; +pub const __NR_rt_sigreturn: usize = 15; +pub const __NR_rt_sigsuspend: usize = 130; +pub const __NR_rt_sigtimedwait: usize = 128; +pub const __NR_rt_tgsigqueueinfo: usize = 297; +pub const __NR_sched_get_priority_max: usize = 146; +pub const __NR_sched_get_priority_min: usize = 147; +pub const __NR_sched_getaffinity: usize = 204; +pub const __NR_sched_getattr: usize = 315; +pub const __NR_sched_getparam: usize = 143; +pub const __NR_sched_getscheduler: usize = 145; +pub const __NR_sched_rr_get_interval: usize = 148; +pub const __NR_sched_setaffinity: usize = 203; +pub const __NR_sched_setattr: usize = 314; +pub const __NR_sched_setparam: usize = 142; +pub const __NR_sched_setscheduler: usize = 144; +pub const __NR_sched_yield: usize = 24; +pub const __NR_seccomp: usize = 317; +pub const __NR_security: usize = 185; +pub const __NR_select: usize = 23; +pub const __NR_semctl: usize = 66; +pub const __NR_semget: usize = 64; +pub const __NR_semop: usize = 65; +pub const __NR_semtimedop: usize = 220; +pub const __NR_sendfile: usize = 40; +pub const __NR_sendmmsg: usize = 307; +pub const __NR_sendmsg: usize = 46; +pub const __NR_sendto: usize = 44; +pub const __NR_set_mempolicy: usize = 238; +pub const __NR_set_robust_list: usize = 273; +pub const __NR_set_thread_area: usize = 205; +pub const __NR_set_tid_address: usize = 218; +pub const __NR_setdomainname: usize = 171; +pub const __NR_setfsgid: usize = 123; +pub const __NR_setfsuid: usize = 122; +pub const __NR_setgid: usize = 106; +pub const __NR_setgroups: usize = 116; +pub const __NR_sethostname: usize = 170; +pub const __NR_setitimer: usize = 38; +pub const __NR_setns: usize = 308; +pub const __NR_setpgid: usize = 109; +pub const __NR_setpriority: usize = 141; +pub const __NR_setregid: usize = 114; +pub const __NR_setresgid: usize = 119; +pub const __NR_setresuid: usize = 117; +pub const __NR_setreuid: usize = 113; +pub const __NR_setrlimit: usize = 160; +pub const __NR_setsid: usize = 112; +pub const __NR_setsockopt: usize = 54; +pub const __NR_settimeofday: usize = 164; +pub const __NR_setuid: usize = 105; +pub const __NR_setxattr: usize = 188; +pub const __NR_shmat: usize = 30; +pub const __NR_shmctl: usize = 31; +pub const __NR_shmdt: usize = 67; +pub const __NR_shmget: usize = 29; +pub const __NR_shutdown: usize = 48; +pub const __NR_sigaltstack: usize = 131; +pub const __NR_signalfd: usize = 282; +pub const __NR_signalfd4: usize = 289; +pub const __NR_socket: usize = 41; +pub const __NR_socketpair: usize = 53; +pub const __NR_splice: usize = 275; +pub const __NR_stat: usize = 4; +pub const __NR_statfs: usize = 137; +pub const __NR_statx: usize = 332; +pub const __NR_swapoff: usize = 168; +pub const __NR_swapon: usize = 167; +pub const __NR_symlink: usize = 88; +pub const __NR_symlinkat: usize = 266; +pub const __NR_sync: usize = 162; +pub const __NR_sync_file_range: usize = 277; +pub const __NR_syncfs: usize = 306; +pub const __NR_sysfs: usize = 139; +pub const __NR_sysinfo: usize = 99; +pub const __NR_syslog: usize = 103; +pub const __NR_tee: usize = 276; +pub const __NR_tgkill: usize = 234; +pub const __NR_time: usize = 201; +pub const __NR_timer_create: usize = 222; +pub const __NR_timer_delete: usize = 226; +pub const __NR_timer_getoverrun: usize = 225; +pub const __NR_timer_gettime: usize = 224; +pub const __NR_timer_settime: usize = 223; +pub const __NR_timerfd_create: usize = 283; +pub const __NR_timerfd_gettime: usize = 287; +pub const __NR_timerfd_settime: usize = 286; +pub const __NR_times: usize = 100; +pub const __NR_tkill: usize = 200; +pub const __NR_truncate: usize = 76; +pub const __NR_tuxcall: usize = 184; +pub const __NR_umask: usize = 95; +pub const __NR_umount2: usize = 166; +pub const __NR_uname: usize = 63; +pub const __NR_unlink: usize = 87; +pub const __NR_unlinkat: usize = 263; +pub const __NR_unshare: usize = 272; +pub const __NR_uselib: usize = 134; +pub const __NR_userfaultfd: usize = 323; +pub const __NR_ustat: usize = 136; +pub const __NR_utime: usize = 132; +pub const __NR_utimensat: usize = 280; +pub const __NR_utimes: usize = 235; +pub const __NR_vfork: usize = 58; +pub const __NR_vhangup: usize = 153; +pub const __NR_vmsplice: usize = 278; +pub const __NR_vserver: usize = 236; +pub const __NR_wait4: usize = 61; +pub const __NR_waitid: usize = 247; +pub const __NR_write: usize = 1; +pub const __NR_writev: usize = 20; diff --git a/src/header/sys_syslog/cbindgen.toml b/src/header/sys_syslog/cbindgen.toml new file mode 100644 index 0000000000..9f62d1a556 --- /dev/null +++ b/src/header/sys_syslog/cbindgen.toml @@ -0,0 +1,11 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/syslog.h.html +# +# There are no spec quotations relating to includes +include_guard = "_RELIBC_SYS_SYSLOG_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_syslog/linux.rs b/src/header/sys_syslog/linux.rs new file mode 100644 index 0000000000..6c4731d2c2 --- /dev/null +++ b/src/header/sys_syslog/linux.rs @@ -0,0 +1,74 @@ +use crate::{ + error::{Errno, Result}, + fs::File, + header::{ + sys_socket::{ + connect, + constants::{AF_UNIX, SOCK_CLOEXEC, SOCK_DGRAM}, + sockaddr, socket, + }, + sys_un::sockaddr_un, + unistd::close, + }, + io::BufWriter, + platform::ERRNO, +}; + +use super::logger::LogSink; + +/// Unix Domain Socket connected to /dev/log. +pub struct LogFile(BufWriter); + +impl LogSink for LogFile { + type Sink = BufWriter; + + fn open() -> Result + where + Self: Sized, + { + let log_addr = { + let path = c"/dev/log"; + let mut sockaddr: sockaddr_un = unsafe { core::mem::zeroed() }; + sockaddr.sun_family = AF_UNIX as _; + unsafe { + core::ptr::copy_nonoverlapping( + path.as_ptr(), + sockaddr.sun_path.as_mut_ptr(), + path.count_bytes(), + ) + }; + path + }; + let log_fd = { + let result = unsafe { socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0) }; + if result > 0 { + Ok(result) + } else { + Err(Errno(ERRNO.get())) + } + }?; + + // SAFETY: + // * connect handles invalid descriptors. + // * log_addr is a sockaddr_un so the size is correct. + if unsafe { + connect( + log_fd, + (&raw const log_addr).cast::(), + size_of::() as u32, + ) < 0 + } { + // In case close sets ERRNO. + let e = ERRNO.get(); + close(log_fd); + return Err(Errno(e)); + } + + Ok(Self(BufWriter::new(File::new(log_fd)))) + } + + #[inline(always)] + fn writer(&mut self) -> &mut Self::Sink { + &mut self.0 + } +} diff --git a/src/header/sys_syslog/logger.rs b/src/header/sys_syslog/logger.rs new file mode 100644 index 0000000000..9a9f09c0be --- /dev/null +++ b/src/header/sys_syslog/logger.rs @@ -0,0 +1,274 @@ +use alloc::{borrow::ToOwned, string::String}; +use core::{ffi::VaList, ptr::null_mut}; + +use crate::{ + c_str::CStr, + error::Result, + header::{ + stdio::{fprintf, printf::printf, stderr}, + time::time, + unistd::getpid, + }, + io::Write, + platform::{ + self, + types::{c_char, c_int}, + }, + sync::Mutex, +}; + +use bitflags::bitflags; +use chrono::{DateTime, Utc}; + +use super::{ + LOG_ALERT, LOG_AUTH, LOG_AUTHPRIV, LOG_CONS, LOG_CRIT, LOG_CRON, LOG_DAEMON, LOG_DEBUG, + LOG_EMERG, LOG_ERR, LOG_FTP, LOG_INFO, LOG_KERN, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, + LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, LOG_LPR, LOG_MAIL, LOG_MASK, + LOG_NDELAY, LOG_NEWS, LOG_NOTICE, LOG_NOWAIT, LOG_ODELAY, LOG_PERROR, LOG_PID, LOG_SYSLOG, + LOG_UPTO, LOG_USER, LOG_UUCP, LOG_WARNING, sys::LogFile, +}; + +pub(super) static LOGGER: Mutex> = Mutex::new(LogParams::new(None)); + +pub(super) struct LogParams { + /// Identity prepended to each log message. POSIX does not specific what to do when it's empty, + /// but the program name is a common default. + ident: String, + pub opt: Config, + pub mask: Priority, + writer: Option, +} + +impl LogParams { + pub const fn new(writer: Option) -> Self { + LogParams { + ident: String::new(), + opt: Config::DelayOpen, + mask: Priority::from_bits_truncate(Priority::User.bits() | Priority::UpToDebug.bits()), + writer, + } + } + + pub fn write_log(&mut self, priority: Priority, message: CStr<'_>, ap: VaList) { + if message.is_empty() { + return; + } + if self.ident.is_empty() { + self.set_identity(None); + } + + let epoch = unsafe { time(null_mut()) }; + let currtime: DateTime = DateTime::from_timestamp(epoch, 0).unwrap_or_default(); + let currtime_s = currtime.format("%b %e %T %Y"); + let pid = self.opt.contains(Config::Pid).then(|| getpid()); + + // journald from systemd rewrites log messages from syslog into its own style. We'll + // still use the same style as other libc even though it's implementation specific. + let mut buffer = if let Some(pid) = pid { + format!( + "<{}>{} {}{}: ", + priority.bits(), + currtime_s, + self.ident, + pid + ) + .into_bytes() + } else { + format!("<{}>{} {}: ", priority.bits(), currtime_s, self.ident).into_bytes() + }; + let prefix = buffer.len(); + + // SAFETY: + // * Assumes caller passed in a valid C string; printf should handle that invariant. + // * `buffer` grows to fit the formatted string. + unsafe { printf(&mut buffer, message, ap) }; + buffer.extend(b"\n\0"); + + if self.maybe_open_logger().is_ok() + && self + .writer + .as_mut() + .map(|w| w.writer().write_all(&buffer).is_err()) + .unwrap_or(true) + { + // Try reopening the log file once and retrying as musl does. + if !self + .open_logger() + .is_ok() + .then(|| { + self.writer + .as_mut() + .map(|w| w.writer().write_all(&buffer).is_ok()) + .unwrap_or_default() + }) + .unwrap_or_default() + && self.opt.contains(Config::Console) + { + // TODO: Log error to /dev/console & Redox equivalent + } + } + if self.opt.contains(Config::PError) { + // SAFETY: + // * `ident` is a valid byte string that is NUL terminated when set. + // * `buffer` is a valid byte string that is NUL terminated above. + unsafe { + // musl and glibc only print the message rather than the prefix + message to stderr + fprintf( + stderr, + c"%s: %s".as_ptr(), + self.ident.as_ptr().cast::(), + buffer[prefix..].as_ptr().cast::(), + ); + } + } + } + + /// Set or clear log identity from a C string. + /// + /// Null or empty identities are valid as it just resets the global ident. + pub fn set_identity_cstr(&mut self, ident: Option>) { + let ident = ident + .and_then(|ident| (!ident.is_empty()).then(|| ident.to_str().ok())) + .flatten(); + self.set_identity(ident); + } + + /// Set or clear log identity. + /// + /// The log identity is prepended to each message. If unset, the program name will be used as a + /// default. + pub fn set_identity(&mut self, ident: Option<&str>) { + self.ident = ident + .and_then(|ident| { + let ident = ident.bytes().chain([0]).collect(); + // SAFETY: Already validated + Some(unsafe { String::from_utf8_unchecked(ident) }) + }) + .unwrap_or_else(|| { + unsafe { CStr::from_nullable_ptr(platform::program_invocation_short_name) } + .and_then(|name| { + let name = name.to_str().ok()?.bytes().chain([0]).collect(); + // SAFETY: Validated above + Some(unsafe { String::from_utf8_unchecked(name) }) + }) + .unwrap_or_else(|| "\0".to_owned()) + }); + } + + /// Open the internal [`LogFile`] if it's not open. + pub fn maybe_open_logger(&mut self) -> Result<()> { + if self.writer.is_none() { + self.open_logger() + } else { + Ok(()) + } + } + + /// Open or reopen the internal [`LogFile`]. + pub fn open_logger(&mut self) -> Result<()> { + L::open().map(|file| { + self.writer.replace(file); + }) + } + + /// Close the open writer to the system logger (optional). + pub fn close(&mut self) { + self.writer.take(); + } +} + +/// Operating system specific log handling. +pub(super) trait LogSink { + type Sink: Write; + + fn open() -> Result + where + Self: Sized; + + fn writer(&mut self) -> &mut Self::Sink; +} + +bitflags! { + #[derive(Clone, Copy)] + pub struct Config: c_int { + const Pid = LOG_PID; + const Console = LOG_CONS; + const DelayOpen = LOG_ODELAY; + const NoDelay = LOG_NDELAY; + const NoWait = LOG_NOWAIT; + const PError = LOG_PERROR; + } +} + +bitflags! { + /// Packed Facility-Priority bit field. + #[derive(Clone, Copy)] + pub struct Priority: c_int { + const Emerg = LOG_EMERG; + const Alert = LOG_ALERT; + const Crit = LOG_CRIT; + const Err = LOG_ERR; + const Warn = LOG_WARNING; + const Notice = LOG_NOTICE; + const Info = LOG_INFO; + const Debug = LOG_DEBUG; + + const UpToEmerg = LOG_UPTO(LOG_EMERG); + const UpToAlert = LOG_UPTO(LOG_ALERT); + const UpToCrit = LOG_UPTO(LOG_CRIT); + const UpToErr = LOG_UPTO(LOG_ERR); + const UpToWarn = LOG_UPTO(LOG_WARNING); + const UpToNotice = LOG_UPTO(LOG_NOTICE); + const UpToInfo = LOG_UPTO(LOG_INFO); + const UpToDebug = LOG_UPTO(LOG_DEBUG); + + const Kern = LOG_KERN; + const User = LOG_USER; + const Mail = LOG_MAIL; + const Daemon = LOG_DAEMON; + const Auth = LOG_AUTH; + const Syslog = LOG_SYSLOG; + const Printer = LOG_LPR; + const News = LOG_NEWS; + const UUCP = LOG_UUCP; + const CRON = LOG_CRON; + const AuthPriv = LOG_AUTHPRIV; + const FTP = LOG_FTP; + const Local0 = LOG_LOCAL0; + const Local1 = LOG_LOCAL1; + const Local2 = LOG_LOCAL2; + const Local3 = LOG_LOCAL3; + const Local4 = LOG_LOCAL4; + const Local5 = LOG_LOCAL5; + const Local6 = LOG_LOCAL6; + const Local7 = LOG_LOCAL7; + + // Internal constants for extracting facility or priority from a packed bitfield. + const FacilityMask = 0x3ff; + const PriorityMask = !Self::FacilityMask.bits(); + } +} + +impl Priority { + /// Keep facility but replace severity mask. + pub fn with_mask(self, mask: c_int) -> Option { + // Fail on invalid bits and drop invalid facility bits. + let mask = Self::from_bits(mask)? & Self::FacilityMask; + let facility = self & Self::PriorityMask; + Some(mask | facility) + } + + /// Keep mask but replace facility. + pub fn with_facility(self, facility: c_int) -> Option { + // Fail on invalid bits and drop invalid priority bits. + let facility = Self::from_bits(facility)? & Self::PriorityMask; + let mask = self & Self::FacilityMask; + Some(mask | facility) + } + + /// Returns if a message of `priority` should be retained by this mask. + pub fn should_log(self, priority: Self) -> bool { + (self & Self::FacilityMask) + .contains(Priority::from_bits_truncate(LOG_MASK(priority.bits()))) + } +} diff --git a/src/header/sys_syslog/mod.rs b/src/header/sys_syslog/mod.rs new file mode 100644 index 0000000000..9aea6ed58a --- /dev/null +++ b/src/header/sys_syslog/mod.rs @@ -0,0 +1,146 @@ +//! `syslog.h` implementation. +//! +//! See . + +// Exported as both syslog.h and sys/syslog.h. + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +pub mod logger; + +use core::ffi::VaList; + +use crate::{ + c_str::CStr, + platform::types::{c_char, c_int}, +}; +use logger::{LOGGER, Priority}; + +/// Record the caller's PID in log messages. +pub const LOG_PID: c_int = 0x01; +/// Write to /dev/console if [`syslog`] fails. +pub const LOG_CONS: c_int = 0x02; +/// Open the log on the first call to [`syslog`] rather than opening it early. +/// This is the default behavior and setting or unsetting this option does nothing. +pub const LOG_ODELAY: c_int = 0x04; +/// Open the log file immediately. +pub const LOG_NDELAY: c_int = 0x08; +pub const LOG_NOWAIT: c_int = 0x10; +/// Print log message to stderr as well as the log. +pub const LOG_PERROR: c_int = 0x20; + +pub const LOG_KERN: c_int = 0 << 3; +pub const LOG_USER: c_int = 1 << 3; +pub const LOG_MAIL: c_int = 2 << 3; +pub const LOG_DAEMON: c_int = 3 << 3; +pub const LOG_AUTH: c_int = 4 << 3; +pub const LOG_SYSLOG: c_int = 5 << 3; +pub const LOG_LPR: c_int = 6 << 3; +pub const LOG_NEWS: c_int = 7 << 3; +pub const LOG_UUCP: c_int = 8 << 3; +pub const LOG_CRON: c_int = 9 << 3; +pub const LOG_AUTHPRIV: c_int = 10 << 3; +pub const LOG_FTP: c_int = 11 << 3; + +pub const LOG_LOCAL0: c_int = 16 << 3; +pub const LOG_LOCAL1: c_int = 17 << 3; +pub const LOG_LOCAL2: c_int = 18 << 3; +pub const LOG_LOCAL3: c_int = 19 << 3; +pub const LOG_LOCAL4: c_int = 20 << 3; +pub const LOG_LOCAL5: c_int = 21 << 3; +pub const LOG_LOCAL6: c_int = 22 << 3; +pub const LOG_LOCAL7: c_int = 23 << 3; +pub const LOG_NFACILITIES: c_int = 24; + +// Priorities +pub const LOG_EMERG: c_int = 0; +pub const LOG_ALERT: c_int = 1; +pub const LOG_CRIT: c_int = 2; +pub const LOG_ERR: c_int = 3; +pub const LOG_WARNING: c_int = 4; +pub const LOG_NOTICE: c_int = 5; +pub const LOG_INFO: c_int = 6; +pub const LOG_DEBUG: c_int = 7; + +/// Create a mask that includes all levels up to a certain priority. +#[unsafe(no_mangle)] +pub const extern "C" fn LOG_UPTO(p: c_int) -> c_int { + (1 << (p + 1)) - 1 +} + +/// Create a mask that enables a single priority. +#[unsafe(no_mangle)] +pub const extern "C" fn LOG_MASK(p: c_int) -> c_int { + 1 << p +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setlogmask(mask: c_int) -> c_int { + let mut params = LOGGER.lock(); + let old = params.mask.bits(); + if mask != 0 + && let Some(mask) = params.mask.with_mask(mask) + { + params.mask = mask; + } + old +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn openlog(ident: *const c_char, opt: c_int, facility: c_int) { + let ident = unsafe { CStr::from_nullable_ptr(ident) }; + let conf = logger::Config::from_bits_truncate(opt); + + let mut params = LOGGER.lock(); + params.set_identity_cstr(ident); + params.opt = conf; + params.mask = params.mask.with_facility(facility).unwrap_or( + params + .mask + .with_facility(Priority::User.bits()) + .expect("`User` is a valid syslog facility"), + ); + + // Ensure log is ready to write now instead of checking on the first message. + if conf.contains(logger::Config::NoDelay) + && let Ok(()) = params.open_logger() + {}; // TODO handle error +} + +/// See . +/// +/// Non-POSIX, 4.3BSD-Reno. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vsyslog(priority: c_int, message: *const c_char, ap: VaList) { + let Some(message) = (unsafe { CStr::from_nullable_ptr(message) }) else { + return; + }; + let Some(priority) = Priority::from_bits(priority) else { + return; + }; + + let mut logger = LOGGER.lock(); + if logger.mask.should_log(priority) { + logger.write_log(priority, message, ap); + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn syslog(priority: c_int, message: *const c_char, mut __valist: ...) { + unsafe { vsyslog(priority, message, __valist.as_va_list()) }; +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn closelog() { + LOGGER.lock().close(); +} diff --git a/src/header/sys_syslog/redox.rs b/src/header/sys_syslog/redox.rs new file mode 100644 index 0000000000..c396224e6a --- /dev/null +++ b/src/header/sys_syslog/redox.rs @@ -0,0 +1,20 @@ +use crate::{error::Result, fs::File, header::fcntl, io::BufWriter}; + +use super::logger::LogSink; + +/// Write logs to Redox's log scheme. +pub struct LogFile(BufWriter); + +impl LogSink for LogFile { + type Sink = BufWriter; + + #[inline(always)] + fn open() -> Result { + File::open(c"/scheme/log".into(), fcntl::O_WRONLY).map(|file| Self(BufWriter::new(file))) + } + + #[inline(always)] + fn writer(&mut self) -> &mut Self::Sink { + &mut self.0 + } +} diff --git a/src/header/sys_time/cbindgen.toml b/src/header/sys_time/cbindgen.toml new file mode 100644 index 0000000000..9e28d70f11 --- /dev/null +++ b/src/header/sys_time/cbindgen.toml @@ -0,0 +1,36 @@ +# sys/types.h brings in features.h (needed for #[deprecated] annotations) +sys_includes = ["sys/types.h", "sys/select.h"] +include_guard = "_SYS_TIME_H" +language = "C" +after_includes = """ +#include // for timespec + +#define timeradd(x,y,res) (void) (\ + (res)->tv_sec = (x)->tv_sec + (y)->tv_sec + (((x)->tv_usec + (y)->tv_usec) / 1000000), \ + (res)->tv_usec = ((x)->tv_usec + (y)->tv_usec) % 1000000 \ + ) +#define timersub(x,y,res) (void) ( \ + (res)->tv_sec = (x)->tv_sec - (y)->tv_sec, \ + (res)->tv_usec = ((x)->tv_usec - (y)->tv_usec), \ + ((res)->tv_usec < 0) && ((res)->tv_sec -= 1, (res)->tv_usec += 1000000) \ + ) +#define timerclear(t) (void) ( \ + (t)->tv_sec = 0, \ + (t)->tv_usec = 0 \ + ) +#define timerisset(t) ((t)->tv_sec || (t)->tv_usec) +#define timercmp(x,y,op) ((x)->tv_sec == (y)->tv_sec ? \ + (x)->tv_usec op (y)->tv_usec \ + : \ + (x)->tv_sec op (y)->tv_sec) +""" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"timespec" = "struct timespec" +"timeval" = "struct timeval" diff --git a/src/header/sys_time/mod.rs b/src/header/sys_time/mod.rs new file mode 100644 index 0000000000..43b27324a9 --- /dev/null +++ b/src/header/sys_time/mod.rs @@ -0,0 +1,145 @@ +//! `sys/time.h` implementation. +//! +//! See . +//! +//! Note that since the Open Group Base Specifications Issue 8, the +//! [`timeval`] struct has been moved to +//! the [`sys/select.h`](crate::header::sys_select) header. + +use crate::{ + c_str::CStr, + error::ResultExt, + header::{bits_timespec::timespec, sys_select::timeval}, + out::Out, + platform::{ + Pal, PalSignal, Sys, + types::{c_char, c_int, c_long}, + }, +}; +use core::ptr::null; + +/// See . +/// +/// # Deprecation +/// The `ITIMER_REAL` symbolic constant was marked obsolescent in the Open +/// Group Base Specifications Issue 7, and removed in Issue 8. +#[deprecated] +pub const ITIMER_REAL: c_int = 0; + +/// See . +/// +/// # Deprecation +/// The `ITIMER_VIRTUAL` symbolic constant was marked obsolescent in the Open +/// Group Base Specifications Issue 7, and removed in Issue 8. +#[deprecated] +pub const ITIMER_VIRTUAL: c_int = 1; + +/// See . +/// +/// # Deprecation +/// The `ITIMER_PROF` symbolic constant was marked obsolescent in the Open +/// Group Base Specifications Issue 7, and removed in Issue 8. +#[deprecated] +pub const ITIMER_PROF: c_int = 2; + +/// See . +/// +/// # Deprecation +/// The `itimerval` struct was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[repr(C)] +#[derive(Default)] +pub struct itimerval { + pub it_interval: timeval, + pub it_value: timeval, +} + +/// Non-POSIX, see . +#[repr(C)] +#[derive(Default)] +pub struct timezone { + pub tz_minuteswest: c_int, + pub tz_dsttime: c_int, +} + +/// See . +/// +/// # Deprecation +/// The `getitimer()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[allow(deprecated)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getitimer(which: c_int, value: *mut itimerval) -> c_int { + Sys::getitimer(which, unsafe { &mut *value }) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// See also +/// for further details on the `tzp` argument. +/// +/// # Deprecation +/// The `gettimeofday()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int { + Sys::gettimeofday(unsafe { Out::nonnull(tp) }, unsafe { Out::nullable(tzp) }) + .map(|()| 0) + .or_minus_one_errno() +} + +// `select()` declared in `sys/select.h`, as specified in modern POSIX + +/// See . +/// +/// # Deprecation +/// The `setitimer()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[allow(deprecated)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn setitimer( + which: c_int, + value: *const itimerval, + ovalue: *mut itimerval, +) -> c_int { + // TODO setitimer is unimplemented on Redox + Sys::setitimer(which, unsafe { &*value }, unsafe { ovalue.as_mut() }) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// # Deprecation +/// The `utimes()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and then unmarked in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn utimes(path: *const c_char, times: *const timeval) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + // Nullptr is valid here, it means "use current time" + let times_spec = if times.is_null() { + null() + } else { + [ + timespec { + tv_sec: unsafe { (*times.offset(0)).tv_sec }, + tv_nsec: c_long::from(unsafe { (*times.offset(0)).tv_usec }) * 1000, + }, + timespec { + tv_sec: unsafe { (*times.offset(1)).tv_sec }, + tv_nsec: c_long::from(unsafe { (*times.offset(1)).tv_usec }) * 1000, + }, + ] + .as_ptr() + }; + unsafe { Sys::utimens(path, times_spec) } + .map(|()| 0) + .or_minus_one_errno() +} diff --git a/src/header/sys_timeb/cbindgen.toml b/src/header/sys_timeb/cbindgen.toml new file mode 100644 index 0000000000..95e95fd8fb --- /dev/null +++ b/src/header/sys_timeb/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["sys/types.h"] +include_guard = "_SYS_TIMEB_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_timeb/mod.rs b/src/header/sys_timeb/mod.rs new file mode 100644 index 0000000000..8935f5b474 --- /dev/null +++ b/src/header/sys_timeb/mod.rs @@ -0,0 +1,64 @@ +//! `sys/timeb.h` implementation. +//! +//! See . +//! +//! # Deprecation +//! The `ftime()` function was marked as legacy in the Open Group Base +//! Specifications Issue 6, and the entire `sys/timeb.h` header was removed in +//! Issue 7. + +use core::ptr::NonNull; + +#[allow(deprecated)] +use crate::header::sys_time::gettimeofday; +use crate::{ + header::{sys_select::timeval, sys_time::timezone}, + platform::types::{c_int, c_short, c_ushort, time_t}, +}; + +/// See . +#[deprecated] +#[repr(C)] +#[derive(Default)] +pub struct timeb { + pub time: time_t, + pub millitm: c_ushort, + pub timezone: c_short, + pub dstflag: c_short, +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `tp` is convertible to a `&mut +/// MaybeUninit`. +#[deprecated] +#[allow(deprecated)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ftime(tp: *mut timeb) -> c_int { + // SAFETY: the caller is required to ensure that the pointer is valid. + let tp_maybe_uninit = unsafe { NonNull::new_unchecked(tp).as_uninit_mut() }; + + let mut tv = timeval::default(); + let mut tz = timezone::default(); + + // SAFETY: tv and tz are created above, and thus will coerce to valid + // pointers. + if unsafe { + #[allow(deprecated)] + gettimeofday(&raw mut tv, &raw mut tz) + } < 0 + { + return -1; + } + + #[allow(deprecated)] + tp_maybe_uninit.write(timeb { + time: tv.tv_sec, + millitm: (tv.tv_usec / 1000) as c_ushort, + timezone: tz.tz_minuteswest as c_short, + dstflag: tz.tz_dsttime as c_short, + }); + + 0 +} diff --git a/src/header/sys_timerfd/cbindgen.toml b/src/header/sys_timerfd/cbindgen.toml new file mode 100644 index 0000000000..3b44695d44 --- /dev/null +++ b/src/header/sys_timerfd/cbindgen.toml @@ -0,0 +1,23 @@ +sys_includes = ["time.h"] +after_includes = """ +#include // for itimerspec +""" +include_guard = "_SYS_TIMERFD_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export] +include = [ + "TFD_CLOEXEC", + "TFD_NONBLOCK", + "TFD_TIMER_ABSTIME", + "TFD_TIMER_CANCEL_ON_SET", +] + +[export.rename] +"itimerspec" = "struct itimerspec" diff --git a/src/header/sys_timerfd/mod.rs b/src/header/sys_timerfd/mod.rs new file mode 100644 index 0000000000..916066fc78 --- /dev/null +++ b/src/header/sys_timerfd/mod.rs @@ -0,0 +1,89 @@ +//! `sys/timerfd.h` implementation. +//! +//! Non-POSIX, see . +use alloc::{collections::BTreeMap, format}; +use core::{mem, slice, sync::atomic::{AtomicBool, Ordering}}; +use crate::{ + c_str::{CStr, CString}, + error::{Errno, ResultExt}, + header::{ + bits_timespec::timespec, + errno::{EBADF, EFAULT, EINVAL, EIO}, + fcntl::{O_CLOEXEC, O_NONBLOCK, O_RDWR}, + }, + out::Out, + platform::{Pal, Sys, types::{c_int, clockid_t}}, + sync::Mutex, +}; +pub use crate::header::time::itimerspec; + +pub const TFD_CLOEXEC: c_int = 0x80000; +pub const TFD_NONBLOCK: c_int = 0x800; +pub const TFD_TIMER_ABSTIME: c_int = 0x1; +pub const TFD_TIMER_CANCEL_ON_SET: c_int = 0x2; + +const NSEC_PER_SEC: i64 = 1_000_000_000; + +struct TimerState { clockid: clockid_t } + +static MAP_INIT: AtomicBool = AtomicBool::new(false); +static TIMERFD_STATE: Mutex>> = Mutex::new(None); + +fn with_map(f: impl FnOnce(&mut BTreeMap) -> R) -> R { + let mut guard = TIMERFD_STATE.lock(); + if !MAP_INIT.load(Ordering::Acquire) { *guard = Some(BTreeMap::new()); MAP_INIT.store(true, Ordering::Release); } + f(guard.as_mut().unwrap()) +} +fn add_timespec(a: ×pec, b: ×pec) -> timespec { + let total_nsec = a.tv_nsec as i64 + b.tv_nsec as i64; + let mut sec = a.tv_sec + b.tv_sec + (total_nsec / NSEC_PER_SEC) as i64; + let mut nsec = (total_nsec % NSEC_PER_SEC) as i64; + if nsec < 0 { sec -= 1; nsec += NSEC_PER_SEC; } + timespec { tv_sec: sec, tv_nsec: nsec } +} +fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> { + match Sys::read(fd, buf)? { n if n == buf.len() => Ok(()), _ => Err(Errno(EIO)) } +} +fn write_exact(fd: c_int, buf: &[u8]) -> Result<(), Errno> { + match Sys::write(fd, buf)? { n if n == buf.len() => Ok(()), _ => Err(Errno(EIO)) } +} + +#[unsafe(no_mangle)] +pub extern "C" fn timerfd_create(clockid: clockid_t, flags: c_int) -> c_int { + if flags & !(TFD_CLOEXEC | TFD_NONBLOCK) != 0 { return Err::(Errno(EINVAL)).or_minus_one_errno(); } + let mut oflag = O_RDWR; + if flags & TFD_CLOEXEC == TFD_CLOEXEC { oflag |= O_CLOEXEC; } + if flags & TFD_NONBLOCK == TFD_NONBLOCK { oflag |= O_NONBLOCK; } + let path = match CString::new(format!("/scheme/time/{clockid}")) { Ok(p) => p, Err(_) => return Err::(Errno(EINVAL)).or_minus_one_errno() }; + let fd = match Sys::open(CStr::borrow(&path), oflag, 0) { Ok(fd) => fd, Err(Errno(e)) => return Err::(Errno(e)).or_minus_one_errno() }; + with_map(|map| { map.insert(fd, TimerState { clockid }); }); + fd +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const itimerspec, old: *mut itimerspec) -> c_int { + if flags & !(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET) != 0 { return Err::(Errno(EINVAL)).or_minus_one_errno(); } + if new.is_null() { return Err::(Errno(EFAULT)).or_minus_one_errno(); } + if !old.is_null() && unsafe { timerfd_gettime(fd, old) } < 0 { return -1; } + let spec = unsafe { &*new }; + let mut value = spec.it_value.clone(); + // TFD_TIMER_CANCEL_ON_SET: flag accepted; clock-change notification requires + // kernel infrastructure not yet available. This is a bounded compatibility surface. + if flags & TFD_TIMER_ABSTIME == 0 { + let clockid = with_map(|map| map.get(&fd).map_or(0, |s| s.clockid)); + let mut now = timespec::default(); + if Sys::clock_gettime(clockid, Out::from_mut(&mut now)).is_err() { return Err::(Errno(EBADF)).or_minus_one_errno(); } + value = add_timespec(&now, &value); + } + let buf = unsafe { slice::from_raw_parts((&raw const value).cast::(), mem::size_of::()) }; + write_exact(fd, buf).map(|()| 0).or_minus_one_errno() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timerfd_gettime(fd: c_int, curr: *mut itimerspec) -> c_int { + if curr.is_null() { return Err::(Errno(EFAULT)).or_minus_one_errno(); } + let curr = unsafe { &mut *curr }; + curr.it_interval = timespec::default(); + let buf = unsafe { slice::from_raw_parts_mut((&raw mut curr.it_value).cast::(), mem::size_of::()) }; + read_exact(fd, buf).map(|()| 0).or_minus_one_errno() +} diff --git a/src/header/sys_times/cbindgen.toml b/src/header/sys_times/cbindgen.toml new file mode 100644 index 0000000000..fe95544def --- /dev/null +++ b/src/header/sys_times/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["sys/types.h"] +include_guard = "_SYS_TIMES_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_times/mod.rs b/src/header/sys_times/mod.rs new file mode 100644 index 0000000000..8149f352bf --- /dev/null +++ b/src/header/sys_times/mod.rs @@ -0,0 +1,21 @@ +//! `sys/times.h` implementation. +//! +//! See . + +use crate::platform; +use platform::{Pal, Sys, types::clock_t}; + +/// See . +#[repr(C)] +pub struct tms { + tms_utime: clock_t, + tms_stime: clock_t, + tms_cutime: clock_t, + tms_cstime: clock_t, +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn times(out: *mut tms) -> clock_t { + Sys::times(out) +} diff --git a/src/header/sys_types/cbindgen.toml b/src/header/sys_types/cbindgen.toml new file mode 100644 index 0000000000..f0a2d8c655 --- /dev/null +++ b/src/header/sys_types/cbindgen.toml @@ -0,0 +1,24 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_types.h.html +# +# There are no spec quotations relating to includes +# +# - sys/types_internal.h are fundamental C types (which includes stddef.h) +# - bits/pthread.h is separate for convenience +# - features.h saves importing separately for any header that imports sys/types.h +# +# no default C includes - they cause recursive dependencies and do weird stuff +no_includes = true +sys_includes = [ + # Import most necessary, internal types first + "sys/types/internal.h", + + "bits/pthread.h", + "features.h", +] + +include_guard = "_SYS_TYPES_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/header/sys_types/mod.rs b/src/header/sys_types/mod.rs new file mode 100644 index 0000000000..601f262aac --- /dev/null +++ b/src/header/sys_types/mod.rs @@ -0,0 +1,8 @@ +//! `sys/types.h` implementation. +//! +//! See . +//! +//! Note that the `useconds_t` type provided in the `sys/types.h` header was +//! removed in the Open Group Base Specifications Issue 7, see +//! +//! for the old specification. diff --git a/src/header/sys_types_internal/cbindgen.toml b/src/header/sys_types_internal/cbindgen.toml new file mode 100644 index 0000000000..9bf86018ec --- /dev/null +++ b/src/header/sys_types_internal/cbindgen.toml @@ -0,0 +1,45 @@ +sys_includes = ["stddef.h"] +# TODO: figure out how to export void* type +after_includes = """ + +typedef void* timer_t; +""" +include_guard = "_RELIBC_SYS_TYPES_INTERNAL_H" +language = "C" +style = "Type" +no_includes = true + +[export] +include = [ + "blksize_t", + "dev_t", + "ino_t", + "reclen_t", + "gid_t", + "uid_t", + "mode_t", + "nlink_t", + "off_t", + "pid_t", + "id_t", + "ssize_t", + "time_t", + "useconds_t", + "suseconds_t", + "clock_t", + "clockid_t", + "blkcnt_t", + "fsblkcnt_t", + "fsfilcnt_t", + "u_char", + "uchar", + "u_short", + "ushort", + "u_int", + "uint", + "u_long", + "ulong", + "quad_t", + "u_quad_t", + "caddr_t" +] diff --git a/src/header/sys_types_internal/mod.rs b/src/header/sys_types_internal/mod.rs new file mode 100644 index 0000000000..4c4cde7e9d --- /dev/null +++ b/src/header/sys_types_internal/mod.rs @@ -0,0 +1,74 @@ +//! `sys/types.h` implementation. +//! +//! See . +//! +//! Note that the `useconds_t` type provided in the `sys/types.h` header was +//! removed in the Open Group Base Specifications Issue 7, see +//! +//! for the old specification. + +use crate::platform::types::{ + c_char, c_int, c_long, c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort, +}; + +/// Used for block sizes. +pub type blksize_t = c_long; +/// Used for device IDs. +pub type dev_t = c_ulonglong; +/// Used for serial numbers. +pub type ino_t = c_ulonglong; +/// Used for directory entry lengths. +pub type reclen_t = c_ushort; +/// Used for group IDs. +pub type gid_t = c_int; +/// Used for user IDs. +pub type uid_t = c_int; +/// Used for some file attributes. +pub type mode_t = c_int; +/// Used for link counts. +pub type nlink_t = c_ulong; +/// Used for file sizes. +pub type off_t = c_longlong; +/// Used for process IDs and process group IDs. +pub type pid_t = c_int; +/// Used as a general identifier; can be used to contain at least a pid_t, uid_t, or gid_t. +pub type id_t = c_uint; +/// Used for a count of bytes or an error indication. +pub type ssize_t = c_long; +/// Used for time in seconds. +pub type time_t = c_longlong; +pub type useconds_t = c_uint; + +#[cfg(target_os = "linux")] +/// Used for time in microseconds. +pub type suseconds_t = c_long; +#[cfg(not(target_os = "linux"))] +/// Used for time in microseconds. +pub type suseconds_t = c_int; + +/// Used for system times in clock ticks or CLOCKS_PER_SEC. +pub type clock_t = c_long; +/// Used for clock ID type in the clock and timer functions. +pub type clockid_t = c_int; + +// timer_t in cbindgen after_includes (how to export void* type?) + +/// Used for file block counts. +pub type blkcnt_t = c_longlong; + +/// Used for file system block counts. +pub type fsblkcnt_t = c_ulong; +/// Used for file system file counts. +pub type fsfilcnt_t = c_ulong; + +pub type u_char = c_uchar; +pub type uchar = c_uchar; +pub type u_short = c_ushort; +pub type ushort = c_ushort; +pub type u_int = c_uint; +pub type uint = c_uint; +pub type u_long = c_ulong; +pub type ulong = c_ulong; +pub type quad_t = c_longlong; +pub type u_quad_t = c_ulonglong; +pub type caddr_t = *mut c_char; diff --git a/src/header/sys_uio/cbindgen.toml b/src/header/sys_uio/cbindgen.toml new file mode 100644 index 0000000000..f24e59d38a --- /dev/null +++ b/src/header/sys_uio/cbindgen.toml @@ -0,0 +1,18 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_uio.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the ssize_t and size_t types as described in ." +# +# bits/iovec.h brings in sys/types.h +sys_includes = ["bits/iovec.h"] +include_guard = "_RELIBC_SYS_UIO_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"iovec" = "struct iovec" diff --git a/src/header/sys_uio/mod.rs b/src/header/sys_uio/mod.rs new file mode 100644 index 0000000000..1e3976ae76 --- /dev/null +++ b/src/header/sys_uio/mod.rs @@ -0,0 +1,92 @@ +//! `sys/uio.h` implementation. +//! +//! See . + +use core::slice; + +use crate::{ + header::{ + bits_iovec::{gather, iovec, scatter}, + errno, unistd, + }, + platform::{ + self, + types::{c_int, c_void, off_t, ssize_t}, + }, +}; + +pub const IOV_MAX: c_int = 1024; + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn preadv( + fd: c_int, + iov: *const iovec, + iovcnt: c_int, + offset: off_t, +) -> ssize_t { + if !(0..=IOV_MAX).contains(&iovcnt) { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + let iovs = unsafe { slice::from_raw_parts(iov, iovcnt as usize) }; + let mut vec = unsafe { gather(iovs) }; + + let ret = unsafe { unistd::pread(fd, vec.as_mut_ptr().cast::(), vec.len(), offset) }; + + unsafe { scatter(iovs, vec) }; + + ret +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pwritev( + fd: c_int, + iov: *const iovec, + iovcnt: c_int, + offset: off_t, +) -> ssize_t { + if !(0..=IOV_MAX).contains(&iovcnt) { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + let iovs = unsafe { slice::from_raw_parts(iov, iovcnt as usize) }; + let vec = unsafe { gather(iovs) }; + + unsafe { unistd::pwrite(fd, vec.as_ptr().cast::(), vec.len(), offset) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn readv(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t { + if !(0..=IOV_MAX).contains(&iovcnt) { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + let iovs = unsafe { slice::from_raw_parts(iov, iovcnt as usize) }; + let mut vec = unsafe { gather(iovs) }; + + let ret = unsafe { unistd::read(fd, vec.as_mut_ptr().cast::(), vec.len()) }; + + unsafe { scatter(iovs, vec) }; + + ret +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn writev(fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t { + if !(0..=IOV_MAX).contains(&iovcnt) { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + + let iovs = unsafe { slice::from_raw_parts(iov, iovcnt as usize) }; + let vec = unsafe { gather(iovs) }; + + unsafe { unistd::write(fd, vec.as_ptr().cast::(), vec.len()) } +} diff --git a/src/header/sys_un/cbindgen.toml b/src/header/sys_un/cbindgen.toml new file mode 100644 index 0000000000..d5ebca0687 --- /dev/null +++ b/src/header/sys_un/cbindgen.toml @@ -0,0 +1,19 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the sa_family_t type as described in ." +sys_includes = [] +after_includes = """ +#include // for sa_family_t from sys/socket.h +""" +include_guard = "_RELIBC_SYS_UN_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[export] +include = ["sockaddr_un"] + +[enum] +prefix_with_name = true diff --git a/src/header/sys_un/mod.rs b/src/header/sys_un/mod.rs new file mode 100644 index 0000000000..2a5ab35a3f --- /dev/null +++ b/src/header/sys_un/mod.rs @@ -0,0 +1,21 @@ +//! `sys/un.h` implementation. +//! +//! See . + +use crate::{header::bits_safamily_t::sa_family_t, platform::types::c_char}; + +/// See . +#[repr(C)] +pub struct sockaddr_un { + pub sun_family: sa_family_t, + pub sun_path: [c_char; 108], +} + +impl sockaddr_un { + pub fn path_offset(&self) -> usize { + let base = core::ptr::from_ref(self) as usize; + let path = &raw const self.sun_path as usize; + log::trace!("base: {:#X}, path: {:#X}", base, path); + path - base + } +} diff --git a/src/header/sys_utsname/cbindgen.toml b/src/header/sys_utsname/cbindgen.toml new file mode 100644 index 0000000000..4a5ff660d4 --- /dev/null +++ b/src/header/sys_utsname/cbindgen.toml @@ -0,0 +1,11 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_utsname.h.html +# +# There are no spec quotations relating to includes +include_guard = "_RELIBC_SYS_UTSNAME_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_utsname/mod.rs b/src/header/sys_utsname/mod.rs new file mode 100644 index 0000000000..49653883e1 --- /dev/null +++ b/src/header/sys_utsname/mod.rs @@ -0,0 +1,34 @@ +//! `sys/utsname.h` implementation. +//! +//! See . + +use crate::{ + error::ResultExt, + out::Out, + platform::{ + Pal, Sys, + types::{c_char, c_int}, + }, +}; + +pub const UTSLENGTH: usize = 65; + +/// See . +#[repr(C)] +#[derive(Clone, Debug, OutProject)] +pub struct utsname { + pub sysname: [c_char; UTSLENGTH], + pub nodename: [c_char; UTSLENGTH], + pub release: [c_char; UTSLENGTH], + pub version: [c_char; UTSLENGTH], + pub machine: [c_char; UTSLENGTH], + pub domainname: [c_char; UTSLENGTH], +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn uname(uts: *mut utsname) -> c_int { + Sys::uname(unsafe { Out::nonnull(uts) }) + .map(|()| 0) + .or_minus_one_errno() +} diff --git a/src/header/sys_wait/cbindgen.toml b/src/header/sys_wait/cbindgen.toml new file mode 100644 index 0000000000..25774409f8 --- /dev/null +++ b/src/header/sys_wait/cbindgen.toml @@ -0,0 +1,20 @@ +sys_includes = ["sys/types.h", "sys/resource.h", "signal.h"] +include_guard = "_RELIBC_SYS_WAIT_H" +after_includes = """ + +#define WEXITSTATUS(s) (((s) >> 8) & 0xff) +#define WTERMSIG(s) ((s) & 0x7f) +#define WSTOPSIG(s) WEXITSTATUS(s) +#define WCOREDUMP(s) (((s) & 0x80) != 0) +#define WIFEXITED(s) (((s) & 0x7f) == 0) +#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f) +#define WIFSIGNALED(s) (((((s) & 0x7f) + 1U) & 0x7f) >= 2) // Ends with 1111111 or 10000000 +#define WIFCONTINUED(s) ((s) == 0xffff) +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/sys_wait/mod.rs b/src/header/sys_wait/mod.rs new file mode 100644 index 0000000000..58d769250e --- /dev/null +++ b/src/header/sys_wait/mod.rs @@ -0,0 +1,173 @@ +//! `sys/wait.h` implementation. +//! +//! See . + +use crate::{ + error::ResultExt, + header::signal::siginfo_t, + out::Out, + platform::{ + ERRNO, Pal, Sys, + types::{c_int, c_uint, pid_t}, + }, +}; + +pub type idtype_t = c_int; +pub type id_t = c_uint; + +pub const WNOHANG: c_int = 1; +pub const WUNTRACED: c_int = 2; + +pub const WSTOPPED: c_int = 2; +pub const WEXITED: c_int = 4; +pub const WCONTINUED: c_int = 8; +pub const WNOWAIT: c_int = 0x0100_0000; + +pub const __WNOTHREAD: c_int = 0x2000_0000; +pub const __WALL: c_int = 0x4000_0000; +#[allow(overflowing_literals)] +pub const __WCLONE: c_int = 0x8000_0000; + +pub const P_ALL: idtype_t = 0; +pub const P_PID: idtype_t = 1; +pub const P_PGID: idtype_t = 2; + +pub const CLD_EXITED: c_int = 1; +pub const CLD_KILLED: c_int = 2; +pub const CLD_DUMPED: c_int = 3; +pub const CLD_TRAPPED: c_int = 4; +pub const CLD_STOPPED: c_int = 5; +pub const CLD_CONTINUED: c_int = 6; + +fn wexitstatus(status: c_int) -> c_int { + (status >> 8) & 0xff +} + +fn wtermsig(status: c_int) -> c_int { + status & 0x7f +} + +fn wstopsig(status: c_int) -> c_int { + wexitstatus(status) +} + +fn wcoredump(status: c_int) -> bool { + (status & 0x80) != 0 +} + +fn wifexited(status: c_int) -> bool { + (status & 0x7f) == 0 +} + +fn wifstopped(status: c_int) -> bool { + (status & 0xff) == 0x7f +} + +fn wifcontinued(status: c_int) -> bool { + status == 0xffff +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wait(stat_loc: *mut c_int) -> pid_t { + unsafe { waitpid(!0, stat_loc, 0) } +} + +fn map_waitid_target(idtype: idtype_t, id: id_t) -> Option { + match idtype { + P_ALL => Some(-1), + P_PID => Some(id as pid_t), + P_PGID => Some(if id == 0 { 0 } else { -(id as pid_t) }), + _ => None, + } +} + +fn map_waitid_options(options: c_int) -> Option { + let interest = options & (WEXITED | WSTOPPED | WCONTINUED); + if interest == 0 { + return None; + } + + let mut waitpid_options = 0; + if options & WNOHANG != 0 { + waitpid_options |= WNOHANG; + } + if options & WSTOPPED != 0 { + waitpid_options |= WUNTRACED; + } + if options & WCONTINUED != 0 { + waitpid_options |= WCONTINUED; + } + if options & WNOWAIT != 0 { + waitpid_options |= WNOWAIT; + } + + Some(waitpid_options) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn waitid( + idtype: idtype_t, + id: id_t, + infop: *mut siginfo_t, + options: c_int, +) -> c_int { + if infop.is_null() { + ERRNO.set(crate::header::errno::EFAULT); + return -1; + } + + let Some(pid_target) = map_waitid_target(idtype, id) else { + ERRNO.set(crate::header::errno::EINVAL); + return -1; + }; + let Some(waitpid_options) = map_waitid_options(options) else { + ERRNO.set(crate::header::errno::EINVAL); + return -1; + }; + + let mut status = 0; + let pid = Sys::waitpid(pid_target, Some(Out::from_mut(&mut status)), waitpid_options) + .or_minus_one_errno(); + if pid < 0 { + return -1; + } + + unsafe { + *infop = core::mem::zeroed(); + } + if pid == 0 { + return 0; + } + + unsafe { + (*infop).si_pid = pid; + (*infop).si_signo = crate::header::signal::SIGCHLD as c_int; + (*infop).si_errno = 0; + if wifexited(status) { + (*infop).si_code = CLD_EXITED; + (*infop).si_status = wexitstatus(status); + } else if wifstopped(status) { + (*infop).si_code = CLD_STOPPED; + (*infop).si_status = wstopsig(status); + } else if wifcontinued(status) { + (*infop).si_code = CLD_CONTINUED; + (*infop).si_status = crate::header::signal::SIGCONT as c_int; + } else { + (*infop).si_status = wtermsig(status); + (*infop).si_code = if wcoredump(status) { + CLD_DUMPED + } else { + CLD_KILLED + }; + } + } + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t { + Sys::waitpid(pid, unsafe { Out::nullable(stat_loc) }, options).or_minus_one_errno() +} diff --git a/src/header/tar/cbindgen.toml b/src/header/tar/cbindgen.toml new file mode 100644 index 0000000000..ca3ee9617c --- /dev/null +++ b/src/header/tar/cbindgen.toml @@ -0,0 +1,17 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/tar.h.html +# +# There are no spec quotations relating to includes +include_guard = "_RELIBC_TAR_H" +# cbindgen cannot export Rust &str +after_includes = """ + +#define TMAGIC "ustar" +#define TVERSION "00" +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/tar/mod.rs b/src/header/tar/mod.rs new file mode 100644 index 0000000000..ee7c595482 --- /dev/null +++ b/src/header/tar/mod.rs @@ -0,0 +1,150 @@ +//! `tar.h` implementation. +//! +//! See . + +use core::slice; + +/// Block size for tar archives (512 bytes). +pub const BLOCKSIZE: usize = 512; + +/// Default record size for tar archives (10KB, consisting of 20 blocks). +pub const RECORDSIZE: usize = BLOCKSIZE * 20; // 10KB (default for tar archives) + +/// Field lengths in tar headers +pub const NAME_SIZE: usize = 100; // File name +pub const MODE_SIZE: usize = 8; // File mode +pub const UID_SIZE: usize = 8; // Owner's numeric user ID +pub const GID_SIZE: usize = 8; // Group's numeric user ID +pub const SIZE_SIZE: usize = 12; // File size in bytes +pub const MTIME_SIZE: usize = 12; // Modification time +pub const CHKSUM_SIZE: usize = 8; // Checksum +pub const LINKNAME_SIZE: usize = 100; // Name of linked file +pub const MAGIC_SIZE: usize = 6; // Magic string size +pub const VERSION_SIZE: usize = 2; // Version string size +pub const UNAME_SIZE: usize = 32; // Owner user name +pub const GNAME_SIZE: usize = 32; // Owner group name +pub const DEVMAJOR_SIZE: usize = 8; // Major device number +pub const DEVMINOR_SIZE: usize = 8; // Minor device number +pub const PREFIX_SIZE: usize = 155; // Prefix for file name +pub const HEADER_SIZE: usize = 512; // Total header size + +/// Bits used in the mode field - value in octal +pub const TSUID: u16 = 0o4000; // Set user ID on execution +pub const TSGID: u16 = 0o2000; // Set group ID on execution +pub const TSVTX: u16 = 0o1000; // Sticky bit +pub const TUREAD: u16 = 0o0400; // Read permission, owner +pub const TUWRITE: u16 = 0o0200; // Write permission, owner +pub const TUEXEC: u16 = 0o0100; // Execute/search permission, owner +pub const TGREAD: u16 = 0o0040; // Read permission, group +pub const TGWRITE: u16 = 0o0020; // Write permission, group +pub const TGEXEC: u16 = 0o0010; // Execute/search permission, group +pub const TOREAD: u16 = 0o0004; // Read permission, others +pub const TOWRITE: u16 = 0o0002; // Write permission, others +pub const TOEXEC: u16 = 0o0001; // Execute/search permission, others + +/// Values used in typeflag field +pub const REGTYPE: u8 = b'0'; // Regular file +pub const AREGTYPE: u8 = b'\0'; // Regular file (old format) +pub const LNKTYPE: u8 = b'1'; // Link +pub const SYMTYPE: u8 = b'2'; // Symbolic link +pub const CHRTYPE: u8 = b'3'; // Character special +pub const BLKTYPE: u8 = b'4'; // Block special +pub const DIRTYPE: u8 = b'5'; // Directory +pub const FIFOTYPE: u8 = b'6'; // FIFO special +pub const CONTTYPE: u8 = b'7'; // Contiguous file + +/// tar format magic and version +/// cbindgen:ignore +pub const TMAGIC: &str = "ustar"; // Magic string : ustar and a null +pub const TMAGLEN: usize = 6; // Length of the magic string +/// cbindgen:ignore +pub const TVERSION: &str = "00"; // Version string +pub const TVERSLEN: usize = 2; // Length of the version string + +/// Reserved for future standards +pub const XHDRTYPE: u8 = b'x'; // Extended header referring to the next file in the archive +pub const XGLTYPE: u8 = b'g'; // Global extended header + +// Reserved values for GNU tar extensions +// pub const GNUTYPE_DUMPDIR: u8 = b'D'; // Directory dump +// pub const GNUTYPE_MULTIVOL: u8 = b'M'; // Multi-volume file +// pub const GNUTYPE_LONGNAME: u8 = b'L'; // Long file name +// pub const GNUTYPE_LONGLINK: u8 = b'K'; // Long link name +// pub const GNUTYPE_SPARSE: u8 = b'S'; // Sparse file + +/// Represents a tar archive header following the POSIX ustar format. +/// +/// The header contains metadata about a file in a tar archive, including +/// its name, size, permissions, and other attributes. All text fields are +/// null-terminated. +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct TarHeader { + // Byte offset - usage + pub name: [u8; NAME_SIZE], // 0 - File name + pub mode: [u8; MODE_SIZE], // 100 - Permissions + pub uid: [u8; UID_SIZE], // 108 - User ID + pub gid: [u8; GID_SIZE], // 116 - Group ID + pub size: [u8; SIZE_SIZE], // 124 - File size in bytes + pub mtime: [u8; MTIME_SIZE], // 136 - Modification time + pub chksum: [u8; CHKSUM_SIZE], // 148 - Header checksum + pub typeflag: u8, // 156 - File type + pub linkname: [u8; LINKNAME_SIZE], // 157 - Linked file name + pub magic: [u8; MAGIC_SIZE], // 257 - UStar magic + pub version: [u8; VERSION_SIZE], // 263 - UStar version + pub uname: [u8; UNAME_SIZE], // 265 - Owner user name + pub gname: [u8; GNAME_SIZE], // 297 - Owner group name + pub devmajor: [u8; DEVMAJOR_SIZE], // 329 - Major device number + pub devminor: [u8; DEVMINOR_SIZE], // 337 - Minor device number + pub prefix: [u8; PREFIX_SIZE], // 345 - Prefix for file name + pub padding: [u8; 12], // 500 - Padding to make 512 bytes +} + +impl Default for TarHeader { + fn default() -> Self { + let mut header = Self { + name: [0; NAME_SIZE], + mode: [0; MODE_SIZE], + uid: [0; UID_SIZE], + gid: [0; GID_SIZE], + size: [0; SIZE_SIZE], + mtime: [0; MTIME_SIZE], + chksum: [0; CHKSUM_SIZE], + typeflag: AREGTYPE, + linkname: [0; LINKNAME_SIZE], + magic: [0; MAGIC_SIZE], + version: [0; VERSION_SIZE], + uname: [0; UNAME_SIZE], + gname: [0; GNAME_SIZE], + devmajor: [0; DEVMAJOR_SIZE], + devminor: [0; DEVMINOR_SIZE], + prefix: [0; PREFIX_SIZE], + padding: [0; 12], + }; + + // Set default magic ("ustar") and version ("00") + let magic_bytes = TMAGIC.as_bytes(); // "ustar" + header.magic[..magic_bytes.len()].copy_from_slice(magic_bytes); + // tar specification often expects "ustar\0" + if MAGIC_SIZE >= 6 && TMAGIC.len() < MAGIC_SIZE { + header.magic[TMAGIC.len()] = 0; + } + + let version_bytes = TVERSION.as_bytes(); // "00" + header.version[..version_bytes.len()].copy_from_slice(version_bytes); + + header + } +} + +impl TarHeader { + /// Calculates the checksum of the tar header as required by the specification. + /// Before computing, the checksum field is treated as if it contained all spaces (0x20). + pub fn calculate_checksum(&self) -> usize { + let mut header_copy = *self; + header_copy.chksum.fill(b' '); + let bytes = + unsafe { slice::from_raw_parts((&raw const header_copy).cast::(), HEADER_SIZE) }; + bytes.iter().map(|&b| b as usize).sum() + } +} diff --git a/src/header/termios/cbindgen.toml b/src/header/termios/cbindgen.toml new file mode 100644 index 0000000000..85d08f0d90 --- /dev/null +++ b/src/header/termios/cbindgen.toml @@ -0,0 +1,16 @@ +# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/termios.h.html +# +# Spec quotations relating to includes: +# - "The header shall define the pid_t type as described in ." +sys_includes = ["sys/types.h"] +include_guard = "_RELIBC_TERMIOS_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true + +[export.rename] +"winsize" = "struct winsize" diff --git a/src/header/termios/linux.rs b/src/header/termios/linux.rs new file mode 100644 index 0000000000..2659af5e65 --- /dev/null +++ b/src/header/termios/linux.rs @@ -0,0 +1,117 @@ +/* c_cc { */ +pub const VINTR: usize = 0; +pub const VQUIT: usize = 1; +pub const VERASE: usize = 2; +pub const VKILL: usize = 3; +pub const VEOF: usize = 4; +pub const VTIME: usize = 5; +pub const VMIN: usize = 6; +pub const VSWTC: usize = 7; +pub const VSTART: usize = 8; +pub const VSTOP: usize = 9; +pub const VSUSP: usize = 10; +pub const VEOL: usize = 11; +pub const VREPRINT: usize = 12; +pub const VDISCARD: usize = 13; +pub const VWERASE: usize = 14; +pub const VLNEXT: usize = 15; +pub const VEOL2: usize = 16; +pub const NCCS: usize = 32; +/* } c_cc */ + +/* c_iflag { */ +pub const IGNBRK: usize = 0o000_001; +pub const BRKINT: usize = 0o000_002; +pub const IGNPAR: usize = 0o000_004; +pub const PARMRK: usize = 0o000_010; +pub const INPCK: usize = 0o000_020; +pub const ISTRIP: usize = 0o000_040; +pub const INLCR: usize = 0o000_100; +pub const IGNCR: usize = 0o000_200; +pub const ICRNL: usize = 0o000_400; +pub const IUCLC: usize = 0o001_000; +pub const IXON: usize = 0o002_000; +pub const IXANY: usize = 0o004_000; +pub const IXOFF: usize = 0o010_000; +pub const IMAXBEL: usize = 0o020_000; +pub const IUTF8: usize = 0o040_000; +/* } c_iflag */ + +/* c_oflag { */ +pub const OPOST: usize = 0o000_001; +pub const OLCUC: usize = 0o000_002; +pub const ONLCR: usize = 0o000_004; +pub const OCRNL: usize = 0o000_010; +pub const ONOCR: usize = 0o000_020; +pub const ONLRET: usize = 0o000_040; +pub const OFILL: usize = 0o000_100; +pub const OFDEL: usize = 0o000_200; + +pub const VTDLY: usize = 0o040_000; +pub const VT0: usize = 0o000_000; +pub const VT1: usize = 0o040_000; +/* } c_oflag */ + +/* c_cflag { */ +pub const B0: usize = 0o000_000; +pub const B50: usize = 0o000_001; +pub const B75: usize = 0o000_002; +pub const B110: usize = 0o000_003; +pub const B134: usize = 0o000_004; +pub const B150: usize = 0o000_005; +pub const B200: usize = 0o000_006; +pub const B300: usize = 0o000_007; +pub const B600: usize = 0o000_010; +pub const B1200: usize = 0o000_011; +pub const B1800: usize = 0o000_012; +pub const B2400: usize = 0o000_013; +pub const B4800: usize = 0o000_014; +pub const B9600: usize = 0o000_015; +pub const B19200: usize = 0o000_016; +pub const B38400: usize = 0o000_017; + +pub const B57600: usize = 0o010_001; +pub const B115200: usize = 0o010_002; +pub const B230400: usize = 0o010_003; +pub const B460800: usize = 0o010_004; +pub const B500000: usize = 0o010_005; +pub const B576000: usize = 0o010_006; +pub const B921600: usize = 0o010_007; +pub const B1000000: usize = 0o010_010; +pub const B1152000: usize = 0o010_011; +pub const B1500000: usize = 0o010_012; +pub const B2000000: usize = 0o010_013; +pub const B2500000: usize = 0o010_014; +pub const B3000000: usize = 0o010_015; +pub const B3500000: usize = 0o010_016; +pub const B4000000: usize = 0o010_017; + +pub const CSIZE: usize = 0o000_060; +pub const CS5: usize = 0o000_000; +pub const CS6: usize = 0o000_020; +pub const CS7: usize = 0o000_040; +pub const CS8: usize = 0o000_060; + +pub const CSTOPB: usize = 0o000_100; +pub const CREAD: usize = 0o000_200; +pub const PARENB: usize = 0o000_400; +pub const PARODD: usize = 0o001_000; +pub const HUPCL: usize = 0o002_000; +pub const CLOCAL: usize = 0o004_000; +/* } c_clfag */ + +/* c_lflag { */ +pub const ISIG: usize = 0o000_001; +pub const ICANON: usize = 0o000_002; +pub const ECHO: usize = 0o000_010; +pub const ECHOE: usize = 0o000_020; +pub const ECHOK: usize = 0o000_040; +pub const ECHONL: usize = 0o000_100; +pub const NOFLSH: usize = 0o000_200; +pub const TOSTOP: usize = 0o000_400; +pub const IEXTEN: usize = 0o100_000; +/* } c_lflag */ + +// POSIX extensions +/// Sentinel value to disable a control char. +pub const _POSIX_VDISABLE: u8 = 0; diff --git a/src/header/termios/mod.rs b/src/header/termios/mod.rs new file mode 100644 index 0000000000..5bd8de3318 --- /dev/null +++ b/src/header/termios/mod.rs @@ -0,0 +1,248 @@ +//! `termios.h` implementation. +//! +//! See . + +use crate::{ + header::{ + errno, + sys_ioctl::{self, winsize}, + }, + platform::{ + self, + types::{c_int, c_uchar, c_uint, c_ulong, c_void, pid_t}, + }, +}; + +pub use self::sys::*; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +pub type cc_t = c_uchar; +pub type speed_t = c_uint; +pub type tcflag_t = c_uint; + +pub const TCOOFF: c_int = 0; +pub const TCOON: c_int = 1; +pub const TCIOFF: c_int = 2; +pub const TCION: c_int = 3; + +pub const TCIFLUSH: c_int = 0; +pub const TCOFLUSH: c_int = 1; +pub const TCIOFLUSH: c_int = 2; + +pub const TCSANOW: c_int = 0; +pub const TCSADRAIN: c_int = 1; +pub const TCSAFLUSH: c_int = 2; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cbindgen_export_winsize(winsize: winsize) {} + +/// See . +#[cfg(target_os = "linux")] +#[repr(C)] +#[derive(Default, Clone)] +pub struct termios { + pub c_iflag: tcflag_t, + pub c_oflag: tcflag_t, + pub c_cflag: tcflag_t, + pub c_lflag: tcflag_t, + pub c_line: cc_t, + pub c_cc: [cc_t; NCCS], + pub __c_ispeed: speed_t, + pub __c_ospeed: speed_t, +} + +// Must match structure in redox_termios +/// See . +#[cfg(target_os = "redox")] +#[repr(C)] +#[derive(Default, Clone)] +pub struct termios { + pub c_iflag: tcflag_t, + pub c_oflag: tcflag_t, + pub c_cflag: tcflag_t, + pub c_lflag: tcflag_t, + pub c_cc: [cc_t; NCCS], +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcgetattr(fd: c_int, out: *mut termios) -> c_int { + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCGETS, out.cast::()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcsetattr(fd: c_int, act: c_int, value: *const termios) -> c_int { + if !(0..=2).contains(&act) { + platform::ERRNO.set(errno::EINVAL); + return -1; + } + // This is safe because ioctl shouldn't modify the value + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCSETS + act as c_ulong, value as *mut c_void) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcgetsid(fd: c_int) -> pid_t { + let mut sid = 0; + if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCGSID, (&raw mut sid).cast::()) } < 0 { + return -1; + } + sid +} + +/// See . +#[cfg(target_os = "linux")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t { + unsafe { (*termios_p).__c_ispeed } +} + +/// See . +#[cfg(target_os = "redox")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfgetispeed(termios_p: *const termios) -> speed_t { + //TODO + 0 +} + +/// See . +#[cfg(target_os = "linux")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t { + unsafe { (*termios_p).__c_ospeed } +} + +/// See . +#[cfg(target_os = "redox")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfgetospeed(termios_p: *const termios) -> speed_t { + //TODO + 0 +} + +/// See . +#[cfg(target_os = "linux")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int { + match speed as usize { + B0..=B38400 | B57600..=B4000000 => { + unsafe { (*termios_p).__c_ispeed = speed }; + 0 + } + _ => { + platform::ERRNO.set(errno::EINVAL); + -1 + } + } +} + +/// See . +#[cfg(target_os = "redox")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfsetispeed(termios_p: *mut termios, speed: speed_t) -> c_int { + //TODO + platform::ERRNO.set(errno::EINVAL); + -1 +} + +/// See . +#[cfg(target_os = "linux")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int { + match speed as usize { + B0..=B38400 | B57600..=B4000000 => { + unsafe { (*termios_p).__c_ospeed = speed }; + 0 + } + _ => { + platform::ERRNO.set(errno::EINVAL); + -1 + } + } +} + +/// See . +#[cfg(target_os = "redox")] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfsetospeed(termios_p: *mut termios, speed: speed_t) -> c_int { + //TODO + platform::ERRNO.set(errno::EINVAL); + -1 +} + +/// Non-POSIX, 4.4 BSD extension +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfsetspeed(termios_p: *mut termios, speed: speed_t) -> c_int { + let r = unsafe { cfsetispeed(termios_p, speed) }; + if r < 0 { + return r; + } + unsafe { cfsetospeed(termios_p, speed) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcflush(fd: c_int, queue: c_int) -> c_int { + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCFLSH, queue as *mut c_void) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcdrain(fd: c_int) -> c_int { + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCSBRK, core::ptr::dangling_mut()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcsendbreak(fd: c_int, _dur: c_int) -> c_int { + // non-zero duration is ignored by musl due to it being + // implementation-defined. we do the same. + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCSBRK, core::ptr::null_mut()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcgetwinsize(fd: c_int, sws: *mut winsize) -> c_int { + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCGWINSZ, sws.cast()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcsetwinsize(fd: c_int, sws: *const winsize) -> c_int { + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCSWINSZ, sws.cast_mut().cast()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tcflow(fd: c_int, action: c_int) -> c_int { + // non-zero duration is ignored by musl due to it being + // implementation-defined. we do the same. + unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TCXONC, action as *mut _) } +} + +/// Non-POSIX, BSD extension +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cfmakeraw(termios_p: *mut termios) { + unsafe { + (*termios_p).c_iflag &= + !(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON) as u32; + (*termios_p).c_oflag &= !OPOST as u32; + (*termios_p).c_lflag &= !(ECHO | ECHONL | ICANON | ISIG | IEXTEN) as u32; + (*termios_p).c_cflag &= !(CSIZE | PARENB) as u32; + (*termios_p).c_cflag |= CS8 as u32; + (*termios_p).c_cc[VMIN] = 1; + (*termios_p).c_cc[VTIME] = 0; + } +} diff --git a/src/header/termios/redox.rs b/src/header/termios/redox.rs new file mode 100644 index 0000000000..526e59aa51 --- /dev/null +++ b/src/header/termios/redox.rs @@ -0,0 +1,111 @@ +/* c_cc { */ +pub const VEOF: usize = 0; +pub const VEOL: usize = 1; +pub const VEOL2: usize = 2; +pub const VERASE: usize = 3; +pub const VWERASE: usize = 4; +pub const VKILL: usize = 5; +pub const VREPRINT: usize = 6; +pub const VSWTC: usize = 7; +pub const VINTR: usize = 8; +pub const VQUIT: usize = 9; +pub const VSUSP: usize = 10; +pub const VSTART: usize = 12; +pub const VSTOP: usize = 13; +pub const VLNEXT: usize = 14; +pub const VDISCARD: usize = 15; +pub const VMIN: usize = 16; +pub const VTIME: usize = 17; +pub const NCCS: usize = 32; +/* } c_cc */ + +/* c_iflag { */ +pub const IGNBRK: usize = 0o000_001; +pub const BRKINT: usize = 0o000_002; +pub const IGNPAR: usize = 0o000_004; +pub const PARMRK: usize = 0o000_010; +pub const INPCK: usize = 0o000_020; +pub const ISTRIP: usize = 0o000_040; +pub const INLCR: usize = 0o000_100; +pub const IGNCR: usize = 0o000_200; +pub const ICRNL: usize = 0o000_400; +pub const IXON: usize = 0o001_000; +pub const IXOFF: usize = 0o002_000; +pub const IXANY: usize = 0o004_000; +/* } c_iflag */ + +/* c_oflag { */ +pub const OPOST: usize = 0o000_001; +pub const ONLCR: usize = 0o000_002; +pub const OLCUC: usize = 0o000_004; +pub const OCRNL: usize = 0o000_010; +pub const ONOCR: usize = 0o000_020; +pub const ONLRET: usize = 0o000_040; +pub const OFILL: usize = 0o0000_100; +pub const OFDEL: usize = 0o0000_200; +/* } c_oflag */ + +/* c_cflag { */ +pub const B0: usize = 0o000_000; +pub const B50: usize = 0o000_001; +pub const B75: usize = 0o000_002; +pub const B110: usize = 0o000_003; +pub const B134: usize = 0o000_004; +pub const B150: usize = 0o000_005; +pub const B200: usize = 0o000_006; +pub const B300: usize = 0o000_007; +pub const B600: usize = 0o000_010; +pub const B1200: usize = 0o000_011; +pub const B1800: usize = 0o000_012; +pub const B2400: usize = 0o000_013; +pub const B4800: usize = 0o000_014; +pub const B9600: usize = 0o000_015; +pub const B19200: usize = 0o000_016; +pub const B38400: usize = 0o000_017; + +pub const B57600: usize = 0o0_020; +pub const B115200: usize = 0o0_021; +pub const B230400: usize = 0o0_022; +pub const B460800: usize = 0o0_023; +pub const B500000: usize = 0o0_024; +pub const B576000: usize = 0o0_025; +pub const B921600: usize = 0o0_026; +pub const B1000000: usize = 0o0_027; +pub const B1152000: usize = 0o0_030; +pub const B1500000: usize = 0o0_031; +pub const B2000000: usize = 0o0_032; +pub const B2500000: usize = 0o0_033; +pub const B3000000: usize = 0o0_034; +pub const B3500000: usize = 0o0_035; +pub const B4000000: usize = 0o0_036; + +pub const CSIZE: usize = 0o001_400; +pub const CS5: usize = 0o000_000; +pub const CS6: usize = 0o000_400; +pub const CS7: usize = 0o001_000; +pub const CS8: usize = 0o001_400; + +pub const CSTOPB: usize = 0o002_000; +pub const CREAD: usize = 0o004_000; +pub const PARENB: usize = 0o010_000; +pub const PARODD: usize = 0o020_000; +pub const HUPCL: usize = 0o040_000; + +pub const CLOCAL: usize = 0o0100000; +/* } c_clfag */ + +/* c_lflag { */ +pub const ISIG: usize = 0x0000_0080; +pub const ICANON: usize = 0x0000_0100; +pub const ECHO: usize = 0x0000_0008; +pub const ECHOE: usize = 0x0000_0002; +pub const ECHOK: usize = 0x0000_0004; +pub const ECHONL: usize = 0x0000_0010; +pub const NOFLSH: usize = 0x8000_0000; +pub const TOSTOP: usize = 0x0040_0000; +pub const IEXTEN: usize = 0x0000_0400; +/* } c_lflag */ + +// POSIX extensions +/// Sentinel value to disable a control char. +pub const _POSIX_VDISABLE: u8 = 0; diff --git a/src/header/time/cbindgen.toml b/src/header/time/cbindgen.toml new file mode 100644 index 0000000000..d2476bd3b3 --- /dev/null +++ b/src/header/time/cbindgen.toml @@ -0,0 +1,17 @@ +sys_includes = ["sys/types.h", "stdint.h", "stddef.h", "features.h"] +include_guard = "_RELIBC_TIME_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true +after_includes = """ +#include // for timespec +struct sigevent; +""" + +[enum] +prefix_with_name = true + +[export.rename] +"timespec" = "struct timespec" +"sigevent" = "struct sigevent" diff --git a/src/header/time/constants.rs b/src/header/time/constants.rs new file mode 100644 index 0000000000..2b3d3ba645 --- /dev/null +++ b/src/header/time/constants.rs @@ -0,0 +1,33 @@ +use crate::platform::types::{c_char, c_int, c_long, clockid_t}; + +#[cfg(target_os = "linux")] +#[path = "linux.rs"] +pub mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox.rs"] +pub mod sys; + +pub use self::sys::*; + +pub(crate) const UTC: *const c_char = c"UTC".as_ptr().cast(); + +pub(crate) const DAY_NAMES: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; +pub(crate) const MON_NAMES: [&str; 12] = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +]; + +pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; +// Can't be time_t because cbindgen UGH +pub const CLOCKS_PER_SEC: c_long = 1_000_000; + +pub const TIMER_ABSTIME: c_int = 1; + +// Constants for timespec_get and timespec_getres which are C23 analogues to +// clock_gettime/getres. +// The values are offset by one for simplicity since zero represents an error. + +/// `TIME_UTC` returns the time since the Unix epoch. +pub const TIME_UTC: c_int = CLOCK_REALTIME + 1; +/// `TIME_MONOTONIC` returns the time from the monotonically increasing clock. +pub const TIME_MONOTONIC: c_int = CLOCK_MONOTONIC + 1; diff --git a/src/header/time/linux.rs b/src/header/time/linux.rs new file mode 100644 index 0000000000..c3d67cfd8c --- /dev/null +++ b/src/header/time/linux.rs @@ -0,0 +1,12 @@ +use crate::platform::types::c_int; + +pub const CLOCK_REALTIME: c_int = 0; +pub const CLOCK_MONOTONIC: c_int = 1; +pub const CLOCK_THREAD_CPUTIME_ID: c_int = 3; +pub const CLOCK_MONOTONIC_RAW: c_int = 4; +pub const CLOCK_REALTIME_COARSE: c_int = 5; +pub const CLOCK_MONOTONIC_COARSE: c_int = 6; +pub const CLOCK_BOOTTIME: c_int = 7; +pub const CLOCK_REALTIME_ALARM: c_int = 8; +pub const CLOCK_BOOTTIME_ALARM: c_int = 9; +pub const CLOCK_TAI: c_int = 11; diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs new file mode 100644 index 0000000000..1fe2ba0c40 --- /dev/null +++ b/src/header/time/mod.rs @@ -0,0 +1,929 @@ +//! `time.h` implementation. +//! +//! See . + +use crate::{ + c_str::{CStr, CString}, + error::{Errno, ResultExt}, + header::{ + bits_timespec::timespec, + errno::{EFAULT, EINVAL, ENOMEM, EOVERFLOW, ETIMEDOUT}, + signal::sigevent, + stdlib::getenv, + unistd::readlink, + }, + out::Out, + platform::{ + self, Pal, Sys, + types::{ + c_char, c_double, c_int, c_long, clock_t, clockid_t, pid_t, size_t, time_t, timer_t, + }, + }, + raw_cell::RawCell, + sync::{Mutex, MutexGuard}, +}; +use alloc::collections::BTreeSet; +use chrono::{ + DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, Offset, TimeZone, Timelike, Utc, + offset::MappedLocalTime, +}; +use chrono_tz::{OffsetComponents, OffsetName, Tz}; +use core::{cell::OnceCell, convert::TryFrom, mem, ptr}; + +pub use self::constants::*; + +pub mod constants; + +mod strftime; +mod strptime; +pub use strptime::strptime; + +const YEARS_PER_ERA: time_t = 400; +const DAYS_PER_ERA: time_t = 146097; +const SECS_PER_DAY: time_t = 24 * 60 * 60; +pub(crate) const NANOSECONDS: c_long = 1_000_000_000; +const UTC_STR: &core::ffi::CStr = c"UTC"; + +#[cfg(target_os = "redox")] +impl<'a> From<&'a timespec> for syscall::TimeSpec { + fn from(tp: ×pec) -> Self { + Self { + tv_sec: tp.tv_sec as _, + tv_nsec: tp.tv_nsec as _, + } + } +} + +#[cfg(target_os = "redox")] +impl<'a> From<&'a syscall::TimeSpec> for timespec { + fn from(tp: &syscall::TimeSpec) -> Self { + Self { + tv_sec: tp.tv_sec as _, + tv_nsec: tp.tv_nsec as _, + } + } +} + +/// timer_t internal data, ABI unstable +#[repr(C)] +#[derive(Clone)] +#[cfg(target_os = "redox")] +pub(crate) struct timer_internal_t { + pub clockid: clockid_t, + pub timerfd: usize, + pub eventfd: usize, + pub evp: sigevent, + pub thread: platform::types::pthread_t, + pub caller_thread: crate::pthread::OsTid, + // relibc handles it_interval, not the kernel + pub next_wake_time: itimerspec, + // When non-zero, timer_routine delivers SIGALRM via kill(process_pid, sig) + // instead of rlct_kill (thread-specific). Used by alarm(). + pub process_pid: platform::types::pid_t, +} + +/// See . +#[repr(C)] +pub struct tm { + pub tm_sec: c_int, // 0 - 60 + pub tm_min: c_int, // 0 - 59 + pub tm_hour: c_int, // 0 - 23 + pub tm_mday: c_int, // 1 - 31 + pub tm_mon: c_int, // 0 - 11 + pub tm_year: c_int, // years since 1900 + pub tm_wday: c_int, // 0 - 6 (Sunday - Saturday) + pub tm_yday: c_int, // 0 - 365 + pub tm_isdst: c_int, // >0 if DST, 0 if not, <0 if unknown + pub tm_gmtoff: c_long, // offset from UTC in seconds + pub tm_zone: *const c_char, // timezone abbreviation +} + +unsafe impl Sync for tm {} + +// The C Standard says that localtime and gmtime return the same pointer. +static GMTIME_LOCALTIME_RETURN_TM: RawCell = RawCell::new(blank_tm()); + +// The C Standard says that ctime and asctime return the same pointer. +static mut ASCTIME: [c_char; 26] = [0; 26]; + +#[repr(transparent)] +pub struct TzName([*mut c_char; 2]); + +unsafe impl Sync for TzName {} + +// Name storage for the `tm_zone` field. +static TIMEZONE_NAMES: Mutex>> = Mutex::new(OnceCell::new()); + +// relibc functions should hold `TIMEZONE_LOCK` when accessing `daylight`, +// `timezone`, and `tzname`. However, it cannot guard those variables against +// user access (see `tzset()` specs for details). +static TIMEZONE_LOCK: Mutex<(Option, Option)> = Mutex::new((None, None)); + +// Should only be accessed by relibc when `TIMEZONE_LOCK` is held +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut daylight: c_int = 0; + +// Should only be accessed by relibc when `TIMEZONE_LOCK` is held +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut timezone: c_long = 0; + +// Should only be accessed by relibc when `TIMEZONE_LOCK` is held +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut tzname: TzName = TzName([ptr::null_mut(); 2]); + +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut getdate_err: c_int = 0; + +/// See . +#[repr(C)] +#[derive(Clone, Default)] +pub struct itimerspec { + pub it_interval: timespec, + pub it_value: timespec, +} + +/// See . +/// +/// # Deprecation +/// The `asctime()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asctime(timeptr: *const tm) -> *mut c_char { + unsafe { + #[allow(deprecated)] + asctime_r(timeptr, (&raw mut ASCTIME).cast()) + } +} + +/// See . +/// +/// # Deprecation +/// The `asctime_r()` was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn asctime_r(tm: *const tm, buf: *mut c_char) -> *mut c_char { + let tm_sec = unsafe { (*tm).tm_sec }; + let tm_min = unsafe { (*tm).tm_min }; + let tm_hour = unsafe { (*tm).tm_hour }; + let tm_mday = unsafe { (*tm).tm_mday }; + let tm_mon = unsafe { (*tm).tm_mon }; + let tm_year = unsafe { (*tm).tm_year }; + let tm_wday = unsafe { (*tm).tm_wday }; + + /* Panic when we run into undefined behavior. + * + * POSIX says (since issue 7) that asctime()/asctime_r() cause UB + * when the tm member values would cause out-of-bounds array access + * or overflow the output buffer. This contrasts with ISO C11+, + * which specifies UB for any tm members being outside their normal + * ranges. While POSIX explicitly defers to the C standard in case + * of contradictions, the assertions below follow the interpretation + * that POSIX simply defines some of C's undefined behavior, rather + * than conflict with the ISO standard. + * + * Note that C's "%.2d" formatting, unlike Rust's "{:02}" + * formatting, does not count a minus sign against the two digits to + * print, meaning that we must reject all negative values for + * seconds, minutes and hours. However, C's "%3d" (for day-of-month) + * is similar to Rust's "{:3}". + * + * To avoid year overflow problems (in Rust, where numeric overflow + * is considered an error), we subtract 1900 from the endpoints, + * rather than adding to the tm_year value. POSIX' requirement that + * tm_year be at most {INT_MAX}-1990 is satisfied for all legal + * values of {INT_MAX} through the max-4-digit requirement on the + * year. + * + * The tm_mon and tm_wday fields are used for array access and thus + * will already cause a panic in Rust code when out of range. + * However, using the assertions below allows a consistent error + * message for all fields. */ + const OUT_OF_RANGE_MESSAGE: &str = "tm member out of range"; + + assert!((0..=99).contains(&tm_sec), "{OUT_OF_RANGE_MESSAGE}"); + assert!((0..=99).contains(&tm_min), "{OUT_OF_RANGE_MESSAGE}"); + assert!((0..=99).contains(&tm_hour), "{OUT_OF_RANGE_MESSAGE}"); + assert!((-99..=999).contains(&tm_mday), "{OUT_OF_RANGE_MESSAGE}"); + assert!((0..=11).contains(&tm_mon), "{OUT_OF_RANGE_MESSAGE}"); + assert!( + (-999 - 1900..=9999 - 1900).contains(&tm_year), + "{OUT_OF_RANGE_MESSAGE}" + ); + assert!((0..=6).contains(&tm_wday), "{OUT_OF_RANGE_MESSAGE}"); + + // At this point, we can safely use the values as given. + let write_result = core::fmt::write( + // buf may be either `*mut u8` or `*mut i8` + &mut platform::UnsafeStringWriter(buf.cast()), + format_args!( + "{:.3} {:.3}{:3} {:02}:{:02}:{:02} {}\n", + DAY_NAMES[usize::try_from(tm_wday).unwrap()], + MON_NAMES[usize::try_from(tm_mon).unwrap()], + tm_mday, + tm_hour, + tm_min, + tm_sec, + 1900 + tm_year + ), + ); + match write_result { + Ok(_) => buf, + Err(_) => { + /* asctime()/asctime_r() or the equivalent sprintf() call + * have no defined errno setting */ + ptr::null_mut() + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn clock() -> clock_t { + let mut ts = mem::MaybeUninit::::uninit(); + + if unsafe { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts.as_mut_ptr()) } != 0 { + return -1; + } + let ts = unsafe { ts.assume_init() }; + + let clocks = + ts.tv_sec * CLOCKS_PER_SEC as i64 + (ts.tv_nsec / (1_000_000_000 / CLOCKS_PER_SEC)) as i64; + clock_t::try_from(clocks).unwrap_or(-1) +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn clock_getcpuclockid(pid: pid_t, clock_id: *mut clockid_t) -> c_int { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn clock_getres(clock_id: clockid_t, res: *mut timespec) -> c_int { + Sys::clock_getres(clock_id, unsafe { Out::nullable(res) }) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn clock_gettime(clock_id: clockid_t, tp: *mut timespec) -> c_int { + Sys::clock_gettime(clock_id, unsafe { Out::nonnull(tp) }) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn clock_nanosleep( + clock_id: clockid_t, + flags: c_int, + rqtp: *const timespec, + rmtp: *mut timespec, +) -> c_int { + match clock_id { + CLOCK_REALTIME | CLOCK_MONOTONIC => { + if flags == TIMER_ABSTIME { + return EINVAL; + } + match unsafe { Sys::nanosleep(rqtp, rmtp) } { + Ok(()) => 0, + Err(Errno(ETIMEDOUT)) => ETIMEDOUT, + Err(Errno(e)) => e, + } + } + _ => EINVAL, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn clock_settime(clock_id: clockid_t, tp: *const timespec) -> c_int { + unsafe { Sys::clock_settime(clock_id, tp) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// # Deprecation +/// The `ctime()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ctime(clock: *const time_t) -> *mut c_char { + unsafe { + #[allow(deprecated)] + asctime(localtime(clock)) + } +} + +/// See . +/// +/// # Deprecation +/// The `ctime_r()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ctime_r(clock: *const time_t, buf: *mut c_char) -> *mut c_char { + // Using MaybeUninit seems to cause a panic during the build process + let mut tm1 = blank_tm(); + unsafe { localtime_r(clock, &raw mut tm1) }; + unsafe { + #[allow(deprecated)] + asctime_r(&raw const tm1, buf) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn difftime(time1: time_t, time0: time_t) -> c_double { + (time1 - time0) as _ +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn getdate(string: *const c_char) -> *const tm { + unimplemented!(); +} + +/// See . +/// +/// # Safety +/// The caller is required to ensure that: +/// * `timer` is a valid pointer +/// * the function has exclusive access to the static `tm` structure it +/// returns. This includes avoiding simultaneous calls to this function as +/// well as to [`localtime()`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gmtime(timer: *const time_t) -> *mut tm { + // SAFETY: the caller is required to uphold the safety requirements for + // `gmtime_r()` in addition to exclusive access to + // `GMTIME_LOCALTIME_RETURN_TM`. + unsafe { gmtime_r(timer, GMTIME_LOCALTIME_RETURN_TM.as_mut_ptr()) } +} + +/// See . +/// +/// # Safety +/// The caller is required to ensure that: +/// * `timer` is a valid pointer +/// * `result` is convertible to an [`Out`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gmtime_r(timer: *const time_t, result: *mut tm) -> *mut tm { + // SAFETY: the caller is required to ensure that `timer` is a valid pointer. + let timer_val = unsafe { *timer }; + + // SAFETY: the caller is required to ensure that `result` is convertible + // to an `Out`. + let result_out = unsafe { Out::nonnull(result) }; + + let _ = get_localtime(timer_val, result_out); + result +} + +/// See . +/// +/// # Safety +/// The caller is required to ensure that: +/// * `timer` is a valid pointer +/// * the function has exclusive access to the static `tm` structure it +/// returns. This implies avoiding simultaneous calls to this function as +/// well as to [`gmtime()`] +/// * the variables [`daylight`], [`timezone`] and [`tzname`] are not accessed +/// by user code for the duration of the call. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn localtime(timer: *const time_t) -> *mut tm { + // SAFETY: the caller is required to uphold the safety requirements for + // `localtime_r()` in addition to exclusive access to + // `GMTIME_LOCALTIME_RETURN_TM`. + unsafe { localtime_r(timer, GMTIME_LOCALTIME_RETURN_TM.as_mut_ptr()) } +} + +/// See . +/// +/// # Safety +/// The caller is required to ensure that: +/// * `timer` is a valid pointer +/// * `result` is convertible to an [`Out`] +/// * the variables [`daylight`], [`timezone`] and [`tzname`] are not accessed +/// by user code for the duration of the call. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn localtime_r(timer: *const time_t, result: *mut tm) -> *mut tm { + // SAFETY: the caller is required to ensure that `timer` is a valid pointer. + let timer_val = unsafe { *timer }; + + // SAFETY: the caller is required to ensure that `result` is convertible + // to an `Out`. + let result_out = unsafe { Out::nonnull(result) }; + + let mut lock = TIMEZONE_LOCK.lock(); + + // SAFETY: the caller is required to ensure that `daylight`, `timezone` + // and `tzname` are not accessed by user code. + unsafe { clear_timezone(&mut lock) }; + if let (Some(std_time), dst_time) = get_localtime(timer_val, result_out) { + // SAFETY: the caller is required to ensure that `daylight`, + // `timezone` and `tzname` are not accessed by user code. + unsafe { set_timezone(&mut lock, &std_time, dst_time) }; + } + result +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mktime(timeptr: *mut tm) -> time_t { + let mut lock = TIMEZONE_LOCK.lock(); + unsafe { clear_timezone(&mut lock) }; + + let year = unsafe { (*timeptr).tm_year } + 1900; + let month = (unsafe { (*timeptr).tm_mon } + 1) as _; + let day = unsafe { (*timeptr).tm_mday } as _; + let hour = unsafe { (*timeptr).tm_hour } as _; + let minute = unsafe { (*timeptr).tm_min } as _; + let second = unsafe { (*timeptr).tm_sec } as _; + + let naive_local = match NaiveDate::from_ymd_opt(year, month, day) + .and_then(|date| date.and_hms_opt(hour, minute, second)) + { + Some(datetime) => datetime, + None => { + platform::ERRNO.set(EOVERFLOW); + return -1; + } + }; + + let offset = get_offset(unsafe { (*timeptr).tm_gmtoff }).unwrap(); + let tz = time_zone(); + // Create DateTime + let datetime = match offset.from_local_datetime(&naive_local) { + MappedLocalTime::Single(datetime) => datetime, + _ => { + platform::ERRNO.set(EOVERFLOW); + return -1; + } + }; + + // Convert to UTC and get timestamp + let tz_datetime = datetime.with_timezone(&tz); + let timestamp = tz_datetime.timestamp(); + + unsafe { ptr::write(timeptr, datetime_to_tm(&tz_datetime)) }; + + // Convert UTC time to local time + let (std_time, dst_time) = match tz.timestamp_opt(timestamp, 0) { + MappedLocalTime::Single(t) => (t, None), + // This variant contains the two possible results, in the order (earliest, latest). + MappedLocalTime::Ambiguous(t1, t2) => (t2, Some(t1)), + MappedLocalTime::None => return timestamp, + }; + { + unsafe { set_timezone(&mut lock, &std_time, dst_time) }; + } + + timestamp +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int { + unsafe { Sys::nanosleep(rqtp, rmtp) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strftime( + s: *mut c_char, + maxsize: size_t, + format: *const c_char, + timeptr: *const tm, +) -> size_t { + let ret = unsafe { + strftime::strftime( + &mut platform::StringWriter(s.cast::(), maxsize), + format, + timeptr, + ) + }; + if ret < maxsize { ret } else { 0 } +} + +// See . +// TODO: needs locale_t +// #[unsafe(no_mangle)] +/*pub extern "C" fn strftime_l(s: *mut char, maxsize: size_t, format: *const c_char, timeptr: *const tm, locale: locale_t) -> size_t { + unimplemented!(); +}*/ + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn time(tloc: *mut time_t) -> time_t { + let mut ts = timespec::default(); + if Sys::clock_gettime(CLOCK_REALTIME, Out::from_mut(&mut ts)).is_ok() {}; // TODO what to do if Err? + if !tloc.is_null() { + unsafe { *tloc = ts.tv_sec } + }; + ts.tv_sec +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timegm(tm: *mut tm) -> time_t { + let tm_val = unsafe { &mut *tm }; + let dt = match convert_tm_generic(&Utc, tm_val) { + Some(dt) => dt, + None => return -1, + }; + + unsafe { + (*tm).tm_wday = dt.weekday().num_days_from_sunday() as _; + (*tm).tm_yday = dt.ordinal0() as _; // day of year starting at 0 + (*tm).tm_isdst = 0; // UTC does not use DST + (*tm).tm_gmtoff = 0; // UTC offset is zero + (*tm).tm_zone = UTC_STR.as_ptr().cast::(); + } + + dt.timestamp() +} + +/// Non-POSIX, see . +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timelocal(tm: *mut tm) -> time_t { + let tm_val = unsafe { &mut *tm }; + let tz = time_zone(); + let dt = match convert_tm_generic(&tz, tm_val) { + Some(dt) => dt, + None => return -1, + }; + + let tz_name = CString::new(tz.name()).unwrap(); + unsafe { + (*tm).tm_wday = dt.weekday().num_days_from_sunday() as _; + (*tm).tm_yday = dt.ordinal0() as _; // day of year starting at 0 + (*tm).tm_isdst = dt.offset().dst_offset().num_hours() as _; + (*tm).tm_gmtoff = dt.offset().fix().local_minus_utc().into(); + (*tm).tm_zone = tz_name.into_raw().cast(); + } + + dt.timestamp() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timer_create( + clock_id: clockid_t, + evp: *mut sigevent, + timerid: *mut timer_t, +) -> c_int { + if evp.is_null() || timerid.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + let (evp, timerid) = unsafe { (&*evp, Out::nonnull(timerid)) }; + Sys::timer_create(clock_id, evp, timerid) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timer_delete(timerid: timer_t) -> c_int { + if timerid.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + Sys::timer_delete(timerid).map(|()| 0).or_minus_one_errno() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn timer_getoverrun(timerid: timer_t) -> c_int { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timer_gettime(timerid: timer_t, value: *mut itimerspec) -> c_int { + if timerid.is_null() || value.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + let value = unsafe { Out::nonnull(value) }; + Sys::timer_gettime(timerid, value) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timer_settime( + timerid: timer_t, + flags: c_int, + value: *const itimerspec, + ovalue: *mut itimerspec, +) -> c_int { + if timerid.is_null() || value.is_null() { + return Err(Errno(EFAULT)).or_minus_one_errno(); + } + let (value, ovalue) = unsafe { (&*value, Out::nullable(ovalue)) }; + Sys::timer_settime(timerid, flags, value, ovalue) + .map(|()| 0) + .or_minus_one_errno() +} + +/// ISO C equivalent to [`Sys::clock_gettime`]. +/// +/// The main differences are that this function: +/// * returns `0` on error and `base` on success +/// * only mandates TIME_UTC as a base +/// +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timespec_get(tp: *mut timespec, base: c_int) -> c_int { + let tp = unsafe { Out::nonnull(tp) }; + Sys::clock_gettime(base - 1, tp).map(|()| base).unwrap_or(0) +} + +/// ISO C equivalent to [`Sys::clock_getres`]. +/// +/// The main differences are that this function: +/// * returns `0` on error and `base` on success +/// * only mandates TIME_UTC as a base +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timespec_getres(res: *mut timespec, base: c_int) -> c_int { + let res = unsafe { Out::nullable(res) }; + Sys::clock_getres(base - 1, res).map(|()| base).unwrap_or(0) +} + +/// See . +/// +/// # Safety +/// The caller must ensure that [`daylight`], [`timezone`] and [`tzname`] are +/// not accessed by user code for the duration of the call. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn tzset() { + let mut lock = TIMEZONE_LOCK.lock(); + // SAFETY: the caller is required to ensure that `daylight`, `timezone` + // and `tzname` are not accessed by user code. + unsafe { clear_timezone(&mut lock) }; + + let tz = time_zone(); + let datetime = now(); + let (std_time, dst_time) = match tz.from_local_datetime(&datetime) { + MappedLocalTime::Single(t) => (t, None), + // This variant contains the two possible results, in the order (earliest, latest). + MappedLocalTime::Ambiguous(t1, t2) => (t2, Some(t1)), + MappedLocalTime::None => return, + }; + + // SAFETY: the caller is required to ensure that `daylight`, `timezone` + // and `tzname` are not accessed by user code. + unsafe { set_timezone(&mut lock, &std_time, dst_time) } +} + +fn convert_tm_generic(tz: &Tz, tm_val: &tm) -> Option> { + // Adjust fields: tm_year is years since 1900; tm_mon is 0-indexed. + let year = tm_val.tm_year + 1900; + let month = tm_val.tm_mon + 1; // convert to 1-indexed + let day = tm_val.tm_mday; + let hour = tm_val.tm_hour; + let minute = tm_val.tm_min; + let second = tm_val.tm_sec; + + match tz.with_ymd_and_hms( + year, + month as u32, + day as u32, + hour as u32, + minute as u32, + second as u32, + ) { + MappedLocalTime::Single(dt) => Some(dt), + MappedLocalTime::Ambiguous(dt1, _dt2) => Some(dt1), // choose the earliest value + _ => None, + } +} + +/// # Safety +/// The caller must ensure that `daylight`, `timezone` and `tzname` are not +/// accessed by user code for the duration of the call (relibc functions are +/// required to hold `TIMEZONE_LOCK` when accessing these). +unsafe fn clear_timezone(guard: &mut MutexGuard<'_, (Option, Option)>) { + guard.0 = None; + guard.1 = None; + + // SAFETY: the caller is required to ensure access exclusively for the + // holder of `TIMEZONE_LOCK`. + let daylight_mut = unsafe { &mut *(&raw mut daylight) }; + // SAFETY: the caller is required to ensure access exclusively for the + // holder of `TIMEZONE_LOCK`. + let timezone_mut = unsafe { &mut *(&raw mut timezone) }; + // SAFETY: the caller is required to ensure access exclusively for the + // holder of `TIMEZONE_LOCK`. + let tzname_mut = unsafe { &mut *(&raw mut tzname) }; + + (*tzname_mut).0[0] = ptr::null_mut(); + (*tzname_mut).0[1] = ptr::null_mut(); + *timezone_mut = 0; + *daylight_mut = 0; +} + +#[inline(always)] +fn get_system_time_zone<'a>() -> Option<&'a str> { + // Resolve the symlink for localtime + const BSIZE: size_t = 100; + let mut buffer: [u8; BSIZE] = [0; BSIZE]; + + #[cfg(not(target_os = "redox"))] + let (localtime, prefix) = (c"/etc/localtime", "/usr/share/zoneinfo/"); + + #[cfg(target_os = "redox")] + let (localtime, prefix) = (c"/etc/localtime", "/usr/share/zoneinfo/"); + + if unsafe { readlink(localtime.as_ptr().cast(), buffer.as_mut_ptr().cast(), BSIZE) } == -1 { + return None; + } + + let path = unsafe { CStr::from_ptr(buffer.as_mut_ptr().cast()) }; + + if let Ok(tz_name) = path.to_str() + && let Some(stripped) = tz_name.strip_prefix(prefix) + { + return Some(stripped); + } + + None +} + +fn get_current_time_zone<'a>() -> &'a str { + // Check the `TZ` environment variable + let tz_env = unsafe { getenv(c"TZ".as_ptr().cast()) }; + if !tz_env.is_null() + && let Ok(tz) = unsafe { CStr::from_ptr(tz_env) }.to_str() + { + return tz; + } + + // Fallback to the system's default time zone + if let Some(tz) = get_system_time_zone() { + return tz; + } + + // If all else fails, use UTC + "UTC" +} + +#[inline(always)] +fn time_zone() -> Tz { + get_current_time_zone().parse().unwrap_or(Tz::UTC) +} + +#[inline(always)] +fn now() -> NaiveDateTime { + let mut now = timespec::default(); + if Sys::clock_gettime(CLOCK_REALTIME, Out::from_mut(&mut now)).is_ok() {}; // TODO what to do if Err? + DateTime::from_timestamp(now.tv_sec, now.tv_nsec as _) + .unwrap_or_default() + .naive_local() +} + +#[inline(always)] +fn get_localtime( + timer: time_t, + mut result: Out, +) -> (Option>, Option>) { + let tz = time_zone(); + + // Convert UTC time to local time + let (std_time, dst_time) = match tz.timestamp_opt(timer, 0) { + MappedLocalTime::Single(t) => (Some(t), None), + // This variant contains the two possible results, in the order (earliest, latest). + MappedLocalTime::Ambiguous(t1, t2) => (Some(t2), Some(t1)), + MappedLocalTime::None => return (None, None), + }; + + let localtime = datetime_to_tm(&std_time.unwrap()); + result.write(localtime); + (std_time, dst_time) +} + +fn datetime_to_tm(local_time: &DateTime) -> tm { + let tz = local_time.timezone().name(); + let tz = tz.strip_prefix("Etc/").unwrap_or(tz); + + let mut t = blank_tm(); + // Populate the `tm` structure + t.tm_sec = local_time.second() as _; + t.tm_min = local_time.minute() as _; + t.tm_hour = local_time.hour() as _; + t.tm_mday = local_time.day() as _; + t.tm_mon = local_time.month0() as _; // 0-based month + t.tm_year = (local_time.year() - 1900) as _; // Years since 1900 + t.tm_wday = local_time.weekday().num_days_from_sunday() as _; + t.tm_yday = local_time.ordinal0() as _; // 0-based day of year + + let offset = local_time.offset(); + t.tm_isdst = offset.dst_offset().num_hours() as _; + // Get the UTC offset in seconds + t.tm_gmtoff = offset.fix().local_minus_utc().into(); + + let tm_zone = { + let mut timezone_names = TIMEZONE_NAMES.lock(); + timezone_names.get_or_init(BTreeSet::new); + let cstr = CString::new(tz).unwrap(); + timezone_names.get_mut().unwrap().insert(cstr.clone()); + timezone_names.get().unwrap().get(&cstr).unwrap().as_ptr() + }; + + t.tm_zone = tm_zone.cast(); + t +} + +/// # Safety +/// The caller must ensure that `daylight`, `timezone` and `tzname` are not +/// accessed by user code for the duration of the call (relibc functions are +/// required to hold `TIMEZONE_LOCK` when accessing these). +unsafe fn set_timezone( + guard: &mut MutexGuard<'_, (Option, Option)>, + std: &DateTime, + dst: Option>, +) { + // SAFETY: the caller is required to ensure access exclusively for the + // holder of `TIMEZONE_LOCK`. + let daylight_mut = unsafe { &mut *(&raw mut daylight) }; + // SAFETY: the caller is required to ensure access exclusively for the + // holder of `TIMEZONE_LOCK`. + let timezone_mut = unsafe { &mut *(&raw mut timezone) }; + // SAFETY: the caller is required to ensure access exclusively for the + // holder of `TIMEZONE_LOCK`. + let tzname_mut = unsafe { &mut *(&raw mut tzname) }; + + let ut_offset = std.offset(); + + guard.0 = Some(CString::new(ut_offset.abbreviation().expect("Wrong timezone")).unwrap()); + tzname_mut.0[0] = guard.0.as_ref().unwrap().as_ptr().cast_mut(); + + match dst { + Some(dst) => { + guard.1 = + Some(CString::new(dst.offset().abbreviation().expect("Wrong timezone")).unwrap()); + tzname_mut.0[1] = guard.1.as_ref().unwrap().as_ptr().cast_mut(); + *daylight_mut = 1; + } + None => { + guard.1 = None; + tzname_mut.0[1] = guard.0.as_ref().unwrap().as_ptr().cast_mut(); + *daylight_mut = 0; + } + } + + *timezone_mut = -c_long::from(ut_offset.fix().local_minus_utc()); +} + +#[inline(always)] +pub const fn get_offset(off: c_long) -> Option { + if off < 0 { + FixedOffset::west_opt(off as _) + } else { + FixedOffset::east_opt(off as _) + } +} + +const fn blank_tm() -> tm { + tm { + tm_year: 0, + tm_mon: 0, + tm_mday: 0, + tm_hour: 0, + tm_min: 0, + tm_sec: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: -1, + tm_gmtoff: 0, + tm_zone: ptr::null_mut(), + } +} + +pub(crate) fn timespec_realtime_to_monotonic(abstime: timespec) -> Result { + let mut realtime = timespec::default(); + unsafe { clock_gettime(CLOCK_REALTIME, &raw mut realtime) }; + let mut monotonic = timespec::default(); + unsafe { clock_gettime(CLOCK_MONOTONIC, &raw mut monotonic) }; + let Some(delta) = timespec::subtract(abstime, realtime) else { + return Err(Errno(ETIMEDOUT)); + }; + let Some(relative) = timespec::add(monotonic, delta) else { + return Err(Errno(ENOMEM)); + }; + Ok(relative) +} diff --git a/src/header/time/redox.rs b/src/header/time/redox.rs new file mode 100644 index 0000000000..b19d228a7b --- /dev/null +++ b/src/header/time/redox.rs @@ -0,0 +1,4 @@ +use crate::platform::types::c_int; + +pub const CLOCK_REALTIME: c_int = 1; +pub const CLOCK_MONOTONIC: c_int = 4; diff --git a/src/header/time/strftime.rs b/src/header/time/strftime.rs new file mode 100644 index 0000000000..49ab8e48fc --- /dev/null +++ b/src/header/time/strftime.rs @@ -0,0 +1,304 @@ +// `strftime` implementation. +// +// See . + +use alloc::string::String; + +use super::tm; +use crate::{ + c_str::CStr, + platform::{ + self, WriteByte, + types::{c_char, c_int, size_t}, + }, +}; + +// We use the langinfo constants +use crate::header::langinfo::{ + ABDAY_1, + ABMON_1, + AM_STR, + DAY_1, + MON_1, + PM_STR, + // TODO : other constants if needed + nl_item, + nl_langinfo, +}; + +/// A helper that calls `nl_langinfo(item)` and converts the returned pointer +/// into a `&str`. If it fails or is null, returns an empty string "". +unsafe fn langinfo_to_str(item: nl_item) -> &'static str { + use core::ffi::CStr; + + let ptr = unsafe { nl_langinfo(item) }; + if ptr.is_null() { + return ""; + } + unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or_default() +} + +/// See . +/// +/// Formats time data according to the given `format` string. +/// +/// Use `langinfo` for locale-based day/month names, +/// but still hard-codes other aspects of the "C" locale (like numeric/date +/// formats) and ignores `%E` / `%O` variations. +pub unsafe fn strftime(w: &mut W, format: *const c_char, t: *const tm) -> size_t { + /// Helper that actually parses the format string and writes output. + pub unsafe fn inner_strftime( + w: &mut W, + mut format: *const c_char, + t: *const tm, + ) -> bool { + macro_rules! w { + (byte $b:expr) => {{ + if w.write_u8($b).is_err() { + return false; + } + }}; + (char $chr:expr) => {{ + if w.write_char($chr).is_err() { + return false; + } + }}; + (recurse $fmt:expr) => {{ + let mut tmp = String::with_capacity($fmt.len() + 1); + tmp.push_str($fmt); + tmp.push('\0'); + + if unsafe { !inner_strftime(w, tmp.as_ptr() as *mut c_char, t) } { + return false; + } + }}; + ($str:expr) => {{ + if w.write_str($str).is_err() { + return false; + } + }}; + ($fmt:expr, $($args:expr),+) => {{ + // Could use write!() if we didn't need the exact count + if write!(w, $fmt, $($args),+).is_err() { + return false; + } + }}; + } + + while unsafe { *format } != 0 { + // If the character isn't '%', just copy it out. + if unsafe { *format } as u8 != b'%' { + w!(byte unsafe { *format } as u8); + format = unsafe { format.offset(1) }; + continue; + } + + // Skip '%' + format = unsafe { format.offset(1) }; + + // POSIX says '%E' and '%O' can modify numeric formats for locales, + // but we ignore them in this minimal "C" locale approach. + if unsafe { *format } as u8 == b'E' || unsafe { *format } as u8 == b'O' { + format = unsafe { format.offset(1) }; + } + + match unsafe { *format } as u8 { + // Literal '%' + b'%' => w!(byte b'%'), + + // Newline and tab expansions + b'n' => w!(byte b'\n'), + b't' => w!(byte b'\t'), + + // Abbreviated weekday name: %a + b'a' => { + // `ABDAY_1 + tm_wday` is the correct langinfo ID for abbreviated weekdays + let s = unsafe { langinfo_to_str(ABDAY_1 + (*t).tm_wday) }; + w!(s); + } + + // Full weekday name: %A + b'A' => { + // `DAY_1 + tm_wday` is the correct langinfo ID for full weekdays + let s = unsafe { langinfo_to_str(DAY_1 + (*t).tm_wday) }; + w!(s); + } + + // Abbreviated month name: %b or %h + b'b' | b'h' => { + let s = unsafe { langinfo_to_str(ABMON_1 + (*t).tm_mon) }; + w!(s); + } + + // Full month name: %B + b'B' => { + let s = unsafe { langinfo_to_str(MON_1 + (*t).tm_mon) }; + w!(s); + } + + // Century: %C + b'C' => { + let mut year = unsafe { (*t).tm_year } / 100; + if unsafe { (*t).tm_year } % 100 != 0 { + year += 1; + } + w!("{:02}", year + 19); + } + + // Day of month: %d + b'd' => w!("{:02}", unsafe { (*t).tm_mday }), + + // %D => same as %m/%d/%y + b'D' => w!(recurse "%m/%d/%y"), + + // Day of month, space-padded: %e + b'e' => w!("{:2}", unsafe { (*t).tm_mday }), + + // ISO 8601 date: %F => %Y-%m-%d + b'F' => w!(recurse "%Y-%m-%d"), + + // Hour (00-23): %H + b'H' => w!("{:02}", unsafe { (*t).tm_hour }), + + // Hour (01-12): %I + b'I' => w!("{:02}", (unsafe { (*t).tm_hour } + 12 - 1) % 12 + 1), + + // Day of year: %j + b'j' => w!("{:03}", unsafe { (*t).tm_yday } + 1), + + // etc. + b'k' => w!("{:2}", unsafe { (*t).tm_hour }), + b'l' => w!("{:2}", (unsafe { (*t).tm_hour } + 12 - 1) % 12 + 1), + b'm' => w!("{:02}", unsafe { (*t).tm_mon } + 1), + b'M' => w!("{:02}", unsafe { (*t).tm_min }), + + // AM/PM (uppercase): %p + b'p' => { + // Get "AM" / "PM" from langinfo + if unsafe { (*t).tm_hour } < 12 { + w!(unsafe { langinfo_to_str(AM_STR) }); + } else { + w!(unsafe { langinfo_to_str(PM_STR) }); + } + } + + // am/pm (lowercase): %P + b'P' => { + // Convert the AM_STR / PM_STR to lowercase + if unsafe { (*t).tm_hour } < 12 { + let am = unsafe { langinfo_to_str(AM_STR) }.to_ascii_lowercase(); + w!(&am); + } else { + let pm = unsafe { langinfo_to_str(PM_STR) }.to_ascii_lowercase(); + w!(&pm); + } + } + + // 12-hour clock with seconds + AM/PM: %r => %I:%M:%S %p + b'r' => w!(recurse "%I:%M:%S %p"), + + // 24-hour clock without seconds: %R => %H:%M + b'R' => w!(recurse "%H:%M"), + + // Seconds since the Epoch: %s => calls mktime() to convert tm to time_t + b's' => w!("{}", unsafe { super::mktime(t.cast_mut()) }), + + // Seconds (00-60): %S (unchanged) + b'S' => w!("{:02}", unsafe { (*t).tm_sec }), + + // 24-hour clock with seconds: %T => %H:%M:%S + b'T' => w!(recurse "%H:%M:%S"), + + // Weekday (1-7, Monday=1): %u + b'u' => w!("{}", (unsafe { (*t).tm_wday } + 7 - 1) % 7 + 1), + + // Sunday-based week of year: %U + b'U' => w!( + "{}", + (unsafe { (*t).tm_yday } + 7 - unsafe { (*t).tm_wday }) / 7 + ), + + // ISO-8601 week of year + b'V' => w!("{}", week_of_year(unsafe { &*t })), + + // Weekday (0-6, Sunday=0): %w + b'w' => w!("{}", unsafe { (*t).tm_wday }), + + // Monday-based week of year: %W + b'W' => w!( + "{}", + (unsafe { (*t).tm_yday } + 7 - (unsafe { (*t).tm_wday } + 6) % 7) / 7 + ), + + // Last two digits of year: %y + b'y' => w!("{:02}", unsafe { (*t).tm_year } % 100), + + // Full year: %Y + b'Y' => w!("{}", unsafe { (*t).tm_year } + 1900), + + // Timezone offset: %z + b'z' => { + let offset = unsafe { (*t).tm_gmtoff }; + let (sign, offset) = if offset < 0 { + ('-', -offset) + } else { + ('+', offset) + }; + let mins = offset.div_euclid(60); + let min = mins.rem_euclid(60); + let hour = mins.div_euclid(60); + w!("{}{:02}{:02}", sign, hour, min) + } + + // Timezone name: %Z + b'Z' => w!( + "{}", + unsafe { CStr::from_ptr((*t).tm_zone) }.to_str().unwrap() + ), + + // Date+time+TZ: %+ + b'+' => w!(recurse "%a %b %d %T %Z %Y"), + + // Unrecognized format specifier => fail + _ => return false, + } + + // Move past the format specifier + format = unsafe { format.offset(1) }; + } + true + } + + // Wrap the writer in a CountingWriter to return how many bytes were written. + let mut cw = platform::CountingWriter::new(w); + if unsafe { !inner_strftime(&mut cw, format, t) } { + return 0; + } + cw.written +} + +/// Calculate number of weeks in a year as defined by ISO 8601 +/// +/// ## Source +/// https://en.wikipedia.org/wiki/ISO_week_date +fn weeks_per_year(year: c_int) -> c_int { + let year = f64::from(year); + let p_y = (year + (year / 4.) - (year / 100.) + (year / 400.)) as c_int % 7; + if p_y == 4 { 53 } else { 52 } +} + +/// Calculate the week of the year accounting for leap weeks (ISO 8601) +/// +/// ## Source +/// https://en.wikipedia.org/wiki/ISO_week_date +fn week_of_year(time: &tm) -> c_int { + let week = (10 + time.tm_yday - time.tm_wday) / 7; + + if week <= 1 { + weeks_per_year(time.tm_year - 1) + } else if week > weeks_per_year(time.tm_year) { + 1 + } else { + week + } +} diff --git a/src/header/time/strptime.rs b/src/header/time/strptime.rs new file mode 100644 index 0000000000..f49c5b54b6 --- /dev/null +++ b/src/header/time/strptime.rs @@ -0,0 +1,593 @@ +// `strptime` implementation. +// +// See . + +use crate::header::time::tm; +use alloc::string::String; +use core::{ + ffi::{CStr, c_char, c_int, c_void}, + ptr, + ptr::NonNull, + str, +}; + +/// For convenience, we define some helper constants for the C-locale. +const SHORT_DAYS: [&str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; +const LONG_DAYS: [&str; 7] = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", +]; +const SHORT_MONTHS: [&str; 12] = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +]; +const LONG_MONTHS: [&str; 12] = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +]; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn strptime( + buf: *const c_char, + format: *const c_char, + tm: *mut tm, +) -> *mut c_char { + // Validate inputs + let buf_ptr = if let Some(ptr) = NonNull::new(buf.cast::().cast_mut()) { + ptr + } else { + return ptr::null_mut(); + }; + // + let fmt_ptr = if let Some(ptr) = NonNull::new(format.cast::().cast_mut()) { + ptr + } else { + return ptr::null_mut(); + }; + + let tm_ptr = if let Some(ptr) = NonNull::new(tm) { + ptr + } else { + return ptr::null_mut(); + }; + + // Convert raw pointers into slices/strings. + let input_str = unsafe { + if buf.is_null() { + return ptr::null_mut(); + } + match CStr::from_ptr(buf).to_str() { + Ok(s) => s, + Err(_) => return ptr::null_mut(), // Not a valid UTF-8 + } + }; + + let fmt_str = unsafe { + if format.is_null() { + return ptr::null_mut(); + } + match CStr::from_ptr(format).to_str() { + Ok(s) => s, + Err(_) => return ptr::null_mut(), // Not a valid UTF-8 + } + }; + + // We parse the format specifiers in a loop + let mut fmt_chars = fmt_str.chars().peekable(); + let mut index_in_input = 0; + + while let Some(fc) = fmt_chars.next() { + if fc != '%' { + // If it's a normal character, we expect it to match exactly in input + if input_str.len() <= index_in_input { + return ptr::null_mut(); // input ended too soon + } + let in_char = input_str.as_bytes()[index_in_input] as char; + if in_char != fc { + // mismatch + return ptr::null_mut(); + } + index_in_input += 1; + continue; + } + + // If we see '%', read the next character + let Some(spec) = fmt_chars.next() else { + // format string ended abruptly after '%' + return ptr::null_mut(); + }; + + // POSIX says `%E` or `%O` are modified specifiers for locale. + // We will skip them if they appear (like strftime does) and read the next char. + let final_spec = if spec == 'E' || spec == 'O' { + match fmt_chars.next() { + Some(ch) => ch, + None => return ptr::null_mut(), + } + } else { + spec + }; + + // Handle known specifiers + match final_spec { + // Whitespace: %n or %t + 'n' | 't' => { + // Skip over any whitespace in the input + while index_in_input < input_str.len() + && input_str.as_bytes()[index_in_input].is_ascii_whitespace() + { + index_in_input += 1; + } + } + + // Literal % => "%%" + '%' => { + if index_in_input >= input_str.len() + || input_str.as_bytes()[index_in_input] as char != '%' + { + return ptr::null_mut(); + } + index_in_input += 1; + } + + // Day of Month: %d / %e + 'd' | 'e' => { + // parse a 2-digit day (with or without leading zero) + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + unsafe { + (*tm).tm_mday = val as c_int; + // Day of month is limited to [1,31] according to the standard + if (*tm).tm_mday < 1 || (*tm).tm_mday > 31 { + return ptr::null_mut(); + } + } + index_in_input += len; + } + + // Month: %m + 'm' => { + // parse a 2-digit month + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + // tm_mon is 0-based (0 = Jan, 1 = Feb,...) + unsafe { + (*tm).tm_mon = (val as c_int) - 1; + if (*tm).tm_mon < 0 || (*tm).tm_mon > 11 { + return ptr::null_mut(); + } + } + index_in_input += len; + } + + // Year without century: %y + 'y' => { + // parse a 2-digit year + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + // According to POSIX, %y in strptime is [00,99], and the "year" is 1900..1999 for [00..99], + // but the standard says: "values in [69..99] refer to 1969..1999, [00..68] => 2000..2068" + let fullyear = if val >= 69 { val + 1900 } else { val + 2000 }; + unsafe { + (*tm).tm_year = (fullyear - 1900) as c_int; + } + index_in_input += len; + } + + // Year with century: %Y + 'Y' => { + // parse up to 4-digit (or more) year + // We allow more than 4 digits if needed + let (val, len) = match parse_int(&input_str[index_in_input..], 4, true) { + Some(v) => v, + None => return ptr::null_mut(), + }; + unsafe { + (*tm).tm_year = (val as c_int) - 1900; + } + index_in_input += len; + } + + // Hour (00..23): %H + 'H' => { + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + if val > 23 { + return ptr::null_mut(); + } + unsafe { + (*tm).tm_hour = val as c_int; + } + index_in_input += len; + } + + // Hour (01..12): %I + 'I' => { + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + if !(1..=12).contains(&val) { + return ptr::null_mut(); + } + unsafe { + (*tm).tm_hour = val as c_int; + } + // We’ll interpret AM/PM with %p if it appears later + index_in_input += len; + } + + // Minute (00..59): %M + 'M' => { + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + if val > 59 { + return ptr::null_mut(); + } + unsafe { + (*tm).tm_min = val as c_int; + } + index_in_input += len; + } + + // Seconds (00..60): %S + 'S' => { + let (val, len) = match parse_int(&input_str[index_in_input..], 2, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + if val > 60 { + return ptr::null_mut(); + } + unsafe { + (*tm).tm_sec = val as c_int; + } + index_in_input += len; + } + + // AM/PM: %p + 'p' => { + // Parse either "AM" or "PM" (no case-sensitive) + // We'll read up to 2 or 3 letters from input ("AM", "PM") + let leftover = &input_str[index_in_input..]; + let parsed_len = match parse_am_pm(leftover) { + Some((is_pm, used)) => { + if unsafe { (*tm).tm_hour } == 12 { + // 12 AM => 00:xx, 12 PM => 12:xx + unsafe { + (*tm).tm_hour = if is_pm { 12 } else { 0 }; + } + } else { + // 1..11 AM => 1..11, 1..11 PM => 13..23 + if is_pm { + unsafe { + (*tm).tm_hour += 12; + } + } + } + used + } + None => return ptr::null_mut(), + }; + index_in_input += parsed_len; + } + + // Weekday Name: %a/%A + 'a' => { + // Abbreviated day name (Sun..Sat) + let leftover = &input_str[index_in_input..]; + let parsed_len = match parse_weekday(leftover, true) { + Some((wday, used)) => { + unsafe { + (*tm).tm_wday = wday as c_int; + } + used + } + None => return ptr::null_mut(), + }; + index_in_input += parsed_len; + } + 'A' => { + // Full day name (Sunday..Saturday) + let leftover = &input_str[index_in_input..]; + let parsed_len = match parse_weekday(leftover, false) { + Some((wday, used)) => { + unsafe { + (*tm).tm_wday = wday as c_int; + } + used + } + None => return ptr::null_mut(), + }; + index_in_input += parsed_len; + } + + // Month Name: %b/%B/%h + 'b' | 'h' => { + // Abbreviated month name + let leftover = &input_str[index_in_input..]; + let parsed_len = match parse_month(leftover, true) { + Some((mon, used)) => { + unsafe { + (*tm).tm_mon = mon as c_int; + } + used + } + None => return ptr::null_mut(), + }; + index_in_input += parsed_len; + } + 'B' => { + // Full month name + let leftover = &input_str[index_in_input..]; + let parsed_len = match parse_month(leftover, false) { + Some((mon, used)) => { + unsafe { + (*tm).tm_mon = mon as c_int; + } + used + } + None => return ptr::null_mut(), + }; + index_in_input += parsed_len; + } + + // Day of year: %j + 'j' => { + // parse 3-digit day of year [001..366] + let (val, len) = match parse_int(&input_str[index_in_input..], 3, false) { + Some(v) => v, + None => return ptr::null_mut(), + }; + if !(1..=366).contains(&val) { + return ptr::null_mut(); + } + // store in tm_yday + unsafe { + (*tm).tm_yday = (val - 1) as c_int; + } + index_in_input += len; + } + + // Date shortcuts: %D, %F, etc. + 'D' => { + // Equivalent to "%m/%d/%y" + // We can do a mini strptime recursion or manually parse + // For simplicity, we'll do it inline here + let subfmt = "%m/%d/%y"; + let used = + match unsafe { apply_subformat(&input_str[index_in_input..], subfmt, tm) } { + Some(v) => v, + None => return ptr::null_mut(), + }; + index_in_input += used; + } + 'F' => { + // Equivalent to "%Y-%m-%d" + let subfmt = "%Y-%m-%d"; + let used = + match unsafe { apply_subformat(&input_str[index_in_input..], subfmt, tm) } { + Some(v) => v, + None => return ptr::null_mut(), + }; + index_in_input += used; + } + 'T' => { + // Equivalent to %H:%M:%S + let subfmt = "%H:%M:%S"; + let used = + match unsafe { apply_subformat(&input_str[index_in_input..], subfmt, tm) } { + Some(v) => v, + None => return ptr::null_mut(), + }; + index_in_input += used; + } + + // TODO : not implemented: %x, %X, %c, %r, %R, etc. + // Hint : if you want to implement these, do similarly to %D / %F (or parse manually) + 'x' | 'X' | 'c' | 'r' | 'R' => { + // Return NULL if we don’t want to accept them : + return ptr::null_mut(); + } + + // Timezone: %Z or %z + 'Z' | 'z' => { + // Full/abbrev time zone name or numeric offset + // Implementation omitted. Real support is quite complicated. + return ptr::null_mut(); + } + + _ => { + // We do not recognize this specifier + return ptr::null_mut(); + } + } + } + + // If we got here, parsing was successful. Return pointer to the + // next unparsed character in `buf`. + let ret_ptr = unsafe { buf.add(index_in_input) }; + ret_ptr.cast_mut() +} + +// Helper / Parsing Logic + +/// Parse an integer from the beginning of `input_str`. +/// +/// - `width` is the maximum number of digits to parse +/// - `allow_variable_width` indicates if we can parse fewer digits +/// (e.g., `%Y` can have more than 4 digits, but also might parse "2023" or "12345"). +fn parse_int(input: &str, width: usize, allow_variable: bool) -> Option<(i32, usize)> { + let mut val = 0i32; + let chars = input.chars(); + let mut count = 0; + + for c in chars { + if !c.is_ascii_digit() { + break; + } + + // Check for integer overflow + val = val + .checked_mul(10)? + .checked_add(i32::from(c as u8 - b'0'))?; + + count += 1; + if count == width && !allow_variable { + break; + } + } + + if count == 0 { None } else { Some((val, count)) } +} + +/// Handle AM/PM. Returns (is_pm, length_consumed). +/// Accepts "AM", "am", "PM", "pm" case-insensitively. +fn parse_am_pm(s: &str) -> Option<(bool, usize)> { + let trimmed = s.trim_start(); + // Amount of whitespace skipped; can be 0 + let diff = s.len() - trimmed.len(); + let s = trimmed.get(0..2)?; + + if s.eq_ignore_ascii_case("AM") { + return Some((false, diff + 2)); + } + if s.eq_ignore_ascii_case("PM") { + return Some((true, diff + 2)); + } + None +} + +/// Parse a weekday name from `s`. +/// - if `abbrev == true`, match short forms: "Mont".."Sun" +/// - otherwise, match "Monday".."Sunday" +/// +/// Return (weekday_index, length_consumed). +fn parse_weekday(s: &str, abbrev: bool) -> Option<(usize, usize)> { + let list = if abbrev { &SHORT_DAYS } else { &LONG_DAYS }; + for (i, name) in list.iter().enumerate() { + if s.len() >= name.len() + && s.get(0..name.len()) + .is_some_and(|sub| sub.eq_ignore_ascii_case(name)) + { + return Some((i, name.len())); + } + } + None +} + +/// Parse a month name from `s`. +/// - If `abbrev == true`, match short forms: "Jan".."Dec" +/// - Otherwise, match "January".."December" +/// +/// Return (month_index, length_consumed). +fn parse_month(s: &str, abbrev: bool) -> Option<(usize, usize)> { + let list = if abbrev { &SHORT_MONTHS } else { &LONG_MONTHS }; + for (i, name) in list.iter().enumerate() { + if s.len() >= name.len() + && s.get(0..name.len()) + .is_some_and(|sub| sub.eq_ignore_ascii_case(name)) + { + return Some((i, name.len())); + } + } + None +} + +/// Apply a small subformat (like "%m/%d/%y" or "%Y-%m-%d") to `input`. +/// Return how many characters of `input` were consumed or None on error. +unsafe fn apply_subformat(input: &str, subfmt: &str, tm: *mut tm) -> Option { + // We'll do a temporary strptime call on a substring. + // Then we see how many chars it consumed. If that call fails, we return None. + // Otherwise, we return the count. + + // Convert `input` to a null-terminated buffer temporarily + let mut tmpbuf = String::with_capacity(input.len() + 1); + tmpbuf.push_str(input); + tmpbuf.push('\0'); + + let mut tmpfmt = String::with_capacity(subfmt.len() + 1); + tmpfmt.push_str(subfmt); + tmpfmt.push('\0'); + + // We need a copy of the tm, so if partial parse fails, we don't override. + let old_tm = unsafe { ptr::read(tm) }; // backup + + let consumed_ptr = unsafe { + strptime( + tmpbuf.as_ptr().cast::(), + tmpfmt.as_ptr().cast::(), + tm, + ) + }; + + if consumed_ptr.is_null() { + // revert + unsafe { + *tm = old_tm; + } + return None; + } + + // consumed_ptr - tmpbuf.as_ptr() => # of bytes consumed + let diff = (consumed_ptr as usize) - (tmpbuf.as_ptr() as usize); + Some(diff) +} + +#[cfg(test)] +mod tests { + use super::parse_am_pm; + + #[test] + fn am_pm_parser_works() { + let am = "am"; + let am_expected = Some((false, 2)); + assert_eq!(am_expected, parse_am_pm(am)); + + let pm = "pm"; + let pm_expected = Some((true, 2)); + assert_eq!(pm_expected, parse_am_pm(pm)); + + let am_caps = "AM"; + assert_eq!(am_expected, parse_am_pm(am_caps)); + + let pm_caps = "PM"; + assert_eq!(pm_expected, parse_am_pm(pm_caps)); + + let am_weird = "aM"; + assert_eq!(am_expected, parse_am_pm(am_weird)); + + let am_prefix = " \tam"; + let am_prefix_expected = Some((false, 11)); + assert_eq!(am_prefix_expected, parse_am_pm(am_prefix)); + + let pm_spaces = " pm "; + let pm_spaces_expected = Some((true, 10)); + assert_eq!(pm_spaces_expected, parse_am_pm(pm_spaces)); + } +} diff --git a/src/header/unistd/brk.rs b/src/header/unistd/brk.rs new file mode 100644 index 0000000000..65d6544171 --- /dev/null +++ b/src/header/unistd/brk.rs @@ -0,0 +1,58 @@ +use core::ptr; + +use crate::{ + error::ResultExtPtrMut, + header::errno::ENOMEM, + platform::{ + self, Pal, Sys, + types::{c_int, c_void, intptr_t}, + }, +}; + +static mut BRK: *mut c_void = ptr::null_mut(); + +/// See . +/// +/// # Deprecation +/// The `brk()` function was marked legacy in the System Interface & Headers +/// Issue 5, and removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn brk(addr: *mut c_void) -> c_int { + unsafe { BRK = Sys::brk(addr).or_errno_null_mut() }; + + if unsafe { BRK } < addr { + platform::ERRNO.set(ENOMEM); + return -1; + } + + 0 +} + +/// See . +/// +/// # Deprecation +/// The `sbrk()` function was marked legacy in the System Interface & Headers +/// Issue 5, and removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sbrk(incr: intptr_t) -> *mut c_void { + if unsafe { BRK }.is_null() { + unsafe { BRK = Sys::brk(ptr::null_mut()).or_errno_null_mut() }; + } + + let old_brk = unsafe { BRK }; + + if incr != 0 { + let addr = unsafe { old_brk.offset(incr) }; + + unsafe { BRK = Sys::brk(addr).or_errno_null_mut() }; + + if unsafe { BRK } < addr { + platform::ERRNO.set(ENOMEM); + return -1isize as *mut c_void; + } + } + + old_brk.cast::() +} diff --git a/src/header/unistd/cbindgen.toml b/src/header/unistd/cbindgen.toml new file mode 100644 index 0000000000..187a0d64f0 --- /dev/null +++ b/src/header/unistd/cbindgen.toml @@ -0,0 +1,26 @@ +sys_includes = ["stddef.h", "stdint.h", "sys/types.h", "features.h", "fcntl.h"] +include_guard = "_RELIBC_UNISTD_H" +after_includes = """ + +#define _POSIX_VERSION 200809L +#define _POSIX_BARRIERS 202405L +#define _POSIX_MONOTONIC_CLOCK 200112L +#define _POSIX_REALTIME_SIGNALS 202405L +#define _POSIX_SEMAPHORES 200112L +#define _POSIX_SHELL 1 +#define _POSIX_SHARED_MEMORY_OBJECTS 200112L +#define _POSIX_THREADS 202405L +#define _POSIX_THREAD_ATTR_STACKADDR 202405L +#define _POSIX_THREAD_ATTR_STACKSIZE 202405L +#define _POSIX_TIMEOUTS 202405L +#define _POSIX_TIMERS 202405L +#define _XOPEN_SHM 1 +#define _XOPEN_VERSION 700L +""" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/unistd/getopt.rs b/src/header/unistd/getopt.rs new file mode 100644 index 0000000000..5240e429fa --- /dev/null +++ b/src/header/unistd/getopt.rs @@ -0,0 +1,40 @@ +//! `getopt` implementation. +//! +//! See . + +use core::ptr; + +use crate::{ + header::getopt, + platform::types::{c_char, c_int}, +}; + +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut optarg: *mut c_char = ptr::null_mut(); + +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut opterr: c_int = 1; + +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut optind: c_int = 1; + +/// See . +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut optopt: c_int = -1; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getopt( + argc: c_int, + argv: *const *mut c_char, + optstring: *const c_char, +) -> c_int { + unsafe { getopt::getopt_long(argc, argv, optstring, ptr::null(), ptr::null_mut()) } +} diff --git a/src/header/unistd/getpass.rs b/src/header/unistd/getpass.rs new file mode 100644 index 0000000000..8d7a55431b --- /dev/null +++ b/src/header/unistd/getpass.rs @@ -0,0 +1,71 @@ +use core::ptr; + +use crate::{ + c_str::CStr, + fs::File, + header::{ + fcntl::{O_CLOEXEC, O_RDWR}, + limits::PASS_MAX, + termios, + }, + io::{self, Read, Write}, + raw_cell::RawCell, +}; + +use crate::platform::types::c_char; + +fn getpass_rs(prompt: CStr, passbuff: &mut [u8]) -> Result<*mut c_char, io::Error> { + let mut f = File::open(c"/dev/tty".into(), O_RDWR | O_CLOEXEC)?; + + let mut term = termios::termios::default(); + + unsafe { + termios::tcgetattr(f.fd, &raw mut term); + } + + let old_term = term.clone(); + + term.c_iflag &= !(termios::IGNCR | termios::INLCR) as u32; + term.c_iflag |= termios::ICRNL as u32; + term.c_lflag &= !(termios::ECHO | termios::ISIG) as u32; + term.c_lflag |= termios::ICANON as u32; + + unsafe { + termios::tcsetattr(f.fd, termios::TCSAFLUSH, &raw const term); + } + + f.write(prompt.to_bytes())?; + f.flush()?; + + let mut len = f.read(passbuff)?; + + if len > 0 && passbuff[len - 1] == b'\n' || passbuff.len() == len { + len -= 1; + } + + passbuff[len] = 0; + + unsafe { + termios::tcsetattr(f.fd, termios::TCSAFLUSH, &raw const old_term); + } + + f.write(b"\n")?; + f.flush()?; + + Ok(passbuff.as_mut_ptr().cast::()) +} + +/// See . +/// +/// # Deprecation +/// The `getpass()` function was marked legacy in the Open Group System +/// Interface & Headers Issue 5, and removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getpass(prompt: *const c_char) -> *mut c_char { + static PASSBUFF: RawCell<[u8; PASS_MAX]> = RawCell::new([0; PASS_MAX]); + + unsafe { + getpass_rs(CStr::from_ptr(prompt), &mut *PASSBUFF.as_mut_ptr()).unwrap_or(ptr::null_mut()) + } +} diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs new file mode 100644 index 0000000000..f44a52eaa0 --- /dev/null +++ b/src/header/unistd/mod.rs @@ -0,0 +1,1324 @@ +//! `unistd.h` implementation. +//! +//! See . + +use core::{ + convert::TryFrom, + ffi::VaListImpl, + mem::{self, MaybeUninit}, + ptr, slice, +}; + +#[allow(deprecated)] +use crate::platform::types::useconds_t; +use crate::{ + c_str::CStr, + error::{Errno, ResultExt}, + header::{ + bits_sigset_t::sigset_t, + bits_timespec::timespec, + crypt::{crypt_data, crypt_r}, + errno::{self, ENAMETOOLONG}, + fcntl, limits, + signal::{sigprocmask, sigsuspend}, + stdlib::getenv, + sys_ioctl, sys_resource, + sys_select::timeval, + sys_time, sys_utsname, termios, + }, + out::Out, + platform::{ + self, ERRNO, Pal, PalSignal, Sys, + types::{ + c_char, c_int, c_long, c_short, c_uint, c_ulonglong, c_void, gid_t, off_t, pid_t, + size_t, ssize_t, suseconds_t, time_t, uid_t, + }, + }, +}; + +pub use self::{brk::*, getopt::*, pathconf::*, sysconf::*}; +pub use crate::header::pthread::fork_hooks; + +// Inclusion of ctermid() prototype marked as obsolescent since Issue 7, cf. +// . +// cuserid() marked legacy in Issue 5. +#[deprecated] +pub use crate::header::stdio::ctermid; +#[allow(deprecated)] +pub use crate::header::stdio::cuserid; + +use super::{ + errno::{E2BIG, EINVAL, ENOMEM}, + stdio::snprintf, +}; + +mod brk; +mod getopt; +mod getpass; +mod pathconf; +#[cfg(target_os = "linux")] +pub mod syscall; +mod sysconf; + +pub const F_OK: c_int = 0; +pub const R_OK: c_int = 4; +pub const W_OK: c_int = 2; +pub const X_OK: c_int = 1; + +pub const SEEK_SET: c_int = 0; +pub const SEEK_CUR: c_int = 1; +pub const SEEK_END: c_int = 2; + +pub const F_ULOCK: c_int = 0; +pub const F_LOCK: c_int = 1; +pub const F_TLOCK: c_int = 2; +pub const F_TEST: c_int = 3; + +pub const STDIN_FILENO: c_int = 0; +pub const STDOUT_FILENO: c_int = 1; +pub const STDERR_FILENO: c_int = 2; + +pub const L_cuserid: usize = 9; + +// confstr constants +// These are copied from Rust's libc and match musl as well. +pub const _CS_PATH: c_int = 0; +pub const _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS: c_int = 1; +pub const _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS: c_int = 4; +pub const _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS: c_int = 5; +pub const _CS_POSIX_V6_ILP32_OFF32_CFLAGS: c_int = 1116; +pub const _CS_POSIX_V6_ILP32_OFF32_LDFLAGS: c_int = 1117; +pub const _CS_POSIX_V6_ILP32_OFF32_LIBS: c_int = 1118; +pub const _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS: c_int = 1119; +pub const _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS: c_int = 1120; +pub const _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS: c_int = 1121; +pub const _CS_POSIX_V6_ILP32_OFFBIG_LIBS: c_int = 1122; +pub const _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS: c_int = 1123; +pub const _CS_POSIX_V6_LP64_OFF64_CFLAGS: c_int = 1124; +pub const _CS_POSIX_V6_LP64_OFF64_LDFLAGS: c_int = 1125; +pub const _CS_POSIX_V6_LP64_OFF64_LIBS: c_int = 1126; +pub const _CS_POSIX_V6_LP64_OFF64_LINTFLAGS: c_int = 1127; +pub const _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS: c_int = 1128; +pub const _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS: c_int = 1129; +pub const _CS_POSIX_V6_LPBIG_OFFBIG_LIBS: c_int = 1130; +pub const _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS: c_int = 1131; +pub const _CS_POSIX_V7_ILP32_OFF32_CFLAGS: c_int = 1132; +pub const _CS_POSIX_V7_ILP32_OFF32_LDFLAGS: c_int = 1133; +pub const _CS_POSIX_V7_ILP32_OFF32_LIBS: c_int = 1134; +pub const _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS: c_int = 1135; +pub const _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS: c_int = 1136; +pub const _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS: c_int = 1137; +pub const _CS_POSIX_V7_ILP32_OFFBIG_LIBS: c_int = 1138; +pub const _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS: c_int = 1139; +pub const _CS_POSIX_V7_LP64_OFF64_CFLAGS: c_int = 1140; +pub const _CS_POSIX_V7_LP64_OFF64_LDFLAGS: c_int = 1141; +pub const _CS_POSIX_V7_LP64_OFF64_LIBS: c_int = 1142; +pub const _CS_POSIX_V7_LP64_OFF64_LINTFLAGS: c_int = 1143; +pub const _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS: c_int = 1144; +pub const _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS: c_int = 1145; +pub const _CS_POSIX_V7_LPBIG_OFFBIG_LIBS: c_int = 1146; +pub const _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS: c_int = 1147; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn _Fork() -> pid_t { + unsafe { Sys::fork() }.or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn _exit(status: c_int) -> ! { + Sys::exit(status) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::access(path, mode).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn faccessat( + fd: c_int, + path: *const c_char, + mode: c_int, + flags: c_int, +) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::faccessat(fd, path, mode, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn alarm(seconds: c_uint) -> c_uint { + Sys::alarm(seconds) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::chdir(path).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::chown(path, owner, group) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// # Deprecation +/// The `chroot()` function was marked legacy in the System Interface & Headers +/// Issue 5, and removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn chroot(path: *const c_char) -> c_int { + // TODO: Implement + platform::ERRNO.set(crate::header::errno::EPERM); + + -1 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn close(fildes: c_int) -> c_int { + Sys::close(fildes).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn confstr(name: c_int, buf: *mut c_char, len: size_t) -> size_t { + // confstr returns the number of bytes required to hold the string INCLUDING the NUL + // terminator. This is different from other C functions hence the + 1. + match name { + _CS_PATH => { + let posix2_path = c"/usr/bin"; + unsafe { snprintf(buf, len, c"%s".as_ptr(), posix2_path.as_ptr()) + 1 } + .try_into() + .unwrap_or_default() + } + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS + | _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS + | _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS + | _CS_POSIX_V6_LP64_OFF64_LIBS..=_CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS => 1, + _ => { + platform::ERRNO.set(errno::EINVAL); + 0 + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char { + let mut data = crypt_data::new(); + unsafe { crypt_r(key, salt, &raw mut data) } +} + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub extern "C" fn daemon(nochdir: c_int, noclose: c_int) -> c_int { + if nochdir == 0 && Sys::chdir(c"/".into()).map(|()| 0).or_minus_one_errno() < 0 { + return -1; + } + + if noclose == 0 { + let fd = Sys::open(c"/dev/null".into(), fcntl::O_RDWR, 0).or_minus_one_errno(); + if fd < 0 { + return -1; + } + if dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0 { + close(fd); + return -1; + } + if fd > 2 { + close(fd); + } + } + + match unsafe { fork() } { + 0 => {} + -1 => return -1, + _ => _exit(0), + } + + if setsid() < 0 { + return -1; + } + + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn dup(fildes: c_int) -> c_int { + Sys::dup(fildes).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int { + Sys::dup2(fildes, fildes2).or_minus_one_errno() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int { + // dup3 requires fildes != fildes2 (unlike dup2 which is a no-op in that case) + if fildes == fildes2 { + ERRNO.set(EINVAL); + return -1; + } + match Sys::dup2(fildes, fildes2) { + Ok(newfd) => { + if flag & fcntl::O_CLOEXEC != 0 { + let _ = Sys::fcntl(newfd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong); + } + newfd + } + Err(Errno(e)) => { ERRNO.set(e); -1 } + } +} + +// See . +// +// # Deprecation +// The `encrypt()` function was marked obsolescent in the Open Group Base Specifications Issue 8. +//#[deprecated] +// #[unsafe(no_mangle)] +//pub extern "C" fn encrypt(block: [c_char; 64], edflag: c_int) { +// unimplemented!(); +//} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn execl( + path: *const c_char, + arg0: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { + with_argv(__valist, arg0, |args, _remaining_va| { + execv(path, args.as_ptr().cast()) + }) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn execle( + path: *const c_char, + arg0: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { + with_argv(__valist, arg0, |args, mut remaining_va| { + let envp = remaining_va.arg::<*const *mut c_char>(); + execve(path, args.as_ptr().cast(), envp) + }) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn execlp( + file: *const c_char, + arg0: *const c_char, + mut __valist: ... +) -> c_int { + unsafe { + with_argv(__valist, arg0, |args, _remaining_va| { + execvp(file, args.as_ptr().cast()) + }) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int { + unsafe { execve(path, argv, platform::environ) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn execve( + path: *const c_char, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + unsafe { Sys::execve(path, argv, envp) } + .map(|()| unreachable!()) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fexecve( + fd: c_int, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + unsafe { Sys::fexecve(fd, argv, envp) } + .map(|()| unreachable!()) + .or_minus_one_errno() +} + +const PATH_SEPARATOR: u8 = b':'; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int { + let file = unsafe { CStr::from_ptr(file) }; + + if file.to_bytes().contains(&b'/') + || (cfg!(target_os = "redox") && file.to_bytes().contains(&b':')) + { + unsafe { execv(file.as_ptr(), argv) } + } else { + let mut error = errno::ENOENT; + + let path_env = unsafe { getenv(c"PATH".as_ptr()) }; + if !path_env.is_null() { + let path_env = unsafe { CStr::from_ptr(path_env) }; + for path in path_env.to_bytes().split(|&b| b == PATH_SEPARATOR) { + let file = file.to_bytes(); + let length = file.len() + path.len() + 2; + let mut program = alloc::vec::Vec::with_capacity(length); + program.extend_from_slice(path); + program.push(b'/'); + program.extend_from_slice(file); + program.push(b'\0'); + + let program_c = CStr::from_bytes_with_nul(&program).unwrap(); + unsafe { execv(program_c.as_ptr(), argv) }; + + match platform::ERRNO.get() { + errno::ENOENT => (), + other => error = other, + } + } + } + + platform::ERRNO.set(error); + -1 + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fchdir(fildes: c_int) -> c_int { + Sys::fchdir(fildes).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int { + Sys::fchown(fildes, owner, group) + .map(|()| 0) + .or_minus_one_errno() +} +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fchownat( + fd: c_int, + path: *const c_char, + owner: uid_t, + group: gid_t, + flags: c_int, +) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::fchownat(fd, path, owner, group, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fdatasync(fildes: c_int) -> c_int { + Sys::fdatasync(fildes).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fork() -> pid_t { + for prepare in unsafe { &fork_hooks[0] } { + prepare(); + } + let pid = unsafe { Sys::fork() }.or_minus_one_errno(); + if pid == 0 { + for child in unsafe { &fork_hooks[2] } { + child(); + } + } else if pid != -1 { + for parent in unsafe { &fork_hooks[1] } { + parent(); + } + } + pid +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fsync(fildes: c_int) -> c_int { + Sys::fsync(fildes).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int { + Sys::ftruncate(fildes, length) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char { + let alloc = buf.is_null(); + let mut stack_buf = [0; limits::PATH_MAX]; + if alloc { + buf = stack_buf.as_mut_ptr(); + size = stack_buf.len(); + } + + let ret = match Sys::getcwd(unsafe { Out::from_raw_parts(buf.cast(), size) }) { + Ok(()) => buf, + Err(Errno(errno)) => { + ERRNO.set(errno); + return ptr::null_mut(); + } + }; + + if alloc { + let len = stack_buf + .iter() + .position(|b| *b == 0) + .expect("no nul-byte in getcwd string") + + 1; + let heap_buf = unsafe { platform::alloc(len).cast::() }; + for (i, inner) in stack_buf.iter().enumerate().take(len) { + unsafe { + *heap_buf.add(i) = *inner; + } + } + heap_buf + } else { + ret + } +} + +/// See . +/// +/// # Deprecation +/// The `getdtablesize()` function was marked legacy in the System Interface & +/// Headers Issue 5, and removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn getdtablesize() -> c_int { + let mut lim = mem::MaybeUninit::::uninit(); + let r = unsafe { + sys_resource::getrlimit( + sys_resource::RLIMIT_NOFILE as c_int, + lim.as_mut_ptr().cast::(), + ) + }; + if r == 0 { + let cur = unsafe { lim.assume_init() }.rlim_cur; + return match cur { + c if c < i32::MAX as u64 => c as i32, + _ => i32::MAX, + }; + } + -1 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getegid() -> gid_t { + Sys::getegid() +} + +/// See . +// #[unsafe(no_mangle)] +pub unsafe extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int { + // POSIX limits getentropy to 256 bytes per call + const GETENTROPY_MAX: size_t = 256; + + if length > GETENTROPY_MAX { + ERRNO.set(EINVAL); + return -1; + } + + let path = unsafe { CStr::from_ptr(c"/scheme/rand".as_ptr().cast()) }; + let fd = match Sys::open(path, fcntl::O_RDONLY, 0) { + Ok(fd) => fd, + Err(Errno(e)) => { + ERRNO.set(e); + return -1; + } + }; + + let buf = unsafe { slice::from_raw_parts_mut(buffer.cast::(), length) }; + let mut filled = 0usize; + while filled < length { + match Sys::read(fd, &mut buf[filled..]) { + Ok(0) => break, + Ok(n) => filled += n, + Err(Errno(e)) => { + let _ = Sys::close(fd); + ERRNO.set(e); + return -1; + } + } + } + let _ = Sys::close(fd); + if filled < length { ERRNO.set(errno::EIO); -1 } else { 0 } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn geteuid() -> uid_t { + Sys::geteuid() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getgid() -> gid_t { + Sys::getgid() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getgroups(size: c_int, list: *mut gid_t) -> c_int { + (|| { + let size = usize::try_from(size) + // fails for negative size, but EINVAL required if size != 0 && size < actual size, + // where the actual number of entries in the group list is obviously nonnegative + .map_err(|_| Errno(EINVAL))?; + + let list = unsafe { Out::from_raw_parts(list, size) }; + + Sys::getgroups(list) + })() + .or_minus_one_errno() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn gethostid() -> c_long { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int { + let mut uts = mem::MaybeUninit::::uninit(); + // TODO + let err = Sys::uname(Out::from_uninit_mut(&mut uts)) + .map(|()| 0) + .or_minus_one_errno(); + if err < 0 { + return err; + } + for c in unsafe { uts.assume_init() }.nodename.iter() { + if len == 0 { + break; + } + len -= 1; + + unsafe { *name = *c }; + + if unsafe { *name } == 0 { + // We do want to copy the zero also, so we check this after the copying. + break; + } + + name = unsafe { name.offset(1) }; + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getlogin() -> *mut c_char { + const LOGIN_LEN: usize = limits::LOGIN_NAME_MAX as usize; + static mut LOGIN: [c_char; LOGIN_LEN] = [0; LOGIN_LEN]; + if getlogin_r((&raw mut LOGIN).cast(), LOGIN_LEN) == 0 { + (&raw mut LOGIN).cast() + } else { + ptr::null_mut() + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getlogin_r(name: *mut c_char, namesize: size_t) -> c_int { + //TODO: Determine correct getlogin result on Redox + platform::ERRNO.set(errno::ENOENT); + -1 +} + +/// See . +/// +/// # Deprecation +/// The `getpagesize()` function was marked legacy in the System Interface & +/// Headers Issue 5, and removed in Issue 6. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn getpagesize() -> c_int { + // Panic if we can't uphold the required behavior (no errors are specified for this function) + Sys::getpagesize() + .try_into() + .expect("page size not representable as type `int`") +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getpgid(pid: pid_t) -> pid_t { + Sys::getpgid(pid).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getpgrp() -> pid_t { + Sys::getpgid(Sys::getpid()).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getpid() -> pid_t { + Sys::getpid() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getppid() -> pid_t { + Sys::getppid() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getresgid(rgid: *mut gid_t, egid: *mut gid_t, sgid: *mut gid_t) -> c_int { + Sys::getresgid( + unsafe { Out::nullable(rgid) }, + unsafe { Out::nullable(egid) }, + unsafe { Out::nullable(sgid) }, + ) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getresuid(ruid: *mut uid_t, euid: *mut uid_t, suid: *mut uid_t) -> c_int { + Sys::getresuid( + unsafe { Out::nullable(ruid) }, + unsafe { Out::nullable(euid) }, + unsafe { Out::nullable(suid) }, + ) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getsid(pid: pid_t) -> pid_t { + Sys::getsid(pid).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn getuid() -> uid_t { + Sys::getuid() +} + +/// See . +/// +/// # Deprecation +/// The `getwd()` function was marked legacy in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char { + unsafe { getcwd(path_name, limits::PATH_MAX) } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn isatty(fd: c_int) -> c_int { + let mut t = termios::termios::default(); + if unsafe { termios::tcgetattr(fd, &raw mut t) == 0 } { + 1 + } else { + 0 + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::lchown(path, owner, group) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int { + let path1 = unsafe { CStr::from_ptr(path1) }; + let path2 = unsafe { CStr::from_ptr(path2) }; + Sys::link(path1, path2).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn linkat( + fd1: c_int, + path1: *const c_char, + fd2: c_int, + path2: *const c_char, + flags: c_int, +) -> c_int { + let path1 = unsafe { CStr::from_ptr(path1) }; + let path2 = unsafe { CStr::from_ptr(path2) }; + Sys::linkat(fd1, path1, fd2, path2, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int { + let mut fl = fcntl::flock { + l_type: fcntl::F_WRLCK as c_short, + l_whence: SEEK_CUR as c_short, + l_start: 0, + l_len: size, + l_pid: -1, + }; + + match function { + fcntl::F_TEST => { + fl.l_type = fcntl::F_RDLCK as c_short; + if unsafe { fcntl::fcntl(fildes, fcntl::F_GETLK, &raw mut fl as c_ulonglong) } < 0 { + return -1; + } + if fl.l_type == fcntl::F_UNLCK as c_short || fl.l_pid == getpid() { + return 0; + } + platform::ERRNO.set(errno::EACCES); + -1 + } + fcntl::F_ULOCK => { + fl.l_type = fcntl::F_UNLCK as c_short; + unsafe { fcntl::fcntl(fildes, fcntl::F_SETLK, &raw mut fl as c_ulonglong) } + } + fcntl::F_TLOCK => unsafe { + fcntl::fcntl(fildes, fcntl::F_SETLK, &raw mut fl as c_ulonglong) + }, + fcntl::F_LOCK => unsafe { + fcntl::fcntl(fildes, fcntl::F_SETLKW, &raw mut fl as c_ulonglong) + }, + _ => { + platform::ERRNO.set(errno::EINVAL); + -1 + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t { + Sys::lseek(fildes, offset, whence).or_minus_one_errno() +} + +/// See . +// #[unsafe(no_mangle)] +pub extern "C" fn nice(incr: c_int) -> c_int { + unimplemented!(); +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pause() -> c_int { + let mut pset = mem::MaybeUninit::::uninit(); + unsafe { sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr()) }; + let set = unsafe { pset.assume_init() }; + unsafe { sigsuspend(&raw const set) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pipe(fildes: *mut c_int) -> c_int { + unsafe { pipe2(fildes, 0) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pipe2(fildes: *mut c_int, flags: c_int) -> c_int { + Sys::pipe2(unsafe { Out::nonnull(fildes.cast::<[c_int; 2]>()) }, flags) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn posix_close(fildes: c_int, flag: c_int) -> c_int { + // Since we do not define `POSIX_CLOSE_RESTART`, this function is + // equivalent to `close`. In the future when we move file descriptors + // to userspace, it would only make sense to define `POSIX_CLOSE_RESTART` + // if `close` is not atomic. + close(fildes) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pread( + fildes: c_int, + buf: *mut c_void, + nbyte: size_t, + offset: off_t, +) -> ssize_t { + Sys::pread( + fildes, + unsafe { slice::from_raw_parts_mut(buf.cast::(), nbyte) }, + offset, + ) + .map(|read| read as ssize_t) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pwrite( + fildes: c_int, + buf: *const c_void, + nbyte: size_t, + offset: off_t, +) -> ssize_t { + Sys::pwrite( + fildes, + unsafe { slice::from_raw_parts(buf.cast::(), nbyte) }, + offset, + ) + .map(|read| read as ssize_t) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn read(fildes: c_int, buf: *mut c_void, nbyte: size_t) -> ssize_t { + let buf = unsafe { slice::from_raw_parts_mut(buf.cast::(), nbyte) }; + trace_expr!( + Sys::read(fildes, buf) + .map(|read| read as ssize_t) + .or_minus_one_errno(), + "read({}, {:p}, {})", + fildes, + buf, + nbyte + ) +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn readlink( + path: *const c_char, + buf: *mut c_char, + bufsize: size_t, +) -> ssize_t { + let path = unsafe { CStr::from_ptr(path) }; + let buf = unsafe { slice::from_raw_parts_mut(buf.cast::(), bufsize) }; + Sys::readlink(path, buf) + .map(|read| read as ssize_t) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn readlinkat( + dirfd: c_int, + pathname: *const c_char, + buf: *mut c_char, + len: size_t, +) -> ssize_t { + let pathname = unsafe { CStr::from_ptr(pathname) }; + let buf = unsafe { slice::from_raw_parts_mut(buf.cast(), len) }; + Sys::readlinkat(dirfd, pathname, buf) + .map(|read| { + read.try_into() + .map_err(|_| Errno(ENAMETOOLONG)) + .or_minus_one_errno() + }) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::rmdir(path).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setegid(gid: gid_t) -> c_int { + Sys::setresgid(-1, gid, -1).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn seteuid(uid: uid_t) -> c_int { + Sys::setresuid(-1, uid, -1).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setgid(gid: gid_t) -> c_int { + Sys::setresgid(gid, gid, -1) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int { + Sys::setpgid(pid, pgid).map(|()| 0).or_minus_one_errno() +} + +/// See . +/// +/// # Deprecation +/// The `setpgrp()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 7, and removed in Issue 8. +#[deprecated] +#[unsafe(no_mangle)] +pub extern "C" fn setpgrp() -> pid_t { + setpgid(0, 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int { + Sys::setresgid(rgid, egid, -1) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int { + Sys::setresgid(rgid, egid, sgid) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int { + Sys::setresuid(ruid, euid, suid) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int { + Sys::setresuid(ruid, euid, -1) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setsid() -> pid_t { + Sys::setsid().or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn setuid(uid: uid_t) -> c_int { + Sys::setresuid(uid, uid, -1) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn sleep(seconds: c_uint) -> c_uint { + let rqtp = timespec { + tv_sec: time_t::from(seconds), + tv_nsec: 0, + }; + let mut rmtp = timespec { + tv_sec: 0, + tv_nsec: 0, + }; + + // If sleep() returns because the requested time has elapsed, the value returned shall be 0. + // If sleep() returns due to delivery of a signal, the return value shall be the "unslept" amount + // (the requested time minus the time actually slept) in seconds. + match unsafe { Sys::nanosleep(&raw const rqtp, &raw mut rmtp) } { + Err(Errno(EINTR)) => rmtp.tv_sec as c_uint, + r => 0, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) { + if nbytes <= 0 { + return; + } + let number_of_swaps = nbytes / 2; + let mut offset = 0; + for _ in 0..number_of_swaps { + unsafe { + src.offset(offset).copy_to(dest.offset(offset + 1), 1); + src.offset(offset + 1).copy_to(dest.offset(offset), 1); + } + offset += 2; + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int { + let path1 = unsafe { CStr::from_ptr(path1) }; + let path2 = unsafe { CStr::from_ptr(path2) }; + Sys::symlink(path1, path2).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn symlinkat(path1: *const c_char, fd: c_int, path2: *const c_char) -> c_int { + let path1 = unsafe { CStr::from_ptr(path1) }; + let path2 = unsafe { CStr::from_ptr(path2) }; + Sys::symlinkat(path1, fd, path2) + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn sync() { + if let Ok(()) = Sys::sync() {}; // TODO handle error +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn tcgetpgrp(fd: c_int) -> pid_t { + let mut pgrp = 0; + if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCGPGRP, (&raw mut pgrp).cast()) } < 0 { + return -1; + } + pgrp +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> c_int { + if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCSPGRP, &raw const pgrp as _) } < 0 { + return -1; + } + pgrp +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn truncate(path: *const c_char, length: off_t) -> c_int { + let file = unsafe { CStr::from_ptr(path) }; + // TODO: Rustify + let fd = Sys::open(file, fcntl::O_WRONLY, 0).or_minus_one_errno(); + if fd < 0 { + return -1; + } + + let res = ftruncate(fd, length); + + if let Ok(()) = Sys::close(fd) {}; // TODO handle error + + res +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char { + const TTYNAME_LEN: usize = 4096; + static mut TTYNAME: [c_char; TTYNAME_LEN] = [0; TTYNAME_LEN]; + if ttyname_r(fildes, (&raw mut TTYNAME).cast(), TTYNAME_LEN) == 0 { + (&raw mut TTYNAME).cast() + } else { + ptr::null_mut() + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t) -> c_int { + let name = unsafe { slice::from_raw_parts_mut(name.cast::(), namesize) }; + if name.is_empty() { + return errno::ERANGE; + } + + let len = Sys::fpath(fildes, &mut name[..namesize - 1]) + .map(|read| read as ssize_t) + .or_minus_one_errno(); + if len < 0 { + return -platform::ERRNO.get(); + } + name[len as usize] = 0; + + 0 +} + +/// See . +/// +/// # Deprecation +/// The `ualarm()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[allow(deprecated)] +#[unsafe(no_mangle)] +pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t { + // TODO setitimer is unimplemented on Redox and obsolete + let mut timer = sys_time::itimerval { + it_value: timeval { + tv_sec: 0, + tv_usec: usecs as suseconds_t, + }, + it_interval: timeval { + tv_sec: 0, + tv_usec: interval as suseconds_t, + }, + }; + let errno_backup = platform::ERRNO.get(); + let ret = + if unsafe { sys_time::setitimer(sys_time::ITIMER_REAL, &raw const timer, &raw mut timer) } + < 0 + { + 0 + } else { + timer.it_value.tv_sec as useconds_t * 1_000_000 + timer.it_value.tv_usec as useconds_t + }; + platform::ERRNO.set(errno_backup); + + ret +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::unlink(path).map(|()| 0).or_minus_one_errno() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn unlinkat(fd: c_int, path: *const c_char, flags: c_int) -> c_int { + let path = unsafe { CStr::from_ptr(path) }; + Sys::unlinkat(fd, path, flags) + .map(|()| 0) + .or_minus_one_errno() +} +/// See . +/// +/// # Deprecation +/// The `usleep()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +#[allow(deprecated)] +#[unsafe(no_mangle)] +pub extern "C" fn usleep(useconds: useconds_t) -> c_int { + #[cfg(not(target_arch = "x86"))] + let tv_nsec = c_long::from((useconds % 1_000_000) * 1000); + #[cfg(target_arch = "x86")] + let tv_nsec = ((useconds % 1_000_000) * 1000) as c_long; + let rqtp = timespec { + tv_sec: time_t::from(useconds / 1_000_000), + tv_nsec, + }; + let rmtp = ptr::null_mut(); + unsafe { Sys::nanosleep(&raw const rqtp, rmtp) } + .map(|()| 0) + .or_minus_one_errno() +} + +/// See . +/// +/// # Deprecation +/// The `vfork()` function was marked obsolescent in the Open Group Base +/// Specifications Issue 6, and removed in Issue 7. +#[deprecated] +// #[unsafe(no_mangle)] +pub extern "C" fn vfork() -> pid_t { + unimplemented!(); +} + +unsafe fn with_argv( + mut va: VaListImpl, + arg0: *const c_char, + f: impl FnOnce(&[*const c_char], VaListImpl) -> c_int, +) -> c_int { + let argc = 1 + unsafe { + va.with_copy(|mut copy| { + core::iter::from_fn(|| Some(copy.arg::<*const c_char>())) + .position(|p| p.is_null()) + .unwrap() + }) + }; + + let mut stack: [MaybeUninit<*const c_char>; 32] = [MaybeUninit::uninit(); 32]; + + let out = if argc < 32 { + stack.as_mut_slice() + } else if argc < 4096 { + // TODO: Use ARG_MAX, not this hardcoded constant + let ptr = + unsafe { crate::header::stdlib::malloc((argc + 1) * mem::size_of::<*const c_char>()) }; + if ptr.is_null() { + platform::ERRNO.set(ENOMEM); + return -1; + } + unsafe { slice::from_raw_parts_mut(ptr.cast::>(), argc + 1) } + } else { + platform::ERRNO.set(E2BIG); + return -1; + }; + out[0].write(arg0); + + for inner in out.iter_mut().take(argc).skip(1) { + (*inner).write(unsafe { va.arg::<*const c_char>() }); + } + out[argc].write(core::ptr::null()); + // NULL + unsafe { va.arg::<*const c_char>() }; + + f(unsafe { (&*out).assume_init_ref() }, va); + + // f only returns if it fails + if argc >= 32 { + unsafe { crate::header::stdlib::free(out.as_mut_ptr().cast()) }; + } + -1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn write(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t { + let buf = unsafe { slice::from_raw_parts(buf.cast::(), nbyte) }; + Sys::write(fildes, buf) + .map(|bytes| bytes as ssize_t) + .or_minus_one_errno() +} diff --git a/src/header/unistd/pathconf.rs b/src/header/unistd/pathconf.rs new file mode 100644 index 0000000000..74a90cead7 --- /dev/null +++ b/src/header/unistd/pathconf.rs @@ -0,0 +1,80 @@ +use crate::{ + header::{ + errno, + limits::{ + FILESIZEBITS, LINK_MAX, MAX_CANON, MAX_INPUT, NAME_MAX, PATH_MAX, PIPE_BUF, + POSIX_ALLOC_SIZE_MIN, SYMLINK_MAX, + }, + termios::_POSIX_VDISABLE, + }, + platform::{ + self, + types::{c_char, c_int, c_long}, + }, +}; + +pub const _PC_LINK_MAX: c_int = 0; +pub const _PC_MAX_CANON: c_int = 1; +pub const _PC_MAX_INPUT: c_int = 2; +pub const _PC_NAME_MAX: c_int = 3; +pub const _PC_PATH_MAX: c_int = 4; +pub const _PC_PIPE_BUF: c_int = 5; +pub const _PC_CHOWN_RESTRICTED: c_int = 6; +pub const _PC_NO_TRUNC: c_int = 7; +/// Check if file (terminal) supports disabling control chars (CC) +pub const _PC_VDISABLE: c_int = 8; +pub const _PC_SYNC_IO: c_int = 9; +pub const _PC_ASYNC_IO: c_int = 10; +pub const _PC_PRIO_IO: c_int = 11; +pub const _PC_SOCK_MAXBUF: c_int = 12; +pub const _PC_FILESIZEBITS: c_int = 13; +pub const _PC_REC_INCR_XFER_SIZE: c_int = 14; +pub const _PC_REC_MAX_XFER_SIZE: c_int = 15; +pub const _PC_REC_MIN_XFER_SIZE: c_int = 16; +pub const _PC_REC_XFER_ALIGN: c_int = 17; +pub const _PC_ALLOC_SIZE_MIN: c_int = 18; +pub const _PC_SYMLINK_MAX: c_int = 19; +pub const _PC_2_SYMLINKS: c_int = 20; + +fn pc(name: c_int) -> c_long { + // Settings from musl, some adjusted + match name { + _PC_LINK_MAX => LINK_MAX.try_into().unwrap_or(-1), + _PC_MAX_CANON => MAX_CANON.try_into().unwrap_or(-1), + _PC_MAX_INPUT => MAX_INPUT.try_into().unwrap_or(-1), + _PC_NAME_MAX => NAME_MAX.try_into().unwrap_or(-1), + _PC_PATH_MAX => PATH_MAX.try_into().unwrap_or(-1), + _PC_PIPE_BUF => PIPE_BUF.try_into().unwrap_or(-1), + _PC_CHOWN_RESTRICTED => 1, + _PC_NO_TRUNC => 1, + _PC_VDISABLE => _POSIX_VDISABLE.into(), + _PC_SYNC_IO => 1, + _PC_ASYNC_IO => -1, + _PC_PRIO_IO => -1, + _PC_SOCK_MAXBUF => -1, + _PC_FILESIZEBITS => FILESIZEBITS.into(), + _PC_REC_INCR_XFER_SIZE => -1, + _PC_REC_MAX_XFER_SIZE => -1, + _PC_REC_MIN_XFER_SIZE => 4096, + _PC_REC_XFER_ALIGN => 4096, + _PC_ALLOC_SIZE_MIN => POSIX_ALLOC_SIZE_MIN.try_into().unwrap_or(-1), + _PC_SYMLINK_MAX => SYMLINK_MAX.try_into().unwrap_or(-1), + _PC_2_SYMLINKS => 1, + _ => { + platform::ERRNO.set(errno::EINVAL); + -1 + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn fpathconf(_fildes: c_int, name: c_int) -> c_long { + pc(name) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn pathconf(_path: *const c_char, name: c_int) -> c_long { + pc(name) +} diff --git a/src/header/unistd/syscall.rs b/src/header/unistd/syscall.rs new file mode 100644 index 0000000000..d9d00218bf --- /dev/null +++ b/src/header/unistd/syscall.rs @@ -0,0 +1,14 @@ +use crate::platform::types::c_long; + +/// Non-POSIX, see . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn syscall(sysno: c_long, mut args: ...) -> c_long { + let a1 = unsafe { args.arg::() }; + let a2 = unsafe { args.arg::() }; + let a3 = unsafe { args.arg::() }; + let a4 = unsafe { args.arg::() }; + let a5 = unsafe { args.arg::() }; + let a6 = unsafe { args.arg::() }; + + (unsafe { sc::syscall6(sysno as usize, a1, a2, a3, a4, a5, a6) }) as c_long +} diff --git a/src/header/unistd/sysconf.rs b/src/header/unistd/sysconf.rs new file mode 100644 index 0000000000..dd61586ff5 --- /dev/null +++ b/src/header/unistd/sysconf.rs @@ -0,0 +1,18 @@ +/// sysconf.h: . + +#[cfg(target_os = "redox")] +#[path = "sysconf/redox.rs"] +mod sys; + +#[cfg(target_os = "linux")] +#[path = "sysconf/linux.rs"] +mod sys; + +pub use sys::*; + +use core::ffi::{c_int, c_long}; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn sysconf(name: c_int) -> c_long { + sysconf_impl(name) +} diff --git a/src/header/unistd/sysconf/linux.rs b/src/header/unistd/sysconf/linux.rs new file mode 100644 index 0000000000..8ec01d2dc8 --- /dev/null +++ b/src/header/unistd/sysconf/linux.rs @@ -0,0 +1,390 @@ +/// Linux sysconf implementation. +/// Constants borrowed from musl. +use core::{ + ffi::{c_int, c_long}, + mem::size_of, +}; + +use crate::{ + header::{errno, limits::*, signal}, + platform, +}; + +pub const _XOPEN_IOV_MAX: c_long = 16; +pub const _XOPEN_NAME_MAX: c_long = 255; +pub const _XOPEN_PATH_MAX: c_long = 1024; + +pub const _SC_ARG_MAX: c_int = 0; +pub const _SC_CHILD_MAX: c_int = 1; +pub const _SC_CLK_TCK: c_int = 2; +pub const _SC_NGROUPS_MAX: c_int = 3; +pub const _SC_OPEN_MAX: c_int = 4; +pub const _SC_STREAM_MAX: c_int = 5; +pub const _SC_TZNAME_MAX: c_int = 6; +pub const _SC_JOB_CONTROL: c_int = 7; +pub const _SC_SAVED_IDS: c_int = 8; +pub const _SC_REALTIME_SIGNALS: c_int = 9; +pub const _SC_PRIORITY_SCHEDULING: c_int = 10; +pub const _SC_TIMERS: c_int = 11; +pub const _SC_ASYNCHRONOUS_IO: c_int = 12; +pub const _SC_PRIORITIZED_IO: c_int = 13; +pub const _SC_SYNCHRONIZED_IO: c_int = 14; +pub const _SC_FSYNC: c_int = 15; +pub const _SC_MAPPED_FILES: c_int = 16; +pub const _SC_MEMLOCK: c_int = 17; +pub const _SC_MEMLOCK_RANGE: c_int = 18; +pub const _SC_MEMORY_PROTECTION: c_int = 19; +pub const _SC_MESSAGE_PASSING: c_int = 20; +pub const _SC_SEMAPHORES: c_int = 21; +pub const _SC_SHARED_MEMORY_OBJECTS: c_int = 22; +pub const _SC_AIO_LISTIO_MAX: c_int = 23; +pub const _SC_AIO_MAX: c_int = 24; +pub const _SC_AIO_PRIO_DELTA_MAX: c_int = 25; +pub const _SC_DELAYTIMER_MAX: c_int = 26; +pub const _SC_MQ_OPEN_MAX: c_int = 27; +pub const _SC_MQ_PRIO_MAX: c_int = 28; +pub const _SC_VERSION: c_int = 29; +pub const _SC_PAGE_SIZE: c_int = 30; +pub const _SC_PAGESIZE: c_int = 30; +pub const _SC_RTSIG_MAX: c_int = 31; +pub const _SC_SEM_NSEMS_MAX: c_int = 32; +pub const _SC_SEM_VALUE_MAX: c_int = 33; +pub const _SC_SIGQUEUE_MAX: c_int = 34; +pub const _SC_TIMER_MAX: c_int = 35; +pub const _SC_BC_BASE_MAX: c_int = 36; +pub const _SC_BC_DIM_MAX: c_int = 37; +pub const _SC_BC_SCALE_MAX: c_int = 38; +pub const _SC_BC_STRING_MAX: c_int = 39; +pub const _SC_COLL_WEIGHTS_MAX: c_int = 40; +pub const _SC_EXPR_NEST_MAX: c_int = 42; +pub const _SC_LINE_MAX: c_int = 43; +pub const _SC_RE_DUP_MAX: c_int = 44; +pub const _SC_2_VERSION: c_int = 46; +pub const _SC_2_C_BIND: c_int = 47; +pub const _SC_2_C_DEV: c_int = 48; +pub const _SC_2_FORT_DEV: c_int = 49; +pub const _SC_2_FORT_RUN: c_int = 50; +pub const _SC_2_SW_DEV: c_int = 51; +pub const _SC_2_LOCALEDEF: c_int = 52; +pub const _SC_UIO_MAXIOV: c_int = 60; +pub const _SC_IOV_MAX: c_int = 60; +pub const _SC_THREADS: c_int = 67; +pub const _SC_THREAD_SAFE_FUNCTIONS: c_int = 68; +pub const _SC_GETGR_R_SIZE_MAX: c_int = 69; +pub const _SC_GETPW_R_SIZE_MAX: c_int = 70; +pub const _SC_LOGIN_NAME_MAX: c_int = 71; +pub const _SC_TTY_NAME_MAX: c_int = 72; +pub const _SC_THREAD_DESTRUCTOR_ITERATIONS: c_int = 73; +pub const _SC_THREAD_KEYS_MAX: c_int = 74; +pub const _SC_THREAD_STACK_MIN: c_int = 75; +pub const _SC_THREAD_THREADS_MAX: c_int = 76; +pub const _SC_THREAD_ATTR_STACKADDR: c_int = 77; +pub const _SC_THREAD_ATTR_STACKSIZE: c_int = 78; +pub const _SC_THREAD_PRIORITY_SCHEDULING: c_int = 79; +pub const _SC_THREAD_PRIO_INHERIT: c_int = 80; +pub const _SC_THREAD_PRIO_PROTECT: c_int = 81; +pub const _SC_THREAD_PROCESS_SHARED: c_int = 82; +pub const _SC_NPROCESSORS_CONF: c_int = 83; +pub const _SC_NPROCESSORS_ONLN: c_int = 84; +pub const _SC_PHYS_PAGES: c_int = 85; +pub const _SC_AVPHYS_PAGES: c_int = 86; +pub const _SC_ATEXIT_MAX: c_int = 87; +pub const _SC_PASS_MAX: c_int = 88; +pub const _SC_XOPEN_VERSION: c_int = 89; +pub const _SC_XOPEN_XCU_VERSION: c_int = 90; +pub const _SC_XOPEN_UNIX: c_int = 91; +pub const _SC_XOPEN_CRYPT: c_int = 92; +pub const _SC_XOPEN_ENH_I18N: c_int = 93; +pub const _SC_XOPEN_SHM: c_int = 94; +pub const _SC_2_CHAR_TERM: c_int = 95; +pub const _SC_2_UPE: c_int = 97; +pub const _SC_XOPEN_XPG2: c_int = 98; +pub const _SC_XOPEN_XPG3: c_int = 99; +pub const _SC_XOPEN_XPG4: c_int = 100; +pub const _SC_NZERO: c_int = 109; +pub const _SC_XBS5_ILP32_OFF32: c_int = 125; +pub const _SC_XBS5_ILP32_OFFBIG: c_int = 126; +pub const _SC_XBS5_LP64_OFF64: c_int = 127; +pub const _SC_XBS5_LPBIG_OFFBIG: c_int = 128; +pub const _SC_XOPEN_LEGACY: c_int = 129; +pub const _SC_XOPEN_REALTIME: c_int = 130; +pub const _SC_XOPEN_REALTIME_THREADS: c_int = 131; +pub const _SC_ADVISORY_INFO: c_int = 132; +pub const _SC_BARRIERS: c_int = 133; +pub const _SC_CLOCK_SELECTION: c_int = 137; +pub const _SC_CPUTIME: c_int = 138; +pub const _SC_THREAD_CPUTIME: c_int = 139; +pub const _SC_MONOTONIC_CLOCK: c_int = 149; +pub const _SC_READER_WRITER_LOCKS: c_int = 153; +pub const _SC_SPIN_LOCKS: c_int = 154; +pub const _SC_REGEXP: c_int = 155; +pub const _SC_SHELL: c_int = 157; +pub const _SC_SPAWN: c_int = 159; +pub const _SC_SPORADIC_SERVER: c_int = 160; +pub const _SC_THREAD_SPORADIC_SERVER: c_int = 161; +pub const _SC_TIMEOUTS: c_int = 164; +pub const _SC_TYPED_MEMORY_OBJECTS: c_int = 165; +pub const _SC_2_PBS: c_int = 168; +pub const _SC_2_PBS_ACCOUNTING: c_int = 169; +pub const _SC_2_PBS_LOCATE: c_int = 170; +pub const _SC_2_PBS_MESSAGE: c_int = 171; +pub const _SC_2_PBS_TRACK: c_int = 172; +pub const _SC_SYMLOOP_MAX: c_int = 173; +pub const _SC_STREAMS: c_int = 174; +pub const _SC_2_PBS_CHECKPOINT: c_int = 175; +pub const _SC_V6_ILP32_OFF32: c_int = 176; +pub const _SC_V6_ILP32_OFFBIG: c_int = 177; +pub const _SC_V6_LP64_OFF64: c_int = 178; +pub const _SC_V6_LPBIG_OFFBIG: c_int = 179; +pub const _SC_HOST_NAME_MAX: c_int = 180; +pub const _SC_TRACE: c_int = 181; +pub const _SC_TRACE_EVENT_FILTER: c_int = 182; +pub const _SC_TRACE_INHERIT: c_int = 183; +pub const _SC_TRACE_LOG: c_int = 184; + +pub const _SC_IPV6: c_int = 235; +pub const _SC_RAW_SOCKETS: c_int = 236; +pub const _SC_V7_ILP32_OFF32: c_int = 237; +pub const _SC_V7_ILP32_OFFBIG: c_int = 238; +pub const _SC_V7_LP64_OFF64: c_int = 239; +pub const _SC_V7_LPBIG_OFFBIG: c_int = 240; +pub const _SC_SS_REPL_MAX: c_int = 241; +pub const _SC_TRACE_EVENT_NAME_MAX: c_int = 242; +pub const _SC_TRACE_NAME_MAX: c_int = 243; +pub const _SC_TRACE_SYS_MAX: c_int = 244; +pub const _SC_TRACE_USER_EVENT_MAX: c_int = 245; +pub const _SC_XOPEN_STREAMS: c_int = 246; +pub const _SC_THREAD_ROBUST_PRIO_INHERIT: c_int = 247; +pub const _SC_THREAD_ROBUST_PRIO_PROTECT: c_int = 248; +pub const _SC_MINSIGSTKSZ: c_int = 249; +pub const _SC_SIGSTKSZ: c_int = 250; + +// Defined in unistd.h but we defined it in C +const _POSIX_VERSION: c_long = 200809; +const _XOPEN_VERSION: c_long = 700; + +pub(super) fn sysconf_impl(name: c_int) -> c_long { + // Values from musl which we can assume is correct. + match name { + _SC_CLK_TCK => 100, + _SC_CHILD_MAX => { + let mut lim = core::mem::MaybeUninit::::uninit(); + let r = unsafe { + crate::header::sys_resource::getrlimit( + crate::header::sys_resource::RLIMIT_NPROC as c_int, + lim.as_mut_ptr().cast::(), + ) + }; + if r == 0 { + let cur = unsafe { lim.assume_init() }.rlim_cur; + if cur == crate::header::sys_resource::RLIM_INFINITY { -1 } else if cur > c_long::MAX as u64 { c_long::MAX } else { cur as c_long } + } else { -1 } + } + _SC_NGROUPS_MAX => NGROUPS_MAX as c_long, + _SC_OPEN_MAX => { + let mut lim = core::mem::MaybeUninit::::uninit(); + let r = unsafe { + crate::header::sys_resource::getrlimit( + crate::header::sys_resource::RLIMIT_NOFILE as c_int, + lim.as_mut_ptr().cast::(), + ) + }; + if r == 0 { + let cur = unsafe { lim.assume_init() }.rlim_cur; + if cur == crate::header::sys_resource::RLIM_INFINITY { -1 } else if cur > c_long::MAX as u64 { c_long::MAX } else { cur as c_long } + } else { -1 } + } + _SC_STREAM_MAX => -1, + // TODO: limits.h + _SC_TZNAME_MAX => -1, + _SC_JOB_CONTROL => 1, + _SC_SAVED_IDS => 1, + _SC_REALTIME_SIGNALS => _POSIX_VERSION, + _SC_PRIORITY_SCHEDULING => -1, + _SC_TIMERS => _POSIX_VERSION, + _SC_ASYNCHRONOUS_IO => _POSIX_VERSION, + _SC_PRIORITIZED_IO => -1, + _SC_SYNCHRONIZED_IO => -1, + _SC_FSYNC => _POSIX_VERSION, + _SC_MAPPED_FILES => _POSIX_VERSION, + _SC_MEMLOCK => _POSIX_VERSION, + _SC_MEMLOCK_RANGE => _POSIX_VERSION, + _SC_MEMORY_PROTECTION => _POSIX_VERSION, + _SC_MESSAGE_PASSING => _POSIX_VERSION, + _SC_SEMAPHORES => _POSIX_VERSION, + _SC_SHARED_MEMORY_OBJECTS => _POSIX_VERSION, + _SC_AIO_LISTIO_MAX => -1, + _SC_AIO_MAX => -1, + _SC_AIO_PRIO_DELTA_MAX => 0, + // TODO: limits.h? + _SC_DELAYTIMER_MAX => -1, + _SC_MQ_OPEN_MAX => -1, + // TODO: limits.h? + _SC_MQ_PRIO_MAX => -1, + _SC_VERSION => _POSIX_VERSION, + _SC_PAGE_SIZE => PAGE_SIZE.try_into().unwrap_or(-1), + _SC_RTSIG_MAX => (signal::SIGRTMAX - signal::SIGRTMIN) + .try_into() + .unwrap_or(-1), + // TODO: limits.h + _SC_SEM_NSEMS_MAX => -1, + // TODO: limits.h + _SC_SEM_VALUE_MAX => -1, + _SC_SIGQUEUE_MAX => -1, + _SC_TIMER_MAX => -1, + _SC_BC_BASE_MAX => BC_BASE_MAX, + _SC_BC_DIM_MAX => BC_DIM_MAX, + _SC_BC_SCALE_MAX => BC_SCALE_MAX, + _SC_BC_STRING_MAX => BC_STRING_MAX, + _SC_COLL_WEIGHTS_MAX => COLL_WEIGHTS_MAX, + _SC_EXPR_NEST_MAX => EXPR_NEST_MAX, + _SC_LINE_MAX => LINE_MAX, + _SC_RE_DUP_MAX => RE_DUP_MAX, + _SC_2_VERSION => _POSIX_VERSION, + _SC_2_C_BIND => _POSIX_VERSION, + _SC_2_C_DEV => -1, + _SC_2_FORT_DEV => -1, + _SC_2_FORT_RUN => -1, + _SC_2_SW_DEV => -1, + _SC_2_LOCALEDEF => -1, + _SC_IOV_MAX => _XOPEN_IOV_MAX, + _SC_THREADS => _POSIX_VERSION, + _SC_THREAD_SAFE_FUNCTIONS => _POSIX_VERSION, + _SC_GETGR_R_SIZE_MAX => -1, + _SC_GETPW_R_SIZE_MAX => -1, + _SC_LOGIN_NAME_MAX => 256, + // TODO: limits.h + _SC_TTY_NAME_MAX => -1, + // TODO: limits.h + _SC_THREAD_DESTRUCTOR_ITERATIONS => -1, + // TODO: limits.h + _SC_THREAD_KEYS_MAX => -1, + // TODO: limits.h + _SC_THREAD_STACK_MIN => -1, + _SC_THREAD_THREADS_MAX => -1, + _SC_THREAD_ATTR_STACKADDR => _POSIX_VERSION, + _SC_THREAD_ATTR_STACKSIZE => _POSIX_VERSION, + _SC_THREAD_PRIORITY_SCHEDULING => _POSIX_VERSION, + _SC_THREAD_PRIO_INHERIT => -1, + _SC_THREAD_PRIO_PROTECT => -1, + _SC_THREAD_PROCESS_SHARED => _POSIX_VERSION, + // TODO: Use getaffinity syscall on Linux + _SC_NPROCESSORS_CONF => -1, + _SC_NPROCESSORS_ONLN => -1, + // TODO: sysinfo + _SC_PHYS_PAGES => -1, + _SC_AVPHYS_PAGES => -1, + _SC_ATEXIT_MAX => -1, + _SC_PASS_MAX => -1, + _SC_XOPEN_VERSION => _XOPEN_VERSION, + _SC_XOPEN_XCU_VERSION => _XOPEN_VERSION, + _SC_XOPEN_UNIX => 1, + _SC_XOPEN_CRYPT => -1, + _SC_XOPEN_ENH_I18N => 1, + _SC_XOPEN_SHM => 1, + _SC_2_CHAR_TERM => -1, + _SC_2_UPE => -1, + _SC_XOPEN_XPG2 => -1, + _SC_XOPEN_XPG3 => -1, + _SC_XOPEN_XPG4 => -1, + // TODO: ? + _SC_NZERO => -1, + _SC_XBS5_ILP32_OFF32 => -1, + _SC_XBS5_ILP32_OFFBIG => { + if size_of::() == 4 { + 1 + } else { + -1 + } + } + _SC_XBS5_LP64_OFF64 => { + if size_of::() == 8 { + 1 + } else { + -1 + } + } + _SC_XBS5_LPBIG_OFFBIG => -1, + _SC_XOPEN_LEGACY => -1, + _SC_XOPEN_REALTIME => -1, + _SC_XOPEN_REALTIME_THREADS => -1, + _SC_ADVISORY_INFO => _POSIX_VERSION, + _SC_BARRIERS => _POSIX_VERSION, + _SC_CLOCK_SELECTION => _POSIX_VERSION, + _SC_CPUTIME => _POSIX_VERSION, + _SC_THREAD_CPUTIME => _POSIX_VERSION, + _SC_MONOTONIC_CLOCK => _POSIX_VERSION, + _SC_READER_WRITER_LOCKS => _POSIX_VERSION, + _SC_SPIN_LOCKS => _POSIX_VERSION, + _SC_REGEXP => 1, + _SC_SHELL => 1, + _SC_SPAWN => _POSIX_VERSION, + _SC_SPORADIC_SERVER => -1, + _SC_THREAD_SPORADIC_SERVER => -1, + _SC_TIMEOUTS => _POSIX_VERSION, + _SC_TYPED_MEMORY_OBJECTS => -1, + _SC_2_PBS => -1, + _SC_2_PBS_ACCOUNTING => -1, + _SC_2_PBS_LOCATE => -1, + _SC_2_PBS_MESSAGE => -1, + _SC_2_PBS_TRACK => -1, + // TODO: SYMLOOP_MAX in paths.h + _SC_SYMLOOP_MAX => -1, + _SC_STREAMS => 0, + _SC_2_PBS_CHECKPOINT => -1, + _SC_V6_ILP32_OFF32 => -1, + _SC_V6_ILP32_OFFBIG => { + if size_of::() == 4 { + 1 + } else { + -1 + } + } + _SC_V6_LP64_OFF64 => { + if size_of::() == 8 { + 1 + } else { + -1 + } + } + _SC_V6_LPBIG_OFFBIG => -1, + _SC_HOST_NAME_MAX => HOST_NAME_MAX.try_into().unwrap_or(-1), + _SC_TRACE => -1, + _SC_TRACE_EVENT_FILTER => -1, + _SC_TRACE_INHERIT => -1, + _SC_TRACE_LOG => -1, + _SC_IPV6 => _POSIX_VERSION, + _SC_RAW_SOCKETS => _POSIX_VERSION, + _SC_V7_ILP32_OFF32 => -1, + _SC_V7_ILP32_OFFBIG => { + if size_of::() == 4 { + 1 + } else { + -1 + } + } + _SC_V7_LP64_OFF64 => { + if size_of::() == 8 { + 1 + } else { + -1 + } + } + _SC_V7_LPBIG_OFFBIG => -1, + _SC_SS_REPL_MAX => -1, + _SC_TRACE_EVENT_NAME_MAX => -1, + _SC_TRACE_NAME_MAX => -1, + _SC_TRACE_SYS_MAX => -1, + _SC_TRACE_USER_EVENT_MAX => -1, + _SC_XOPEN_STREAMS => 0, + _SC_THREAD_ROBUST_PRIO_INHERIT => -1, + _SC_THREAD_ROBUST_PRIO_PROTECT => -1, + // TODO: Working getauxval + _SC_MINSIGSTKSZ => -1, + _SC_SIGSTKSZ => -1, + _ => { + platform::ERRNO.set(errno::EINVAL); + -1 + } + } +} diff --git a/src/header/unistd/sysconf/redox.rs b/src/header/unistd/sysconf/redox.rs new file mode 100644 index 0000000000..3d7f96dc28 --- /dev/null +++ b/src/header/unistd/sysconf/redox.rs @@ -0,0 +1,147 @@ +use core::convert::TryInto; + +use alloc::string::String; + +use crate::{ + error::Errno, + fs::File, + header::{errno, fcntl, limits, sys_resource, sys_statvfs}, + io::Read, + out::Out, + platform::{ + self, Pal, Sys, + types::{c_int, c_long}, + }, +}; + +// POSIX.1 { +pub const _SC_ARG_MAX: c_int = 0; +pub const _SC_CHILD_MAX: c_int = 1; +pub const _SC_CLK_TCK: c_int = 2; +pub const _SC_NGROUPS_MAX: c_int = 3; +pub const _SC_OPEN_MAX: c_int = 4; +pub const _SC_STREAM_MAX: c_int = 5; +pub const _SC_TZNAME_MAX: c_int = 6; +// ... +pub const _SC_TIMERS: c_int = 11; +// ... +pub const _SC_SHARED_MEMORY_OBJECTS: c_int = 22; +// ... +pub const _SC_VERSION: c_int = 29; +pub const _SC_PAGESIZE: c_int = 30; +pub const _SC_PAGE_SIZE: c_int = 30; +// ... +pub const _SC_RE_DUP_MAX: c_int = 44; + +pub const _SC_NPROCESSORS_CONF: c_int = 57; +pub const _SC_NPROCESSORS_ONLN: c_int = 58; +pub const _SC_PHYS_PAGES: c_int = 59; +pub const _SC_AVPHYS_PAGES: c_int = 60; + +// ... +pub const _SC_THREADS: c_int = 67; +pub const _SC_GETGR_R_SIZE_MAX: c_int = 69; +pub const _SC_GETPW_R_SIZE_MAX: c_int = 70; +pub const _SC_LOGIN_NAME_MAX: c_int = 71; +pub const _SC_TTY_NAME_MAX: c_int = 72; +// ... +pub const _SC_THREAD_ATTR_STACKADDR: c_int = 77; +pub const _SC_THREAD_ATTR_STACKSIZE: c_int = 78; +// ... +pub const _SC_MONOTONIC_CLOCK: c_int = 149; +pub const _SC_SEMAPHORES: c_int = 21; +// ... +pub const _SC_BARRIERS: c_int = 133; +// ... +pub const _SC_SHELL: c_int = 157; +// ... +pub const _SC_TIMEOUTS: c_int = 164; +// ... +pub const _SC_SYMLOOP_MAX: c_int = 173; +// ... +pub const _SC_HOST_NAME_MAX: c_int = 180; +// ... +pub const _SC_SIGQUEUE_MAX: c_int = 190; +pub const _SC_REALTIME_SIGNALS: c_int = 191; +// } POSIX.1 + +fn resource_limit_sysconf(resource: c_int) -> c_long { + let mut lim = core::mem::MaybeUninit::::uninit(); + let r = unsafe { sys_resource::getrlimit(resource, lim.as_mut_ptr()) }; + if r != 0 { + return -1; + } + + let cur = unsafe { lim.assume_init() }.rlim_cur; + if cur == sys_resource::RLIM_INFINITY { + -1 + } else if cur > c_long::MAX as u64 { + c_long::MAX + } else { + cur as c_long + } +} + +pub(super) fn sysconf_impl(name: c_int) -> c_long { + //TODO: Real values + match name { + _SC_ARG_MAX => 4096, + _SC_CHILD_MAX => resource_limit_sysconf(sys_resource::RLIMIT_NPROC as c_int), + _SC_CLK_TCK => 100, + _SC_NGROUPS_MAX => limits::NGROUPS_MAX as c_long, + _SC_OPEN_MAX => resource_limit_sysconf(sys_resource::RLIMIT_NOFILE as c_int), + _SC_STREAM_MAX => 16, + _SC_TZNAME_MAX => -1, + _SC_VERSION => 200809, + _SC_PAGESIZE => Sys::getpagesize().try_into().unwrap_or(-1), + _SC_RE_DUP_MAX => 32767, + _SC_GETGR_R_SIZE_MAX => -1, + _SC_GETPW_R_SIZE_MAX => -1, + _SC_LOGIN_NAME_MAX => 256, + _SC_TTY_NAME_MAX => 32, + _SC_MONOTONIC_CLOCK => 200112, + _SC_SEMAPHORES => 200112, + _SC_BARRIERS => 202405, + _SC_SHELL => 1, + _SC_SHARED_MEMORY_OBJECTS => 200112, + _SC_THREADS => 202405, + _SC_THREAD_ATTR_STACKADDR => 202405, + _SC_THREAD_ATTR_STACKSIZE => 202405, + _SC_TIMEOUTS => 202405, + _SC_TIMERS => 202405, + _SC_SYMLOOP_MAX => -1, + _SC_HOST_NAME_MAX => limits::HOST_NAME_MAX.try_into().unwrap_or(-1), + _SC_NPROCESSORS_CONF => get_cpu_count().unwrap_or(None).unwrap_or(1), + _SC_NPROCESSORS_ONLN => get_cpu_count().unwrap_or(None).unwrap_or(1), + _SC_PHYS_PAGES => get_mem_stat().map(|s| s.f_blocks as c_long).unwrap_or(-1), + _SC_AVPHYS_PAGES => get_mem_stat().map(|s| s.f_bfree as c_long).unwrap_or(-1), + _SC_SIGQUEUE_MAX => 32, + _SC_REALTIME_SIGNALS => 202405, + _ => { + platform::ERRNO.set(errno::EINVAL); + -1 + } + } +} + +pub fn get_cpu_count() -> Result, Errno> { + let mut string = String::new(); + let mut file = File::open(c"/scheme/sys/cpu".into(), fcntl::O_RDONLY)?; + file.read_to_string(&mut string) + .map_err(|_| Errno(errno::EIO).sync())?; + + Ok(string + .lines() + .find(|line| line.starts_with("CPUs:")) + .and_then(|line| line.split(':').nth(1)) + .and_then(|num_str| num_str.trim().parse::().ok())) +} + +pub fn get_mem_stat() -> Result { + let fd = Sys::open(c"/scheme/memory".into(), fcntl::O_PATH, 0)?; + let mut buf = sys_statvfs::statvfs::default(); + let res = Sys::fstatvfs(fd, Out::from_mut(&mut buf)); + if let Ok(()) = Sys::close(fd) {}; // TODO handle error + let _ = res?; + return Ok(buf); +} diff --git a/src/header/utime/cbindgen.toml b/src/header/utime/cbindgen.toml new file mode 100644 index 0000000000..722d677d06 --- /dev/null +++ b/src/header/utime/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["sys/types.h", "features.h"] +include_guard = "_RELIBC_UTIME_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/utime/mod.rs b/src/header/utime/mod.rs new file mode 100644 index 0000000000..88e10e1062 --- /dev/null +++ b/src/header/utime/mod.rs @@ -0,0 +1,47 @@ +//! `utime.h` implementation. +//! +//! See . +//! +//! The `utime.h` header was marked obsolescent in the Open Group Base +//! Specifications Issue 7, and removed in Issue 8. + +use crate::{ + c_str::CStr, + error::ResultExt, + header::bits_timespec::timespec, + platform::{ + Pal, Sys, + types::{c_char, c_int, time_t}, + }, +}; + +/// See . +#[deprecated] +#[repr(C)] +#[derive(Clone)] +pub struct utimbuf { + pub actime: time_t, + pub modtime: time_t, +} + +/// See . +#[deprecated] +#[allow(deprecated)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn utime(filename: *const c_char, times: *const utimbuf) -> c_int { + let filename_cstr = unsafe { CStr::from_ptr(filename) }; + let times_ref = unsafe { &*times }; + let times_spec = [ + timespec { + tv_sec: times_ref.actime, + tv_nsec: 0, + }, + timespec { + tv_sec: times_ref.modtime, + tv_nsec: 0, + }, + ]; + unsafe { Sys::utimens(filename_cstr, times_spec.as_ptr()) } + .map(|()| 0) + .or_minus_one_errno() +} diff --git a/src/header/utmp/cbindgen.toml b/src/header/utmp/cbindgen.toml new file mode 100644 index 0000000000..386681d572 --- /dev/null +++ b/src/header/utmp/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = [] +include_guard = "_RELIBC_UTMP_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/utmp/mod.rs b/src/header/utmp/mod.rs new file mode 100644 index 0000000000..8512050b29 --- /dev/null +++ b/src/header/utmp/mod.rs @@ -0,0 +1,40 @@ +//! `utmp.h` implementation. +//! +//! Non-POSIX, see . + +use crate::{ + header::{sys_ioctl, unistd}, + platform::types::{c_int, c_void}, +}; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn login_tty(fd: c_int) -> c_int { + // Create a new session + unistd::setsid(); + + // Set controlling terminal + let mut arg: c_int = 0; + if unsafe { + sys_ioctl::ioctl( + fd, + sys_ioctl::TIOCSCTTY, + core::ptr::from_mut::(&mut arg).cast::(), + ) + } != 0 + { + return -1; + } + + // Overwrite stdio + unistd::dup2(fd, 0); + unistd::dup2(fd, 1); + unistd::dup2(fd, 2); + + // Close if needed + if fd > 2 { + unistd::close(fd); + } + + 0 +} diff --git a/src/header/wchar/cbindgen.toml b/src/header/wchar/cbindgen.toml new file mode 100644 index 0000000000..a31881baad --- /dev/null +++ b/src/header/wchar/cbindgen.toml @@ -0,0 +1,45 @@ +sys_includes = [ + "stdio.h", + "time.h", + "features.h", +] +after_includes = """ +// int32_t, uint32_t, WCHAR_MIN, WCHAR_MAX +#include + +#ifndef _WCHAR_T +#define _WCHAR_T +#ifndef __cplusplus + #ifndef __WCHAR_TYPE__ + #define __WCHAR_TYPE__ int32_t + #endif + typedef __WCHAR_TYPE__ wchar_t; +#endif // __cplusplus +#endif // _WCHAR_T + +#ifndef _WINT_T +#define _WINT_T + #ifndef __WINT_TYPE__ + #define __WINT_TYPE__ uint32_t + #endif + typedef __WINT_TYPE__ wint_t; +#endif // _WINT_T + +// NULL, size_t, must come after wchar_t and wint_t +#define __need_size_t +#define __need_NULL +#include + +#define WEOF (0xffffffffu) +""" +include_guard = "_RELIBC_WCHAR_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[export.rename] +"tm" = "struct tm" + +[enum] +prefix_with_name = true diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs new file mode 100644 index 0000000000..fa4ff454f3 --- /dev/null +++ b/src/header/wchar/mod.rs @@ -0,0 +1,1095 @@ +//! `wchar.h` implementation. +//! +//! See . + +use core::{char, ffi::VaList as va_list, mem, ptr, slice}; + +use crate::{ + c_str::{WStr, Wide}, + header::{ + ctype::isspace, + errno::{EILSEQ, ENOMEM, ERANGE}, + stdio::*, + stdlib::{MB_CUR_MAX, MB_LEN_MAX, malloc}, + string, + time::*, + wchar::reader::Reader, + wctype::*, + }, + iter::{NulTerminated, NulTerminatedInclusive}, + platform::{ + self, ERRNO, + types::{ + c_char, c_double, c_int, c_long, c_longlong, c_uchar, c_ulong, c_ulonglong, c_void, + size_t, wchar_t, wint_t, + }, + }, +}; + +mod utf8; +mod wprintf; +mod wscanf; + +pub use utf8::get_char_encoded_length; + +/// See . +#[repr(C)] +#[derive(Clone, Copy)] +pub struct mbstate_t; + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn btowc(c: c_int) -> wint_t { + //Check for EOF + if c == EOF { + return WEOF; + } + + let uc = c as u8; + let c = uc as c_char; + let mut ps: mbstate_t = mbstate_t; + let mut wc: wchar_t = 0; + let saved_errno = platform::ERRNO.get(); + let status = unsafe { mbrtowc(&raw mut wc, ptr::from_ref::(&c), 1, &raw mut ps) }; + if status == usize::MAX || status == usize::MAX - 1 { + platform::ERRNO.set(saved_errno); + return WEOF; + } + wc as wint_t +} + +// not in POSIX. +pub unsafe fn fgetwc_unlocked(stream: *mut FILE) -> wint_t { + // TODO: Process locale + let mut buf: [c_uchar; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize]; + let mut encoded_length = 0; + let mut bytes_read = 0; + let mut wc: wchar_t = 0; + + loop { + unsafe { + let ret = getc_unlocked(stream); + if ret == EOF { + return WEOF; + } + *buf.as_mut_ptr().add(bytes_read) = ret as c_uchar; + } + + bytes_read += 1; + + if bytes_read == 1 { + encoded_length = if let Some(el) = get_char_encoded_length(buf[0]) { + el + } else { + unsafe { + (*stream).flags |= F_ERR; + } + ERRNO.set(EILSEQ); + return WEOF; + }; + } + + if bytes_read >= encoded_length { + break; + } + } + + unsafe { + mbrtowc( + &raw mut wc, + buf.as_ptr().cast::(), + encoded_length, + ptr::null_mut(), + ) + }; + + wc as wint_t +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t { + let mut stream = unsafe { (*stream).lock() }; + unsafe { fgetwc_unlocked(&raw mut *stream) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fgetws(ws: *mut wchar_t, n: c_int, stream: *mut FILE) -> *mut wchar_t { + let mut i = 0; + let mut stream = unsafe { (*stream).lock() }; + while ((i + 1) as c_int) < n { + let wc = unsafe { fgetwc_unlocked(&raw mut *stream) }; + if wc == WEOF { + break; + } + unsafe { *ws.add(i) = wc as wchar_t }; + i += 1; + if wc as wchar_t == '\n' as wchar_t { + break; + } + } + // NUL-terminate result + unsafe { *ws.add(i) = 0 }; + if i == 0 || unsafe { ferror(&raw mut *stream) != 0 } { + core::ptr::null_mut() + } else { + ws + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fputwc(wc: wchar_t, stream: *mut FILE) -> wint_t { + //Convert wchar_t to multibytes first + static mut INTERNAL: mbstate_t = mbstate_t; + let mut bytes: [c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize]; + + let amount = unsafe { wcrtomb(bytes.as_mut_ptr(), wc, &raw mut INTERNAL) }; + + for b in bytes.iter().take(amount) { + unsafe { fputc(c_int::from(*b), &raw mut *stream) }; + } + + wc as wint_t +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fputws(ws: *const wchar_t, stream: *mut FILE) -> c_int { + let mut i = 0; + loop { + let wc = unsafe { *ws.add(i) }; + if wc == 0 { + return 0; + } + if unsafe { fputwc(wc, stream) } == WEOF { + return -1; + } + i += 1; + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fwide(stream: *mut FILE, mode: c_int) -> c_int { + unsafe { (*stream).try_set_orientation(mode) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fwscanf( + stream: *mut FILE, + format: *const wchar_t, + mut __valist: ... +) -> c_int { + unsafe { vfwscanf(stream, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getwc(stream: *mut FILE) -> wint_t { + unsafe { fgetwc(stream) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn getwchar() -> wint_t { + unsafe { fgetwc(stdin) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbsinit(ps: *const mbstate_t) -> c_int { + //Add a check for the state maybe + if ps.is_null() { 1 } else { 0 } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbrlen(s: *const c_char, n: size_t, ps: *mut mbstate_t) -> size_t { + static mut INTERNAL: mbstate_t = mbstate_t; + unsafe { mbrtowc(ptr::null_mut(), s, n, &raw mut INTERNAL) } +} + +/// See . +/// +/// Only works for UTF8 at the moment. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbrtowc( + pwc: *mut wchar_t, + s: *const c_char, + n: size_t, + ps: *mut mbstate_t, +) -> size_t { + static mut INTERNAL: mbstate_t = mbstate_t; + + if ps.is_null() { + let ps = &raw mut INTERNAL; + } + if s.is_null() { + let xs: [c_char; 1] = [0]; + unsafe { utf8::mbrtowc(pwc, ptr::from_ref::(&xs[0]), 1, ps) } + } else { + unsafe { utf8::mbrtowc(pwc, s, n, ps) } + } +} + +/// See . +/// +/// Convert a multibyte string to a wide string with a limited amount of bytes. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbsnrtowcs( + dst_ptr: *mut wchar_t, + src_ptr: *mut *const c_char, + src_len: size_t, + dst_len: size_t, + ps: *mut mbstate_t, +) -> size_t { + static mut INTERNAL: mbstate_t = mbstate_t; + + if ps.is_null() { + let ps = &raw mut INTERNAL; + } + + let mut src = unsafe { *src_ptr }; + + let mut dst_offset: usize = 0; + let mut src_offset: usize = 0; + + while (dst_ptr.is_null() || dst_offset < dst_len) && src_offset < src_len { + let ps_copy = unsafe { *ps }; + let mut wc: wchar_t = 0; + let amount = unsafe { mbrtowc(&raw mut wc, src.add(src_offset), src_len - src_offset, ps) }; + + // Stop in the event a decoding error occured. + if amount == -1isize as usize { + unsafe { *src_ptr = src.add(src_offset) }; + return 1isize as usize; + } + + // Stop decoding early in the event we encountered a partial character. + if amount == -2isize as usize { + unsafe { *ps = ps_copy }; + break; + } + + // Store the decoded wide character in the destination buffer. + if !dst_ptr.is_null() { + unsafe { *dst_ptr.add(dst_offset) = wc }; + } + + // Stop decoding after decoding a null character and return a NULL + // source pointer to the caller, not including the null character in the + // number of characters stored in the destination buffer. + if wc == 0 { + src = ptr::null(); + src_offset = 0; + break; + } + + dst_offset += 1; + src_offset += amount; + } + + unsafe { *src_ptr = src.add(src_offset) }; + dst_offset +} + +/// See . +/// +/// Convert a multibyte string to a wide string. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn mbsrtowcs( + dst: *mut wchar_t, + src: *mut *const c_char, + len: size_t, + ps: *mut mbstate_t, +) -> size_t { + unsafe { mbsnrtowcs(dst, src, size_t::MAX, len, ps) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putwc(wc: wchar_t, stream: *mut FILE) -> wint_t { + unsafe { fputwc(wc, &raw mut *stream) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn putwchar(wc: wchar_t) -> wint_t { + unsafe { fputwc(wc, &raw mut *stdout) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vswscanf( + s: *const wchar_t, + format: *const wchar_t, + __valist: va_list, +) -> c_int { + unsafe { + let format = WStr::from_ptr(format); + let s = WStr::from_ptr(s); + wscanf::scanf(s.into(), format.into(), __valist) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn swscanf( + s: *const wchar_t, + format: *const wchar_t, + mut __valist: ... +) -> c_int { + unsafe { vswscanf(s, format, __valist.as_va_list()) } +} + +/// See . +/// +/// Push wide character `wc` back onto `stream` so it'll be read next +#[unsafe(no_mangle)] +pub unsafe extern "C" fn ungetwc(wc: wint_t, stream: &mut FILE) -> wint_t { + if wc == WEOF { + return wc; + } + static mut INTERNAL: mbstate_t = mbstate_t; + let mut bytes: [c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize]; + + let amount = unsafe { wcrtomb(bytes.as_mut_ptr(), wc as wchar_t, &raw mut INTERNAL) }; + if amount == usize::MAX { + return WEOF; + } + + /* + We might have unget multiple bytes for a single wchar, eg, `ç` is [195, 167]. + We need to unget them in reversed, so they are pused as [..., 167, 195, ...] + When we do fgetwc, we pop from the Vec, getting the write order of bytes [195, 167]. + If we called ungetc in the non-reversed order, we would get [167, 195] + */ + for i in 0..amount { + unsafe { ungetc(c_int::from(bytes[amount - 1 - i]), &raw mut *stream) }; + } + + wc +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vfwprintf( + stream: *mut FILE, + format: *const wchar_t, + arg: va_list, +) -> c_int { + let mut stream = unsafe { (*stream).lock() }; + if (*stream).try_set_wide_orientation_unlocked().is_err() { + return -1; + } + + unsafe { wprintf::wprintf(&mut *stream, WStr::from_ptr(format), arg) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fwprintf( + stream: *mut FILE, + format: *const wchar_t, + mut __valist: ... +) -> c_int { + unsafe { vfwprintf(stream, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vwprintf(format: *const wchar_t, arg: va_list) -> c_int { + unsafe { vfwprintf(&raw mut *stdout, format, arg) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wprintf(format: *const wchar_t, mut __valist: ...) -> c_int { + unsafe { vfwprintf(&raw mut *stdout, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vswprintf( + s: *mut wchar_t, + n: size_t, + format: *const wchar_t, + arg: va_list, +) -> c_int { + //TODO: implement vswprintf. This is not as simple as wprintf, since the output is not UTF-8 + // but instead is a wchar array. + todo_skip!(0, "vswprintf not implemented"); + -1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn swprintf( + s: *mut wchar_t, + n: size_t, + format: *const wchar_t, + mut __valist: ... +) -> c_int { + unsafe { vswprintf(s, n, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcpcpy(d: *mut wchar_t, s: *const wchar_t) -> *mut wchar_t { + unsafe { (wcscpy(d, s)).add(wcslen(s)) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcpncpy(d: *mut wchar_t, s: *const wchar_t, n: size_t) -> *mut wchar_t { + unsafe { (wcsncpy(d, s, n)).add(wcsnlen(s, n)) } +} + +/// See . +/// +/// widechar to multibyte. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcrtomb(s: *mut c_char, wc: wchar_t, ps: *mut mbstate_t) -> size_t { + let mut buffer: [c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize]; + let (s_cpy, wc_cpy) = if s.is_null() { + (buffer.as_mut_ptr(), 0) + } else { + (s, wc) + }; + + unsafe { utf8::wcrtomb(s_cpy, wc_cpy, ps) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsdup(s: *const wchar_t) -> *mut wchar_t { + let l = unsafe { wcslen(s) }; + + let d = unsafe { malloc((l + 1) * mem::size_of::()) }.cast::(); + + if d.is_null() { + ERRNO.set(ENOMEM); + return ptr::null_mut(); + } + + unsafe { wmemcpy(d, s, l + 1) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsrtombs( + s: *mut c_char, + ws: *mut *const wchar_t, + n: size_t, + mut st: *mut mbstate_t, +) -> size_t { + let mut mbs = mbstate_t {}; + if st.is_null() { + st = &raw mut mbs; + } + unsafe { wcsnrtombs(s, ws, size_t::MAX, n, st) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcscat(ws1: *mut wchar_t, ws2: *const wchar_t) -> *mut wchar_t { + unsafe { wcsncat(ws1, ws2, usize::MAX) } +} + +/// See . +/// +/// # Safety +/// The caller is required to ensure that `ws` is a valid pointer to a buffer +/// containing at least one nul value. The pointed-to buffer must not be +/// modified for the duration of the call. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcschr(ws: *const wchar_t, wc: wchar_t) -> *mut wchar_t { + // We iterate over non-mut references and thus need to coerce the + // resulting reference via a *const pointer before we can get our *mut. + // SAFETY: the caller is required to ensure that ws points to a valid + // nul-terminated buffer. + let ptr: *const wchar_t = + match unsafe { NulTerminatedInclusive::new(ws) }.find(|&&wsc| wsc == wc) { + Some(wsc_ref) => wsc_ref, + None => ptr::null(), + }; + ptr.cast_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcscmp(ws1: *const wchar_t, ws2: *const wchar_t) -> c_int { + unsafe { wcsncmp(ws1, ws2, usize::MAX) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcscoll(ws1: *const wchar_t, ws2: *const wchar_t) -> c_int { + //TODO: locale comparison + unsafe { wcscmp(ws1, ws2) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcscpy(ws1: *mut wchar_t, ws2: *const wchar_t) -> *mut wchar_t { + let mut i = 0; + loop { + let wc = unsafe { *ws2.add(i) }; + unsafe { *ws1.add(i) = wc }; + i += 1; + if wc == 0 { + return ws1; + } + } +} + +unsafe fn inner_wcsspn(mut wcs: *const wchar_t, set: *const wchar_t, reject: bool) -> size_t { + let mut count = 0; + while unsafe { *wcs } != 0 && unsafe { wcschr(set, *wcs).is_null() } == reject { + wcs = unsafe { wcs.add(1) }; + count += 1; + } + count +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcscspn(wcs: *const wchar_t, set: *const wchar_t) -> size_t { + unsafe { inner_wcsspn(wcs, set, true) } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn wcsftime( + wcs: *mut wchar_t, + maxsize: size_t, + format: *const wchar_t, + timptr: *const tm, +) -> size_t { + todo_skip!(0, "wcsftime is not implemented"); + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcslen(ws: *const wchar_t) -> size_t { + unsafe { NulTerminated::new(ws).unwrap() }.count() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsncat( + ws1: *mut wchar_t, + ws2: *const wchar_t, + n: size_t, +) -> *mut wchar_t { + let len = unsafe { wcslen(ws1) }; + let dest = unsafe { ws1.add(len) }; + let mut i = 0; + while i < n { + let wc = unsafe { *ws2.add(i) }; + if wc == 0 { + break; + } + unsafe { *dest.add(i) = wc }; + i += 1; + } + unsafe { *dest.add(i) = 0 }; + ws1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsncmp(ws1: *const wchar_t, ws2: *const wchar_t, n: size_t) -> c_int { + for i in 0..n { + let wc1 = unsafe { *ws1.add(i) }; + let wc2 = unsafe { *ws2.add(i) }; + if wc1 != wc2 { + return wc1 - wc2; + } else if wc1 == 0 { + break; + } + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsncpy( + ws1: *mut wchar_t, + ws2: *const wchar_t, + n: size_t, +) -> *mut wchar_t { + let mut i = 0; + while i < n { + let wc = unsafe { *ws2.add(i) }; + unsafe { *ws1.add(i) = wc }; + i += 1; + if wc == 0 { + break; + } + } + while i < n { + unsafe { *ws1.add(i) = 0 }; + i += 1; + } + ws1 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsnlen(mut s: *const wchar_t, maxlen: size_t) -> size_t { + let mut len = 0; + + while len < maxlen { + if unsafe { *s } == 0 { + break; + } + + len += 1; + s = unsafe { s.offset(1) }; + } + + len +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsnrtombs( + mut dest: *mut c_char, + src: *mut *const wchar_t, + nwc: size_t, + len: size_t, + mut ps: *mut mbstate_t, +) -> size_t { + let mut written = 0; + let mut read = 0; + let mut buf: [c_char; MB_LEN_MAX as usize] = [0; MB_LEN_MAX as usize]; + let mut mbs = mbstate_t {}; + + if ps.is_null() { + ps = &raw mut mbs; + } + + while read < nwc { + buf.fill(0); + + let ret = unsafe { wcrtomb(buf.as_mut_ptr(), **src, ps) }; + + if ret == size_t::MAX { + ERRNO.set(EILSEQ); + return size_t::MAX; + } + + if !dest.is_null() && len < written + ret { + return written; + } + + if !dest.is_null() { + unsafe { ptr::copy_nonoverlapping(buf.as_ptr(), dest, ret) }; + dest = unsafe { dest.add(ret) }; + } + + if unsafe { **src } == '\0' as wchar_t { + unsafe { *src = ptr::null() }; + return written; + } + + unsafe { *src = (*src).add(1) }; + read += 1; + written += ret; + } + written +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcspbrk(mut wcs: *const wchar_t, set: *const wchar_t) -> *mut wchar_t { + wcs = unsafe { wcs.add(wcscspn(wcs, set)) }; + if unsafe { *wcs } == 0 { + ptr::null_mut() + } else { + // Once again, C wants us to transmute a const pointer to a + // mutable one... + wcs.cast_mut() + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsrchr(ws1: *const wchar_t, wc: wchar_t) -> *mut wchar_t { + let mut last_matching_wc = ptr::null::(); + let mut i = 0; + + while unsafe { *ws1.add(i) } != 0 { + if unsafe { *ws1.add(i) } == wc { + last_matching_wc = unsafe { ws1.add(i) }; + } + i += 1; + } + + last_matching_wc.cast_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsspn(wcs: *const wchar_t, set: *const wchar_t) -> size_t { + unsafe { inner_wcsspn(wcs, set, false) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsstr(ws1: *const wchar_t, ws2: *const wchar_t) -> *mut wchar_t { + // Get length of ws2, not including null terminator + let ws2_len = unsafe { wcslen(ws2) }; + + // The standard says that we must return ws1 if ws2 has length 0 + if ws2_len == 0 { + ws1.cast_mut() + } else { + let ws1_len = unsafe { wcslen(ws1) }; + + // Construct slices without null terminator + let ws1_slice = unsafe { slice::from_raw_parts(ws1, ws1_len) }; + let ws2_slice = unsafe { slice::from_raw_parts(ws2, ws2_len) }; + + /* Sliding ws2-sized window iterator on ws1. The iterator + * returns None if ws2 is longer than ws1. */ + let mut ws1_windows = ws1_slice.windows(ws2_len); + + /* Find the first offset into ws1 where the window is equal to + * the ws2 contents. Return null pointer if no match is found. */ + match ws1_windows.position(|ws1_window| ws1_window == ws2_slice) { + Some(pos) => unsafe { ws1.add(pos).cast_mut() }, + None => ptr::null_mut(), + } + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstod(mut ptr: *const wchar_t, end: *mut *mut wchar_t) -> c_double { + const RADIX: u32 = 10; + + skipws!(ptr); + let negative = unsafe { *ptr } == '-' as wchar_t; + if negative { + ptr = unsafe { ptr.add(1) }; + } + + let mut result: c_double = 0.0; + while let Some(digit) = char::from_u32(unsafe { *ptr } as _).and_then(|c| c.to_digit(RADIX)) { + result *= 10.0; + if negative { + result -= c_double::from(digit); + } else { + result += c_double::from(digit); + } + ptr = unsafe { ptr.add(1) }; + } + if unsafe { *ptr } == '.' as wchar_t { + ptr = unsafe { ptr.add(1) }; + + let mut scale = 1.0; + while let Some(digit) = char::from_u32(unsafe { *ptr } as _).and_then(|c| c.to_digit(RADIX)) + { + scale /= 10.0; + if negative { + result -= c_double::from(digit) * scale; + } else { + result += c_double::from(digit) * scale; + } + ptr = unsafe { ptr.add(1) }; + } + } + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstok( + mut wcs: *mut wchar_t, + delim: *const wchar_t, + state: *mut *mut wchar_t, +) -> *mut wchar_t { + // Choose starting position + if wcs.is_null() { + if (unsafe { *state }).is_null() { + // There was no next token + return ptr::null_mut(); + } + wcs = unsafe { *state }; + } + + // Advance past any delimiters + wcs = unsafe { wcs.add(wcsspn(wcs, delim)) }; + + // Check end + if unsafe { *wcs } == 0 { + unsafe { *state = ptr::null_mut() }; + return ptr::null_mut(); + } + + // Advance *to* any delimiters + let end = unsafe { wcspbrk(wcs, delim) }; + if end.is_null() { + unsafe { *state = ptr::null_mut() }; + } else { + unsafe { *end = 0 }; + unsafe { *state = end.add(1) }; + } + wcs +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstol( + mut ptr: *const wchar_t, + end: *mut *mut wchar_t, + base: c_int, +) -> c_long { + skipws!(ptr); + let result = strto_impl!(c_long, ptr, base); + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstoll( + mut ptr: *const wchar_t, + end: *mut *mut wchar_t, + base: c_int, +) -> c_longlong { + skipws!(ptr); + let result = strto_impl!(c_longlong, ptr, base); + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstoul( + mut ptr: *const wchar_t, + end: *mut *mut wchar_t, + base: c_int, +) -> c_ulong { + skipws!(ptr); + let result = strtou_impl!(c_ulong, ptr, base); + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcstoull( + mut ptr: *const wchar_t, + end: *mut *mut wchar_t, + base: c_int, +) -> c_ulonglong { + skipws!(ptr); + let result = strtou_impl!(c_ulonglong, ptr, base); + if !end.is_null() { + unsafe { *end = ptr.cast_mut() }; + } + result +} + +/// See . +/// +/// Marked legacy in issue 6. +/// Encouraged to use `wcsstr` instead, which this implementation simply forwards to. +#[deprecated] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcswcs(ws1: *const wchar_t, ws2: *const wchar_t) -> *mut wchar_t { + unsafe { wcsstr(ws1, ws2) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcswidth(pwcs: *const wchar_t, n: size_t) -> c_int { + let mut total_width = 0; + for i in 0..n { + let wc_width = wcwidth(unsafe { *pwcs.add(i) }); + if wc_width < 0 { + return -1; + } + total_width += wc_width; + } + total_width +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn wcsxfrm(ws1: *mut wchar_t, ws2: *const wchar_t, n: size_t) -> size_t { + todo_skip!(0, "wcsxfrm is not implemented"); + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn wctob(c: wint_t) -> c_int { + if c <= 0x7F { c as c_int } else { EOF } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn wcwidth(wc: wchar_t) -> c_int { + match char::from_u32(wc as u32) { + Some(c) => match unicode_width::UnicodeWidthChar::width(c) { + Some(width) => width as c_int, + None => -1, + }, + None => -1, + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wmemchr(ws: *const wchar_t, wc: wchar_t, n: size_t) -> *mut wchar_t { + for i in 0..n { + if unsafe { *ws.add(i) } == wc { + return unsafe { ws.add(i) }.cast_mut(); + } + } + ptr::null_mut() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wmemcmp(ws1: *const wchar_t, ws2: *const wchar_t, n: size_t) -> c_int { + for i in 0..n { + let wc1 = unsafe { *ws1.add(i) }; + let wc2 = unsafe { *ws2.add(i) }; + if wc1 != wc2 { + return wc1 - wc2; + } + } + 0 +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wmemcpy( + ws1: *mut wchar_t, + ws2: *const wchar_t, + n: size_t, +) -> *mut wchar_t { + (unsafe { + string::memcpy( + ws1.cast::(), + ws2.cast::(), + n * mem::size_of::(), + ) + }) + .cast::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wmemmove( + ws1: *mut wchar_t, + ws2: *const wchar_t, + n: size_t, +) -> *mut wchar_t { + (unsafe { + string::memmove( + ws1.cast::(), + ws2.cast::(), + n * mem::size_of::(), + ) + }) + .cast::() +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wmemset(ws: *mut wchar_t, wc: wchar_t, n: size_t) -> *mut wchar_t { + for i in 0..n { + unsafe { *ws.add(i) = wc }; + } + ws +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vfwscanf( + stream: *mut FILE, + format: *const wchar_t, + __valist: va_list, +) -> c_int { + let mut file = unsafe { (*stream).lock() }; + if file.try_set_byte_orientation_unlocked().is_err() { + return -1; + } + + let f: &mut FILE = &mut file; + let reader: Reader = f.into(); + + unsafe { + let format = WStr::from_ptr(format); + wscanf::scanf(reader, format.into(), __valist) + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn vwscanf(format: *const wchar_t, __valist: va_list) -> c_int { + unsafe { vfwscanf(stdin, format, __valist) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wscanf(format: *const wchar_t, mut __valist: ...) -> c_int { + unsafe { vfwscanf(stdin, format, __valist.as_va_list()) } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcscasecmp(mut s1: *const wchar_t, mut s2: *const wchar_t) -> c_int { + unsafe { + while *s1 != 0 && *s2 != 0 { + if towlower(*s1 as wint_t) != towlower(*s2 as wint_t) { + break; + } + s1 = s1.add(1); + s2 = s2.add(1); + } + let result = towlower(*s1 as wint_t).wrapping_sub(towlower(*s2 as wint_t)); + result as c_int + } +} + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wcsncasecmp( + mut s1: *const wchar_t, + mut s2: *const wchar_t, + n: size_t, +) -> c_int { + if n == 0 { + return 0; + } + unsafe { + for _ in 0..n { + if *s1 == 0 || *s2 == 0 || towlower(*s1 as wint_t) != towlower(*s2 as wint_t) { + return towlower(*s1 as wint_t).wrapping_sub(towlower(*s2 as wint_t)) as c_int; + } + s1 = s1.add(1); + s2 = s2.add(1); + } + 0 + } +} diff --git a/src/header/wchar/utf8.rs b/src/header/wchar/utf8.rs new file mode 100644 index 0000000000..dc0da1f94a --- /dev/null +++ b/src/header/wchar/utf8.rs @@ -0,0 +1,107 @@ +//UTF implementation parts for wchar.h. +//Partially ported from the Sortix libc + +use core::{char, slice, str}; + +use crate::{ + header::errno, + platform::{ + self, + types::{c_char, wchar_t}, + }, +}; + +use super::mbstate_t; + +// Based on +// https://github.com/rust-lang/rust/blob/f24ce9b/library/core/src/str/validations.rs#L232-L257, +// because apparently somebody removed the `pub use` statement from `core::str`. + +// https://tools.ietf.org/html/rfc3629 +static UTF8_CHAR_WIDTH: [u8; 256] = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x1F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x3F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x5F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, // 0x7F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 0x9F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 0xBF + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, // 0xDF + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF +]; + +// Given a first byte, determines how many bytes are in this UTF-8 character. +#[inline] +fn utf8_char_width(b: u8) -> usize { + UTF8_CHAR_WIDTH[usize::from(b)].into() +} + +//It's guaranteed that we don't have any nullpointers here +pub unsafe fn mbrtowc(pwc: *mut wchar_t, s: *const c_char, n: usize, ps: *mut mbstate_t) -> usize { + let size = utf8_char_width(unsafe { *s } as u8); + if size > n { + platform::ERRNO.set(errno::EILSEQ); + return -2isize as usize; + } + if size == 0 { + platform::ERRNO.set(errno::EILSEQ); + return -1isize as usize; + } + + let slice = unsafe { slice::from_raw_parts(s.cast::(), size) }; + let decoded = str::from_utf8(slice); + if decoded.is_err() { + platform::ERRNO.set(errno::EILSEQ); + return -1isize as usize; + } + + let wc = decoded.unwrap(); + + let result: wchar_t = wc.chars().next().unwrap() as wchar_t; + + if !pwc.is_null() { + unsafe { *pwc = result }; + } + + if result != 0 { size } else { 0 } +} + +//It's guaranteed that we don't have any nullpointers here +pub unsafe fn wcrtomb(s: *mut c_char, wc: wchar_t, ps: *mut mbstate_t) -> usize { + let dc = char::from_u32(wc as u32); + + if dc.is_none() { + platform::ERRNO.set(errno::EILSEQ); + return -1isize as usize; + } + + let c = dc.unwrap(); + let size = c.len_utf8(); + let slice = unsafe { slice::from_raw_parts_mut(s.cast::(), size) }; + + c.encode_utf8(slice); + + size +} + +/// Gets the encoded length of a character. It is used to recognize wide characters +pub fn get_char_encoded_length(first_byte: u8) -> Option { + if first_byte >> 7 == 0 { + Some(1) + } else if first_byte >> 5 == 6 { + Some(2) + } else if first_byte >> 4 == 0xe { + Some(3) + } else if first_byte >> 3 == 0x1e { + Some(4) + } else { + None + } +} diff --git a/src/header/wchar/wprintf.rs b/src/header/wchar/wprintf.rs new file mode 100644 index 0000000000..6a951a8dae --- /dev/null +++ b/src/header/wchar/wprintf.rs @@ -0,0 +1,12 @@ +// TODO: reuse more code with the thin printf impl +use crate::{ + c_str::{self, WStr}, + header::stdio::printf::inner_printf, + io::Write, + platform::types::c_int, +}; +use core::ffi::VaList; + +pub unsafe fn wprintf(w: impl Write, format: WStr, ap: VaList) -> c_int { + unsafe { inner_printf::(w, format, ap).unwrap_or(-1) } +} diff --git a/src/header/wchar/wscanf.rs b/src/header/wchar/wscanf.rs new file mode 100644 index 0000000000..e7797ed1b4 --- /dev/null +++ b/src/header/wchar/wscanf.rs @@ -0,0 +1,13 @@ +use crate::{ + c_str::{self, WStr}, + header::stdio::{reader::Reader, scanf::inner_scanf}, + platform::types::c_int, +}; +use core::ffi::VaList as va_list; + +pub unsafe fn scanf(r: Reader<'_, c_str::Wide>, format: WStr, ap: va_list) -> c_int { + match unsafe { inner_scanf::(r, format.into(), ap) } { + Ok(n) => n, + Err(n) => n, + } +} diff --git a/src/header/wctype/alpha.rs b/src/header/wctype/alpha.rs new file mode 100644 index 0000000000..02c398b0e3 --- /dev/null +++ b/src/header/wctype/alpha.rs @@ -0,0 +1,193 @@ +// From musl src/ctype/alpha.h +// Licensed under the MIT license +// Copyright 2005-2020 Rich Felker, et al. + +use crate::platform::types::c_uchar; + +pub fn is(wc: usize) -> c_uchar { + if wc < 0x20000 { + return (table[(table[wc >> 8] as usize) * 32 + ((wc & 255) >> 3)] >> (wc & 7)) & 1; + } + if wc < 0x2fffe { + return 1; + } + 0 +} + +const table: [c_uchar; 3904] = [ + 18, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 17, 34, 35, 36, 17, 37, 38, + 39, 40, 41, 42, 43, 44, 17, 45, 46, 47, 16, 16, 48, 16, 16, 16, 16, 16, 16, 16, 49, 50, 51, 16, + 52, 53, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 54, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 55, 17, 17, 17, 17, 56, 17, 57, 58, + 59, 60, 61, 62, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 63, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 64, 65, 17, 66, 67, 68, 69, 70, 71, 72, 73, 74, 17, 75, + 76, 77, 78, 79, 80, 81, 16, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 16, 94, 95, 96, 16, + 17, 17, 17, 97, 98, 99, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 100, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 101, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 17, 17, 102, 103, 16, 16, 104, 105, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 106, 17, 17, 107, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 17, 108, 109, 16, 16, 16, 16, 16, 16, 16, 16, 16, 110, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 111, 112, 113, 114, 16, 16, + 16, 16, 16, 16, 16, 16, 115, 116, 117, 16, 16, 16, 16, 16, 118, 119, 16, 16, 16, 16, 120, 16, + 16, 121, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 7, 254, 255, 255, 7, + 0, 0, 0, 0, 0, 4, 32, 4, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 195, + 255, 3, 0, 31, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 223, 188, 64, 215, 255, + 255, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 191, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, 252, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 127, 2, + 255, 255, 255, 255, 255, 1, 0, 0, 0, 0, 255, 191, 182, 0, 255, 255, 255, 135, 7, 0, 0, 0, 255, + 7, 255, 255, 255, 255, 255, 255, 255, 254, 255, 195, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 239, 31, 254, 225, 255, 159, 0, 0, 255, 255, 255, 255, 255, 255, 0, 224, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, 0, 255, 255, 255, 255, 255, 7, + 48, 4, 255, 255, 255, 252, 255, 31, 0, 0, 255, 255, 255, 1, 255, 7, 0, 0, 0, 0, 0, 0, 255, 255, + 223, 63, 0, 0, 240, 255, 248, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 223, + 225, 255, 207, 255, 254, 255, 239, 159, 249, 255, 255, 253, 197, 227, 159, 89, 128, 176, 207, + 255, 3, 16, 238, 135, 249, 255, 255, 253, 109, 195, 135, 25, 2, 94, 192, 255, 63, 0, 238, 191, + 251, 255, 255, 253, 237, 227, 191, 27, 1, 0, 207, 255, 0, 30, 238, 159, 249, 255, 255, 253, + 237, 227, 159, 25, 192, 176, 207, 255, 2, 0, 236, 199, 61, 214, 24, 199, 255, 195, 199, 29, + 129, 0, 192, 255, 0, 0, 239, 223, 253, 255, 255, 253, 255, 227, 223, 29, 96, 7, 207, 255, 0, 0, + 239, 223, 253, 255, 255, 253, 239, 227, 223, 29, 96, 64, 207, 255, 6, 0, 239, 223, 253, 255, + 255, 255, 255, 231, 223, 93, 240, 128, 207, 255, 0, 252, 236, 255, 127, 252, 255, 255, 251, 47, + 127, 128, 95, 255, 192, 255, 12, 0, 254, 255, 255, 255, 255, 127, 255, 7, 63, 32, 255, 3, 0, 0, + 0, 0, 214, 247, 255, 255, 175, 255, 255, 59, 95, 32, 255, 243, 0, 0, 0, 0, 1, 0, 0, 0, 255, 3, + 0, 0, 255, 254, 255, 255, 255, 31, 254, 255, 3, 255, 255, 254, 255, 255, 255, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 127, 249, 255, 3, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 63, 255, 255, 255, 255, 191, 32, 255, 255, 255, 255, 255, 247, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 61, 127, 61, 255, 255, 255, 255, 255, 61, 255, 255, 255, 255, 61, 127, + 61, 255, 127, 255, 255, 255, 255, 255, 255, 255, 61, 255, 255, 255, 255, 255, 255, 255, 255, 7, + 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 63, 254, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 159, 255, 255, 254, 255, 255, 7, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 199, 255, 1, 255, 223, 15, 0, 255, 255, 15, 0, 255, 255, 15, 0, 255, 223, 13, 0, 255, + 255, 255, 255, 255, 255, 207, 255, 255, 1, 128, 16, 255, 3, 0, 0, 0, 0, 255, 3, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 1, 255, 255, 255, 255, 255, 7, 255, 255, 255, 255, 255, + 255, 255, 255, 63, 0, 255, 255, 255, 127, 255, 15, 255, 1, 192, 255, 255, 255, 255, 63, 31, 0, + 255, 255, 255, 255, 255, 15, 255, 255, 255, 3, 255, 3, 0, 0, 0, 0, 255, 255, 255, 15, 255, 255, + 255, 255, 255, 255, 255, 127, 254, 255, 31, 0, 255, 3, 255, 3, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, 255, 255, 239, 255, 239, 15, 255, 3, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 243, 255, 255, 255, 255, 255, 255, 191, 255, 3, 0, 255, 255, 255, 255, 255, 255, 127, 0, + 255, 227, 255, 255, 255, 255, 255, 63, 255, 1, 255, 255, 255, 255, 255, 231, 0, 0, 0, 0, 0, + 222, 111, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 128, 255, 31, 0, 255, 255, 63, 63, 255, + 255, 255, 255, 63, 63, 255, 170, 255, 255, 255, 63, 255, 255, 255, 255, 255, 255, 223, 95, 220, + 31, 207, 15, 255, 31, 220, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 128, 0, 0, 255, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 252, 47, 62, 80, 189, 255, 243, 224, 67, 0, 0, 255, + 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 255, 255, 255, 255, 255, 3, 0, 0, 255, 255, 255, + 255, 255, 127, 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 31, 120, 12, 0, 255, 255, 255, 255, 191, 32, 255, 255, 255, 255, + 255, 255, 255, 128, 0, 0, 255, 255, 127, 0, 127, 127, 127, 127, 127, 127, 127, 127, 255, 255, + 255, 255, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 224, 0, 0, 0, 254, 3, 62, 31, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 127, 224, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, 224, 255, 255, 255, 255, + 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 0, 0, 255, 255, 255, 7, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 63, 255, 31, 255, + 255, 255, 15, 0, 0, 255, 255, 255, 255, 255, 127, 240, 143, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 128, 255, 252, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 249, 255, 255, 255, 255, 255, 255, 124, 0, 0, 0, 0, 0, 128, 255, + 191, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 15, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 47, 0, 255, 3, 0, 0, 252, 232, 255, 255, 255, 255, 255, 7, 255, 255, 255, 255, + 7, 0, 255, 255, 255, 31, 255, 255, 255, 255, 255, 255, 247, 255, 0, 128, 255, 3, 255, 255, 255, + 127, 255, 255, 255, 255, 255, 255, 127, 0, 255, 63, 255, 3, 255, 255, 127, 252, 255, 255, 255, + 255, 255, 255, 255, 127, 5, 0, 0, 56, 255, 255, 60, 0, 126, 126, 126, 0, 127, 127, 255, 255, + 255, 255, 255, 247, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 7, 255, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 15, 0, 255, 255, 127, 248, 255, 255, 255, 255, 255, 15, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 3, 0, 0, 0, 0, 127, 0, 248, 224, 255, 253, 127, 95, 219, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, 0, 0, 0, 248, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 252, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 223, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, + 255, 3, 254, 255, 255, 7, 254, 255, 255, 7, 192, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 127, 252, 252, 252, 28, 0, 0, 0, 0, 255, 239, 255, 255, 127, 255, 255, 183, 255, 63, 255, + 63, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 31, 255, 255, + 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 224, 255, 255, 255, 7, 255, 255, + 255, 255, 255, 7, 255, 255, 255, 63, 255, 255, 255, 255, 15, 255, 62, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 255, + 3, 255, 255, 255, 255, 15, 255, 255, 255, 255, 15, 255, 255, 255, 255, 255, 0, 255, 255, 255, + 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 127, 0, 255, 255, 63, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 63, 253, 255, 255, 255, 255, 191, 145, 255, 255, 63, 0, 255, 255, 127, 0, 255, 255, 255, + 127, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 55, 0, 255, 255, 63, 0, 255, 255, 255, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 111, 240, 239, 254, + 255, 255, 63, 0, 0, 0, 0, 0, 255, 255, 255, 31, 255, 255, 255, 31, 0, 0, 0, 0, 255, 254, 255, + 255, 31, 0, 0, 0, 255, 255, 255, 255, 255, 255, 63, 0, 255, 255, 63, 0, 255, 255, 7, 0, 255, + 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 7, 0, 255, 255, 255, 255, 255, 255, 7, 0, 255, + 255, 255, 255, 255, 0, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 31, 128, 0, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 127, 0, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 192, 255, 0, + 0, 252, 255, 255, 255, 255, 255, 255, 1, 0, 0, 255, 255, 255, 1, 255, 3, 255, 255, 255, 255, + 255, 255, 199, 255, 112, 0, 255, 255, 255, 255, 71, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 30, 0, 255, 23, 0, 0, 0, 0, 255, 255, 251, 255, 255, 255, 159, 64, 0, 0, 0, 0, 0, 0, 0, 0, 127, + 189, 255, 191, 255, 1, 255, 255, 255, 255, 255, 255, 255, 1, 255, 3, 239, 159, 249, 255, 255, + 253, 237, 227, 159, 25, 129, 224, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 187, 7, 255, 131, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 179, 0, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 63, 127, 0, 0, 0, 63, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 127, 17, 0, 255, 3, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 63, 1, 255, 3, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 231, 255, 7, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 3, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 252, 255, 255, 255, 255, 255, 252, 26, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 231, 127, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 32, 0, 0, 0, 0, 255, 255, 255, + 255, 255, 255, 255, 1, 255, 253, 255, 255, 255, 255, 127, 127, 1, 0, 255, 3, 0, 0, 252, 255, + 255, 255, 252, 255, 255, 254, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 251, 255, 255, 255, 255, + 127, 180, 203, 0, 255, 3, 191, 253, 255, 255, 255, 127, 123, 1, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 127, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 127, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 127, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 1, 255, 255, 255, 127, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 63, + 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 15, 0, 255, 3, 248, 255, 255, 224, 255, 255, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, + 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 135, 255, 255, 255, 255, 255, 255, 255, 128, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, + 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 7, 0, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 7, 0, 240, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 7, 255, 31, 255, 1, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 255, 255, 255, 255, 255, 255, 255, 255, 223, + 100, 222, 255, 235, 239, 255, 255, 255, 255, 255, 255, 255, 191, 231, 223, 223, 255, 255, 255, + 123, 95, 252, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 63, 255, 255, 255, 253, 255, 255, 247, 255, 255, 255, 247, + 255, 255, 223, 255, 255, 255, 223, 255, 255, 127, 255, 255, 255, 127, 255, 255, 255, 253, 255, + 255, 255, 253, 255, 255, 247, 207, 255, 255, 255, 255, 255, 255, 127, 255, 255, 249, 219, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 31, 128, 63, 255, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 15, 255, 3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 143, 8, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 255, + 255, 255, 150, 254, 247, 10, 132, 234, 150, 170, 150, 247, 247, 94, 255, 251, 255, 15, 238, + 251, 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 3, 255, 255, 255, 3, + 255, 255, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; diff --git a/src/header/wctype/casecmp.rs b/src/header/wctype/casecmp.rs new file mode 100644 index 0000000000..4c25080911 --- /dev/null +++ b/src/header/wctype/casecmp.rs @@ -0,0 +1,414 @@ +// From musl src/ctype/casemap.h +// Licensed under the MIT license +// Copyright 2005-2020 Rich Felker, et al. + +use crate::platform::types::{c_int, c_uchar, c_uint, wint_t}; + +const tab: [c_uchar; 2666] = [ + 7, 8, 9, 10, 11, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, 14, 6, 6, 6, 6, 6, 6, 6, 6, 15, + 16, 17, 18, 6, 19, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 20, 21, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 22, 23, 6, 6, 6, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 25, 6, 6, 6, 6, 26, 6, 6, 6, 6, 6, 6, 6, 27, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 28, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 29, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 30, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 43, 43, 43, + 43, 43, 43, 43, 43, 1, 0, 84, 86, 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 43, 43, 43, 43, 43, 43, 43, 7, 43, 43, 91, 86, 86, 86, 86, + 86, 86, 86, 74, 86, 86, 5, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 36, + 80, 121, 49, 80, 49, 80, 49, 56, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, + 78, 49, 2, 78, 13, 13, 78, 3, 78, 0, 36, 110, 0, 78, 49, 38, 110, 81, 78, 36, 80, 78, 57, 20, + 129, 27, 29, 29, 83, 49, 80, 49, 80, 13, 49, 80, 49, 80, 49, 80, 27, 83, 36, 80, 49, 2, 92, + 123, 92, 123, 92, 123, 92, 123, 92, 123, 20, 121, 92, 123, 92, 123, 92, 45, 43, 73, 3, 72, 3, + 120, 92, 123, 20, 0, 150, 10, 1, 43, 40, 6, 6, 0, 42, 6, 42, 42, 43, 7, 187, 181, 43, 30, 0, + 43, 7, 43, 43, 43, 1, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 1, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 205, 70, 205, 43, 0, 37, 43, 7, 1, 6, 1, 85, 86, 86, 86, 86, 86, 85, 86, 86, 2, + 36, 129, 129, 129, 129, 129, 21, 129, 129, 129, 0, 0, 43, 0, 178, 209, 178, 209, 178, 209, 178, + 209, 0, 0, 205, 204, 1, 0, 215, 215, 215, 215, 215, 131, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 28, 0, 0, 0, 0, 0, 49, 80, 49, + 80, 49, 80, 49, 80, 49, 80, 49, 2, 0, 0, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, + 80, 49, 80, 49, 80, 78, 49, 80, 49, 80, 78, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, + 80, 49, 2, 135, 166, 135, 166, 135, 166, 135, 166, 135, 166, 135, 166, 135, 166, 135, 166, 42, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 84, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 84, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 12, 0, 12, 42, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 7, 42, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 86, 86, 108, 129, 21, 0, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 7, 108, 3, 65, 43, 43, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 44, 86, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 108, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 37, 6, + 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, + 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 86, 122, 158, 38, 6, 37, + 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, 37, 6, + 37, 6, 1, 43, 43, 79, 86, 86, 44, 43, 127, 86, 86, 57, 43, 43, 85, 86, 86, 43, 43, 79, 86, 86, + 44, 43, 127, 86, 86, 129, 55, 117, 91, 123, 92, 43, 43, 79, 86, 86, 2, 172, 4, 0, 0, 57, 43, + 43, 85, 86, 86, 43, 43, 79, 86, 86, 44, 43, 43, 86, 86, 50, 19, 129, 87, 0, 111, 129, 126, 201, + 215, 126, 45, 129, 129, 14, 126, 57, 127, 111, 87, 0, 129, 129, 126, 21, 0, 126, 3, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 7, 43, 36, 43, 151, 43, 43, 43, 43, 43, 43, 43, 43, 43, 42, + 43, 43, 43, 43, 43, 86, 86, 86, 86, 86, 128, 129, 129, 129, 129, 57, 187, 42, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 1, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 201, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 208, 13, 0, 78, 49, 2, 180, 193, 193, 215, 215, 36, 80, 49, 80, 49, 80, 49, 80, + 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, + 49, 80, 215, 215, 83, 193, 71, 212, 215, 215, 215, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 7, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 78, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 49, 80, 13, 0, 0, 0, 0, 0, 36, 80, + 49, 80, 49, 80, 49, 80, 49, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 121, 92, + 123, 92, 123, 79, 123, 92, 123, 92, 123, 92, 123, 92, 123, 92, 123, 92, 123, 92, 123, 92, 123, + 92, 123, 92, 45, 43, 43, 121, 20, 92, 123, 92, 45, 121, 42, 92, 39, 92, 123, 92, 123, 92, 123, + 164, 0, 10, 180, 92, 123, 92, 123, 79, 3, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 43, 43, 43, 43, 43, 43, 43, 7, + 0, 72, 86, 86, 86, 86, 86, 86, 86, 86, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 85, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 7, 0, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 7, 0, 0, 0, 0, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 85, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +const rules: [c_int; 240] = [ + 0x0, 0x2001, -0x2000, 0x1dbf00, 0x2e700, 0x7900, 0x2402, 0x101, -0x100, 0x0, 0x201, -0x200, + -0xc6ff, -0xe800, -0x78ff, -0x12c00, 0xc300, 0xd201, 0xce01, 0xcd01, 0x4f01, 0xca01, 0xcb01, + 0xcf01, 0x6100, 0xd301, 0xd101, 0xa300, 0xd501, 0x8200, 0xd601, 0xda01, 0xd901, 0xdb01, 0x3800, + 0x3, -0x4f00, -0x60ff, -0x37ff, 0x242802, 0x0, 0x101, -0x100, -0xcd00, -0xda00, -0x81ff, + 0x2a2b01, -0xa2ff, 0x2a2801, 0x2a3f00, -0xc2ff, 0x4501, 0x4701, 0x2a1f00, 0x2a1c00, 0x2a1e00, + -0xd200, -0xce00, -0xca00, -0xcb00, 0xa54f00, 0xa54b00, -0xcf00, 0xa52800, 0xa54400, -0xd100, + -0xd300, 0x29f700, 0xa54100, 0x29fd00, -0xd500, -0xd600, 0x29e700, 0xa54300, 0xa52a00, -0x4500, + -0xd900, -0x4700, -0xdb00, 0xa51500, 0xa51200, 0x4c2402, 0x0, 0x2001, -0x2000, 0x101, -0x100, + 0x5400, 0x7401, 0x2601, 0x2501, 0x4001, 0x3f01, -0x2600, -0x2500, -0x1f00, -0x4000, -0x3f00, + 0x801, -0x3e00, -0x3900, -0x2f00, -0x3600, -0x800, -0x5600, -0x5000, 0x700, -0x7400, -0x3bff, + -0x6000, -0x6ff, 0x701a02, 0x101, -0x100, 0x2001, -0x2000, 0x5001, 0xf01, -0xf00, 0x0, 0x3001, + -0x3000, 0x101, -0x100, 0x0, 0xbc000, 0x1c6001, 0x0, 0x97d001, 0x801, -0x800, 0x8a0502, 0x0, + -0xbbfff, -0x186200, 0x89c200, -0x182500, -0x186e00, -0x186d00, -0x186400, -0x186300, + -0x185c00, 0x0, 0x8a3800, 0x8a0400, 0xee600, 0x101, -0x100, 0x0, -0x3b00, -0x1dbeff, 0x8f1d02, + 0x800, -0x7ff, 0x0, 0x5600, -0x55ff, 0x4a00, 0x6400, 0x8000, 0x7000, 0x7e00, 0x900, -0x49ff, + -0x8ff, -0x1c2500, -0x63ff, -0x6fff, -0x7fff, -0x7dff, 0xac0502, 0x0, 0x1001, -0x1000, 0x1c01, + 0x101, -0x1d5cff, -0x20beff, -0x2045ff, -0x1c00, 0xb10b02, 0x101, -0x100, 0x3001, -0x3000, 0x0, + -0x29f6ff, -0xee5ff, -0x29e6ff, -0x2a2b00, -0x2a2800, -0x2a1bff, -0x29fcff, -0x2a1eff, + -0x2a1dff, -0x2a3eff, 0x0, -0x1c6000, 0x0, 0x101, -0x100, 0xbc0c02, 0x0, 0x101, -0x100, + -0xa543ff, 0x3a001, -0x8a03ff, -0xa527ff, 0x3000, -0xa54eff, -0xa54aff, -0xa540ff, -0xa511ff, + -0xa529ff, -0xa514ff, -0x2fff, -0xa542ff, -0x8a37ff, 0x0, -0x97d000, -0x3a000, 0x0, 0x2001, + -0x2000, 0x0, 0x2801, -0x2800, 0x0, 0x4001, -0x4000, 0x0, 0x2001, -0x2000, 0x0, 0x2001, + -0x2000, 0x0, 0x2201, -0x2200, +]; + +const rulebases: [c_uchar; 512] = [ + 0, 6, 39, 81, 111, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, + 131, 142, 146, 151, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 198, 201, 0, 0, 0, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +const exceptions: [[c_uchar; 2]; 200] = [ + [48, 12], + [49, 13], + [120, 14], + [127, 15], + [128, 16], + [129, 17], + [134, 18], + [137, 19], + [138, 19], + [142, 20], + [143, 21], + [144, 22], + [147, 19], + [148, 23], + [149, 24], + [150, 25], + [151, 26], + [154, 27], + [156, 25], + [157, 28], + [158, 29], + [159, 30], + [166, 31], + [169, 31], + [174, 31], + [177, 32], + [178, 32], + [183, 33], + [191, 34], + [197, 35], + [200, 35], + [203, 35], + [221, 36], + [242, 35], + [246, 37], + [247, 38], + [32, 45], + [58, 46], + [61, 47], + [62, 48], + [63, 49], + [64, 49], + [67, 50], + [68, 51], + [69, 52], + [80, 53], + [81, 54], + [82, 55], + [83, 56], + [84, 57], + [89, 58], + [91, 59], + [92, 60], + [97, 61], + [99, 62], + [101, 63], + [102, 64], + [104, 65], + [105, 66], + [106, 64], + [107, 67], + [108, 68], + [111, 66], + [113, 69], + [114, 70], + [117, 71], + [125, 72], + [130, 73], + [135, 74], + [137, 75], + [138, 76], + [139, 76], + [140, 77], + [146, 78], + [157, 79], + [158, 80], + [69, 87], + [123, 29], + [124, 29], + [125, 29], + [127, 88], + [134, 89], + [136, 90], + [137, 90], + [138, 90], + [140, 91], + [142, 92], + [143, 92], + [172, 93], + [173, 94], + [174, 94], + [175, 94], + [194, 95], + [204, 96], + [205, 97], + [206, 97], + [207, 98], + [208, 99], + [209, 100], + [213, 101], + [214, 102], + [215, 103], + [240, 104], + [241, 105], + [242, 106], + [243, 107], + [244, 108], + [245, 109], + [249, 110], + [253, 45], + [254, 45], + [255, 45], + [80, 105], + [81, 105], + [82, 105], + [83, 105], + [84, 105], + [85, 105], + [86, 105], + [87, 105], + [88, 105], + [89, 105], + [90, 105], + [91, 105], + [92, 105], + [93, 105], + [94, 105], + [95, 105], + [130, 0], + [131, 0], + [132, 0], + [133, 0], + [134, 0], + [135, 0], + [136, 0], + [137, 0], + [192, 117], + [207, 118], + [128, 137], + [129, 138], + [130, 139], + [133, 140], + [134, 141], + [112, 157], + [113, 157], + [118, 158], + [119, 158], + [120, 159], + [121, 159], + [122, 160], + [123, 160], + [124, 161], + [125, 161], + [179, 162], + [186, 163], + [187, 163], + [188, 164], + [190, 165], + [195, 162], + [204, 164], + [218, 166], + [219, 166], + [229, 106], + [234, 167], + [235, 167], + [236, 110], + [243, 162], + [248, 168], + [249, 168], + [250, 169], + [251, 169], + [252, 164], + [38, 176], + [42, 177], + [43, 178], + [78, 179], + [132, 8], + [98, 186], + [99, 187], + [100, 188], + [101, 189], + [102, 190], + [109, 191], + [110, 192], + [111, 193], + [112, 194], + [126, 195], + [127, 195], + [125, 207], + [141, 208], + [148, 209], + [171, 210], + [172, 211], + [173, 212], + [176, 213], + [177, 214], + [178, 215], + [196, 216], + [197, 217], + [198, 218], +]; + +pub fn casemap(mut c: u32, dir: i32) -> wint_t { + if c >= 0x20000 { + return c; + } + let c0 = c; + let b = c >> 8; + c &= 255; + let x = c / 3; + let y = c % 3; + /* lookup entry in two-level base-6 table */ + let mut v: c_uint = c_uint::from(tab[(u32::from(tab[b as usize]) * 86 + x) as usize]); + let mt: [c_uint; 3] = [2048, 342, 57]; + v = ((v * mt[y as usize]) >> 11) % 6; + + /* use the bit vector out of the tables as an index into + * a block-specific set of rules and decode the rule into + * a type and a case-mapping delta. */ + let mut r: c_int = rules[(c_uint::from(rulebases[b as usize]) + v) as usize]; + let mut rt: c_uint = (r & 255) as c_uint; + let mut rd: c_int = r >> 8; + + /* rules 0/1 are simple lower/upper case with a delta. + * apply according to desired mapping direction. */ + if rt < 2 { + let tmp = -((rt ^ dir as c_uint) as c_int) as c_uint; + return c0.wrapping_add(rd as c_uint & tmp); + } + + /* binary search. endpoints of the binary search for + * this block are stored in the rule delta field. */ + let mut xn: c_uint = (rd & 0xff) as c_uint; + let mut xb: c_uint = rd as c_uint >> 8; + while xn != 0 { + let attempt: c_uint = c_uint::from(exceptions[(xb + xn / 2) as usize][0]); + if attempt == c { + r = rules[exceptions[(xb + xn / 2) as usize][1] as usize]; + rt = r as c_uint & 255; + rd = r >> 8; + if rt < 2 { + let tmp = -((rt ^ dir as c_uint) as c_int) as c_uint; + return c0.wrapping_add(rd as c_uint & tmp); + } + /* Hard-coded for the four exceptional titlecase */ + if dir == 0 { + return c0 + 1; + } else { + return c0 - 1; + } + } else if attempt > c { + xn /= 2; + } else { + xb += xn / 2; + xn -= xn / 2; + } + } + c0 +} diff --git a/src/header/wctype/cbindgen.toml b/src/header/wctype/cbindgen.toml new file mode 100644 index 0000000000..bd9cdd85a4 --- /dev/null +++ b/src/header/wctype/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["wchar.h" ] +include_guard = "_RELIBC_WCTYPE_H" +language = "C" +style = "Type" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/wctype/mod.rs b/src/header/wctype/mod.rs new file mode 100644 index 0000000000..f9aac7454b --- /dev/null +++ b/src/header/wctype/mod.rs @@ -0,0 +1,232 @@ +//! `wctype.h` implementation. +//! +//! See . + +// TODO: *_l functions + +use self::casecmp::casemap; +use crate::{ + c_str::CStr, + header::ctype, + platform::types::{c_char, c_int, wint_t}, +}; + +mod alpha; +mod casecmp; +mod punct; + +pub type wctype_t = u32; +pub type wctrans_t = *const i32; + +pub const WEOF: wint_t = 0xFFFF_FFFFu32; + +pub const WCTYPE_ALNUM: wctype_t = 1; +pub const WCTYPE_ALPHA: wctype_t = 2; +pub const WCTYPE_BLANK: wctype_t = 3; +pub const WCTYPE_CNTRL: wctype_t = 4; +pub const WCTYPE_DIGIT: wctype_t = 5; +pub const WCTYPE_GRAPH: wctype_t = 6; +pub const WCTYPE_LOWER: wctype_t = 7; +pub const WCTYPE_PRINT: wctype_t = 8; +pub const WCTYPE_PUNCT: wctype_t = 9; +pub const WCTYPE_SPACE: wctype_t = 10; +pub const WCTYPE_UPPER: wctype_t = 11; +pub const WCTYPE_XDIGIT: wctype_t = 12; + +const WCTRANSUP: wctrans_t = 1 as wctrans_t; +const WCTRANSLW: wctrans_t = 2 as wctrans_t; + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswalnum(wc: wint_t) -> c_int { + c_int::from(iswdigit(wc) != 0 || iswalpha(wc) != 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswalpha(wc: wint_t) -> c_int { + c_int::from(alpha::is(wc as usize)) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswblank(wc: wint_t) -> c_int { + ctype::isblank(wc as c_int) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswcntrl(wc: wint_t) -> c_int { + c_int::from( + wc < 32 + || wc.wrapping_sub(0x7f) < 33 + || wc.wrapping_sub(0x2028) < 2 + || wc.wrapping_sub(0xfff9) < 3, + ) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswctype(wc: wint_t, desc: wctype_t) -> c_int { + match desc { + WCTYPE_ALNUM => iswalnum(wc), + WCTYPE_ALPHA => iswalpha(wc), + WCTYPE_BLANK => iswblank(wc), + WCTYPE_CNTRL => iswcntrl(wc), + WCTYPE_DIGIT => iswdigit(wc), + WCTYPE_GRAPH => iswgraph(wc), + WCTYPE_LOWER => iswlower(wc), + WCTYPE_PRINT => iswprint(wc), + WCTYPE_PUNCT => iswpunct(wc), + WCTYPE_SPACE => iswspace(wc), + WCTYPE_UPPER => iswupper(wc), + WCTYPE_XDIGIT => iswxdigit(wc), + _ => 0, + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswdigit(wc: wint_t) -> c_int { + c_int::from(wc.wrapping_sub('0' as wint_t) < 10) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswgraph(wc: wint_t) -> c_int { + c_int::from(iswspace(wc) == 0 && iswprint(wc) != 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswlower(wc: wint_t) -> c_int { + c_int::from(towupper(wc) != wc) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswprint(wc: wint_t) -> c_int { + if wc < 0xff { + c_int::from(((wc + 1) & 0x7f) >= 0x21) + } else if wc < 0x2028 + || wc.wrapping_sub(0x202a) < 0xd800 - 0x202a + || wc.wrapping_sub(0xe000) < 0xfff9 - 0xe000 + { + 1 + } else if wc.wrapping_sub(0xfffc) > 0x10ffff - 0xfffc || (wc & 0xfffe) == 0xfffe { + 0 + } else { + 1 + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswpunct(wc: wint_t) -> c_int { + c_int::from(punct::is(wc as usize)) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswspace(wc: wint_t) -> c_int { + c_int::from( + [ + ' ' as wint_t, + '\t' as wint_t, + '\n' as wint_t, + '\r' as wint_t, + 11, + 12, + 0x0085, + 0x2000, + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2008, + 0x2009, + 0x200a, + 0x2028, + 0x2029, + 0x205f, + 0x3000, + ] + .contains(&wc), + ) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswupper(wc: wint_t) -> c_int { + c_int::from(towlower(wc) != wc) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn iswxdigit(wc: wint_t) -> c_int { + c_int::from(wc.wrapping_sub('0' as wint_t) < 10 || (wc | 32).wrapping_sub('a' as wint_t) < 6) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn towctrans(wc: wint_t, trans: wctrans_t) -> wint_t { + match trans { + WCTRANSUP => towupper(wc), + WCTRANSLW => towlower(wc), + _ => wc, + } +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn towlower(wc: wint_t) -> wint_t { + casemap(wc, 0) +} + +/// See . +#[unsafe(no_mangle)] +pub extern "C" fn towupper(wc: wint_t) -> wint_t { + casemap(wc, 1) +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `class` is convertible to a slice reference, up +/// to and including a terminating nul. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wctrans(class: *const c_char) -> wctrans_t { + let class_cstr = unsafe { CStr::from_ptr(class) }; + match class_cstr.to_bytes() { + b"toupper" => WCTRANSUP, + b"tolower" => WCTRANSLW, + _ => 0 as wctrans_t, + } +} + +/// See . +/// +/// # Safety +/// The caller must ensure that `name` is convertible to a slice reference, up +/// to and including a terminating nul. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wctype(name: *const c_char) -> wctype_t { + let name_cstr = unsafe { CStr::from_ptr(name) }; + match name_cstr.to_bytes() { + b"alnum" => WCTYPE_ALNUM, + b"alpha" => WCTYPE_ALPHA, + b"blank" => WCTYPE_BLANK, + b"cntrl" => WCTYPE_CNTRL, + b"digit" => WCTYPE_DIGIT, + b"graph" => WCTYPE_GRAPH, + b"lower" => WCTYPE_LOWER, + b"print" => WCTYPE_PRINT, + b"punct" => WCTYPE_PUNCT, + b"space" => WCTYPE_SPACE, + b"upper" => WCTYPE_UPPER, + b"xdigit" => WCTYPE_XDIGIT, + _ => 0, + } +} diff --git a/src/header/wctype/punct.rs b/src/header/wctype/punct.rs new file mode 100644 index 0000000000..2bfb1fc65a --- /dev/null +++ b/src/header/wctype/punct.rs @@ -0,0 +1,166 @@ +// From musl src/ctype/punct.h +// Licensed under the MIT license +// Copyright 2005-2020 Rich Felker, et al. + +use crate::platform::types::c_uchar; + +pub fn is(wc: usize) -> c_uchar { + if wc < 0x20000 { + return (table[(table[wc >> 8] as usize) * 32 + ((wc & 255) >> 3)] >> (wc & 7)) & 1; + } + 0 +} + +const table: [c_uchar; 4000] = [ + 18, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 16, 16, 34, 35, 16, 36, 37, + 38, 39, 40, 41, 42, 43, 16, 44, 45, 46, 17, 17, 47, 17, 17, 17, 17, 17, 17, 48, 49, 50, 51, 52, + 53, 54, 55, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 56, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 57, 16, 58, 59, + 60, 61, 62, 63, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 64, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 65, 16, 16, 66, 16, 67, 68, 69, 16, 70, 71, 72, 16, 73, 16, 16, + 74, 75, 76, 77, 78, 16, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 16, 92, 93, 94, 95, + 16, 16, 16, 16, 96, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 97, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 98, 99, 16, 16, 100, 101, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 102, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 103, 104, 105, 106, 16, 16, 107, 108, 17, 17, 109, 16, + 16, 16, 16, 16, 16, 110, 111, 16, 16, 16, 16, 16, 112, 113, 16, 16, 114, 115, 116, 16, 117, + 118, 119, 17, 17, 17, 120, 121, 122, 123, 124, 16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 254, 255, 0, 252, 1, 0, 0, 248, 1, 0, 0, + 120, 0, 0, 0, 0, 255, 251, 223, 251, 0, 0, 128, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 252, 255, 224, 175, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 223, 255, 255, 255, 255, 255, 32, 64, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 0, 0, 0, 230, 254, 255, 255, + 255, 0, 64, 73, 0, 0, 0, 0, 0, 24, 0, 255, 255, 0, 216, 0, 0, 0, 0, 0, 0, 0, 1, 0, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 224, 1, 30, 0, 96, 255, 191, 0, 0, 0, 0, 0, 0, 255, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 207, 227, 0, 0, 0, 3, 0, 32, 255, 127, 0, + 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 7, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 0, 32, 30, 0, 48, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 0, 0, 0, 252, 111, 0, 0, 0, + 0, 0, 0, 0, 16, 0, 32, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 0, 0, 0, 3, 224, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 0, 0, 0, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, + 255, 7, 16, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 128, 255, 16, 0, 0, 0, 0, 0, 0, 16, 0, 32, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 160, 0, 127, 0, 0, 255, 3, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 128, 0, 128, 192, 223, 0, 12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 31, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 0, 252, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 252, 0, 0, 0, 0, 0, 0, 192, 255, 223, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 6, 0, + 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 224, 255, 255, 255, 31, 0, 0, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 1, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, + 16, 0, 0, 0, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 254, 127, 47, 0, 0, + 255, 3, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 196, 255, 255, 255, 255, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 224, 159, 0, 0, 0, 0, 127, + 63, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 0, 252, 255, 255, 255, + 31, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 64, 0, 12, 240, 0, 0, 0, 0, 0, 0, 128, 248, 0, 0, 0, + 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 255, 255, 33, 144, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 127, 0, 224, 251, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 3, 224, 0, 224, 0, 224, + 0, 96, 128, 248, 255, 255, 255, 252, 255, 255, 255, 255, 255, 127, 223, 255, 241, 127, 255, + 127, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 1, 0, 123, 3, 208, 193, 175, 66, 0, + 12, 31, 188, 255, 255, 0, 0, 0, 0, 0, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 255, 7, 0, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 252, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 207, 255, 255, 255, 63, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 224, 135, 3, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 128, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 255, 255, 255, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 255, 15, 30, 255, 255, 255, 1, 252, 193, 224, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 15, 0, 0, 0, 255, + 255, 255, 127, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 192, + 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 15, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 255, 0, 255, 255, 127, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 15, 255, 3, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 192, 0, 0, 255, 255, 3, 23, 0, 0, 0, 0, 0, 248, 0, 0, 0, 0, 8, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 0, 255, 63, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 128, 3, 0, + 0, 0, 0, 0, 0, 0, 128, 2, 0, 0, 192, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 255, 3, 255, 255, + 255, 255, 255, 255, 247, 255, 127, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, + 254, 255, 0, 252, 1, 0, 0, 248, 1, 0, 0, 248, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 127, 127, 0, 48, 135, 255, 255, 255, 255, 255, 143, 255, 0, 0, 0, 0, 0, 0, 224, 255, 255, 127, + 255, 15, 1, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 255, 0, 0, 128, 255, 0, 0, 0, 0, 128, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 0, 0, + 192, 143, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 255, 255, 252, 255, + 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 135, 255, 1, 255, 1, 0, 0, 0, 224, 0, 0, 0, 224, 0, 0, + 0, 0, 0, 1, 0, 0, 96, 248, 127, 0, 0, 0, 0, 0, 0, 0, 0, 254, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, + 0, 30, 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, + 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 127, 0, 0, 0, 192, 255, + 255, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 192, 63, 252, 255, 63, 0, 0, 128, 3, 0, 0, 0, 0, 0, 0, 254, 3, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 24, 0, 15, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 63, 0, 232, 254, 255, + 31, 0, 0, 0, 0, 0, 0, 0, 96, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 32, 0, 0, 192, 31, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 248, 0, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, + 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 14, 0, 0, 0, 255, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 128, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 223, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 62, 0, 0, + 252, 255, 31, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 3, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 48, 0, 0, 248, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 255, 255, + 255, 255, 127, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 1, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 15, 0, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 127, 0, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 8, 0, 0, 0, + 8, 0, 0, 32, 0, 0, 0, 32, 0, 0, 128, 0, 0, 0, 128, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 8, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 15, 0, 248, 254, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 112, 7, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 255, 255, 255, 255, 255, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 15, 0, 255, 127, 254, 255, 254, 255, 254, 255, 255, 255, 63, 0, 255, 31, 255, 255, + 255, 255, 0, 0, 0, 252, 0, 0, 0, 28, 0, 0, 0, 252, 255, 255, 255, 31, 0, 0, 0, 0, 0, 0, 192, + 255, 255, 255, 7, 0, 255, 255, 255, 255, 255, 15, 255, 1, 3, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 255, 31, 255, 7, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 1, 255, 15, 0, 0, 255, 15, 255, 255, 255, 255, 255, 255, + 255, 0, 255, 3, 255, 255, 255, 255, 255, 0, 255, 255, 255, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 239, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 123, 252, 255, 255, 255, + 255, 231, 199, 255, 255, 255, 231, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 15, 0, 255, 63, 15, 7, 7, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; diff --git a/src/io/buffered.rs b/src/io/buffered.rs new file mode 100644 index 0000000000..a25273185c --- /dev/null +++ b/src/io/buffered.rs @@ -0,0 +1,1123 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Buffering wrappers for I/O traits + +use core::{cmp, fmt}; + +use crate::io::{ + self, DEFAULT_BUF_SIZE, Error, ErrorKind, Initializer, SeekFrom, Write, prelude::*, +}; + +/// The `BufReader` struct adds buffering to any reader. +/// +/// It can be excessively inefficient to work directly with a [`Read`] instance. +/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] +/// results in a system call. A `BufReader` performs large, infrequent reads on +/// the underlying [`Read`] and maintains an in-memory buffer of the results. +/// +/// `BufReader` can improve the speed of programs that make *small* and +/// *repeated* read calls to the same file or network socket. It does not +/// help when reading very large amounts at once, or reading just one or a few +/// times. It also provides no advantage when reading from a source that is +/// already in memory, like a `Vec`. +/// +/// [`Read`]: ../../std/io/trait.Read.html +/// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::BufReader; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let f = File::open("log.txt")?; +/// let mut reader = BufReader::new(f); +/// +/// let mut line = String::new(); +/// let len = reader.read_line(&mut line)?; +/// println!("First line is {} bytes long", len); +/// Ok(()) +/// } +/// ``` +pub struct BufReader { + inner: R, + buf: Box<[u8]>, + pos: usize, + cap: usize, +} + +impl BufReader { + /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, + /// but may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let reader = BufReader::new(f); + /// Ok(()) + /// } + /// ``` + pub fn new(inner: R) -> BufReader { + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufReader` with the specified buffer capacity. + /// + /// # Examples + /// + /// Creating a buffer with ten bytes of capacity: + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let reader = BufReader::with_capacity(10, f); + /// Ok(()) + /// } + /// ``` + #[allow(clippy::uninit_vec)] // buffer initialized after set_len + pub fn with_capacity(cap: usize, inner: R) -> BufReader { + unsafe { + let mut buffer = Vec::with_capacity(cap); + buffer.set_len(cap); + inner.initializer().initialize(&mut buffer); + BufReader { + inner, + buf: buffer.into_boxed_slice(), + pos: 0, + cap: 0, + } + } + } + + /// Gets a reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); + /// + /// let f2 = reader.get_ref(); + /// Ok(()) + /// } + /// ``` + pub fn get_ref(&self) -> &R { + &self.inner + } +} + +impl BufReader { + /// Seeks relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + let pos = self.pos as u64; + if offset < 0 { + if let Some(new_pos) = pos.checked_sub((-offset) as u64) { + self.pos = new_pos as usize; + return Ok(()); + } + } else if let Some(new_pos) = pos.checked_add(offset as u64) + && new_pos <= self.cap as u64 + { + self.pos = new_pos as usize; + return Ok(()); + } + self.seek(SeekFrom::Current(offset)).map(|_| ()) + } +} + +impl Read for BufReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.pos == self.cap && buf.len() >= self.buf.len() { + return self.inner.read(buf); + } + let nread = { + let mut rem = self.fill_buf()?; + rem.read(buf)? + }; + self.consume(nread); + Ok(nread) + } + + // we can't skip unconditionally because of the large buffer case in read. + unsafe fn initializer(&self) -> Initializer { + unsafe { self.inner.initializer() } + } +} + +impl BufRead for BufReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + // If we've reached the end of our internal buffer then we need to fetch + // some more data from the underlying reader. + // Branch using `>=` instead of the more correct `==` + // to tell the compiler that the pos..cap slice is always valid. + if self.pos >= self.cap { + debug_assert!(self.pos == self.cap); + self.cap = self.inner.read(&mut self.buf)?; + self.pos = 0; + } + Ok(&self.buf[self.pos..self.cap]) + } + + fn consume(&mut self, amt: usize) { + self.pos = cmp::min(self.pos + amt, self.cap); + } +} + +impl Seek for BufReader { + /// Seek to an offset, in bytes, in the underlying reader. + /// + /// The position used for seeking with `SeekFrom::Current(_)` is the + /// position the underlying reader would be at if the `BufReader` had no + /// internal buffer. + /// + /// Seeking always discards the internal buffer, even if the seek position + /// would otherwise fall within it. This guarantees that calling + /// `.into_inner()` immediately after a seek yields the underlying reader + /// at the same position. + /// + /// To seek without discarding the internal buffer, use [`seek_relative`](crate::io::BufReader::seek_relative). + /// + /// See [`std::io::Seek`](https://doc.rust-lang.org/std/io/trait.Seek.html) for more details. + /// + /// Note: In the edge case where you're seeking with `SeekFrom::Current(n)` + /// where `n` minus the internal buffer length overflows an `i64`, two + /// seeks will be performed instead of one. If the second seek returns + /// `Err`, the underlying reader will be left at the same position it would + /// have if you called `seek` with `SeekFrom::Current(0)`. + fn seek(&mut self, pos: SeekFrom) -> io::Result { + let result: u64; + if let SeekFrom::Current(n) = pos { + let remainder = (self.cap - self.pos) as i64; + // it should be safe to assume that remainder fits within an i64 as the alternative + // means we managed to allocate 8 exbibytes and that's absurd. + // But it's not out of the realm of possibility for some weird underlying reader to + // support seeking by i64::min_value() so we need to handle underflow when subtracting + // remainder. + if let Some(offset) = n.checked_sub(remainder) { + result = self.inner.seek(SeekFrom::Current(offset))?; + } else { + // seek backwards by our remainder, and then by the offset + self.inner.seek(SeekFrom::Current(-remainder))?; + self.pos = self.cap; // empty the buffer + result = self.inner.seek(SeekFrom::Current(n))?; + } + } else { + // Seeking with Start/End doesn't care about our buffer length. + result = self.inner.seek(pos)?; + } + self.pos = self.cap; // empty the buffer + Ok(result) + } +} + +/// Wraps a writer and buffers its output. +/// +/// It can be excessively inefficient to work directly with something that +/// implements [`Write`]. For example, every call to +/// [`write`][`Tcpstream::write`] on [`TcpStream`] results in a system call. A +/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying +/// writer in large, infrequent batches. +/// +/// `BufWriter` can improve the speed of programs that make *small* and +/// *repeated* write calls to the same file or network socket. It does not +/// help when writing very large amounts at once, or writing just one or a few +/// times. It also provides no advantage when writing to a destination that is +/// in memory, like a `Vec`. +/// +/// When the `BufWriter` is dropped, the contents of its buffer will be written +/// out. However, any errors that happen in the process of flushing the buffer +/// when the writer is dropped will be ignored. Code that wishes to handle such +/// errors must manually call [`flush`] before the writer is dropped. +/// +/// # Examples +/// +/// Let's write the numbers one through ten to a [`TcpStream`]: +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::net::TcpStream; +/// +/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); +/// +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); +/// } +/// ``` +/// +/// Because we're not buffering, we write each one in turn, incurring the +/// overhead of a system call per byte written. We can fix this with a +/// `BufWriter`: +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); +/// } +/// ``` +/// +/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped +/// together by the buffer, and will all be written out in one system call when +/// the `stream` is dropped. +/// +/// [`Write`]: ../../std/io/trait.Write.html +/// [`Tcpstream::write`]: ../../std/net/struct.TcpStream.html#method.write +/// [`TcpStream`]: ../../std/net/struct.TcpStream.html +/// [`flush`]: #method.flush +pub struct BufWriter { + inner: Option, + pub buf: Vec, + // #30888: If the inner writer panics in a call to write, we don't want to + // write the buffered data a second time in BufWriter's destructor. This + // flag tells the Drop impl if it should skip the flush. + panicked: bool, +} + +/// An error returned by `into_inner` which combines an error that +/// happened while writing out the buffer, and the buffered writer object +/// which may be used to recover from the condition. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// // do stuff with the stream +/// +/// // we want to get our `TcpStream` back, so let's try: +/// +/// let stream = match stream.into_inner() { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } +/// }; +/// ``` +#[derive(Debug)] +pub struct IntoInnerError(W, Error); + +impl BufWriter { + /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, + /// but may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// ``` + pub fn new(inner: W) -> BufWriter { + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufWriter` with the specified buffer capacity. + /// + /// # Examples + /// + /// Creating a buffer with a buffer of a hundred bytes. + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); + /// let mut buffer = BufWriter::with_capacity(100, stream); + /// ``` + pub fn with_capacity(cap: usize, inner: W) -> BufWriter { + BufWriter { + inner: Some(inner), + buf: Vec::with_capacity(cap), + panicked: false, + } + } + + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // we can use reference just like buffer + /// let reference = buffer.get_ref(); + /// ``` + pub fn get_ref(&self) -> &W { + self.inner.as_ref().unwrap() + } + + fn flush_buf(&mut self) -> io::Result<()> { + let mut written = 0; + let len = self.buf.len(); + let mut ret = Ok(()); + while written < len { + self.panicked = true; + let r = self.inner.as_mut().unwrap().write(&self.buf[written..]); + self.panicked = false; + + match r { + Ok(0) => { + ret = Err(Error::new( + ErrorKind::WriteZero, + "failed to write the buffered data", + )); + break; + } + Ok(n) => written += n, + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => { + ret = Err(e); + break; + } + } + } + if written > 0 { + self.buf.drain(..written); + } + ret + } + + /// Gets a mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // we can use reference just like buffer + /// let reference = buffer.get_mut(); + /// ``` + pub fn get_mut(&mut self) -> &mut W { + self.inner.as_mut().unwrap() + } + + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// The buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An `Err` will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // unwrap the TcpStream and flush the buffer + /// let stream = buffer.into_inner().unwrap(); + /// ``` + pub fn into_inner(mut self) -> Result>> { + match self.flush_buf() { + Err(e) => Err(IntoInnerError(self, e)), + Ok(()) => Ok(self.inner.take().unwrap()), + } + } +} + +impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.inner.as_mut().unwrap().write(buf); + self.panicked = false; + r + } else { + Write::write(&mut self.buf, buf) + } + } + fn flush(&mut self) -> io::Result<()> { + self.flush_buf().and_then(|()| self.get_mut().flush()) + } +} + +impl fmt::Debug for BufWriter +where + W: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("BufWriter") + .field("writer", &self.inner.as_ref().unwrap()) + .field( + "buffer", + &format_args!("{}/{}", self.buf.len(), self.buf.capacity()), + ) + .finish() + } +} + +impl Seek for BufWriter { + /// Seek to the offset, in bytes, in the underlying writer. + /// + /// Seeking always writes out the internal buffer before seeking. + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.flush_buf().and_then(|_| self.get_mut().seek(pos)) + } +} + +/// Wraps a writer and buffers output to it, flushing whenever a newline +/// (`0x0a`, `'\n'`) is detected. +/// +/// The [`BufWriter`][bufwriter] struct wraps a writer and buffers its output. +/// But it only does this batched write when it goes out of scope, or when the +/// internal buffer is full. Sometimes, you'd prefer to write each line as it's +/// completed, rather than the entire buffer at once. Enter `LineWriter`. It +/// does exactly that. +/// +/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the +/// `LineWriter` goes out of scope or when its internal buffer is full. +/// +/// [bufwriter]: struct.BufWriter.html +/// +/// If there's still a partial line in the buffer when the `LineWriter` is +/// dropped, it will flush those contents. +/// +/// # Examples +/// +/// We can use `LineWriter` to write one line at a time, significantly +/// reducing the number of actual writes to the file. +/// +/// ```no_run +/// use std::fs::{self, File}; +/// use std::io::prelude::*; +/// use std::io::LineWriter; +/// +/// fn main() -> std::io::Result<()> { +/// let road_not_taken = b"I shall be telling this with a sigh +/// Somewhere ages and ages hence: +/// Two roads diverged in a wood, and I - +/// I took the one less traveled by, +/// And that has made all the difference."; +/// +/// let file = File::create("poem.txt")?; +/// let mut file = LineWriter::new(file); +/// +/// file.write_all(b"I shall be telling this with a sigh")?; +/// +/// // No bytes are written until a newline is encountered (or +/// // the internal buffer is filled). +/// assert_eq!(fs::read_to_string("poem.txt")?, ""); +/// file.write_all(b"\n")?; +/// assert_eq!( +/// fs::read_to_string("poem.txt")?, +/// "I shall be telling this with a sigh\n", +/// ); +/// +/// // Write the rest of the poem. +/// file.write_all(b"Somewhere ages and ages hence: +/// Two roads diverged in a wood, and I - +/// I took the one less traveled by, +/// And that has made all the difference.")?; +/// +/// // The last line of the poem doesn't end in a newline, so +/// // we have to flush or drop the `LineWriter` to finish +/// // writing. +/// file.flush()?; +/// +/// // Confirm the whole poem was written. +/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]); +/// Ok(()) +/// } +/// ``` +pub struct LineWriter { + pub inner: BufWriter, + need_flush: bool, +} + +impl LineWriter { + /// Creates a new `LineWriter`. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// Ok(()) + /// } + /// ``` + pub fn new(inner: W) -> LineWriter { + // Lines typically aren't that long, don't use a giant buffer + LineWriter::with_capacity(1024, inner) + } + + /// Creates a new `LineWriter` with a specified capacity for the internal + /// buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::with_capacity(100, file); + /// Ok(()) + /// } + /// ``` + pub fn with_capacity(cap: usize, inner: W) -> LineWriter { + LineWriter { + inner: BufWriter::with_capacity(cap, inner), + need_flush: false, + } + } + + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// + /// let reference = file.get_ref(); + /// Ok(()) + /// } + /// ``` + pub fn get_ref(&self) -> &W { + self.inner.get_ref() + } + + /// Gets a mutable reference to the underlying writer. + /// + /// Caution must be taken when calling methods on the mutable reference + /// returned as extra writes could corrupt the output stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let mut file = LineWriter::new(file); + /// + /// // we can use reference just like file + /// let reference = file.get_mut(); + /// Ok(()) + /// } + /// ``` + pub fn get_mut(&mut self) -> &mut W { + self.inner.get_mut() + } +} + +impl Write for LineWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.need_flush { + self.flush()?; + } + + // Find the last newline character in the buffer provided. If found then + // we're going to write all the data up to that point and then flush, + // otherwise we just write the whole block to the underlying writer. + let i = match memchr::memrchr(b'\n', buf) { + Some(i) => i, + None => return self.inner.write(buf), + }; + + // Ok, we're going to write a partial amount of the data given first + // followed by flushing the newline. After we've successfully written + // some data then we *must* report that we wrote that data, so future + // errors are ignored. We set our internal `need_flush` flag, though, in + // case flushing fails and we need to try it first next time. + let n = self.inner.write(&buf[..i + 1])?; + self.need_flush = true; + if self.flush().is_err() || n != i + 1 { + return Ok(n); + } + + // At this point we successfully wrote `i + 1` bytes and flushed it out, + // meaning that the entire line is now flushed out on the screen. While + // we can attempt to finish writing the rest of the data provided. + // Remember though that we ignore errors here as we've successfully + // written data, so we need to report that. + match self.inner.write(&buf[i + 1..]) { + Ok(i) => Ok(n + i), + Err(_) => Ok(n), + } + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush()?; + self.need_flush = false; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use alloc::string::String; + + use crate::io::{self, BufReader, BufWriter, LineWriter, SeekFrom, prelude::*}; + use test; + // use crate::sync::atomic::{AtomicUsize, Ordering}; + + /// A dummy reader intended at testing short-reads propagation. + pub struct ShortReader { + lengths: Vec, + } + + impl Read for ShortReader { + fn read(&mut self, _: &mut [u8]) -> io::Result { + if self.lengths.is_empty() { + Ok(0) + } else { + Ok(self.lengths.remove(0)) + } + } + } + + #[test] + fn test_buffered_reader() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, inner); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf, b); + + let mut buf = [0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 2); + let b: &[_] = &[0, 1]; + assert_eq!(buf, b); + + let mut buf = [0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + let b: &[_] = &[2]; + assert_eq!(buf, b); + + let mut buf = [0, 0, 0]; + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + let b: &[_] = &[3, 0, 0]; + assert_eq!(buf, b); + + let nread = reader.read(&mut buf); + assert_eq!(nread.unwrap(), 1); + let b: &[_] = &[4, 0, 0]; + assert_eq!(buf, b); + + assert_eq!(reader.read(&mut buf).unwrap(), 0); + } + + #[test] + fn test_buffered_reader_seek() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); + + assert_eq!(reader.seek(SeekFrom::Start(3)).ok(), Some(3)); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(3)); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert_eq!(reader.seek(SeekFrom::Current(1)).ok(), Some(4)); + assert_eq!(reader.fill_buf().ok(), Some(&[1, 2][..])); + reader.consume(1); + assert_eq!(reader.seek(SeekFrom::Current(-2)).ok(), Some(3)); + } + + #[test] + fn test_buffered_reader_seek_relative() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); + + assert!(reader.seek_relative(3).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(0).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[1][..])); + assert!(reader.seek_relative(-1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(2).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..])); + } + + #[test] + fn test_buffered_reader_seek_underflow() { + // gimmick reader that yields its position modulo 256 for each byte + struct PositionReader { + pos: u64, + } + impl Read for PositionReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = buf.len(); + for x in buf { + *x = self.pos as u8; + self.pos = self.pos.wrapping_add(1); + } + Ok(len) + } + } + impl Seek for PositionReader { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + match pos { + SeekFrom::Start(n) => { + self.pos = n; + } + SeekFrom::Current(n) => { + self.pos = self.pos.wrapping_add(n as u64); + } + SeekFrom::End(n) => { + self.pos = u64::max_value().wrapping_add(n as u64); + } + } + Ok(self.pos) + } + } + + let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 }); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..])); + assert_eq!( + reader.seek(SeekFrom::End(-5)).ok(), + Some(u64::max_value() - 5) + ); + assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); + // the following seek will require two underlying seeks + let expected = 9223372036854775802; + assert_eq!( + reader.seek(SeekFrom::Current(i64::min_value())).ok(), + Some(expected) + ); + assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); + // seeking to 0 should empty the buffer. + assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected)); + assert_eq!(reader.get_ref().pos, expected); + } + + #[test] + fn test_buffered_writer() { + let inner = Vec::new(); + let mut writer = BufWriter::with_capacity(2, inner); + + writer.write(&[0, 1]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[2]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.write(&[3]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[4]).unwrap(); + writer.write(&[5]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3]); + + writer.write(&[6]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]); + + writer.write(&[7, 8]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]); + + writer.write(&[9, 10, 11]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + } + + #[test] + fn test_buffered_writer_inner_flushes() { + let mut w = BufWriter::with_capacity(3, Vec::new()); + w.write(&[0, 1]).unwrap(); + assert_eq!(*w.get_ref(), []); + let w = w.into_inner().unwrap(); + assert_eq!(w, [0, 1]); + } + + #[test] + fn test_buffered_writer_seek() { + let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new())); + w.write_all(&[0, 1, 2, 3, 4, 5]).unwrap(); + w.write_all(&[6, 7]).unwrap(); + assert_eq!(w.seek(SeekFrom::Current(0)).ok(), Some(8)); + assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]); + assert_eq!(w.seek(SeekFrom::Start(2)).ok(), Some(2)); + w.write_all(&[8, 9]).unwrap(); + assert_eq!( + &w.into_inner().unwrap().into_inner()[..], + &[0, 1, 8, 9, 4, 5, 6, 7] + ); + } + + #[test] + fn test_read_until() { + let inner: &[u8] = &[0, 1, 2, 1, 0]; + let mut reader = BufReader::with_capacity(2, inner); + let mut v = Vec::new(); + reader.read_until(0, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(2, &mut v).unwrap(); + assert_eq!(v, [1, 2]); + v.truncate(0); + reader.read_until(1, &mut v).unwrap(); + assert_eq!(v, [1]); + v.truncate(0); + reader.read_until(8, &mut v).unwrap(); + assert_eq!(v, [0]); + v.truncate(0); + reader.read_until(9, &mut v).unwrap(); + assert_eq!(v, []); + } + + #[test] + fn test_line_buffer_fail_flush() { + // Issue #32085 + struct FailFlushWriter<'a>(&'a mut Vec); + + impl<'a> Write for FailFlushWriter<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.extend_from_slice(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Err(io::Error::new(io::ErrorKind::Other, "flush failed")) + } + } + + let mut buf = Vec::new(); + { + let mut writer = LineWriter::new(FailFlushWriter(&mut buf)); + let to_write = b"abc\ndef"; + if let Ok(written) = writer.write(to_write) { + assert!(written < to_write.len(), "didn't flush on new line"); + // PASS + return; + } + } + assert!(buf.is_empty(), "write returned an error but wrote data"); + } + + #[test] + fn test_line_buffer() { + let mut writer = LineWriter::new(Vec::new()); + writer.write(&[0]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.write(&[1]).unwrap(); + assert_eq!(*writer.get_ref(), []); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1]); + writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']); + writer.flush().unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]); + writer.write(&[3, b'\n']).unwrap(); + assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']); + } + + #[test] + fn test_read_line() { + let in_buf: &[u8] = b"a\nb\nc"; + let mut reader = BufReader::with_capacity(2, in_buf); + let mut s = String::new(); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "a\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "b\n"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, "c"); + s.truncate(0); + reader.read_line(&mut s).unwrap(); + assert_eq!(s, ""); + } + + // #[test] + // fn test_lines() { + // let in_buf: &[u8] = b"a\nb\nc"; + // let reader = BufReader::with_capacity(2, in_buf); + // let mut it = reader.lines(); + // assert_eq!(it.next().unwrap().unwrap(), "a".to_string()); + // assert_eq!(it.next().unwrap().unwrap(), "b".to_string()); + // assert_eq!(it.next().unwrap().unwrap(), "c".to_string()); + // assert!(it.next().is_none()); + // } + + #[test] + fn test_short_reads() { + let inner = ShortReader { + lengths: vec![0, 1, 2, 0, 1, 0], + }; + let mut reader = BufReader::new(inner); + let mut buf = [0, 0]; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.read(&mut buf).unwrap(), 2); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + } + + #[test] + #[should_panic] + fn dont_panic_in_drop_on_panicked_flush() { + struct FailFlushWriter; + + impl Write for FailFlushWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Err(io::Error::last_os_error()) + } + } + + let writer = FailFlushWriter; + let _writer = BufWriter::new(writer); + + // If writer panics *again* due to the flush error then the process will + // abort. + panic!(); + } + + // #[test] + // #[cfg_attr(target_os = "emscripten", ignore)] + // fn panic_in_write_doesnt_flush_in_drop() { + // static WRITES: AtomicUsize = AtomicUsize::new(0); + + // struct PanicWriter; + + // impl Write for PanicWriter { + // fn write(&mut self, _: &[u8]) -> io::Result { + // WRITES.fetch_add(1, Ordering::SeqCst); + // panic!(); + // } + // fn flush(&mut self) -> io::Result<()> { Ok(()) } + // } + + // thread::spawn(|| { + // let mut writer = BufWriter::new(PanicWriter); + // let _ = writer.write(b"hello world"); + // let _ = writer.flush(); + // }).join().unwrap_err(); + + // assert_eq!(WRITES.load(Ordering::SeqCst), 1); + // } + + // #[bench] + // fn bench_buffered_reader(b: &mut test::Bencher) { + // b.iter(|| { + // BufReader::new(io::empty()) + // }); + // } + + // #[bench] + // fn bench_buffered_writer(b: &mut test::Bencher) { + // b.iter(|| { + // BufWriter::new(io::sink()) + // }); + // } + + struct AcceptOneThenFail { + written: bool, + flushed: bool, + } + + impl Write for AcceptOneThenFail { + fn write(&mut self, data: &[u8]) -> io::Result { + if !self.written { + assert_eq!(data, b"a\nb\n"); + self.written = true; + Ok(data.len()) + } else { + Err(io::Error::new(io::ErrorKind::NotFound, "test")) + } + } + + fn flush(&mut self) -> io::Result<()> { + assert!(self.written); + assert!(!self.flushed); + self.flushed = true; + Err(io::Error::new(io::ErrorKind::Other, "test")) + } + } + + #[test] + fn erroneous_flush_retried() { + let a = AcceptOneThenFail { + written: false, + flushed: false, + }; + + let mut l = LineWriter::new(a); + assert_eq!(l.write(b"a\nb\na").unwrap(), 4); + assert!(l.get_ref().written); + assert!(l.get_ref().flushed); + l.get_mut().flushed = false; + + assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other) + } +} diff --git a/src/io/cursor.rs b/src/io/cursor.rs new file mode 100644 index 0000000000..fd5500137c --- /dev/null +++ b/src/io/cursor.rs @@ -0,0 +1,724 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::cmp; + +use crate::io::{self, Error, ErrorKind, Initializer, SeekFrom, prelude::*}; + +/// A `Cursor` wraps an in-memory buffer and provides it with a +/// [`Seek`] implementation. +/// +/// `Cursor`s are used with in-memory buffers, anything implementing +/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`], +/// allowing these buffers to be used anywhere you might use a reader or writer +/// that does actual I/O. +/// +/// The standard library implements some I/O traits on various types which +/// are commonly used as a buffer, like `Cursor<`[`Vec`]`>` and +/// `Cursor<`[`&[u8]`][bytes]`>`. +/// +/// # Examples +/// +/// We may want to write bytes to a [`File`] in our production +/// code, but use an in-memory buffer in our tests. We can do this with +/// `Cursor`: +/// +/// [`Seek`]: trait.Seek.html +/// [`Read`]: ../../std/io/trait.Read.html +/// [`Write`]: ../../std/io/trait.Write.html +/// [`Vec`]: ../../std/vec/struct.Vec.html +/// [bytes]: ../../std/primitive.slice.html +/// [`File`]: ../fs/struct.File.html +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::{self, SeekFrom}; +/// use std::fs::File; +/// +/// // a library function we've written +/// fn write_ten_bytes_at_end(writer: &mut W) -> io::Result<()> { +/// writer.seek(SeekFrom::End(-10))?; +/// +/// for i in 0..10 { +/// writer.write(&[i])?; +/// } +/// +/// // all went well +/// Ok(()) +/// } +/// +/// # fn foo() -> io::Result<()> { +/// // Here's some code that uses this library function. +/// // +/// // We might want to use a BufReader here for efficiency, but let's +/// // keep this example focused. +/// let mut file = File::create("foo.txt")?; +/// +/// write_ten_bytes_at_end(&mut file)?; +/// # Ok(()) +/// # } +/// +/// // now let's write a test +/// #[test] +/// fn test_writes_bytes() { +/// // setting up a real File is much slower than an in-memory buffer, +/// // let's use a cursor instead +/// use std::io::Cursor; +/// let mut buff = Cursor::new(vec![0; 15]); +/// +/// write_ten_bytes_at_end(&mut buff).unwrap(); +/// +/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +/// } +/// ``` +#[derive(Clone, Debug)] +pub struct Cursor { + inner: T, + pos: u64, +} + +impl Cursor { + /// Creates a new cursor wrapping the provided underlying in-memory buffer. + /// + /// Cursor initial position is `0` even if underlying buffer (e.g. `Vec`) + /// is not empty. So writing to cursor starts with overwriting `Vec` + /// content, not with appending to it. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// ``` + pub fn new(inner: T) -> Cursor { + Cursor { pos: 0, inner } + } + + /// Consumes this cursor, returning the underlying value. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let vec = buff.into_inner(); + /// ``` + pub fn into_inner(self) -> T { + self.inner + } + + /// Gets a reference to the underlying value in this cursor. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let reference = buff.get_ref(); + /// ``` + pub fn get_ref(&self) -> &T { + &self.inner + } + + /// Gets a mutable reference to the underlying value in this cursor. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying value as it may corrupt this cursor's position. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let mut buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let reference = buff.get_mut(); + /// ``` + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + /// Returns the current position of this cursor. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// use std::io::prelude::*; + /// use std::io::SeekFrom; + /// + /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(buff.position(), 0); + /// + /// buff.seek(SeekFrom::Current(2)).unwrap(); + /// assert_eq!(buff.position(), 2); + /// + /// buff.seek(SeekFrom::Current(-1)).unwrap(); + /// assert_eq!(buff.position(), 1); + /// ``` + pub fn position(&self) -> u64 { + self.pos + } + + /// Sets the position of this cursor. + /// + /// # Examples + /// + /// ``` + /// use std::io::Cursor; + /// + /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(buff.position(), 0); + /// + /// buff.set_position(2); + /// assert_eq!(buff.position(), 2); + /// + /// buff.set_position(4); + /// assert_eq!(buff.position(), 4); + /// ``` + pub fn set_position(&mut self, pos: u64) { + self.pos = pos; + } +} + +impl io::Seek for Cursor +where + T: AsRef<[u8]>, +{ + fn seek(&mut self, style: SeekFrom) -> io::Result { + let (base_pos, offset) = match style { + SeekFrom::Start(n) => { + self.pos = n; + return Ok(n); + } + SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n), + SeekFrom::Current(n) => (self.pos, n), + }; + let new_pos = if offset >= 0 { + base_pos.checked_add(offset as u64) + } else { + base_pos.checked_sub((offset.wrapping_neg()) as u64) + }; + match new_pos { + Some(n) => { + self.pos = n; + Ok(self.pos) + } + None => Err(Error::new( + ErrorKind::InvalidInput, + "invalid seek to a negative or overflowing position", + )), + } + } +} + +impl Read for Cursor +where + T: AsRef<[u8]>, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let n = Read::read(&mut self.get_buf()?, buf)?; + self.pos += n as u64; + Ok(n) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let n = buf.len(); + Read::read_exact(&mut self.get_buf()?, buf)?; + self.pos += n as u64; + Ok(()) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + unsafe { Initializer::nop() } + } +} + +impl Cursor +where + T: AsRef<[u8]>, +{ + fn get_buf(&mut self) -> io::Result<&[u8]> { + let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64); + Ok(&self.inner.as_ref()[(amt as usize)..]) + } +} + +impl BufRead for Cursor +where + T: AsRef<[u8]>, +{ + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.get_buf() + } + fn consume(&mut self, amt: usize) { + self.pos += amt as u64; + } +} + +// Non-resizing write implementation +fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let amt = (&mut slice[(pos as usize)..]).write(buf)?; + *pos_mut += amt as u64; + Ok(amt) +} + +// Resizing write implementation +fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| { + Error::new( + ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length", + ) + })?; + // Make sure the internal buffer is as least as big as where we + // currently are + let len = vec.len(); + if len < pos { + // use `resize` so that the zero filling is as efficient as possible + vec.resize(pos, 0); + } + // Figure out what bytes will be used to overwrite what's currently + // there (left), and what will be appended on the end (right) + { + let space = vec.len() - pos; + let (left, right) = buf.split_at(cmp::min(space, buf.len())); + vec[pos..pos + left.len()].copy_from_slice(left); + vec.extend_from_slice(right); + } + + // Bump us forward + *pos_mut = (pos + buf.len()) as u64; + Ok(buf.len()) +} + +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + slice_write(&mut self.pos, self.inner, buf) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Write for Cursor<&mut Vec> { + fn write(&mut self, buf: &[u8]) -> io::Result { + vec_write(&mut self.pos, self.inner, buf) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Write for Cursor> { + fn write(&mut self, buf: &[u8]) -> io::Result { + vec_write(&mut self.pos, &mut self.inner, buf) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Write for Cursor<::alloc::boxed::Box<[u8]>> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + slice_write(&mut self.pos, &mut self.inner, buf) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::io::{Cursor, SeekFrom, prelude::*}; + + #[test] + fn test_vec_writer() { + let mut writer = Vec::new(); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer, b); + } + + #[test] + fn test_mem_writer() { + let mut writer = Cursor::new(Vec::new()); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + } + + #[test] + fn test_mem_mut_writer() { + let mut vec = Vec::new(); + let mut writer = Cursor::new(&mut vec); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + } + + #[test] + fn test_box_slice_writer() { + let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write(&[8, 9]).unwrap(), 1); + assert_eq!(writer.write(&[10]).unwrap(), 0); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(&**writer.get_ref(), b); + } + + #[test] + fn test_buf_writer() { + let mut buf = [0 as u8; 9]; + { + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + assert_eq!(writer.write(&[]).unwrap(), 0); + assert_eq!(writer.position(), 8); + + assert_eq!(writer.write(&[8, 9]).unwrap(), 1); + assert_eq!(writer.write(&[10]).unwrap(), 0); + } + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(buf, b); + } + + #[test] + fn test_buf_writer_seek() { + let mut buf = [0 as u8; 8]; + { + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[1]).unwrap(), 1); + assert_eq!(writer.position(), 1); + + assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2); + assert_eq!(writer.position(), 2); + assert_eq!(writer.write(&[2]).unwrap(), 1); + assert_eq!(writer.position(), 3); + + assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[3]).unwrap(), 1); + assert_eq!(writer.position(), 2); + + assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); + assert_eq!(writer.position(), 7); + assert_eq!(writer.write(&[4]).unwrap(), 1); + assert_eq!(writer.position(), 8); + } + let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; + assert_eq!(buf, b); + } + + #[test] + fn test_buf_writer_error() { + let mut buf = [0 as u8; 2]; + let mut writer = Cursor::new(&mut buf[..]); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.write(&[0, 0]).unwrap(), 1); + assert_eq!(writer.write(&[0, 0]).unwrap(), 0); + } + + #[test] + fn test_mem_reader() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + } + + #[test] + fn test_boxed_slice_reader() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice()); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + } + + #[test] + fn read_to_end() { + let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]); + let mut v = Vec::new(); + reader.read_to_end(&mut v).unwrap(); + assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); + } + + #[test] + fn test_slice_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(&buf[..], b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(&buf[..], b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + } + + #[test] + fn test_read_exact() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let reader = &mut &in_buf[..]; + let mut buf = []; + assert!(reader.read_exact(&mut buf).is_ok()); + let mut buf = [8]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf[0], 0); + assert_eq!(reader.len(), 7); + let mut buf = [0, 0, 0, 0, 0, 0, 0]; + assert!(reader.read_exact(&mut buf).is_ok()); + assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]); + assert_eq!(reader.len(), 0); + let mut buf = [0]; + assert!(reader.read_exact(&mut buf).is_err()); + } + + #[test] + fn test_buf_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = Cursor::new(&in_buf[..]); + let mut buf = []; + assert_eq!(reader.read(&mut buf).unwrap(), 0); + assert_eq!(reader.position(), 0); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf).unwrap(), 1); + assert_eq!(reader.position(), 1); + let b: &[_] = &[0]; + assert_eq!(buf, b); + let mut buf = [0; 4]; + assert_eq!(reader.read(&mut buf).unwrap(), 4); + assert_eq!(reader.position(), 5); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf, b); + assert_eq!(reader.read(&mut buf).unwrap(), 3); + let b: &[_] = &[5, 6, 7]; + assert_eq!(&buf[..3], b); + assert_eq!(reader.read(&mut buf).unwrap(), 0); + } + + #[test] + fn seek_past_end() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.read(&mut [0]).unwrap(), 0); + + let mut r = Cursor::new(vec![10]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.read(&mut [0]).unwrap(), 0); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 0); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 0); + } + + #[test] + fn seek_past_i64() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut r = Cursor::new(vec![10]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6); + assert_eq!( + r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), + 0x7ffffffffffffff6 + ); + assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006); + assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006); + assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err()); + assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6); + } + + #[test] + fn seek_before_0() { + let buf = [0xff]; + let mut r = Cursor::new(&buf[..]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec![10]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut buf = [0]; + let mut r = Cursor::new(&mut buf[..]); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + + let mut r = Cursor::new(vec![10].into_boxed_slice()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + } + + #[test] + fn test_seekable_mem_writer() { + let mut writer = Cursor::new(Vec::::new()); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[0]).unwrap(), 1); + assert_eq!(writer.position(), 1); + assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); + assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); + assert_eq!(writer.position(), 8); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(writer.position(), 0); + assert_eq!(writer.write(&[3, 4]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3); + assert_eq!(writer.write(&[0, 1]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); + assert_eq!(writer.write(&[1, 2]).unwrap(), 2); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; + assert_eq!(&writer.get_ref()[..], b); + + assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10); + assert_eq!(writer.write(&[1]).unwrap(), 1); + let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; + assert_eq!(&writer.get_ref()[..], b); + } + + #[test] + fn vec_seek_past_end() { + let mut r = Cursor::new(Vec::new()); + assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(r.write(&[3]).unwrap(), 1); + } + + #[test] + fn vec_seek_before_0() { + let mut r = Cursor::new(Vec::new()); + assert!(r.seek(SeekFrom::End(-2)).is_err()); + } + + #[test] + #[cfg(target_pointer_width = "32")] + fn vec_seek_and_write_past_usize_max() { + let mut c = Cursor::new(Vec::new()); + c.set_position(::max_value() as u64 + 1); + assert!(c.write_all(&[1, 2, 3]).is_err()); + } +} diff --git a/src/io/error.rs b/src/io/error.rs new file mode 100644 index 0000000000..203f628a88 --- /dev/null +++ b/src/io/error.rs @@ -0,0 +1,313 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::{boxed::Box, string::String}; +use core::{fmt, result, str}; + +use crate::platform::types::c_int; + +/// A specialized [`Result`](../result/enum.Result.html) type for I/O +/// operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This typedef is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the prelude's import +/// of [`std::result::Result`][`Result`]. +/// +/// [`std::io`]: ../io/index.html +/// [`io::Error`]: ../io/struct.Error.html +/// [`Result`]: ../result/enum.Result.html +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// [`ErrorKind`]. +/// +/// [`Read`]: ../io/trait.Read.html +/// [`Write`]: ../io/trait.Write.html +/// [`Seek`]: ../io/trait.Seek.html +/// [`ErrorKind`]: enum.ErrorKind.html +pub struct Error { + repr: Repr, +} + +// TODO? +impl Error { + pub fn raw_os_error(&self) -> Option { + if let Repr::Os(os) = self.repr { + Some(os) + } else { + None + } + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self.repr { + Repr::Os(code) => { + write!(fmt, "os error {}", code) + } + Repr::Custom(ref c) => c.error.fmt(fmt), + Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), + } + } +} + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this `Error`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// ``` + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into, + { + Self::_new(kind, error.into()) + } + + fn _new(kind: ErrorKind, error: String) -> Error { + Error { + repr: Repr::Custom(Box::new(Custom { kind, error })), + } + } + + /// Creates a new instance of an `Error` from a particular OS error code. + /// + /// # Examples + /// + /// On Linux: + /// + /// ``` + /// # if cfg!(target_os = "linux") { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(22); + /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); + /// # } + /// ``` + /// + /// On Windows: + /// + /// ``` + /// # if cfg!(windows) { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(10022); + /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); + /// # } + /// ``` + pub fn from_raw_os_error(code: i32) -> Error { + Error { + repr: Repr::Os(code), + } + } + + /// Returns the corresponding `ErrorKind` for this error. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + pub fn kind(&self) -> ErrorKind { + match self.repr { + Repr::Os(_code) => ErrorKind::Other, + Repr::Custom(ref c) => c.kind, + Repr::Simple(kind) => kind, + } + } + + pub fn last_os_error() -> Error { + let errno = crate::platform::ERRNO.get(); + Error::from_raw_os_error(errno) + } +} + +enum Repr { + Os(i32), + Simple(ErrorKind), + Custom(Box), +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Repr::Os(code) => fmt.debug_struct("Os").field("code", &code).finish(), + Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), + Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + } + } +} + +#[derive(Debug)] +struct Custom { + kind: ErrorKind, + error: String, +} + +/// A list specifying general categories of I/O error. +/// +/// This list is intended to grow over time and it is not recommended to +/// exhaustively match against it. +/// +/// It is used with the [`io::Error`] type. +/// +/// [`io::Error`]: struct.Error.html +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[non_exhaustive] +pub enum ErrorKind { + /// An entity was not found, often a file. + NotFound, + /// The operation lacked the necessary privileges to complete. + PermissionDenied, + /// The connection was refused by the remote server. + ConnectionRefused, + /// The connection was reset by the remote server. + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + ConnectionAborted, + /// The network operation failed because it was not connected yet. + NotConnected, + /// A socket address could not be bound because the address is already in + /// use elsewhere. + AddrInUse, + /// A nonexistent interface was requested or the requested address was not + /// local. + AddrNotAvailable, + /// The operation failed because a pipe was closed. + BrokenPipe, + /// An entity already exists, often a file. + AlreadyExists, + /// The operation needs to block to complete, but the blocking operation was + /// requested to not occur. + WouldBlock, + /// A parameter was incorrect. + InvalidInput, + /// Data not valid for the operation were encountered. + /// + /// Unlike [`InvalidInput`], this typically means that the operation + /// parameters were valid, however the error was caused by malformed + /// input data. + /// + /// For example, a function that reads a file into a string will error with + /// `InvalidData` if the file's contents are not valid UTF-8. + /// + /// [`InvalidInput`]: #variant.InvalidInput + InvalidData, + /// The I/O operation's timeout expired, causing it to be canceled. + TimedOut, + /// An error returned when an operation could not be completed because a + /// call to [`write`] returned [`Ok(0)`]. + /// + /// This typically means that an operation could only succeed if it wrote a + /// particular number of bytes but only a smaller number of bytes could be + /// written. + /// + /// [`write`]: ../../std/io/trait.Write.html#tymethod.write + /// [`Ok(0)`]: ../../std/io/type.Result.html + WriteZero, + /// This operation was interrupted. + /// + /// Interrupted operations can typically be retried. + Interrupted, + /// Any I/O error not part of this list. + Other, + + /// An error returned when an operation could not be completed because an + /// "end of file" was reached prematurely. + /// + /// This typically means that an operation could only succeed if it read a + /// particular number of bytes but only a smaller number of bytes could be + /// read. + UnexpectedEof, +} + +impl ErrorKind { + fn as_str(&self) -> &'static str { + match *self { + ErrorKind::NotFound => "entity not found", + ErrorKind::PermissionDenied => "permission denied", + ErrorKind::ConnectionRefused => "connection refused", + ErrorKind::ConnectionReset => "connection reset", + ErrorKind::ConnectionAborted => "connection aborted", + ErrorKind::NotConnected => "not connected", + ErrorKind::AddrInUse => "address in use", + ErrorKind::AddrNotAvailable => "address not available", + ErrorKind::BrokenPipe => "broken pipe", + ErrorKind::AlreadyExists => "entity already exists", + ErrorKind::WouldBlock => "operation would block", + ErrorKind::InvalidInput => "invalid input parameter", + ErrorKind::InvalidData => "invalid data", + ErrorKind::TimedOut => "timed out", + ErrorKind::WriteZero => "write zero", + ErrorKind::Interrupted => "operation interrupted", + ErrorKind::Other => "other os error", + ErrorKind::UnexpectedEof => "unexpected end of file", + } + } +} diff --git a/src/io/impls.rs b/src/io/impls.rs new file mode 100644 index 0000000000..8b74284081 --- /dev/null +++ b/src/io/impls.rs @@ -0,0 +1,298 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::string::String; +use core::{cmp, fmt, mem}; + +use crate::io::{self, Error, ErrorKind, Initializer, Seek, SeekFrom, Write, prelude::*}; + +impl Read for &mut R { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + unsafe { (**self).initializer() } + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (**self).read_to_end(buf) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (**self).read_to_string(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + (**self).read_exact(buf) + } +} + +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} +impl Seek for &mut S { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } +} + +impl BufRead for &mut B { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} + +impl Read for Box { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + unsafe { (**self).initializer() } + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (**self).read_to_end(buf) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (**self).read_to_string(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + (**self).read_exact(buf) + } +} + +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} + +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: SeekFrom) -> io::Result { + (**self).seek(pos) + } +} + +impl BufRead for Box { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} + +// ============================================================================= +// In-memory buffer implementations + +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +impl Read for &[u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + unsafe { Initializer::nop() } + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if buf.len() > self.len() { + return Err(Error::new( + ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )); + } + let (a, b) = self.split_at(buf.len()); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if buf.len() == 1 { + buf[0] = a[0]; + } else { + buf.copy_from_slice(a); + } + + *self = b; + Ok(()) + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + buf.extend_from_slice(self); + let len = self.len(); + *self = &self[len..]; + Ok(len) + } +} + +impl BufRead for &[u8] { + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, data: &[u8]) -> io::Result { + let amt = cmp::min(data.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&data[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn write_all(&mut self, data: &[u8]) -> io::Result<()> { + if self.write(data)? == data.len() { + Ok(()) + } else { + Err(Error::new( + ErrorKind::WriteZero, + "failed to write whole buffer", + )) + } + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Write is implemented for `Vec` by appending to the vector. +/// The vector will grow as needed. +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.extend_from_slice(buf); + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/src/io/mod.rs b/src/io/mod.rs new file mode 100644 index 0000000000..d4f09e1d2d --- /dev/null +++ b/src/io/mod.rs @@ -0,0 +1,2266 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Traits, helpers, and type definitions for core I/O functionality. +//! +//! The `std::io` module contains a number of common things you'll need +//! when doing input and output. The most core part of this module is +//! the [`Read`] and [`Write`] traits, which provide the +//! most general interface for reading and writing input and output. +//! +//! # Read and Write +//! +//! Because they are traits, [`Read`] and [`Write`] are implemented by a number +//! of other types, and you can implement them for your types too. As such, +//! you'll see a few different types of I/O throughout the documentation in +//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec`]s. For +//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on +//! [`File`]s: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let mut f = File::open("foo.txt")?; +//! let mut buffer = [0; 10]; +//! +//! // read up to 10 bytes +//! f.read(&mut buffer)?; +//! +//! println!("The bytes: {:?}", buffer); +//! Ok(()) +//! } +//! ``` +//! +//! [`Read`] and [`Write`] are so important, implementors of the two traits have a +//! nickname: readers and writers. So you'll sometimes see 'a reader' instead +//! of 'a type that implements the [`Read`] trait'. Much easier! +//! +//! ## Seek and BufRead +//! +//! Beyond that, there are two important traits that are provided: [`Seek`] +//! and [`BufRead`]. Both of these build on top of a reader to control +//! how the reading happens. [`Seek`] lets you control where the next byte is +//! coming from: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::SeekFrom; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let mut f = File::open("foo.txt")?; +//! let mut buffer = [0; 10]; +//! +//! // skip to the last 10 bytes of the file +//! f.seek(SeekFrom::End(-10))?; +//! +//! // read up to 10 bytes +//! f.read(&mut buffer)?; +//! +//! println!("The bytes: {:?}", buffer); +//! Ok(()) +//! } +//! ``` +//! +//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but +//! to show it off, we'll need to talk about buffers in general. Keep reading! +//! +//! ## BufReader and BufWriter +//! +//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be +//! making near-constant calls to the operating system. To help with this, +//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap +//! readers and writers. The wrapper uses a buffer, reducing the number of +//! calls and providing nicer methods for accessing exactly what you want. +//! +//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra +//! methods to any reader: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufReader; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::open("foo.txt")?; +//! let mut reader = BufReader::new(f); +//! let mut buffer = String::new(); +//! +//! // read a line into buffer +//! reader.read_line(&mut buffer)?; +//! +//! println!("{}", buffer); +//! Ok(()) +//! } +//! ``` +//! +//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call +//! to [`write`][`Write::write`]: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufWriter; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::create("foo.txt")?; +//! { +//! let mut writer = BufWriter::new(f); +//! +//! // write a byte to the buffer +//! writer.write(&[42])?; +//! +//! } // the buffer is flushed once writer goes out of scope +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Standard input and output +//! +//! A very common source of input is standard input: +//! +//! ```no_run +//! use std::io; +//! +//! fn main() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input)?; +//! +//! println!("You typed: {}", input.trim()); +//! Ok(()) +//! } +//! ``` +//! +//! Note that you cannot use the [`?` operator] in functions that do not return +//! a [`Result`][`Result`]. Instead, you can call [`.unwrap()`] +//! or `match` on the return value to catch any possible errors: +//! +//! ```no_run +//! use std::io; +//! +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input).unwrap(); +//! ``` +//! +//! And a very common source of output is standard output: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! +//! fn main() -> io::Result<()> { +//! io::stdout().write(&[42])?; +//! Ok(()) +//! } +//! ``` +//! +//! Of course, using [`io::stdout`] directly is less common than something like +//! [`println!`]. +//! +//! ## Iterator types +//! +//! A large number of the structures provided by `std::io` are for various +//! ways of iterating over I/O. For example, [`Lines`] is used to split over +//! lines: +//! +//! ```no_run +//! use std::io; +//! use std::io::prelude::*; +//! use std::io::BufReader; +//! use std::fs::File; +//! +//! fn main() -> io::Result<()> { +//! let f = File::open("foo.txt")?; +//! let reader = BufReader::new(f); +//! +//! for line in reader.lines() { +//! println!("{}", line?); +//! } +//! Ok(()) +//! } +//! ``` +//! +//! ## Functions +//! +//! There are a number of [functions][functions-list] that offer access to various +//! features. For example, we can use three of these functions to copy everything +//! from standard input to standard output: +//! +//! ```no_run +//! use std::io; +//! +//! fn main() -> io::Result<()> { +//! io::copy(&mut io::stdin(), &mut io::stdout())?; +//! Ok(()) +//! } +//! ``` +//! +//! [functions-list]: #functions-1 +//! +//! ## io::Result +//! +//! Last, but certainly not least, is [`io::Result`]. This type is used +//! as the return type of many `std::io` functions that can cause an error, and +//! can be returned from your own functions as well. Many of the examples in this +//! module use the [`?` operator]: +//! +//! ``` +//! use std::io; +//! +//! fn read_input() -> io::Result<()> { +//! let mut input = String::new(); +//! +//! io::stdin().read_line(&mut input)?; +//! +//! println!("You typed: {}", input.trim()); +//! +//! Ok(()) +//! } +//! ``` +//! +//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very +//! common type for functions which don't have a 'real' return value, but do want to +//! return errors if they happen. In this case, the only purpose of this function is +//! to read the line and print it, so we use `()`. +//! +//! ## Platform-specific behavior +//! +//! Many I/O functions throughout the standard library are documented to indicate +//! what various library or syscalls they are delegated to. This is done to help +//! applications both understand what's happening under the hood as well as investigate +//! any possibly unclear semantics. Note, however, that this is informative, not a binding +//! contract. The implementation of many of these functions are subject to change over +//! time and may call fewer or more syscalls/library functions. +//! +//! [`Read`]: trait.Read.html +//! [`Write`]: trait.Write.html +//! [`Seek`]: trait.Seek.html +//! [`BufRead`]: trait.BufRead.html +//! [`File`]: ../fs/struct.File.html +//! [`TcpStream`]: ../net/struct.TcpStream.html +//! [`Vec`]: ../vec/struct.Vec.html +//! [`BufReader`]: struct.BufReader.html +//! [`BufWriter`]: struct.BufWriter.html +//! [`Write::write`]: trait.Write.html#tymethod.write +//! [`io::stdout`]: fn.stdout.html +//! [`println!`]: ../macro.println.html +//! [`Lines`]: struct.Lines.html +//! [`io::Result`]: type.Result.html +//! [`?` operator]: ../../book/first-edition/syntax-index.html +//! [`Read::read`]: trait.Read.html#tymethod.read +//! [`Result`]: ../result/enum.Result.html +//! [`.unwrap()`]: ../result/enum.Result.html#method.unwrap + +pub mod buffered; +pub mod cursor; +pub mod error; +mod impls; +pub mod prelude; + +use crate::out::Out; + +pub use self::{buffered::*, cursor::*, error::*}; + +use self::prelude::*; + +use alloc::string::String; +use core::{cmp, fmt, ptr, str}; + +const DEFAULT_BUF_SIZE: usize = 8 * 1024; + +#[inline] +pub fn last_os_error() -> Error { + Error::last_os_error() +} + +struct Guard<'a> { + buf: &'a mut Vec, + len: usize, +} + +impl<'a> Drop for Guard<'a> { + fn drop(&mut self) { + unsafe { + self.buf.set_len(self.len); + } + } +} + +// A few methods below (read_to_string, read_line) will append data into a +// `String` buffer, but we need to be pretty careful when doing this. The +// implementation will just call `.as_mut_vec()` and then delegate to a +// byte-oriented reading method, but we must ensure that when returning we never +// leave `buf` in a state such that it contains invalid UTF-8 in its bounds. +// +// To this end, we use an RAII guard (to protect against panics) which updates +// the length of the string when it is dropped. This guard initially truncates +// the string to the prior length and only after we've validated that the +// new contents are valid UTF-8 do we allow it to set a longer length. +// +// The unsafety in this function is twofold: +// +// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8 +// checks. +// 2. We're passing a raw buffer to the function `f`, and it is expected that +// the function only *appends* bytes to the buffer. We'll get undefined +// behavior if existing bytes are overwritten to have non-UTF-8 data. +fn append_to_string(buf: &mut String, f: F) -> Result +where + F: FnOnce(&mut Vec) -> Result, +{ + unsafe { + let mut g = Guard { + len: buf.len(), + buf: buf.as_mut_vec(), + }; + let ret = f(g.buf); + if str::from_utf8(&g.buf[g.len..]).is_err() { + ret.and_then(|_| { + Err(Error::new( + ErrorKind::InvalidData, + "stream did not contain valid UTF-8", + )) + }) + } else { + g.len = g.buf.len(); + ret + } + } +} + +// This uses an adaptive system to extend the vector when it fills. We want to +// avoid paying to allocate and zero a huge chunk of memory if the reader only +// has 4 bytes while still making large reads if the reader does have a ton +// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every +// time is 4,500 times (!) slower than a default reservation size of 32 if the +// reader has a very small amount of data to return. +// +// Because we're extending the buffer with uninitialized data for trusted +// readers, we need to make sure to truncate that if any of this panics. +fn read_to_end(r: &mut R, buf: &mut Vec) -> Result { + read_to_end_with_reservation(r, buf, 32) +} + +fn read_to_end_with_reservation( + r: &mut R, + buf: &mut Vec, + reservation_size: usize, +) -> Result { + let start_len = buf.len(); + let mut g = Guard { + len: buf.len(), + buf, + }; + let ret; + loop { + if g.len == g.buf.len() { + unsafe { + g.buf.reserve(reservation_size); + let capacity = g.buf.capacity(); + g.buf.set_len(capacity); + r.initializer().initialize(&mut g.buf[g.len..]); + } + } + + match r.read(&mut g.buf[g.len..]) { + Ok(0) => { + ret = Ok(g.len - start_len); + break; + } + Ok(n) => g.len += n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + ret = Err(e); + break; + } + } + } + + ret +} + +fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { + let mut read = 0; + loop { + let (done, used) = { + let available = match r.fill_buf() { + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + match memchr::memchr(delim, available) { + Some(i) => { + buf.extend_from_slice(&available[..i + 1]); + (true, i + 1) + } + None => { + buf.extend_from_slice(available); + (false, available.len()) + } + } + }; + r.consume(used); + read += used; + if done || used == 0 { + return Ok(read); + } + } +} + +/// The `Read` trait allows for reading bytes from a source. +/// +/// Implementors of the `Read` trait are called 'readers'. +/// +/// Readers are defined by one required method, [`read()`]. Each call to [`read()`] +/// will attempt to pull bytes from this source into a provided buffer. A +/// number of other methods are implemented in terms of [`read()`], giving +/// implementors a number of ways to read bytes while only needing to implement +/// a single method. +/// +/// Readers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Read` +/// trait. +/// +/// Please note that each call to [`read()`] may involve a system call, and +/// therefore, using something that implements [`BufRead`], such as +/// [`BufReader`], will be more efficient. +/// +/// # Examples +/// +/// [`File`]s implement `Read`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// f.read(&mut buffer)?; +/// +/// let mut buffer = vec![0; 10]; +/// // read the whole file +/// f.read_to_end(&mut buffer)?; +/// +/// // read into a String, so that you don't need to do the conversion. +/// let mut buffer = String::new(); +/// f.read_to_string(&mut buffer)?; +/// +/// // and more! See the other methods for more details. +/// Ok(()) +/// } +/// ``` +/// +/// Read from [`&str`] because [`&[u8]`][slice] implements `Read`: +/// +/// ```no_run +/// # use std::io; +/// use std::io::prelude::*; +/// +/// fn main() -> io::Result<()> { +/// let mut b = "This string will be read".as_bytes(); +/// let mut buffer = [0; 10]; +/// +/// // read up to 10 bytes +/// b.read(&mut buffer)?; +/// +/// // etc... it works exactly as a File does! +/// Ok(()) +/// } +/// ``` +/// +/// [`read()`]: trait.Read.html#tymethod.read +/// [`std::io`]: ../../std/io/index.html +/// [`File`]: ../fs/struct.File.html +/// [`BufRead`]: trait.BufRead.html +/// [`BufReader`]: struct.BufReader.html +/// [`&str`]: ../../std/primitive.str.html +/// [slice]: ../../std/primitive.slice.html +pub trait Read { + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + /// + /// This function does not provide any guarantees about whether it blocks + /// waiting for data, but if an object needs to block for a read but cannot + /// it will typically signal this via an [`Err`] return value. + /// + /// If the return value of this method is [`Ok(n)`], then it must be + /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates + /// that the buffer `buf` has been filled in with `n` bytes of data from this + /// source. If `n` is `0`, then it can indicate one of two scenarios: + /// + /// 1. This reader has reached its "end of file" and will likely no longer + /// be able to produce bytes. Note that this does not mean that the + /// reader will *always* no longer be able to produce bytes. + /// 2. The buffer specified was 0 bytes in length. + /// + /// No guarantees are provided about the contents of `buf` when this + /// function is called, implementations cannot rely on any property of the + /// contents of `buf` being true. It is recommended that implementations + /// only write data to `buf` instead of reading its contents. + /// + /// # Errors + /// + /// If this function encounters any form of I/O or other error, an error + /// variant will be returned. If an error is returned then it must be + /// guaranteed that no bytes were read. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read + /// operation should be retried if there is nothing else to do. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`Ok(n)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`File`]: ../fs/struct.File.html + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // read up to 10 bytes + /// f.read(&mut buffer[..])?; + /// Ok(()) + /// } + /// ``` + fn read(&mut self, buf: &mut [u8]) -> Result; + + fn read_out(&mut self, mut out: Out<[u8]>) -> Result { + // XXX: Technically incorrect but hopefully not UB + let slice: &mut [u8] = unsafe { &mut *out.as_mut_ptr() }; + self.read(slice) + } + + /// Determines if this `Read`er can work with buffers of uninitialized + /// memory. + /// + /// The default implementation returns an initializer which will zero + /// buffers. + /// + /// If a `Read`er guarantees that it can work properly with uninitialized + /// memory, it should call [`Initializer::nop()`]. See the documentation for + /// [`Initializer`] for details. + /// + /// The behavior of this method must be independent of the state of the + /// `Read`er - the method only takes `&self` so that it can be used through + /// trait objects. + /// + /// # Safety + /// + /// This method is unsafe because a `Read`er could otherwise return a + /// non-zeroing `Initializer` from another `Read` type without an `unsafe` + /// block. + /// + /// [`Initializer::nop()`]: ../../std/io/struct.Initializer.html#method.nop + /// [`Initializer`]: ../../std/io/struct.Initializer.html + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::zeroing() + } + + /// Read all bytes until EOF in this source, placing them into `buf`. + /// + /// All bytes read from this source will be appended to the specified buffer + /// `buf`. This function will continuously call [`read()`] to append more data to + /// `buf` until [`read()`] returns either [`Ok(0)`] or an error of + /// non-[`ErrorKind::Interrupted`] kind. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If any other read error is encountered then this function immediately + /// returns. Any bytes which have already been read will be appended to + /// `buf`. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`read()`]: trait.Read.html#tymethod.read + /// [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`File`]: ../fs/struct.File.html + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// + /// // read the whole file + /// f.read_to_end(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the [`std::fs::read`] convenience function for reading from a + /// file.) + /// + /// [`std::fs::read`]: ../fs/fn.read.html + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + read_to_end(self, buf) + } + + /// Read all bytes until EOF in this source, appending them to `buf`. + /// + /// If successful, this function returns the number of bytes which were read + /// and appended to `buf`. + /// + /// # Errors + /// + /// If the data in this stream is *not* valid UTF-8 then an error is + /// returned and `buf` is unchanged. + /// + /// See [`read_to_end`][readtoend] for other error semantics. + /// + /// [readtoend]: #method.read_to_end + /// + /// # Examples + /// + /// [`File`][file]s implement `Read`: + /// + /// [file]: ../fs/struct.File.html + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = String::new(); + /// + /// f.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + /// + /// (See also the [`std::fs::read_to_string`] convenience function for + /// reading from a file.) + /// + /// [`std::fs::read_to_string`]: ../fs/fn.read_to_string.html + fn read_to_string(&mut self, buf: &mut String) -> Result { + // Note that we do *not* call `.read_to_end()` here. We are passing + // `&mut Vec` (the raw contents of `buf`) into the `read_to_end` + // method to fill it up. An arbitrary implementation could overwrite the + // entire contents of the vector, not just append to it (which is what + // we are expecting). + // + // To prevent extraneously checking the UTF-8-ness of the entire buffer + // we pass it to our hardcoded `read_to_end` implementation which we + // know is guaranteed to only read data into the end of the buffer. + append_to_string(buf, |b| read_to_end(self, b)) + } + + /// Read the exact number of bytes required to fill `buf`. + /// + /// This function reads as many bytes as necessary to completely fill the + /// specified buffer `buf`. + /// + /// No guarantees are provided about the contents of `buf` when this + /// function is called, implementations cannot rely on any property of the + /// contents of `buf` being true. It is recommended that implementations + /// only write data to `buf` instead of reading its contents. + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: ../fs/struct.File.html + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`ErrorKind::UnexpectedEof`]: ../../std/io/enum.ErrorKind.html#variant.UnexpectedEof + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 10]; + /// + /// // read exactly 10 bytes + /// f.read_exact(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { + while !buf.is_empty() { + match self.read(buf) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(Error::new( + ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )) + } else { + Ok(()) + } + } + + /// Creates a "by reference" adaptor for this instance of `Read`. + /// + /// The returned adaptor also implements `Read` and will simply borrow this + /// current reader. + /// + /// # Examples + /// + /// [`File`][file]s implement `Read`: + /// + /// [file]: ../fs/struct.File.html + /// + /// ```no_run + /// use std::io; + /// use std::io::Read; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = Vec::new(); + /// let mut other_buffer = Vec::new(); + /// + /// { + /// let reference = f.by_ref(); + /// + /// // read at most 5 bytes + /// reference.take(5).read_to_end(&mut buffer)?; + /// + /// } // drop our &mut reference so we can use f again + /// + /// // original file still usable, read the rest + /// f.read_to_end(&mut other_buffer)?; + /// Ok(()) + /// } + /// ``` + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } + + /// Transforms this `Read` instance to an [`Iterator`] over its bytes. + /// + /// The returned type implements [`Iterator`] where the `Item` is + /// [`Result`]`<`[`u8`]`, `[`io::Error`]`>`. + /// The yielded item is [`Ok`] if a byte was successfully read and [`Err`] + /// otherwise. EOF is mapped to returning [`None`] from this iterator. + /// + /// # Examples + /// + /// [`File`][file]s implement `Read`: + /// + /// [file]: ../fs/struct.File.html + /// [`Iterator`]: ../../std/iter/trait.Iterator.html + /// [`Result`]: ../../std/result/enum.Result.html + /// [`io::Error`]: ../../std/io/struct.Error.html + /// [`u8`]: ../../std/primitive.u8.html + /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// + /// for byte in f.bytes() { + /// println!("{}", byte.unwrap()); + /// } + /// Ok(()) + /// } + /// ``` + fn bytes(self) -> Bytes + where + Self: Sized, + { + Bytes { inner: self } + } + + /// Creates an adaptor which will chain this stream with another. + /// + /// The returned `Read` instance will first read all bytes from this object + /// until EOF is encountered. Afterwards the output is equivalent to the + /// output of `next`. + /// + /// # Examples + /// + /// [`File`][file]s implement `Read`: + /// + /// [file]: ../fs/struct.File.html + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f1 = File::open("foo.txt")?; + /// let mut f2 = File::open("bar.txt")?; + /// + /// let mut handle = f1.chain(f2); + /// let mut buffer = String::new(); + /// + /// // read the value into a String. We could use any Read method here, + /// // this is just one example. + /// handle.read_to_string(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + fn chain(self, next: R) -> Chain + where + Self: Sized, + { + Chain { + first: self, + second: next, + done_first: false, + } + } + + /// Creates an adaptor which will read at most `limit` bytes from it. + /// + /// This function returns a new instance of `Read` which will read at most + /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any + /// read errors will not count towards the number of bytes read and future + /// calls to [`read()`] may succeed. + /// + /// # Examples + /// + /// [`File`]s implement `Read`: + /// + /// [`File`]: ../fs/struct.File.html + /// [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`read()`]: trait.Read.html#tymethod.read + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::open("foo.txt")?; + /// let mut buffer = [0; 5]; + /// + /// // read at most five bytes + /// let mut handle = f.take(5); + /// + /// handle.read(&mut buffer)?; + /// Ok(()) + /// } + /// ``` + fn take(self, limit: u64) -> Take + where + Self: Sized, + { + Take { inner: self, limit } + } +} + +fn read_one_byte(reader: &mut dyn Read) -> Option> { + let mut buf = [0]; + loop { + return match reader.read(&mut buf) { + Ok(0) => None, + Ok(..) => Some(Ok(buf[0])), + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => Some(Err(e)), + }; + } +} + +/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it +/// to perform extra ways of reading. +/// +/// For example, reading line-by-line is inefficient without using a buffer, so +/// if you want to read by line, you'll need `BufRead`, which includes a +/// [`read_line`] method as well as a [`lines`] iterator. +/// +/// # Examples +/// +/// A locked standard input implements `BufRead`: +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// +/// let stdin = io::stdin(); +/// for line in stdin.lock().lines() { +/// println!("{}", line.unwrap()); +/// } +/// ``` +/// +/// If you have something that implements [`Read`], you can use the [`BufReader` +/// type][`BufReader`] to turn it into a `BufRead`. +/// +/// For example, [`File`] implements [`Read`], but not `BufRead`. +/// [`BufReader`] to the rescue! +/// +/// [`BufReader`]: struct.BufReader.html +/// [`File`]: ../fs/struct.File.html +/// [`read_line`]: #method.read_line +/// [`lines`]: #method.lines +/// [`Read`]: trait.Read.html +/// +/// ```no_run +/// use std::io::{self, BufReader}; +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> io::Result<()> { +/// let f = File::open("foo.txt")?; +/// let f = BufReader::new(f); +/// +/// for line in f.lines() { +/// println!("{}", line.unwrap()); +/// } +/// +/// Ok(()) +/// } +/// ``` +/// +pub trait BufRead: Read { + /// Returns the contents of the internal buffer, filling it with more data + /// from the inner reader if it is empty. + /// + /// This function is a lower-level call. It needs to be paired with the + /// [`consume`] method to function properly. When calling this + /// method, none of the contents will be "read" in the sense that later + /// calling `read` may return the same contents. As such, [`consume`] must + /// be called with the number of bytes that are consumed from this buffer to + /// ensure that the bytes are never returned twice. + /// + /// [`consume`]: #tymethod.consume + /// + /// An empty buffer returned indicates that the stream has reached EOF. + /// + /// # Errors + /// + /// This function will return an I/O error if the underlying reader was + /// read, but returned an error. + /// + /// # Examples + /// + /// A locked standard input implements `BufRead`: + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// + /// let stdin = io::stdin(); + /// let mut stdin = stdin.lock(); + /// + /// // we can't have two `&mut` references to `stdin`, so use a block + /// // to end the borrow early. + /// let length = { + /// let buffer = stdin.fill_buf().unwrap(); + /// + /// // work with buffer + /// println!("{:?}", buffer); + /// + /// buffer.len() + /// }; + /// + /// // ensure the bytes we worked with aren't returned again later + /// stdin.consume(length); + /// ``` + fn fill_buf(&mut self) -> Result<&[u8]>; + + /// Tells this buffer that `amt` bytes have been consumed from the buffer, + /// so they should no longer be returned in calls to `read`. + /// + /// This function is a lower-level call. It needs to be paired with the + /// [`fill_buf`] method to function properly. This function does + /// not perform any I/O, it simply informs this object that some amount of + /// its buffer, returned from [`fill_buf`], has been consumed and should + /// no longer be returned. As such, this function may do odd things if + /// [`fill_buf`] isn't called before calling it. + /// + /// The `amt` must be `<=` the number of bytes in the buffer returned by + /// [`fill_buf`]. + /// + /// # Examples + /// + /// Since `consume()` is meant to be used with [`fill_buf`], + /// that method's example includes an example of `consume()`. + /// + /// [`fill_buf`]: #tymethod.fill_buf + fn consume(&mut self, amt: usize); + + /// Read all bytes into `buf` until the delimiter `byte` or EOF is reached. + /// + /// This function will read bytes from the underlying stream until the + /// delimiter or EOF is found. Once found, all bytes up to, and including, + /// the delimiter (if found) will be appended to `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Errors + /// + /// This function will ignore all instances of [`ErrorKind::Interrupted`] and + /// will otherwise return any errors returned by [`fill_buf`]. + /// + /// If an I/O error is encountered then all bytes read so far will be + /// present in `buf` and its length will have been adjusted appropriately. + /// + /// [`fill_buf`]: #tymethod.fill_buf + /// [`ErrorKind::Interrupted`]: enum.ErrorKind.html#variant.Interrupted + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the bytes in a byte slice + /// in hyphen delimited segments: + /// + /// [`Cursor`]: struct.Cursor.html + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"lorem-ipsum"); + /// let mut buf = vec![]; + /// + /// // cursor is at 'l' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 6); + /// assert_eq!(buf, b"lorem-"); + /// buf.clear(); + /// + /// // cursor is at 'i' + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 5); + /// assert_eq!(buf, b"ipsum"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_until(b'-', &mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, b""); + /// ``` + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> Result { + read_until(self, byte, buf) + } + + /// Read all bytes until a newline (the 0xA byte) is reached, and append + /// them to the provided buffer. + /// + /// This function will read bytes from the underlying stream until the + /// newline delimiter (the 0xA byte) or EOF is found. Once found, all bytes + /// up to, and including, the delimiter (if found) will be appended to + /// `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// An empty buffer returned indicates that the stream has reached EOF. + /// + /// # Errors + /// + /// This function has the same error semantics as [`read_until`] and will + /// also return an error if the read bytes are not valid UTF-8. If an I/O + /// error is encountered then `buf` may contain some bytes already read in + /// the event that all data read so far was valid UTF-8. + /// + /// [`read_until`]: #method.read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to read all the lines in a byte slice: + /// + /// [`Cursor`]: struct.Cursor.html + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let mut cursor = io::Cursor::new(b"foo\nbar"); + /// let mut buf = String::new(); + /// + /// // cursor is at 'f' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 4); + /// assert_eq!(buf, "foo\n"); + /// buf.clear(); + /// + /// // cursor is at 'b' + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 3); + /// assert_eq!(buf, "bar"); + /// buf.clear(); + /// + /// // cursor is at EOF + /// let num_bytes = cursor.read_line(&mut buf) + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 0); + /// assert_eq!(buf, ""); + /// ``` + fn read_line(&mut self, buf: &mut String) -> Result { + // Note that we are not calling the `.read_until` method here, but + // rather our hardcoded implementation. For more details as to why, see + // the comments in `read_to_end`. + append_to_string(buf, |b| read_until(self, b'\n', b)) + } + + /// Returns an iterator over the contents of this reader split on the byte + /// `byte`. + /// + /// The iterator returned from this function will return instances of + /// [`io::Result`]`<`[`Vec`]`>`. Each vector returned will *not* have + /// the delimiter byte at the end. + /// + /// This function will yield errors whenever [`read_until`] would have + /// also yielded an error. + /// + /// [`io::Result`]: type.Result.html + /// [`Vec`]: ../vec/struct.Vec.html + /// [`read_until`]: #method.read_until + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all hyphen delimited + /// segments in a byte slice + /// + /// [`Cursor`]: struct.Cursor.html + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem-ipsum-dolor"); + /// + /// let mut split_iter = cursor.split(b'-').map(|l| l.unwrap()); + /// assert_eq!(split_iter.next(), Some(b"lorem".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"ipsum".to_vec())); + /// assert_eq!(split_iter.next(), Some(b"dolor".to_vec())); + /// assert_eq!(split_iter.next(), None); + /// ``` + fn split(self, byte: u8) -> Split + where + Self: Sized, + { + Split { + buf: self, + delim: byte, + } + } + + /// Returns an iterator over the lines of this reader. + /// + /// The iterator returned from this function will yield instances of + /// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline + /// byte (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. + /// + /// [`io::Result`]: type.Result.html + /// [`String`]: ../string/struct.String.html + /// + /// # Examples + /// + /// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In + /// this example, we use [`Cursor`] to iterate over all the lines in a byte + /// slice. + /// + /// [`Cursor`]: struct.Cursor.html + /// + /// ``` + /// use std::io::{self, BufRead}; + /// + /// let cursor = io::Cursor::new(b"lorem\nipsum\r\ndolor"); + /// + /// let mut lines_iter = cursor.lines().map(|l| l.unwrap()); + /// assert_eq!(lines_iter.next(), Some(String::from("lorem"))); + /// assert_eq!(lines_iter.next(), Some(String::from("ipsum"))); + /// assert_eq!(lines_iter.next(), Some(String::from("dolor"))); + /// assert_eq!(lines_iter.next(), None); + /// ``` + /// + /// # Errors + /// + /// Each line of the iterator has the same error semantics as [`BufRead::read_line`]. + /// + /// [`BufRead::read_line`]: trait.BufRead.html#method.read_line + fn lines(self) -> Lines + where + Self: Sized, + { + Lines { buf: self } + } +} + +/// A type used to conditionally initialize buffers passed to `Read` methods. +#[derive(Debug)] +pub struct Initializer(bool); + +impl Initializer { + /// Returns a new `Initializer` which will zero out buffers. + #[inline] + pub fn zeroing() -> Initializer { + Initializer(true) + } + + /// Returns a new `Initializer` which will not zero out buffers. + /// + /// # Safety + /// + /// This may only be called by `Read`ers which guarantee that they will not + /// read from buffers passed to `Read` methods, and that the return value of + /// the method accurately reflects the number of bytes that have been + /// written to the head of the buffer. + #[inline] + pub unsafe fn nop() -> Initializer { + Initializer(false) + } + + /// Indicates if a buffer should be initialized. + #[inline] + pub fn should_initialize(&self) -> bool { + self.0 + } + + /// Initializes a buffer if necessary. + #[inline] + pub fn initialize(&self, buf: &mut [u8]) { + if self.should_initialize() { + unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } + } + } +} + +/// Adaptor to chain together two readers. +/// +/// This struct is generally created by calling [`chain`] on a reader. +/// Please see the documentation of [`chain`] for more details. +/// +/// [`chain`]: trait.Read.html#method.chain +pub struct Chain { + first: T, + second: U, + done_first: bool, +} + +impl Chain { + /// Consumes the `Chain`, returning the wrapped readers. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut foo_file = File::open("foo.txt")?; + /// let mut bar_file = File::open("bar.txt")?; + /// + /// let chain = foo_file.chain(bar_file); + /// let (foo_file, bar_file) = chain.into_inner(); + /// Ok(()) + /// } + /// ``` + pub fn into_inner(self) -> (T, U) { + (self.first, self.second) + } + + /// Gets references to the underlying readers in this `Chain`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut foo_file = File::open("foo.txt")?; + /// let mut bar_file = File::open("bar.txt")?; + /// + /// let chain = foo_file.chain(bar_file); + /// let (foo_file, bar_file) = chain.get_ref(); + /// Ok(()) + /// } + /// ``` + pub fn get_ref(&self) -> (&T, &U) { + (&self.first, &self.second) + } + + /// Gets mutable references to the underlying readers in this `Chain`. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying readers as doing so may corrupt the internal state of this + /// `Chain`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut foo_file = File::open("foo.txt")?; + /// let mut bar_file = File::open("bar.txt")?; + /// + /// let mut chain = foo_file.chain(bar_file); + /// let (foo_file, bar_file) = chain.get_mut(); + /// Ok(()) + /// } + /// ``` + pub fn get_mut(&mut self) -> (&mut T, &mut U) { + (&mut self.first, &mut self.second) + } +} + +impl Read for Chain { + fn read(&mut self, buf: &mut [u8]) -> Result { + if !self.done_first { + match self.first.read(buf)? { + 0 if !buf.is_empty() => { + self.done_first = true; + } + n => return Ok(n), + } + } + self.second.read(buf) + } + + unsafe fn initializer(&self) -> Initializer { + let initializer = unsafe { self.first.initializer() }; + if initializer.should_initialize() { + initializer + } else { + unsafe { self.second.initializer() } + } + } +} + +impl BufRead for Chain { + fn fill_buf(&mut self) -> Result<&[u8]> { + if !self.done_first { + match self.first.fill_buf()? { + [] => { + self.done_first = true; + } + buf => return Ok(buf), + } + } + self.second.fill_buf() + } + + fn consume(&mut self, amt: usize) { + if !self.done_first { + self.first.consume(amt) + } else { + self.second.consume(amt) + } + } +} + +/// Reader adaptor which limits the bytes read from an underlying reader. +/// +/// This struct is generally created by calling [`take`] on a reader. +/// Please see the documentation of [`take`] for more details. +/// +/// [`take`]: trait.Read.html#method.take +#[derive(Debug)] +pub struct Take { + inner: T, + limit: u64, +} + +impl Take { + /// Returns the number of bytes that can be read before this instance will + /// return EOF. + /// + /// # Note + /// + /// This instance may reach `EOF` after reading fewer bytes than indicated by + /// this method if the underlying [`Read`] instance reaches EOF. + /// + /// [`Read`]: ../../std/io/trait.Read.html + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = File::open("foo.txt")?; + /// + /// // read at most five bytes + /// let handle = f.take(5); + /// + /// println!("limit: {}", handle.limit()); + /// Ok(()) + /// } + /// ``` + pub fn limit(&self) -> u64 { + self.limit + } + + /// Sets the number of bytes that can be read before this instance will + /// return EOF. This is the same as constructing a new `Take` instance, so + /// the amount of bytes read and the previous limit value don't matter when + /// calling this method. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let f = File::open("foo.txt")?; + /// + /// // read at most five bytes + /// let mut handle = f.take(5); + /// handle.set_limit(10); + /// + /// assert_eq!(handle.limit(), 10); + /// Ok(()) + /// } + /// ``` + pub fn set_limit(&mut self, limit: u64) { + self.limit = limit; + } + + /// Consumes the `Take`, returning the wrapped reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// + /// let mut buffer = [0; 5]; + /// let mut handle = file.take(5); + /// handle.read(&mut buffer)?; + /// + /// let file = handle.into_inner(); + /// Ok(()) + /// } + /// ``` + pub fn into_inner(self) -> T { + self.inner + } + + /// Gets a reference to the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// + /// let mut buffer = [0; 5]; + /// let mut handle = file.take(5); + /// handle.read(&mut buffer)?; + /// + /// let file = handle.get_ref(); + /// Ok(()) + /// } + /// ``` + pub fn get_ref(&self) -> &T { + &self.inner + } + + /// Gets a mutable reference to the underlying reader. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying reader as doing so may corrupt the internal limit of this + /// `Take`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> io::Result<()> { + /// let mut file = File::open("foo.txt")?; + /// + /// let mut buffer = [0; 5]; + /// let mut handle = file.take(5); + /// handle.read(&mut buffer)?; + /// + /// let file = handle.get_mut(); + /// Ok(()) + /// } + /// ``` + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl Read for Take { + fn read(&mut self, buf: &mut [u8]) -> Result { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(0); + } + + let max = cmp::min(buf.len() as u64, self.limit) as usize; + let n = self.inner.read(&mut buf[..max])?; + self.limit -= n as u64; + Ok(n) + } + + unsafe fn initializer(&self) -> Initializer { + unsafe { self.inner.initializer() } + } + + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let reservation_size = cmp::min(self.limit, 32) as usize; + + read_to_end_with_reservation(self, buf, reservation_size) + } +} + +impl BufRead for Take { + fn fill_buf(&mut self) -> Result<&[u8]> { + // Don't call into inner reader at all at EOF because it may still block + if self.limit == 0 { + return Ok(&[]); + } + + let buf = self.inner.fill_buf()?; + let cap = cmp::min(buf.len() as u64, self.limit) as usize; + Ok(&buf[..cap]) + } + + fn consume(&mut self, amt: usize) { + // Don't let callers reset the limit by passing an overlarge value + let amt = cmp::min(amt as u64, self.limit) as usize; + self.limit -= amt as u64; + self.inner.consume(amt); + } +} + +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by two required methods, [`write`] and [`flush`]: +/// +/// * The [`write`] method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// * The [`flush`] method is useful for adaptors and explicit buffers +/// themselves for ensuring that all buffered data has been pushed out to the +/// 'true sink'. +/// +/// Writers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Write` +/// trait. +/// +/// [`write`]: #tymethod.write +/// [`flush`]: #tymethod.flush +/// [`std::io`]: index.html +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let mut buffer = File::create("foo.txt")?; +/// +/// buffer.write(b"some bytes")?; +/// Ok(()) +/// } +/// ``` +pub trait Write { + /// Write a buffer into this object, returning how many bytes were written. + /// + /// This function will attempt to write the entire contents of `buf`, but + /// the entire write may not succeed, or the write may also generate an + /// error. A call to `write` represents *at most one* attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can be indicated through + /// an [`Err`] variant. + /// + /// If the return value is [`Ok(n)`] then it must be guaranteed that + /// `0 <= n <= buf.len()`. A return value of `0` typically means that the + /// underlying object is no longer able to accept bytes and will likely not + /// be able to in the future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the + /// write operation should be retried if there is nothing else to do. + /// + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// [`Ok(n)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::io::BufWriter; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = BufWriter::new(File::create("foo.txt")?); + /// + /// buffer.write(b"some bytes")?; + /// buffer.flush()?; + /// Ok(()) + /// } + /// ``` + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this write. + /// + /// This method will continuously call [`write`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. + /// + /// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`write`]: #tymethod.write + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// buffer.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::new( + ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the + /// [`format_args!`][formatargs] macro, but it is rare that this should + /// explicitly be called. The [`write!`][write] macro should be favored to + /// invoke this method instead. + /// + /// [formatargs]: ../macro.format_args.html + /// [write]: ../macro.write.html + /// + /// This function internally uses the [`write_all`][writeall] method on + /// this trait and hence will continuously write data so long as no errors + /// are received. This also means that partial writes are not indicated in + /// this signature. + /// + /// [writeall]: #method.write_all + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // this call + /// write!(buffer, "{:.*}", 2, 1.234567)?; + /// // turns into this: + /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; + /// Ok(()) + /// } + /// ``` + fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()> { + // Create a shim which translates a Write to a fmt::Write and saves + // off I/O errors. instead of discarding them + struct Adaptor<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl<'a, T: Write + ?Sized> fmt::Write for Adaptor<'a, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adaptor { + inner: self, + error: Ok(()), + }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => { + // check if the error came from the underlying `Write` or not + if output.error.is_err() { + output.error + } else { + Err(Error::new(ErrorKind::Other, "formatter error")) + } + } + } + } + + /// Creates a "by reference" adaptor for this instance of `Write`. + /// + /// The returned adaptor also implements `Write` and will simply borrow this + /// current writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// let reference = buffer.by_ref(); + /// + /// // we can use reference just like our original buffer + /// reference.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. +/// +/// # Examples +/// +/// [`File`][file]s implement `Seek`: +/// +/// [file]: ../fs/struct.File.html +/// +/// ```no_run +/// use std::io; +/// use std::io::prelude::*; +/// use std::fs::File; +/// use std::io::SeekFrom; +/// +/// fn main() -> io::Result<()> { +/// let mut f = File::open("foo.txt")?; +/// +/// // move the cursor 42 bytes from the start of the file +/// f.seek(SeekFrom::Start(42))?; +/// Ok(()) +/// } +/// ``` +pub trait Seek { + /// Seek to an offset, in bytes, in a stream. + /// + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// If the seek operation completed successfully, + /// this method returns the new position from the start of the stream. + /// That position can be used later with [`SeekFrom::Start`]. + /// + /// # Errors + /// + /// Seeking to a negative offset is considered an error. + /// + /// [`SeekFrom::Start`]: enum.SeekFrom.html#variant.Start + fn seek(&mut self, pos: SeekFrom) -> Result; +} + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// It is used by the [`Seek`] trait. +/// +/// [`Seek`]: trait.Seek.html +#[derive(Copy, PartialEq, Eq, Clone, Debug)] +pub enum SeekFrom { + /// Set the offset to the provided number of bytes. + Start(u64), + + /// Set the offset to the size of this object plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + End(i64), + + /// Set the offset to the current position plus the specified number of + /// bytes. + /// + /// It is possible to seek beyond the end of an object, but it's an error to + /// seek before byte 0. + Current(i64), +} + +/// An iterator over `u8` values of a reader. +/// +/// This struct is generally created by calling [`bytes`] on a reader. +/// Please see the documentation of [`bytes`] for more details. +/// +/// [`bytes`]: trait.Read.html#method.bytes +#[derive(Debug)] +pub struct Bytes { + inner: R, +} + +impl Iterator for Bytes { + type Item = Result; + + fn next(&mut self) -> Option> { + read_one_byte(&mut self.inner) + } +} + +/// An iterator over the contents of an instance of `BufRead` split on a +/// particular byte. +/// +/// This struct is generally created by calling [`split`][split] on a +/// `BufRead`. Please see the documentation of `split()` for more details. +/// +/// [split]: trait.BufRead.html#method.split +#[derive(Debug)] +pub struct Split { + buf: B, + delim: u8, +} + +impl Iterator for Split { + type Item = Result>; + + fn next(&mut self) -> Option>> { + let mut buf = Vec::new(); + match self.buf.read_until(self.delim, &mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf[buf.len() - 1] == self.delim { + buf.pop(); + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +/// An iterator over the lines of an instance of `BufRead`. +/// +/// This struct is generally created by calling [`lines`][lines] on a +/// `BufRead`. Please see the documentation of `lines()` for more details. +/// +/// [lines]: trait.BufRead.html#method.lines +#[derive(Debug)] +pub struct Lines { + buf: B, +} + +impl Iterator for Lines { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut buf = String::new(); + match self.buf.read_line(&mut buf) { + Ok(0) => None, + Ok(_n) => { + if buf.ends_with("\n") { + buf.pop(); + if buf.ends_with("\r") { + buf.pop(); + } + } + Some(Ok(buf)) + } + Err(e) => Some(Err(e)), + } + } +} + +#[cfg(test)] +mod tests { + use alloc::string::String; + + use crate::io::{self, cursor::Cursor, prelude::*}; + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn read_until() { + let mut buf = Cursor::new(&b"12"[..]); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2); + assert_eq!(v, b"12"); + + let mut buf = Cursor::new(&b"1233"[..]); + let mut v = Vec::new(); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3); + assert_eq!(v, b"123"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1); + assert_eq!(v, b"3"); + v.truncate(0); + assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0); + assert_eq!(v, []); + } + + #[test] + fn split() { + let buf = Cursor::new(&b"12"[..]); + let mut s = buf.split(b'3'); + assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); + assert!(s.next().is_none()); + + let buf = Cursor::new(&b"1233"[..]); + let mut s = buf.split(b'3'); + assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']); + assert_eq!(s.next().unwrap().unwrap(), vec![]); + assert!(s.next().is_none()); + } + + #[test] + fn read_line() { + let mut buf = Cursor::new(&b"12"[..]); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v).unwrap(), 2); + assert_eq!(v, "12"); + + let mut buf = Cursor::new(&b"12\n\n"[..]); + let mut v = String::new(); + assert_eq!(buf.read_line(&mut v).unwrap(), 3); + assert_eq!(v, "12\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v).unwrap(), 1); + assert_eq!(v, "\n"); + v.truncate(0); + assert_eq!(buf.read_line(&mut v).unwrap(), 0); + assert_eq!(v, ""); + } + + // #[test] + // fn lines() { + // let buf = Cursor::new(&b"12\r"[..]); + // let mut s = buf.lines(); + // assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string()); + // assert!(s.next().is_none()); + + // let buf = Cursor::new(&b"12\r\n\n"[..]); + // let mut s = buf.lines(); + // assert_eq!(s.next().unwrap().unwrap(), "12".to_string()); + // assert_eq!(s.next().unwrap().unwrap(), "".to_string()); + // assert!(s.next().is_none()); + // } + + #[test] + fn read_to_end() { + let mut c = Cursor::new(&b""[..]); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v).unwrap(), 0); + assert_eq!(v, []); + + let mut c = Cursor::new(&b"1"[..]); + let mut v = Vec::new(); + assert_eq!(c.read_to_end(&mut v).unwrap(), 1); + assert_eq!(v, b"1"); + + let cap = 1024 * 1024; + let data = (0..cap).map(|i| (i / 3) as u8).collect::>(); + let mut v = Vec::new(); + let (a, b) = data.split_at(data.len() / 2); + assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len()); + assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len()); + assert_eq!(v, data); + } + + #[test] + fn read_to_string() { + let mut c = Cursor::new(&b""[..]); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v).unwrap(), 0); + assert_eq!(v, ""); + + let mut c = Cursor::new(&b"1"[..]); + let mut v = String::new(); + assert_eq!(c.read_to_string(&mut v).unwrap(), 1); + assert_eq!(v, "1"); + + let mut c = Cursor::new(&b"\xff"[..]); + let mut v = String::new(); + assert!(c.read_to_string(&mut v).is_err()); + } + + #[test] + fn read_exact() { + let mut buf = [0; 4]; + + let mut c = Cursor::new(&b""[..]); + assert_eq!( + c.read_exact(&mut buf).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + + let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..])); + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"1234"); + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"5678"); + assert_eq!( + c.read_exact(&mut buf).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + } + + #[test] + fn read_exact_slice() { + let mut buf = [0; 4]; + + let mut c = &b""[..]; + assert_eq!( + c.read_exact(&mut buf).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + + let mut c = &b"123"[..]; + assert_eq!( + c.read_exact(&mut buf).unwrap_err().kind(), + io::ErrorKind::UnexpectedEof + ); + // make sure the optimized (early returning) method is being used + assert_eq!(&buf, &[0; 4]); + + let mut c = &b"1234"[..]; + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"1234"); + + let mut c = &b"56789"[..]; + c.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, b"5678"); + assert_eq!(c, b"9"); + } + + #[test] + fn take_eof() { + struct R; + + impl Read for R { + fn read(&mut self, _: &mut [u8]) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, "")) + } + } + impl BufRead for R { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Err(io::Error::new(io::ErrorKind::Other, "")) + } + fn consume(&mut self, _amt: usize) {} + } + + let mut buf = [0; 1]; + assert_eq!(0, R.take(0).read(&mut buf).unwrap()); + assert_eq!(b"", R.take(0).fill_buf().unwrap()); + } + + fn cmp_bufread(mut br1: Br1, mut br2: Br2, exp: &[u8]) { + let mut cat = Vec::new(); + loop { + let consume = { + let buf1 = br1.fill_buf().unwrap(); + let buf2 = br2.fill_buf().unwrap(); + let minlen = if buf1.len() < buf2.len() { + buf1.len() + } else { + buf2.len() + }; + assert_eq!(buf1[..minlen], buf2[..minlen]); + cat.extend_from_slice(&buf1[..minlen]); + minlen + }; + if consume == 0 { + break; + } + br1.consume(consume); + br2.consume(consume); + } + assert_eq!(br1.fill_buf().unwrap().len(), 0); + assert_eq!(br2.fill_buf().unwrap().len(), 0); + assert_eq!(&cat[..], &exp[..]) + } + + #[test] + fn chain_bufread() { + let testdata = b"ABCDEFGHIJKL"; + let chain1 = (&testdata[..3]) + .chain(&testdata[3..6]) + .chain(&testdata[6..9]) + .chain(&testdata[9..]); + let chain2 = (&testdata[..4]) + .chain(&testdata[4..8]) + .chain(&testdata[8..]); + cmp_bufread(chain1, chain2, &testdata[..]); + } + + #[test] + fn chain_zero_length_read_is_not_eof() { + let a = b"A"; + let b = b"B"; + let mut s = String::new(); + let mut chain = (&a[..]).chain(&b[..]); + chain.read(&mut []).unwrap(); + chain.read_to_string(&mut s).unwrap(); + assert_eq!("AB", s); + } + + // #[bench] + // #[cfg_attr(target_os = "emscripten", ignore)] + // fn bench_read_to_end(b: &mut test::Bencher) { + // b.iter(|| { + // let mut lr = repeat(1).take(10000000); + // let mut vec = Vec::with_capacity(1024); + // super::read_to_end(&mut lr, &mut vec) + // }); + // } +} diff --git a/src/io/prelude.rs b/src/io/prelude.rs new file mode 100644 index 0000000000..d0240613dd --- /dev/null +++ b/src/io/prelude.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The I/O Prelude +//! +//! The purpose of this module is to alleviate imports of many common I/O traits +//! by adding a glob import to the top of I/O heavy modules: +//! +//! ``` +//! # #![allow(unused_imports)] +//! use std::io::prelude::*; +//! ``` + +pub use super::{BufRead, Read, Seek, Write}; + +pub use alloc::{boxed::Box, vec::Vec}; diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000000..369d2dadc8 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,206 @@ +//! Utilities to help use Rust iterators on C strings. + +use core::{ + iter::{FusedIterator, Iterator}, + marker::PhantomData, + mem::MaybeUninit, + ptr::NonNull, +}; + +use crate::platform::types::*; + +/// A minimal alternative to the `Zero` trait from num-traits, for use in +/// `NulTerminated`. +/// +/// May be replaced with the one from num-traits at a later time if so +/// desired. +pub unsafe trait Zero { + fn is_zero(&self) -> bool; +} + +unsafe impl Zero for c_char { + fn is_zero(&self) -> bool { + self == &0 + } +} + +unsafe impl Zero for wchar_t { + fn is_zero(&self) -> bool { + self == &0 + } +} + +/// An iterator over a nul-terminated buffer. +/// +/// This is intended to allow safe, ergonomic iteration over C-style byte and +/// wide strings without first having to read through the string and construct +/// a slice. Assuming the safety requirements are upheld when constructing the +/// iterator, it allows for string iteration in safe Rust. +pub struct NulTerminated<'a, T: Zero> { + ptr: NonNull, + phantom: PhantomData<&'a T>, +} + +impl<'a, T: Zero> Iterator for NulTerminated<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + // SAFETY: the caller is required to ensure a valid pointer to a + // 0-terminated buffer is provided, and the zero-check below ensures + // that iteration and pointer increments will stop in time. + let val_ref = unsafe { self.ptr.as_ref() }; + if val_ref.is_zero() { + None + } else { + // SAFETY: the caller is required to provide a 0-terminated + // buffer, and this point will only be reached if the next element + // is at most the terminating 0. + self.ptr = unsafe { self.ptr.add(1) }; + Some(val_ref) + } + } +} + +impl<'a, T: Zero> NulTerminated<'a, T> { + /// Constructs a new iterator, starting at `ptr`, yielding elements of + /// type `&T` up to (but not including) the terminating nul. + /// + /// The iterator returns `None` after the terminating nul has been + /// encountered. + /// + /// # Safety + /// The provided pointer must be a valid pointer to a buffer of contiguous + /// elements of type `T`, and the value 0 must be present within the + /// buffer at or after `ptr` (not necessarily at the end). The buffer must + /// not be written to for the lifetime of the iterator. + pub unsafe fn new(ptr: *const T) -> Option { + Some(NulTerminated { + // NonNull can only wrap only *mut pointers... + ptr: NonNull::new(ptr.cast_mut())?, + phantom: PhantomData, + }) + } +} + +// Once the terminating nul has been encountered, the pointer will not advance +// further and the iterator will thus keep returning None. +impl<'a, T: Zero> FusedIterator for NulTerminated<'a, T> {} + +/// An iterator over a nul-terminated buffer, including the terminating nul. +/// +/// Similar to [`NulTerminated`], but includes the terminating nul. +pub struct NulTerminatedInclusive<'a, T: Zero> { + ptr_opt: Option>, + phantom: PhantomData<&'a T>, +} + +impl<'a, T: Zero> Iterator for NulTerminatedInclusive<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if let Some(old_ptr) = self.ptr_opt { + // SAFETY: the caller is required to ensure a valid pointer to a + // 0-terminated buffer is provided, and the zero-check below + // ensures that iteration and pointer increments will stop in + // time. + let val_ref = unsafe { old_ptr.as_ref() }; + self.ptr_opt = if val_ref.is_zero() { + None + } else { + // SAFETY: if a terminating nul value has been encountered, + // this will not be called + Some(unsafe { old_ptr.add(1) }) + }; + Some(val_ref) + } else { + None + } + } +} + +impl<'a, T: Zero> NulTerminatedInclusive<'a, T> { + /// Constructs a new iterator, starting at `ptr`, yielding elements of + /// type `&T` up to and including the terminating nul. + /// + /// The iterator returns `None` after the terminating nul has been + /// encountered. + /// + /// # Safety + /// The provided pointer must be a valid pointer to a buffer of contiguous + /// elements of type `T`, and the value 0 must be present within the + /// buffer at or after `ptr` (not necessarily at the end). The buffer must + /// not be written to for the lifetime of the iterator. + pub unsafe fn new(ptr: *const T) -> Self { + NulTerminatedInclusive { + // NonNull can only wrap only *mut pointers... + ptr_opt: NonNull::new(ptr.cast_mut()), + phantom: PhantomData, + } + } +} + +// Once the terminating nul has been encountered, the internal Option will be +// set to None, ensuring that we will keep returning None. +impl<'a, T: Zero> FusedIterator for NulTerminatedInclusive<'a, T> {} + +/// A zipped iterator mapping an input iterator to an "out" pointer. +/// +/// This is intended to allow safe, iterative writing to an "out pointer". +/// Special care needs to be taken to avoid creating references past the end +/// of the output buffer, thus the output is zipped with an "input" iterator +/// to ensure up-front control of the range of memory on which we create +/// references. +pub struct SrcDstPtrIter<'a, I: Iterator, U: Copy> { + src_iter: I, + dst_ptr: *mut U, + phantom: PhantomData<&'a mut U>, +} + +impl<'a, I: Iterator, U: Copy> Iterator for SrcDstPtrIter<'a, I, U> { + type Item = (I::Item, &'a mut MaybeUninit); + + fn next(&mut self) -> Option { + if let Some(src_item) = self.src_iter.next() { + let old_dst_ptr = self.dst_ptr; + + // SAFETY: due to the caller requirements on `I` upon + // construction, the new pointer here may be either valid to turn + // into a reference or "one past the end". The latter is okay as + // long as it is only represented as a raw pointer. + self.dst_ptr = unsafe { self.dst_ptr.add(1) }; + + // SAFETY: self.dst_ptr may point "one past the end", but the + // caller is required upon construction to ensure that `I` does + // not over-iterate, and thus old_dst_ptr is always okay to + // dereference. + let out_mut_ref = unsafe { old_dst_ptr.as_uninit_mut() }.unwrap(); + + Some((src_item, out_mut_ref)) + } else { + None + } + } +} + +impl<'a, I: Iterator, U: Copy> SrcDstPtrIter<'a, I, U> { + /// Constructs a new iterator of "zipped" input and output. + /// + /// The caller must provide an "input" iterator `I` and an "out pointer" + /// `ptr`. Assuming `I` has item type `T`, the new iterator will have + /// `type Item = (T, &mut MaybeUninit)`. + /// + /// # Safety + /// `ptr` must be a valid pointer to a writable buffer of contiguous (but + /// possibly uninitialized) elements of type `U`. The caller must ensure + /// that `I` does not return `Some` any more times than there are elements + /// in the output buffer. The caller must ensure that the iterator has + /// exclusive access to that buffer for the entire lifetime of the + /// iterator. + pub unsafe fn new(iter: I, ptr: *mut U) -> Self { + SrcDstPtrIter { + src_iter: iter, + dst_ptr: ptr, + phantom: PhantomData, + } + } +} diff --git a/src/ld_so/access.rs b/src/ld_so/access.rs new file mode 100644 index 0000000000..267e9e897f --- /dev/null +++ b/src/ld_so/access.rs @@ -0,0 +1,10 @@ +use crate::{ + c_str::{CStr, CString}, + error::Errno, + platform::{Pal, Sys, types::*}, +}; + +pub fn accessible(path: &str, mode: c_int) -> Result<(), Errno> { + let path_c = CString::new(path.as_bytes()).unwrap(); + unsafe { Sys::access(CStr::from_ptr(path_c.as_ptr()), mode) } +} diff --git a/src/ld_so/callbacks.rs b/src/ld_so/callbacks.rs new file mode 100644 index 0000000000..5b322ef440 --- /dev/null +++ b/src/ld_so/callbacks.rs @@ -0,0 +1,39 @@ +use super::linker::{Linker, ObjectHandle, Resolve, Result, ScopeKind}; +use crate::platform::types::c_void; +use alloc::boxed::Box; + +pub struct LinkerCallbacks { + pub unload: Box, + pub load_library: + Box, Resolve, ScopeKind, bool) -> Result>, + pub get_sym: Box, &str) -> Option<*mut c_void>>, +} + +impl LinkerCallbacks { + #[allow(clippy::new_without_default)] + pub fn new() -> LinkerCallbacks { + LinkerCallbacks { + unload: Box::new(unload), + load_library: Box::new(load_library), + get_sym: Box::new(get_sym), + } + } +} + +fn unload(linker: &mut Linker, handle: ObjectHandle) { + linker.unload(handle) +} + +fn load_library( + linker: &mut Linker, + name: Option<&str>, + resolve: Resolve, + scope: ScopeKind, + noload: bool, +) -> Result { + linker.load_library(name, resolve, scope, noload) +} + +fn get_sym(linker: &Linker, handle: Option, name: &str) -> Option<*mut c_void> { + linker.get_sym(handle, name) +} diff --git a/src/ld_so/debug.rs b/src/ld_so/debug.rs new file mode 100644 index 0000000000..784fb339e3 --- /dev/null +++ b/src/ld_so/debug.rs @@ -0,0 +1,138 @@ +use crate::{c_str::CString, platform::types::*}; +use alloc::boxed::Box; +use core::ptr; + +#[repr(C)] +pub enum RTLDState { + /// Mapping change is complete. + RtConsistent, + /// Beginning to add a new object. + RtAdd, + /// Beginning to remove an object mapping. + RtDelete, +} + +/// Data structure for sharing debugging information from the +/// run-time dynamic linker for loaded ELF shared objects. +#[repr(C)] +pub struct RTLDDebug { + /// Version number for this protocol. + r_version: i32, + /// Head of the chain of loaded objects. + r_map: *mut LinkMap, + //struct link_map *r_map; + /// This is the address of a function internal to the run-time linker, + /// that will always be called when the linker begins to map in a + /// library or unmap it, and again when the mapping change is complete. + /// The debugger can set a breakpoint at this address if it wants to + /// notice shared object mapping changes. + pub r_brk: extern "C" fn(), + + /// This state value describes the mapping change taking place when + /// the `r_brk' address is called. + pub state: RTLDState, + + /// Base address the linker is loaded at. + pub r_ldbase: usize, +} + +impl RTLDDebug { + const NEW: Self = RTLDDebug { + r_version: 1, + r_map: ptr::null_mut::(), + r_brk: _dl_debug_state, + state: RTLDState::RtConsistent, + r_ldbase: 0, + }; + + pub fn insert(&mut self, l_addr: usize, name: &str, l_ld: usize) { + if self.r_map.is_null() { + self.r_map = LinkMap::new_with_args(l_addr, name, l_ld); + } else { + unsafe { (*self.r_map).add_object(l_addr, name, l_ld) }; + } + } + pub fn insert_first(&mut self, l_addr: usize, name: &str, l_ld: usize) { + if self.r_map.is_null() { + self.r_map = LinkMap::new_with_args(l_addr, name, l_ld); + } else { + let tmp = self.r_map; + self.r_map = LinkMap::new_with_args(l_addr, name, l_ld); + unsafe { (*self.r_map).link(&mut *tmp) }; + } + } +} + +/// SAFETY: safe as long as caller wraps the instance in a mutex, +/// or similar structure that guarantees exclusive mutable access. +/// Separate instances must not contain pointers to the same LinkMap instance. +unsafe impl Send for RTLDDebug {} +/// SAFETY: safe as long as caller wraps the instance in a mutex, +/// or similar structure that guarantees exclusive mutable access. +/// Separate instances must not contain pointers to the same LinkMap instance. +unsafe impl Sync for RTLDDebug {} + +#[repr(C)] +struct LinkMap { + /* These members are part of the protocol with the debugger. + This is the same format used in SVR4. */ + /// Difference between the address in the ELF + /// file and the addresses in memory. + l_addr: usize, + /// Absolute file name object was found in. + l_name: *const c_char, + /// Dynamic section of the shared object. + l_ld: usize, + l_next: *mut LinkMap, + l_prev: *mut LinkMap, +} + +impl LinkMap { + fn new() -> *mut Self { + let map = Box::new(LinkMap { + l_addr: 0, + l_name: ptr::null(), + l_ld: 0, + l_next: ptr::null_mut(), + l_prev: ptr::null_mut(), + }); + Box::into_raw(map) + } + fn link(&mut self, map: &mut LinkMap) { + map.l_prev = ptr::from_mut::(self); + self.l_next = ptr::from_mut::(map); + } + fn new_with_args(l_addr: usize, name: &str, l_ld: usize) -> *mut Self { + let map = LinkMap::new(); + unsafe { + (*map).l_addr = l_addr; + (*map).l_ld = l_ld; + let c_name = CString::new(name).unwrap(); + (*map).l_name = c_name.into_raw().cast_const(); + } + map + } + + fn add_object(&mut self, l_addr: usize, name: &str, l_ld: usize) { + let node = LinkMap::new_with_args(l_addr, name, l_ld); + let mut last = self; + while !last.l_next.is_null() { + last = unsafe { last.l_next.as_mut() }.unwrap(); + } + unsafe { + (*node).l_prev = last; + last.l_next = node; + } + } +} + +/* + * Gdb may be looking for this fuction with that exact name and set + * break point there + */ +#[linkage = "weak"] +#[unsafe(no_mangle)] +pub extern "C" fn _dl_debug_state() {} + +#[unsafe(no_mangle)] +pub static _r_debug: spin::Mutex = spin::Mutex::new(RTLDDebug::NEW); diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs new file mode 100644 index 0000000000..afb4c7c1e6 --- /dev/null +++ b/src/ld_so/dso.rs @@ -0,0 +1,1310 @@ +//! See: +//! * +//! * + +use object::{ + NativeEndian, Object, StringTable, SymbolIndex, elf, + read::elf::{ + Dyn as _, GnuHashTable, HashTable as SysVHashTable, ProgramHeader as _, Rel as _, + Rela as _, Sym as _, Version, VersionTable, + }, +}; + +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +use super::tcb::Tcb; +use super::{ + debug::{_r_debug, RTLDDebug}, + linker::{__plt_resolve_trampoline, GLOBAL_SCOPE, Resolve, Scope, Symbol}, + tcb::Master, +}; +use crate::{ + header::{dl_tls::__tls_get_addr, sys_mman}, + platform::{Pal, Sys, types::c_void}, +}; +use alloc::{ + boxed::Box, + string::{String, ToString}, + sync::Arc, + vec::Vec, +}; +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +use core::mem::offset_of; +use core::{ + ffi::c_char, + mem::size_of, + ptr::{self, NonNull}, + slice, + sync::atomic::{AtomicBool, Ordering}, +}; + +pub const CHAR_BITS: usize = c_char::BITS as usize; +pub type Relr = usize; + +#[cfg(target_pointer_width = "32")] +mod shim { + use object::{NativeEndian, elf::*, read::elf::ElfFile32}; + pub type Dyn = Dyn32; + pub type Rel = Rel32; + pub type Rela = Rela32; + pub type Sym = Sym32; + pub type FileHeader = FileHeader32; + pub type ProgramHeader = ProgramHeader32; + pub type ElfFile<'a> = ElfFile32<'a, NativeEndian>; +} + +#[cfg(target_pointer_width = "64")] +mod shim { + use object::{NativeEndian, elf::*, read::elf::ElfFile64}; + pub type Dyn = Dyn64; + pub type Rel = Rel64; + pub type Rela = Rela64; + pub type Sym = Sym64; + pub type FileHeader = FileHeader64; + pub type ProgramHeader = ProgramHeader64; + pub type ElfFile<'a> = ElfFile64<'a, NativeEndian>; +} + +pub use shim::*; + +// TODO: missing from the `object` crate +pub const DT_RELRSZ: u32 = 35; +pub const DT_RELR: u32 = 36; +pub const DT_RELRENT: u32 = 37; + +/// Undefined Symbol Index +pub const STN_UNDEF: SymbolIndex = SymbolIndex(0); + +enum HashTable<'a> { + Gnu(GnuHashTable<'a, FileHeader>), + Sysv(SysVHashTable<'a, FileHeader>), +} + +impl<'a> HashTable<'a> { + /// Use the hash table to find the symbol table entry with the given name, hash, and version. + #[inline] + pub fn find( + &self, + name: &str, + version: Option<&Version<'_>>, + symbols: &'a [Sym], + strings: StringTable<'a>, + versions: &VersionTable<'a, FileHeader>, + ) -> Option<(SymbolIndex, &'a Sym)> { + let name = name.as_bytes(); + + match self { + Self::Gnu(hash_table) => { + let hash = elf::gnu_hash(name); + hash_table.find( + NativeEndian, + name, + hash, + version, + symbols, + strings, + versions, + ) + } + + Self::Sysv(hash_table) => { + let hash = elf::hash(name); + hash_table.find( + NativeEndian, + name, + hash, + version, + symbols, + strings, + versions, + ) + } + } + } + + fn symbol_table_length(&self) -> usize { + match self { + Self::Gnu(hash_table) => hash_table + .symbol_table_length(NativeEndian) + .expect("empty GNU symbol hash table") + as usize, + Self::Sysv(hash_table) => hash_table.symbol_table_length() as usize, + } + } +} + +type InitFn = unsafe extern "C" fn(); + +pub(super) struct Dynamic<'data> { + runpath: Option, + got: Option>, + needed: Vec<&'data str>, + pub(super) jmprel: usize, + hash_table: HashTable<'data>, + pub(super) dynstrtab: StringTable<'data>, + soname: Option<&'data str>, + init_array: &'data [unsafe extern "C" fn()], + fini_array: &'data [unsafe extern "C" fn()], + rela: &'data [Rela], + relr: &'data [Relr], + rel: &'data [Rel], + symbols: &'data [Sym], + explicit_addend: bool, + pltrelsz: usize, +} + +impl<'data> Dynamic<'data> { + pub fn symbol(&self, index: SymbolIndex) -> Option<&'data Sym> { + // Symbol table entry for index 0 is reserved. + assert!(index != SymbolIndex(0)); + self.symbols.get(index.0) + } + + fn symbol_name(&self, index: SymbolIndex) -> Option<&'data str> { + let sym = self.symbol(index)?; + let name = sym.name(NativeEndian, self.dynstrtab).ok()?; + Some(core::str::from_utf8(name).expect("non UTF-8 ELF symbol name")) + } + + fn static_relocations(&self) -> impl Iterator + '_ { + self.rela + .iter() + .map(Relocation::from) + .chain(self.rel.iter().map(Relocation::from)) + } +} + +unsafe impl Send for Dynamic<'_> {} +unsafe impl Sync for Dynamic<'_> {} + +#[derive(Debug)] +pub(super) struct Relocation { + pub(super) offset: usize, + pub(super) addend: Option, + pub(super) sym: SymbolIndex, + pub(super) kind: RelocationKind, +} + +#[cfg(target_pointer_width = "32")] +impl From<&Rela> for Relocation { + fn from(reloc: &Rela) -> Self { + Self { + offset: reloc.r_offset(NativeEndian) as usize, + addend: Some(reloc.r_addend(NativeEndian) as usize), + sym: SymbolIndex(reloc.r_sym(NativeEndian) as usize), + kind: RelocationKind::new(reloc.r_type(NativeEndian)), + } + } +} + +#[cfg(target_pointer_width = "64")] +impl From<&Rela> for Relocation { + fn from(reloc: &Rela) -> Self { + let is_mips64el = cfg!(all(target_arch = "mips64", target_endian = "little")); + Self { + offset: reloc.r_offset(NativeEndian) as usize, + addend: Some(reloc.r_addend(NativeEndian) as usize), + sym: SymbolIndex(reloc.r_sym(NativeEndian, is_mips64el) as usize), + kind: RelocationKind::new(reloc.r_type(NativeEndian, is_mips64el)), + } + } +} + +impl From<&Rel> for Relocation { + fn from(reloc: &Rel) -> Self { + Self { + offset: reloc.r_offset(NativeEndian) as usize, + addend: None, + sym: SymbolIndex(reloc.r_sym(NativeEndian) as usize), + kind: RelocationKind::new(reloc.r_type(NativeEndian)), + } + } +} + +// This is matched up to REL_* constants used by musl for ease of comparison +#[allow(dead_code)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum RelocationKind { + COPY, + DTPMOD, + DTPOFF, + GOT, + IRELATIVE, + OFFSET, + PLT, + RELATIVE, + SYMBOLIC, + TLSDESC, + TPOFF, + UNKNOWN(u32), +} + +impl RelocationKind { + #[cfg(target_arch = "aarch64")] + pub fn new(kind: u32) -> Self { + //WARNING: Only use R_AARCH64_* constants here! + match kind { + elf::R_AARCH64_COPY => Self::COPY, + elf::R_AARCH64_TLS_DTPMOD => Self::DTPMOD, + elf::R_AARCH64_TLS_DTPREL => Self::DTPOFF, + elf::R_AARCH64_GLOB_DAT => Self::GOT, + elf::R_AARCH64_IRELATIVE => Self::IRELATIVE, + elf::R_AARCH64_JUMP_SLOT => Self::PLT, + elf::R_AARCH64_RELATIVE => Self::RELATIVE, + elf::R_AARCH64_ABS64 => Self::SYMBOLIC, + elf::R_AARCH64_TLSDESC => Self::TLSDESC, + elf::R_AARCH64_TLS_TPREL => Self::TPOFF, + _ => Self::UNKNOWN(kind), + } + } + + #[cfg(target_arch = "riscv64")] + pub fn new(kind: u32) -> Self { + //WARNING: Only use R_RISCV_* constants here! + match kind { + elf::R_RISCV_COPY => Self::COPY, + elf::R_RISCV_TLS_DTPMOD64 => Self::DTPMOD, + elf::R_RISCV_TLS_DTPREL64 => Self::DTPOFF, + elf::R_RISCV_IRELATIVE => Self::IRELATIVE, + elf::R_RISCV_JUMP_SLOT => Self::PLT, + elf::R_RISCV_RELATIVE => Self::RELATIVE, + elf::R_RISCV_64 => Self::SYMBOLIC, + //TODO: not defined, should be 12: elf::R_RISCV_TLSDESC => Self::TLSDESC, + elf::R_RISCV_TLS_TPREL64 => Self::TPOFF, + _ => Self::UNKNOWN(kind), + } + } + + #[cfg(target_arch = "x86")] + pub fn new(kind: u32) -> Self { + //WARNING: Only use R_386_* constants here! + match kind { + elf::R_386_COPY => Self::COPY, + elf::R_386_TLS_DTPMOD32 => Self::DTPMOD, + elf::R_386_TLS_DTPOFF32 => Self::DTPOFF, + elf::R_386_GLOB_DAT => Self::GOT, + elf::R_386_IRELATIVE => Self::IRELATIVE, + elf::R_386_JMP_SLOT => Self::PLT, + elf::R_386_PC32 => Self::OFFSET, + elf::R_386_RELATIVE => Self::RELATIVE, + elf::R_386_32 => Self::SYMBOLIC, + elf::R_386_TLS_DESC => Self::TLSDESC, + elf::R_386_TLS_TPOFF => Self::TPOFF, + _ => Self::UNKNOWN(kind), + } + } + + #[cfg(target_arch = "x86_64")] + pub fn new(kind: u32) -> Self { + //WARNING: Only use R_X86_64_* constants here! + match kind { + elf::R_X86_64_COPY => Self::COPY, + elf::R_X86_64_DTPMOD64 => Self::DTPMOD, + elf::R_X86_64_DTPOFF64 => Self::DTPOFF, + elf::R_X86_64_GLOB_DAT => Self::GOT, + elf::R_X86_64_IRELATIVE => Self::IRELATIVE, + elf::R_X86_64_JUMP_SLOT => Self::PLT, + elf::R_X86_64_RELATIVE => Self::RELATIVE, + elf::R_X86_64_64 => Self::SYMBOLIC, + elf::R_X86_64_TLSDESC => Self::TLSDESC, + elf::R_X86_64_TPOFF64 => Self::TPOFF, + _ => Self::UNKNOWN(kind), + } + } +} + +#[derive(Debug, PartialEq)] +#[repr(u8)] +pub enum SymbolBinding { + /// Global symbols are visible to all object files being combined. One + /// file's definition of a global symbol will satisfy another file's + /// undefined reference to the same global symbol. + Global = elf::STB_GLOBAL, + /// Weak symbols resemble global symbols, but their definitions have lower + /// precedence. + Weak = elf::STB_WEAK, +} + +impl SymbolBinding { + #[inline] + pub fn is_global(&self) -> bool { + matches!(self, Self::Global) + } +} + +/// Use to represent a library as well as all the symbols that is loaded withen it. +pub struct DSO { + pub name: String, + pub id: usize, + pub dlopened: bool, + pub entry_point: usize, + /// Loaded library in-memory data + pub mmap: &'static [u8], + pub tls_module_id: usize, + pub tls_offset: usize, + + pub(super) dynamic: Dynamic<'static>, + + pub scope: spin::Once, + /// Position Independent Executable. + pub pie: bool, + + /// Whether this DSO *and* its dependencies have been successfully loaded. + is_ready: AtomicBool, +} + +impl DSO { + pub fn new( + path: &str, + data: &[u8], + base_addr: Option, + dlopened: bool, + id: usize, + tls_module_id: usize, + tls_offset: usize, + ) -> Result<(DSO, Option, Vec), String> { + let elf = ElfFile::parse(data).map_err(|err| err.to_string())?; + let (mmap, tcb_master, dynamic) = + DSO::mmap_and_copy(path, &elf, data, base_addr, tls_offset)?; + + let name = match dynamic.soname { + Some(soname) => soname.to_string(), + _ => basename(path), + }; + let tls_offset = match tcb_master { + Some(ref master) => master.offset, + _ => 0, + }; + let entry_point = if is_pie_enabled(&elf) { + mmap.as_ptr() as usize + elf.entry() as usize + } else { + elf.entry() as usize + }; + + let dso = DSO { + name, + id, + dlopened, + entry_point, + mmap, + tls_module_id: if tcb_master.is_some() { + tls_module_id + } else { + 0 + }, + tls_offset, + + pie: is_pie_enabled(&elf), + dynamic, + scope: spin::Once::new(), + is_ready: AtomicBool::new(false), + }; + + Ok((dso, tcb_master, elf.elf_program_headers().to_vec())) + } + + #[inline] + pub fn mark_ready(&self) { + self.is_ready.store(true, Ordering::SeqCst); + } + + #[inline] + pub fn scope(&self) -> &Scope { + self.scope.get().expect("scope not initialized") + } + + /// Global Offset Table + #[inline] + pub fn got(&self) -> Option> { + self.dynamic.got + } + + #[inline] + pub fn runpath(&self) -> Option<&String> { + self.dynamic.runpath.as_ref() + } + + #[inline] + pub fn dependencies(&self) -> &[&str] { + &self.dynamic.needed + } + + pub fn get_sym<'a>(&self, name: &'a str) -> Option<(Symbol<'a>, SymbolBinding)> { + let (_, sym) = self.dynamic.hash_table.find( + name, + None, + self.dynamic.symbols, + self.dynamic.dynstrtab, + &VersionTable::default(), + )?; + + if sym.st_shndx(NativeEndian) == elf::SHN_UNDEF { + return None; + } + + Some(( + Symbol { + name, + base: if self.pie { + self.mmap.as_ptr() as usize + } else { + 0 + }, + value: sym.st_value(NativeEndian) as usize, + size: sym.st_size(NativeEndian) as usize, + sym_type: sym.st_type(), + }, + // TODO(andypython): move this into [`Symbol`] + match sym.st_bind() { + elf::STB_GLOBAL => SymbolBinding::Global, + elf::STB_WEAK => SymbolBinding::Weak, + bind => unreachable!("get_sym bind {bind}"), + }, + )) + } + + pub fn run_init(&self) { + for f in self.dynamic.init_array { + unsafe { f() } + } + } + + pub fn run_fini(&self) { + for f in self.dynamic.fini_array.iter().rev() { + unsafe { f() } + } + } + + fn mmap_and_copy<'a>( + path: &str, + elf: &ElfFile<'a>, + data: &'a [u8], + base_addr: Option, + tls_offset: usize, + ) -> Result<(&'static [u8], Option, Dynamic<'static>), String> { + let endian = elf.endian(); + log::trace!("# {}", path); + // data for struct LinkMap + let mut l_ld = 0; + // Calculate virtual memory bounds + let bounds = { + let mut bounds_opt: Option<(usize, usize)> = None; + for ph in elf.elf_program_headers() { + let voff = ph.p_vaddr(endian) % ph.p_align(endian); + let vaddr = (ph.p_vaddr(endian) - voff) as usize; + let vsize = ((ph.p_memsz(endian) + voff) as usize) + .next_multiple_of(ph.p_align(endian) as usize); + + match ph.p_type(endian) { + elf::PT_DYNAMIC => { + l_ld = ph.p_vaddr(endian); + } + elf::PT_LOAD => { + log::trace!(" load {:#x}, {:#x}: {:x?}", vaddr, vsize, ph); + if let Some(ref mut bounds) = bounds_opt { + if vaddr < bounds.0 { + bounds.0 = vaddr; + } + if vaddr + vsize > bounds.1 { + bounds.1 = vaddr + vsize; + } + } else { + bounds_opt = Some((vaddr, vaddr + vsize)); + } + } + _ => (), + } + } + bounds_opt.ok_or_else(|| "Unable to find PT_LOAD section".to_string())? + }; + log::trace!(" bounds {:#x}, {:#x}", bounds.0, bounds.1); + // Allocate memory + let mmap = unsafe { + if let Some(addr) = base_addr { + let size = if is_pie_enabled(elf) { + bounds.1 + } else { + bounds.1 - bounds.0 + }; + _r_debug + .lock() + .insert_first(addr + bounds.0, path, addr + l_ld as usize); + slice::from_raw_parts_mut((addr + bounds.0) as *mut u8, size) + } else { + let (start, end) = bounds; + let size = end - start; + let mut flags = sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE; + if start != 0 { + flags |= sys_mman::MAP_FIXED_NOREPLACE; + } + log::trace!(" mmap({:#x}, {:x}, {:x})", start, size, flags); + let ptr = Sys::mmap( + start as *mut c_void, + size, + //TODO: Make it possible to not specify PROT_EXEC on Redox + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + flags, + -1, + 0, + ) + .map_err(|e| format!("failed to map {}. errno: {}", path, e.0))?; + + if !(start as *mut c_void).is_null() { + assert_eq!( + ptr, start as *mut c_void, + "mmap must always map on the destination we requested" + ); + } + log::trace!(" = {:p}", ptr); + _r_debug + .lock() + .insert(ptr as usize, path, ptr as usize + l_ld as usize); + slice::from_raw_parts_mut(ptr.cast::(), size) + } + }; + + let skip_load_segment_copy = base_addr.is_some(); + let mut tcb_master = None; + + // Copy data + let mut dynamic = None; + for ph in elf.elf_program_headers() { + match ph.p_type(endian) { + elf::PT_LOAD => { + if skip_load_segment_copy { + continue; + } + let obj_data = { + let (offset, size) = ph.file_range(endian); + let offset = offset as usize; + let range = offset..(offset + size as usize); + match data.get(range.clone()) { + Some(some) => some, + None => return Err(format!("failed to read {:x?}", range)), + } + }; + + let mmap_data = { + let range = if is_pie_enabled(elf) { + let addr = ph.p_vaddr(endian) as usize; + addr..addr + obj_data.len() + } else { + let addr = ph.p_vaddr(endian) as usize - mmap.as_ptr() as usize; + addr..addr + obj_data.len() + }; + match mmap.get_mut(range.clone()) { + Some(some) => some, + None => { + return Err(format!("failed to write {:x?}", range)); + } + } + }; + let _voff = ph.p_vaddr(endian) % ph.p_align(endian); + let _vsize = ((ph.p_memsz(endian) + _voff) as usize) + .next_multiple_of(ph.p_align(endian) as usize); + log::trace!( + " copy {:#x}, {:#x}: {:#x}, {:#x}", + ph.p_vaddr(endian) - _voff, + _vsize, + _voff, + obj_data.len() + ); + mmap_data.copy_from_slice(obj_data); + } + elf::PT_TLS => { + let ptr = unsafe { + if is_pie_enabled(elf) { + mmap.as_ptr().add(ph.p_vaddr(endian) as usize) + } else { + ph.p_vaddr(endian) as *const u8 + } + }; + tcb_master = Some(Master { + ptr, + image_size: ph.p_filesz(endian) as usize, + segment_size: ph.p_memsz(endian) as usize, + offset: tls_offset + ph.p_memsz(endian) as usize, + }); + log::trace!(" tcb master {:x?}", tcb_master); + } + + elf::PT_DYNAMIC => { + let entries = ph + .dynamic(endian, data) + .map_err(|err| err.to_string())? + .ok_or_else(|| "Unable to parse PT_DYNAMIC section".to_string())?; + dynamic = Some((ph, entries)); + } + _ => (), + } + } + + let dynamic = dynamic.ok_or_else(|| "Unable to find PT_DYNAMIC section".to_string())?; + + let (parsed_dynamic, debug) = Self::parse_dynamic(path, mmap, is_pie_enabled(elf), dynamic) + .map_err(|e| e.to_string())?; + + if let Some(i) = debug { + // FIXME: cleanup + let (ph, _) = dynamic; + let vaddr = ph.p_vaddr(endian) as usize; + let bytes: [u8; size_of::() / 2] = + ((&raw const _r_debug).cast::<*const RTLDDebug>() as usize).to_ne_bytes(); + let start = if is_pie_enabled(elf) { + vaddr + i * size_of::() + size_of::() / 2 + } else { + vaddr + i * size_of::() + size_of::() / 2 + - mmap.as_ptr().cast_mut() as usize + }; + unsafe { + ptr::copy_nonoverlapping( + bytes.as_ptr(), + mmap.as_ptr().cast_mut().add(start), + bytes.len(), + ); + } + } + + Ok((mmap, tcb_master, parsed_dynamic)) + } + + fn parse_dynamic<'a>( + path: &str, + mmap: &'a [u8], + is_pie: bool, + (_, entries): (&ProgramHeader, &[Dyn]), + ) -> object::Result<(Dynamic<'a>, Option)> { + let mut runpath = None; + let mut got = None; + let mut needed = vec![]; + let mut jmprel = None; + let mut soname = None; + let mut hash_table = None; + let mut explicit_addend = None; + let mut pltrelsz = None; + let mut debug = None; + let mut symtab_ptr = None; + let (mut rel_ptr, mut rel_len) = (None, None); + let (mut relr_ptr, mut relr_len) = (None, None); + let (mut strtab_offset, mut strtab_size) = (None, None); + let (mut init_array_ptr, mut init_array_len) = (None, None); + let (mut fini_array_ptr, mut fini_array_len) = (None, None); + let (mut rela_offset, mut rela_len) = (None, None); + + for (i, entry) in entries.iter().enumerate() { + let val = entry.d_val(NativeEndian); + let relative_idx = val as usize - if is_pie { 0 } else { mmap.as_ptr() as usize }; + let ptr = (val as usize + if is_pie { mmap.as_ptr() as usize } else { 0 }) as *const u8; + let tag = entry.d_tag(NativeEndian) as u32; + + match tag { + elf::DT_DEBUG => debug = Some(i), + + // {Gnu,SysV}HashTable::parse() + // + // > The header does not contain a length field, and so all of + // > `data` will be used as the hash table values. It does not + // > matter if this is longer than needed... + elf::DT_GNU_HASH => { + let value = GnuHashTable::parse(NativeEndian, &mmap[relative_idx..])?; + hash_table = Some(HashTable::Gnu(value)); + } + + // XXX: Both GNU_HASH and HASH may be present, we give priority + // to GNU_HASH as it is significantly faster. + elf::DT_HASH if hash_table.is_none() => { + let value = SysVHashTable::parse(NativeEndian, &mmap[relative_idx..])?; + hash_table = Some(HashTable::Sysv(value)); + } + + elf::DT_PLTGOT => { + got = Some(NonNull::new(ptr as *mut usize).expect("DT_PLTGOT is NULL")); + } + + elf::DT_NEEDED => needed.push(entry), + elf::DT_JMPREL => jmprel = Some(ptr as usize), + elf::DT_RUNPATH => runpath = Some(entry), // FIXME(andypython): rpath + elf::DT_STRTAB => strtab_offset = Some(relative_idx), + elf::DT_STRSZ => strtab_size = Some(val), + elf::DT_SONAME => soname = Some(entry), + + elf::DT_RELA => rela_offset = Some(ptr.cast::()), + elf::DT_RELASZ => rela_len = Some(val as usize / size_of::()), + elf::DT_RELAENT => { + assert_eq!(val, size_of::() as _) + } + + elf::DT_REL => rel_ptr = Some(ptr.cast::()), + elf::DT_RELSZ => rel_len = Some(val as usize / size_of::()), + elf::DT_RELENT => { + assert_eq!(val, size_of::() as _) + } + + DT_RELR => relr_ptr = Some(ptr.cast::()), + DT_RELRSZ => relr_len = Some(val as usize / size_of::()), + DT_RELRENT => { + assert_eq!(val, size_of::() as _) + } + + elf::DT_PLTREL => { + let val = val as u32; + if val == elf::DT_RELA { + explicit_addend = Some(true); + } else { + assert_eq!(val, elf::DT_REL); + explicit_addend = Some(false); + } + } + elf::DT_PLTRELSZ => pltrelsz = Some(val as usize), + + elf::DT_INIT_ARRAY if val != 0 => init_array_ptr = Some(ptr.cast::()), + elf::DT_INIT_ARRAYSZ => init_array_len = Some(val as usize / size_of::()), + + elf::DT_FINI_ARRAY if val != 0 => fini_array_ptr = Some(ptr.cast::()), + elf::DT_FINI_ARRAYSZ => fini_array_len = Some(val as usize / size_of::()), + + elf::DT_SYMTAB => symtab_ptr = Some(ptr.cast::()), + elf::DT_SYMENT => { + assert_eq!(val as usize, size_of::()); + } + + _ => {} + } + } + + let strtab_offset = strtab_offset.expect("mandatory DT_STRTAB not present"); + let strtab_size = strtab_size.expect("mandatory DT_STRSZ not present"); + + let dynstrtab = StringTable::new( + &*mmap, + strtab_offset as u64, + strtab_offset as u64 + strtab_size as u64, + ); + + let get_str = |entry: &Dyn| { + entry + .string(NativeEndian, dynstrtab) + .map(|bytes| core::str::from_utf8(bytes).expect("non utf-8 elf symbol name")) + }; + + unsafe fn get_array<'a, T>(ptr: Option<*const T>, len: Option) -> &'a [T] { + if let Some(ptr) = ptr { + let len = len.expect("dynamic entry was present without it's corresponding size"); + unsafe { core::slice::from_raw_parts(ptr, len) } + } else { + assert!(len.is_none()); + &[] + } + } + + let needed = needed + .into_iter() + .map(get_str) + .collect::>>()?; + + let base = dirname(path); + + let runpath = runpath + .map(get_str) + .transpose()? + .map(|value| value.replace("$ORIGIN", &base)); + + let soname = soname.map(get_str).transpose()?; + + let jmprel = jmprel.unwrap_or_default(); + let hash_table = hash_table.expect("either DT_GNU_HASH and/or DT_HASH mut be present"); + + let init_array = unsafe { get_array(init_array_ptr, init_array_len) }; + let fini_array = unsafe { get_array(fini_array_ptr, fini_array_len) }; + let rela = unsafe { get_array(rela_offset, rela_len) }; + let relr = unsafe { get_array(relr_ptr, relr_len) }; + let rel = unsafe { get_array(rel_ptr, rel_len) }; + + Ok(( + Dynamic { + symbols: unsafe { get_array(symtab_ptr, Some(hash_table.symbol_table_length())) }, + runpath, + got, + needed, + jmprel, + soname, + hash_table, + dynstrtab, + init_array, + fini_array, + rela, + rel, + relr, + explicit_addend: explicit_addend.unwrap_or_default(), + pltrelsz: pltrelsz.unwrap_or_default(), + }, + debug, + )) + } + + /// `TLSDESC` relocation being an extension to the original TLS ABI spec can + /// be present in either `.rela.plt` (handled in [`Self::static_relocate`]) + /// or `.rela.dyn` (handled in [`Self::lazy_relocate`]) due to the lack of a + /// standard unfortunately. + /// + /// # Panics + /// + /// Panics if `reloc.kind` is not `RelocationKind::TLSDESC`. + fn do_tlsdesc_reloc(&self, reloc: Relocation, ptr: *mut usize, global_scope: &Scope) { + assert!(reloc.kind == RelocationKind::TLSDESC); + let (sym, tls_module_id, tls_offset) = if reloc.sym != SymbolIndex(0) { + let sym_name = self.dynamic.symbol_name(reloc.sym).unwrap(); + let (sym, _, obj) = resolve_sym(sym_name, &[global_scope, self.scope()]).unwrap(); + (sym.value, obj.tls_module_id, obj.tls_offset) + } else { + (0, self.tls_module_id, self.tls_offset) + }; + + let resolver = unsafe { &mut *ptr }; + let descriptor = unsafe { &mut *ptr.add(1) }; + + if self.dlopened { + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + unimplemented!("`TLSDESC` relocations are not yet implemented for riscv64 and x86"); + } + + let mut tls_index = crate::header::dl_tls::dl_tls_index { + ti_module: tls_module_id, + ti_offset: reloc.addend.unwrap_or_default(), + }; + + // Ensure the DTV entry is initialised. + unsafe { __tls_get_addr(&raw mut tls_index) }; + + *resolver = __tlsdesc_dynamic as *const () as usize; + *descriptor = Box::into_raw(Box::new(TlsDescriptor { + module_id: tls_module_id - 1, + addend: sym + reloc.addend.unwrap_or_default(), + })) as usize; + } else { + *resolver = __tlsdesc_static as *const () as usize; + *descriptor = sym + tls_offset + reloc.addend.unwrap_or_default(); + } + } + + fn static_relocate(&self, global_scope: &Scope, reloc: Relocation) -> object::Result<()> { + let b = self.mmap.as_ptr() as usize; + + let (sym, my_sym) = if reloc.sym != STN_UNDEF { + let name = self.dynamic.symbol_name(reloc.sym).unwrap(); + + let lookup_scopes = [global_scope, self.scope()]; + let sym = if matches!(reloc.kind, RelocationKind::COPY) { + lookup_scopes + .iter() + .find_map(|scope| scope._get_sym(name, 1)) + } else { + resolve_sym(name, &lookup_scopes) + } + .map(|(sym, _, obj)| (sym, obj)); + + (sym, self.dynamic.symbol(reloc.sym)) + } else { + (None, None) + }; + + let (s, tls_obj) = sym + .as_ref() + .map(|(sym, obj)| (sym.as_ptr() as usize, obj.as_ref())) + // (1) According to the System V gABI (Chapter 4, "Relocation"): if + // the symbol index (`reloc.sym`) is undefined, the symbol value + // (`s`) is defined as 0. + // + // (2) According to Drepper's ELF Handling For Thread-Local Storage + // (Section 4.2, "Local Dynamic TLS Model"): a relocation with + // undefined symbol index implies a reference to the current module + // itself. Hence we resolve the object to `self`. + .unwrap_or((0, self)); + + let ptr = if self.pie { + (b + reloc.offset) as *mut u8 + } else { + reloc.offset as *mut u8 + }; + let p = ptr as usize; + let a = match reloc.addend { + Some(some) => some, + None => match reloc.kind { + RelocationKind::COPY | RelocationKind::GOT | RelocationKind::PLT => 0, + _ => unsafe { *ptr.cast::() }, + }, + }; + + // TODO: support different sizes? + let set_usize = |value| unsafe { + *ptr.cast::() = value; + }; + + match reloc.kind { + RelocationKind::DTPMOD => set_usize(tls_obj.tls_module_id), + // TODO: Subtract DTP_OFFSET, which is 0x800 on riscv64, 0 on x86? + RelocationKind::DTPOFF => { + if reloc.sym.0 > 0 { + let (sym, _) = sym + .as_ref() + .expect("RelocationKind::DTPOFF called without valid symbol"); + set_usize(sym.value + a); + } else { + set_usize(a); + } + } + RelocationKind::GOT => set_usize(s), + RelocationKind::OFFSET => set_usize((s + a).wrapping_sub(p)), + RelocationKind::RELATIVE => set_usize(b + a), + RelocationKind::SYMBOLIC => set_usize(s + a), + RelocationKind::TPOFF => { + assert!( + !tls_obj.dlopened, + "The {{local/initial}}-exec access model is used for symbol '{}' in '{}', which requires a static TLS block. However, the definition in '{}' resides in the dynamic TLS block because the object was loaded via dlopen(2).", + reloc.sym, self.name, tls_obj.name + ); + if reloc.sym.0 > 0 { + let (sym, _) = sym + .as_ref() + .expect("RelocationKind::TPOFF called without valid symbol"); + set_usize((sym.value + a).wrapping_sub(tls_obj.tls_offset)); + } else { + set_usize(a.wrapping_sub(tls_obj.tls_offset)); + } + } + RelocationKind::IRELATIVE => unsafe { + let f: unsafe extern "C" fn() -> usize = core::mem::transmute(b + a); + set_usize(f()); + }, + RelocationKind::COPY => unsafe { + let (sym, obj) = sym + .as_ref() + .expect("RelocationKind::COPY called without valid symbol"); + let my_sym = my_sym.expect("RelocationKind::COPY called without valid symbol"); + assert!( + sym.size == my_sym.st_size(NativeEndian) as usize, + "RelocationKind::COPY failed: I was trying to use the symbol {} from {} for {} but they had different sizes. Please consider relinking.", + sym.name, + obj.name, + self.name + ); + // SAFETY: Both the source and destination have the same size. + ptr::copy_nonoverlapping(sym.as_ptr() as *const u8, ptr, sym.size); + }, + RelocationKind::TLSDESC => { + self.do_tlsdesc_reloc(reloc, ptr.cast::(), global_scope) + } + _ => unimplemented!("relocation type {:?}", reloc.kind), + } + + Ok(()) + } + + fn lazy_relocate(&self, global_scope: &Scope, resolve: Resolve) -> object::Result<()> { + let Some(got) = self.got() else { + assert_eq!(self.dynamic.jmprel, 0); + return Ok(()); + }; + + let object_base_addr = self.mmap.as_ptr() as usize; + let jmprel = self.dynamic.jmprel; + let pltrelsz = self.dynamic.pltrelsz; + + unsafe { + got.add(1).write(core::ptr::addr_of!(*self) as usize); + got.add(2) + .write(__plt_resolve_trampoline as *const () as usize); + } + + let relsz = if self.dynamic.explicit_addend { + size_of::() + } else { + size_of::() + }; + + for addr in (jmprel..(jmprel + pltrelsz)).step_by(relsz) { + let reloc: Relocation = if self.dynamic.explicit_addend { + unsafe { &*(addr as *const Rela) }.into() + } else { + unsafe { &*(addr as *const Rel) }.into() + }; + + let ptr = if self.pie { + (object_base_addr + reloc.offset) as *mut usize + } else { + reloc.offset as *mut usize + }; + + match (reloc.kind, resolve) { + (RelocationKind::PLT, Resolve::Lazy) if self.pie => unsafe { + *ptr += object_base_addr; + }, + + (RelocationKind::PLT, Resolve::Lazy) => { + // NOP. + } + + (RelocationKind::PLT, Resolve::Now) => { + let name = self.dynamic.symbol_name(reloc.sym).unwrap(); + + let resolved = resolve_sym(name, &[global_scope, self.scope()]) + .map(|(sym, _, _)| sym.as_ptr() as usize) + .unwrap_or_else(|| { + panic!( + "unresolved symbol: {name} for soname {:?}", + self.dynamic.soname + ) + }); + + unsafe { + *ptr = resolved + reloc.addend.unwrap_or(0); + } + } + + (RelocationKind::TLSDESC, Resolve::Now) => { + self.do_tlsdesc_reloc(reloc, ptr, global_scope); + } + (RelocationKind::TLSDESC, Resolve::Lazy) => { + unreachable!("TLSDESC cannot be lazily resolved") + } + + _ => { + unimplemented!( + "relocation type {:?} with resolve {:?}", + reloc.kind, + resolve + ) + } + } + } + + Ok(()) + } + + pub fn relocate(&self, ph: &[ProgramHeader], resolve: Resolve) -> object::Result<()> { + let global_scope = GLOBAL_SCOPE.read(); + let base = self.mmap.as_ptr(); + + unsafe { + apply_relr(base, self.dynamic.relr); + } + + self.dynamic + .static_relocations() + .try_for_each(|reloc| self.static_relocate(&global_scope, reloc))?; + + self.lazy_relocate(&global_scope, resolve)?; + + // Protect pages + for ph in ph + .iter() + .filter(|ph| ph.p_type(NativeEndian) == elf::PT_LOAD) + { + let voff = ph.p_vaddr(NativeEndian) % ph.p_align(NativeEndian); + let vaddr = (ph.p_vaddr(NativeEndian) - voff) as usize; + let vsize = ((ph.p_memsz(NativeEndian) + voff) as usize) + .next_multiple_of(ph.p_align(NativeEndian) as usize); + + let mut prot = 0; + if ph.p_flags(NativeEndian) & elf::PF_R == elf::PF_R { + prot |= sys_mman::PROT_READ; + } + + // W ^ X. If it is executable, do not allow it to be writable, even if requested + if ph.p_flags(NativeEndian) & elf::PF_X == elf::PF_X { + prot |= sys_mman::PROT_EXEC; + } else if ph.p_flags(NativeEndian) & elf::PF_W == elf::PF_W { + prot |= sys_mman::PROT_WRITE; + } + + unsafe { + let ptr = if self.pie { + self.mmap.as_ptr().add(vaddr) + } else { + vaddr as *const u8 + }; + log::trace!(" prot {:#x}, {:#x}: {:p}, {:#x}", vaddr, vsize, ptr, prot); + Sys::mprotect(ptr as *mut c_void, vsize, prot).expect("[ld.so]: mprotect failed"); + } + } + + Ok(()) + } +} + +impl Drop for DSO { + fn drop(&mut self) { + if self.is_ready.load(Ordering::SeqCst) { + // `run_fini` should not be called if we are being prematurely + // dropped (e.g. failed to satisfy dependencies). + self.run_fini(); + } + unsafe { Sys::munmap(self.mmap.as_ptr() as *mut c_void, self.mmap.len()).unwrap() }; + } +} + +fn is_pie_enabled(elf: &ElfFile) -> bool { + elf.elf_header().e_type.get(elf.endian()) == elf::ET_DYN +} + +fn basename(path: &str) -> String { + path.split("/").last().unwrap_or(path).to_string() +} + +fn dirname(path: &str) -> String { + let mut parts: Vec<&str> = path.split("/").collect(); + parts.truncate(parts.len() - 1); + parts.join("/") +} + +pub fn resolve_sym<'a>( + name: &'a str, + scopes: &[&'a Scope], +) -> Option<(Symbol<'a>, SymbolBinding, Arc)> { + scopes.iter().find_map(|scope| scope.get_sym(name)) +} + +#[repr(C)] +struct TlsDescriptor { + module_id: usize, + addend: usize, +} + +#[cfg(target_arch = "x86_64")] +#[unsafe(naked)] +unsafe extern "C" fn __tlsdesc_static() { + core::arch::naked_asm!("mov rax, [rax + 8]", "ret") +} + +#[cfg(target_arch = "x86")] +#[unsafe(naked)] +unsafe extern "C" fn __tlsdesc_static() { + core::arch::naked_asm!("mov eax, [eax + 4]", "ret") +} + +#[cfg(target_arch = "aarch64")] +#[unsafe(naked)] +unsafe extern "C" fn __tlsdesc_static() { + core::arch::naked_asm!("ldr x0, [x0, #8]", "ret") +} + +#[cfg(target_arch = "riscv64")] +#[unsafe(naked)] +unsafe extern "C" fn __tlsdesc_static() { + core::arch::naked_asm!("ld a0, 8(a0)", "ret"); +} + +unsafe extern "C" { + fn __tlsdesc_dynamic(); +} + +#[cfg(target_arch = "x86_64")] +core::arch::global_asm!( + " +.global __tlsdesc_dynamic +.hidden __tlsdesc_dynamic +__tlsdesc_dynamic: + push rbx + push rcx + + mov rax, [rax + 8] // TLS descriptor + mov rbx, [rax + {TLS_DESCRIPTOR_MODULE_ID_OFF}] // tls_descriptor.module_id + mov rcx, [rax + {TLS_DESCRIPTOR_ADDEND_OFF}] // tls_descriptor.addend + mov rax, qword ptr fs:[{DTV_PTR_OFF}] // tcb.dtv_ptr + + // tcb.dtv_ptr[tls_descriptor.module_id] + tls_descriptor.addend + mov rax, [rax + rbx * 8] + add rax, rcx + sub rax, qword ptr fs:[{TCB_SELF_PTR_OFF}] + + pop rcx + pop rbx + + ret +", + TLS_DESCRIPTOR_MODULE_ID_OFF = const offset_of!(TlsDescriptor, module_id), + TLS_DESCRIPTOR_ADDEND_OFF = const offset_of!(TlsDescriptor, addend), + DTV_PTR_OFF = const offset_of!(Tcb, dtv_ptr), + TCB_SELF_PTR_OFF = const offset_of!(Tcb, generic.tcb_ptr), +); + +#[cfg(target_arch = "x86")] +core::arch::global_asm!( + " +.global __tlsdesc_dynamic +.hidden __tlsdesc_dynamic +__tlsdesc_dynamic: + ud2 +" +); + +#[cfg(target_arch = "aarch64")] +core::arch::global_asm!( + " +.global __tlsdesc_dynamic +.hidden __tlsdesc_dynamic +__tlsdesc_dynamic: + stp x1, x2, [sp, #-16]! + + ldr x0, [x0, #8] // TLS descriptor + + // x0 := tls_descriptor.module_id + // x1 := tls_descriptor.addend + ldp x0, x1, [x0] + + mrs x2, tpidr_el0 // ABI ptr + ldr x2, [x2] // TCB ptr + + sub x1, x1, x2 // tls_descriptor.addend -= tcb + + ldr x2, [x2, {DTV_PTR_OFF}] // tcb.dtv_ptr + ldr x2, [x2, x0, lsl #3] // tcb.dtv_ptr[tls_descriptor.module_id] + add x0, x2, x1 // tcb.dtv_ptr[tls_descriptor.module_id] + tls_descriptor.addend + + ldp x1, x2, [sp], #16 + ret +", + DTV_PTR_OFF = const offset_of!(Tcb, dtv_ptr), +); + +#[cfg(target_arch = "riscv64")] +core::arch::global_asm!( + " +.global __tlsdesc_dynamic +.hidden __tlsdesc_dynamic +__tlsdesc_dynamic: + unimp +" +); + +/// Applies [`DT_RELR`] relative relocations. +pub unsafe fn apply_relr(base: *const u8, relr: &[Relr]) { + let mut addr = ptr::null_mut(); + for &entry in relr { + if entry & 1 == 0 { + // An even entry sets up `addr` for subsequent odd entries. + unsafe { + addr = base.add(entry) as *mut usize; + *addr += base as usize; + addr = addr.add(1); + } + } else { + // An odd entry indicates a bitmap describing at maximum 63 + // (for 64-bit) or 31 (for 32-bit) locations following `addr`. + // Odd entries can be chained. + let mut entry = entry >> 1; + let mut i = 0; + while entry != 0 { + if entry & 1 != 0 { + unsafe { + *addr.add(i) += base as usize; + } + } + entry >>= 1; + i += 1; + } + + addr = unsafe { addr.add(CHAR_BITS * size_of::() - 1) }; + } + } +} diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs new file mode 100644 index 0000000000..4a01b0d9b1 --- /dev/null +++ b/src/ld_so/linker.rs @@ -0,0 +1,1229 @@ +use alloc::{ + collections::BTreeMap, + rc::Rc, + string::{String, ToString}, + sync::{Arc, Weak}, + vec::Vec, +}; +use object::elf; +#[cfg(not(target_arch = "x86"))] +use object::{ + NativeEndian, + read::elf::{Rela as _, Sym}, +}; + +use core::{ + cell::RefCell, + ptr::{self, NonNull}, +}; + +use crate::{ + ALLOCATOR, + c_str::{CStr, CString}, + error::Errno, + header::{ + dl_tls::{__tls_get_addr, dl_tls_index}, + fcntl, sys_mman, + unistd::F_OK, + }, + ld_so::dso::SymbolBinding, + out::Out, + platform::{ + Pal, Sys, + types::{c_int, c_void}, + }, + sync::rwlock::RwLock, +}; + +#[cfg(feature = "ld_so_cache")] +use crate::header::sys_stat::stat; + +#[cfg(not(target_arch = "x86"))] +use crate::{ld_so::dso::resolve_sym, platform::types::c_uint}; + +#[cfg(not(target_arch = "x86"))] +use super::dso::Rela; +use super::{ + PATH_SEP, + access::accessible, + callbacks::LinkerCallbacks, + debug::{_dl_debug_state, _r_debug, RTLDState}, + dso::{DSO, ProgramHeader}, + tcb::{Master, Tcb}, +}; + +#[derive(Debug, Copy, Clone)] +pub enum DlError { + /// Failed to locate the requested DSO. + NotFound, + /// The DSO is malformed somehow. + Malformed, + /// Invalid DSO handle. + InvalidHandle, + /// Out of memory. + Oom, +} + +impl DlError { + /// Returns a human-readable, null-terminated C string describing the error. + pub const fn repr(&self) -> &'static core::ffi::CStr { + match self { + DlError::NotFound => { + c"Failed to locate the requested DSO. Set `LD_DEBUG=all` for more information." + } + + DlError::Malformed => { + c"The DSO is malformed somehow. Set `LD_DEBUG=all` for more information." + } + + DlError::InvalidHandle => { + c"Invalid DSO handle. Set `LD_DEBUG=all` for more information." + } + + DlError::Oom => c"Out of memory.", + } + } +} + +pub type Result = core::result::Result; + +pub(super) static GLOBAL_SCOPE: RwLock = RwLock::new(Scope::global()); + +struct MmapFile { + fd: i32, + ptr: *mut c_void, + size: usize, +} + +impl MmapFile { + fn open(path: CStr, oflag: c_int) -> core::result::Result { + let fd = Sys::open(path, oflag, 0 /* mode */)?; + let mut stat = crate::header::sys_stat::stat::default(); + Sys::fstat(fd, Out::from_mut(&mut stat))?; + + Self::from_fd(fd, stat.st_size as usize) + } + + fn from_fd(fd: i32, size: usize) -> core::result::Result { + let ptr = unsafe { + Sys::mmap( + ptr::null_mut(), + size, + sys_mman::PROT_READ, + sys_mman::MAP_PRIVATE, + fd, + 0, + ) + }?; + + Ok(Self { fd, ptr, size }) + } + + fn anonymous(size: usize) -> core::result::Result { + let ptr = unsafe { + Sys::mmap( + ptr::null_mut(), + size, + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + sys_mman::MAP_PRIVATE | sys_mman::MAP_ANONYMOUS, + -1, + 0, + ) + }?; + + Ok(Self { fd: -1, ptr, size }) + } + + fn data(&self) -> &[u8] { + unsafe { core::slice::from_raw_parts(self.ptr.cast::(), self.size) } + } + + fn as_mut_slice(&self) -> &mut [u8] { + unsafe { core::slice::from_raw_parts_mut(self.ptr.cast::(), self.size) } + } +} + +impl Drop for MmapFile { + fn drop(&mut self) { + unsafe { + Sys::munmap(self.ptr, self.size).unwrap(); + if self.fd != -1 { + Sys::close(self.fd).unwrap(); + } + } + } +} + +#[derive(Clone, Debug)] +pub struct Symbol<'a> { + pub name: &'a str, + pub value: usize, + pub base: usize, + pub size: usize, + pub sym_type: u8, +} + +impl Symbol<'_> { + pub fn as_ptr(&self) -> *mut c_void { + (self.base + self.value) as *mut c_void + } +} + +#[derive(Debug, Default, Copy, Clone, PartialEq)] +pub enum Resolve { + /// Resolve all undefined symbols immediately. + #[cfg_attr(not(target_arch = "x86_64"), default)] + Now, + /// Perform lazy binding (i.e. symbols will be resolved when they are first + /// used). + #[cfg_attr(target_arch = "x86_64", default)] + Lazy, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ScopeKind { + Global, + Local, +} + +pub enum Scope { + /// The global scope initially contains the main program and all of its + /// dependencies. Additional objects will be added to this scope via + /// `dlopen(2)` if the `RTLD_GLOBAL` flag is set. + Global { objs: Vec> }, + Local { + owner: Option>, + objs: Vec>, + }, +} + +impl Scope { + #[inline] + const fn global() -> Self { + Self::Global { objs: Vec::new() } + } + + #[inline] + const fn local() -> Self { + Self::Local { + owner: None, + objs: Vec::new(), + } + } + + fn set_owner(&mut self, obj: Weak) { + match self { + Self::Global { .. } => panic!("attempted to set global scope owner"), + Self::Local { owner, .. } => { + assert!(owner.is_none(), "attempted to change local scope owner"); + *owner = Some(obj); + } + } + } + + fn add(&mut self, target: &Arc) { + match self { + Self::Global { objs } => { + let target = Arc::downgrade(target); + for obj in objs.iter() { + if Weak::ptr_eq(obj, &target) { + return; + } + } + + objs.push(target); + } + + Self::Local { objs, .. } => { + for obj in objs.iter() { + if Arc::ptr_eq(obj, target) { + return; + } + } + + objs.push(target.clone()); + } + } + } + + pub(super) fn get_sym<'a>( + &self, + name: &'a str, + ) -> Option<(Symbol<'a>, SymbolBinding, Arc)> { + self._get_sym(name, 0) + } + + pub(super) fn _get_sym<'a>( + &self, + name: &'a str, + skip: usize, + ) -> Option<(Symbol<'a>, SymbolBinding, Arc)> { + let mut res = None; + + let get_sym = |obj: Arc| { + if let Some((sym, binding)) = obj.get_sym(name) { + if binding.is_global() { + return Some((sym, binding, obj.clone())); + } + + res = Some((sym, binding, obj.clone())); + } + + None + }; + + match self { + Self::Global { objs } => objs + .iter() + .skip(skip) + .map(|o| o.upgrade().unwrap()) + .find_map(get_sym), + Self::Local { owner, objs } => { + let owner = owner + .as_ref() + .expect("local scope without owner") + .upgrade() + .expect("local scope owner was dropped"); + + core::iter::once(owner) + .chain(objs.iter().cloned()) + .skip(skip) + .find_map(get_sym) + } + } + .or(res) + } + + fn copy_into(&self, other: &mut Self) { + match (self, other) { + (Self::Local { owner, objs }, Self::Global { objs: other_objs }) => { + // FIXME: may have duplicates + let owner = owner.as_ref().expect("local scope without owner"); + other_objs.push(owner.clone()); + other_objs.extend(objs.iter().map(Arc::downgrade)); + } + + _ => unreachable!(), + } + } + + fn debug(&self) { + match self { + Self::Global { objs } => { + println!( + "[@global] {:?}", + objs.iter() + .map(|x| x.upgrade().unwrap().name.clone()) + .collect::>() + ); + } + + Self::Local { owner, objs } => { + let owner = owner.as_ref().unwrap().upgrade().unwrap(); + println!( + "[{}] {:?}", + owner.name, + objs.iter().map(|x| x.name.clone()).collect::>() + ) + } + } + } +} + +// Used by dlfcn.h +// +// We need this as the handle must be created and destroyed with the dynamic +// linker's allocator. +pub struct ObjectHandle(*const DSO); + +impl ObjectHandle { + #[inline] + fn new(obj: Arc) -> Self { + Self(Arc::into_raw(obj)) + } + + #[inline] + fn into_inner(self) -> Arc { + unsafe { Arc::from_raw(self.0) } + } + + #[inline] + pub fn as_ptr(&self) -> *const c_void { + self.0.cast() + } + + #[inline] + pub fn from_ptr(ptr: *const c_void) -> Option { + NonNull::new(ptr as *mut DSO).map(|ptr| Self(ptr.as_ptr())) + } +} + +impl AsRef for ObjectHandle { + #[inline] + fn as_ref(&self) -> &DSO { + unsafe { &*self.0 } + } +} + +bitflags::bitflags! { + #[derive(Debug, Default)] + pub struct DebugFlags: u32 { + /// Display what objects and where they are being loaded. + const LOAD = 1 << 1; + /// Display library search paths. + const SEARCH = 1 << 2; + /// Display scope information. + const SCOPES = 1 << 3; + } +} + +#[derive(Default)] +pub struct Config { + pub debug_flags: DebugFlags, + library_path: Option, + /// Resolve symbols at program startup. + bind_now: bool, +} + +impl Config { + pub fn from_env(env: &BTreeMap) -> Self { + let debug_flags = env + .get("LD_DEBUG") + .map(|value| { + let mut flags = DebugFlags::empty(); + for opt in value.split(',') { + flags |= match opt { + "load" => DebugFlags::LOAD, + "search" => DebugFlags::SEARCH, + "scopes" => DebugFlags::SCOPES, + "all" => DebugFlags::all(), + _ => { + eprintln!("[ld.so]: unknown debug flag '{}'", opt); + DebugFlags::empty() + } + }; + } + + flags + }) + .unwrap_or(DebugFlags::empty()); + + Self { + debug_flags, + library_path: env.get("LD_LIBRARY_PATH").cloned(), + bind_now: env + .get("LD_BIND_NOW") + .map(|value| !value.is_empty()) + .unwrap_or_default(), + } + } +} + +pub struct Linker { + config: Config, + + next_object_id: usize, + next_tls_module_id: usize, + tls_size: usize, + objects: BTreeMap>, + name_to_object_id_map: BTreeMap, + pub cbs: Rc>, +} + +const ROOT_ID: usize = 1; + +impl Linker { + pub fn new(config: Config) -> Self { + Self { + config, + next_object_id: ROOT_ID, + next_tls_module_id: 1, + tls_size: 0, + objects: BTreeMap::new(), + name_to_object_id_map: BTreeMap::new(), + cbs: Rc::new(RefCell::new(LinkerCallbacks::new())), + } + } + + pub fn load_program(&mut self, path: &str, base_addr: Option) -> Result { + let dso = self.load_object( + path, + &None, + base_addr, + false, + if self.config.bind_now { + Resolve::Now + } else { + Resolve::default() + }, + ScopeKind::Global, + )?; + Ok(dso.entry_point) + } + + pub fn load_library( + &mut self, + name: Option<&str>, + resolve: Resolve, + scope: ScopeKind, + noload: bool, + ) -> Result { + log::trace!( + "[ld.so] load_library(name={:?}, resolve={:#?}, scope={:#?}, noload={})", + name, + resolve, + scope, + noload + ); + + if noload && resolve == Resolve::Now { + // Do not perform lazy binding anymore. + // * Check if loaded with Resolve::Now and if so, early return. + // * If not, resolve all symbols now. + todo!("resolve symbols now!"); + } + + match name { + Some(name) => { + if let Some(id) = self.name_to_object_id_map.get(name) { + let obj = self.objects.get(id).unwrap(); + + // We may be upgrading the object from a local scope to the + // global scope. + if scope == ScopeKind::Global { + if self.config.debug_flags.contains(DebugFlags::SCOPES) { + eprintln!("[ld.so]: moving {} into the global scope", obj.name); + } + + { + let mut global_scope = GLOBAL_SCOPE.write(); + obj.scope().copy_into(&mut global_scope); + } + self.scope_debug(); + } + + Ok(ObjectHandle::new(obj.clone())) + } else if !noload { + let parent_runpath = &self + .objects + .get(&ROOT_ID) + .and_then(|parent| parent.runpath().map(|path| path.to_string())); + + Ok(ObjectHandle::new(self.load_object( + name, + parent_runpath, + None, + true, + if self.config.bind_now { + Resolve::Now + } else { + resolve + }, + scope, + )?)) + } else { + // FIXME: LoadError? + // Err(Error::Malformed(format!( + // "object '{}' has not yet been loaded", + // name + // ))) + Ok(ObjectHandle(ptr::null())) + } + } + + None => match self.objects.get(&ROOT_ID) { + Some(obj) => Ok(ObjectHandle::new(obj.clone())), + None => Err(DlError::NotFound), + }, + } + } + + pub fn get_sym(&self, handle: Option, name: &str) -> Option<*mut c_void> { + let guard; + + if let Some(handle) = handle.as_ref() { + handle.as_ref().scope() + } else { + guard = GLOBAL_SCOPE.read(); + &guard + } + .get_sym(name) + .map(|(symbol, _, obj)| { + if symbol.sym_type != elf::STT_TLS { + symbol.as_ptr() + } else { + let mut tls_index = dl_tls_index { + ti_module: obj.tls_module_id, + ti_offset: symbol.value, + }; + + unsafe { __tls_get_addr(&raw mut tls_index) } + } + }) + } + + pub fn unload(&mut self, handle: ObjectHandle) { + let obj = handle.into_inner(); + if !obj.dlopened { + return; + } + + log::trace!( + "[ld.so] unloading {} (sc={}, wc={})", + obj.name, + Arc::strong_count(&obj), + Arc::weak_count(&obj) + ); + + // One for the reference we have and the other for the one in the + // objects map. + if Arc::strong_count(&obj) == 2 { + // Remove from the global scope. + match *GLOBAL_SCOPE.write() { + Scope::Global { ref mut objs } => { + objs.retain(|o| !Weak::ptr_eq(o, &Arc::downgrade(&obj))); + } + + _ => unreachable!(), + } + + let _ = self.objects.remove(&obj.id).unwrap(); + for dep in obj.dependencies() { + if let Some(name) = self.name_to_object_id_map.get(*dep) + && let Some(object_name) = self.objects.get(name) + { + self.unload(ObjectHandle::new(object_name.clone())); + } + } + self.name_to_object_id_map.remove(&obj.name); + assert!(Arc::strong_count(&obj) == 1); + drop(obj); + } + + // obj is dropped here. + } + + pub fn fini(&self) { + for obj in self.objects.values() { + obj.run_fini(); + } + } + + fn load_object( + &mut self, + path: &str, + runpath: &Option, + base_addr: Option, + dlopened: bool, + resolve: Resolve, + scope: ScopeKind, + ) -> Result> { + let resolve = if cfg!(target_arch = "x86_64") { + resolve + } else { + // Lazy binding is not currently supported on non-x86_64 architectures. + Resolve::Now + }; + + _r_debug.lock().state = RTLDState::RtAdd; + _dl_debug_state(); + + let mut new_objects = Vec::new(); + let mut objects_data = Vec::new(); + let mut tcb_masters = Vec::new(); + let loaded_dso = self.load_objects_recursive( + path, + runpath, + base_addr, + dlopened, + &mut new_objects, + &mut objects_data, + &mut tcb_masters, + None, + scope, + )?; + + for (i, obj) in new_objects.iter().enumerate() { + obj.relocate(&objects_data[i], resolve).unwrap(); + } + + unsafe { + if !dlopened { + #[cfg(target_os = "redox")] + let (tcb, old_tcb, thr_fd) = { + use redox_rt::signal::tmp_disable_signals; + + let old_tcb = Tcb::current().expect("failed to get bootstrap TCB"); + let thr_fd = (&mut *old_tcb.os_specific.thr_fd.get()) + .take() + .expect("no thread FD present"); + let new_tcb = Tcb::new(self.tls_size)?; // This actually allocates TCB, TLS and ABI page. + + // Stash + let new_tls_end = new_tcb.generic.tls_end; + let new_tls_len = new_tcb.generic.tls_len; + let new_tcb_ptr = new_tcb.generic.tcb_ptr; + let new_tcb_len = new_tcb.generic.tcb_len; + + // Unmap just the TCB page. + Sys::munmap(new_tcb as *mut Tcb as *mut c_void, syscall::PAGE_SIZE).unwrap(); + + let new_addr = ptr::addr_of!(*new_tcb) as usize; + + assert_eq!( + syscall::syscall5( + syscall::SYS_MREMAP, + old_tcb as *mut Tcb as usize, + syscall::PAGE_SIZE, + new_addr, + syscall::PAGE_SIZE, + (syscall::MremapFlags::FIXED | syscall::MremapFlags::KEEP_OLD).bits() + | (syscall::MapFlags::PROT_READ | syscall::MapFlags::PROT_WRITE) + .bits(), + ) + .expect("mremap: failed to alias TCB"), + new_addr, + ); + // XXX: New TCB is now at the same physical address as the old TCB. + + let _guard = tmp_disable_signals(); + // Restore + new_tcb.generic.tls_end = new_tls_end; + new_tcb.generic.tls_len = new_tls_len; + new_tcb.generic.tcb_ptr = new_tcb_ptr; + new_tcb.generic.tcb_len = new_tcb_len; + + drop(_guard); + (new_tcb, old_tcb as *mut Tcb as *mut c_void, thr_fd) + }; + + #[cfg(not(target_os = "redox"))] + let tcb = Tcb::new(self.tls_size)?; + + // We are now loading the main program or its dependencies. The TLS for all initially + // loaded objects reside in the static TLS block. Depending on the architecture, the + // static TLS block is either placed before the TP or after the TP. + // + // Setup the DTVs. + tcb.setup_dtv(tcb_masters.len()); + + for obj in new_objects.iter() { + if obj.tls_module_id == 0 { + // No TLS for this object. + continue; + } + + let dtv_idx = obj.tls_module_id - 1; + + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + // Below the TP + tcb.dtv_mut()[dtv_idx] = tcb.tls_end.sub(obj.tls_offset); + } else { + // FIMXE(andypython): Make it above the TP + // + // tcb.dtv_mut().unwrap()[obj.tls_module_id - 1] = + // tcb_ptr.add(1).cast::().add(obj.tls_offset); + // + // FIXME(andypython): https://gitlab.redox-os.org/redox-os/relibc/-/merge_requests/570#note_35788 + let tls_start = tcb.tls_end.sub(tcb.tls_len); + tcb.dtv_mut()[dtv_idx] = tls_start.add(obj.tls_offset); + } + } + + tcb.append_masters(tcb_masters); + // Copy the master data into the static TLS block. + tcb.copy_masters().map_err(|_| DlError::Malformed)?; + tcb.activate( + #[cfg(target_os = "redox")] + Some(thr_fd), + ); + tcb.mspace = ALLOCATOR.get(); + + #[cfg(target_os = "redox")] + { + // Unmap the old TCB. + Sys::munmap(old_tcb, syscall::PAGE_SIZE).unwrap(); + } + } else { + let tcb = Tcb::current().expect("failed to get current tcb"); + + // TLS variables for dlopen'ed objects are lazily allocated in `__tls_get_addr`. + tcb.append_masters(tcb_masters); + } + } + + for obj in new_objects.into_iter() { + obj.mark_ready(); + self.run_init(&obj); + self.register_object(obj); + } + + _r_debug.lock().state = RTLDState::RtConsistent; + _dl_debug_state(); + + Ok(loaded_dso) + } + + fn register_object(&mut self, obj: Arc) { + self.name_to_object_id_map.insert(obj.name.clone(), obj.id); + self.objects.insert(obj.id, obj); + } + + /// Loads the specified object and all of its dependencies. + /// + /// `new_objects` contains any new objects that were loaded. Order is + /// reverse of how the scope is populated. + /// + /// The scope is populated such that the loaded objects are in breadth-first + /// order. This means that first the requested object is added to the scope, + /// and then its dependencies are added in the order of their respective + /// `DT_NEEDED` entries in the requested object. This is done recursively + /// until all dependencies have been loaded. + /// + /// If a dependency has already been loaded, it is *not* added to the scope + /// nor to `new_objects`. + #[allow(clippy::too_many_arguments)] + fn load_objects_recursive( + &mut self, + name: &str, + parent_runpath: &Option, + base_addr: Option, + dlopened: bool, + new_objects: &mut Vec>, + objects_data: &mut Vec>, + tcb_masters: &mut Vec, + // Scope of the object that caused this object to be loaded. + dependent_scope: Option<&mut Scope>, + scope_kind: ScopeKind, + ) -> Result> { + // fixme: double lookup slow + if let Some(id) = self.name_to_object_id_map.get(name) { + if let Some(obj) = self.objects.get(id) { + if let Some(scope) = dependent_scope { + match scope_kind { + ScopeKind::Local => scope.add(obj), + ScopeKind::Global => GLOBAL_SCOPE.write().add(obj), + } + } else if scope_kind == ScopeKind::Global { + GLOBAL_SCOPE.write().add(obj); + } + return Ok(obj.clone()); + } + } else if let Some(obj) = new_objects.iter().find(|o| o.name == name) { + if let Some(scope) = dependent_scope { + match scope_kind { + ScopeKind::Local => scope.add(obj), + ScopeKind::Global => GLOBAL_SCOPE.write().add(obj), + } + } else if scope_kind == ScopeKind::Global { + GLOBAL_SCOPE.write().add(obj); + } + return Ok(obj.clone()); + } + + let debug = self.config.debug_flags.contains(DebugFlags::LOAD); + + let path = self.search_object(name, parent_runpath)?; + let file = self.read_file(&path)?; + let data = file.data(); + let (obj, tcb_master, elf) = DSO::new( + &path, + data, + base_addr, + dlopened, + self.next_object_id, + self.next_tls_module_id, + // Ensure TLS is aligned to 16 bytes for SSE + self.tls_size.next_multiple_of(16), + ) + .map_err(|err| { + if debug { + eprintln!("[ld.so]: failed to load '{}': {}", name, err) + } + + DlError::Malformed + })?; + + if debug { + eprintln!( + "[ld.so]: loading object: {} at {:#x}:{:#x} (pie: {})", + name, + obj.mmap.as_ptr() as usize, + obj.mmap.as_ptr() as usize + obj.mmap.len(), + obj.pie, + ); + } + + self.next_object_id += 1; + + if let Some(master) = tcb_master { + if !dlopened { + self.tls_size = master.offset; // => aligned ph.p_memsz + } + + tcb_masters.push(master); + self.next_tls_module_id += 1; + } + + let runpath = obj.runpath().map(|rpath| rpath.to_string()); + let dependencies = obj + .dependencies() + .iter() + .map(|dep| dep.to_string()) + .collect::>(); + + let obj = Arc::new(obj); + let mut scope = Scope::local(); + + if let Some(dependent_scope) = dependent_scope { + match scope_kind { + ScopeKind::Local => dependent_scope.add(&obj), + ScopeKind::Global => GLOBAL_SCOPE.write().add(&obj), + } + } else if let ScopeKind::Global = scope_kind { + GLOBAL_SCOPE.write().add(&obj); + } + + for dep_name in dependencies.iter() { + self.load_objects_recursive( + dep_name, + &runpath, + None, + dlopened, + new_objects, + objects_data, + tcb_masters, + Some(&mut scope), + scope_kind, + )?; + } + + objects_data.push(elf); + new_objects.push(obj.clone()); + + scope.set_owner(Arc::downgrade(&obj)); + obj.scope.call_once(|| scope); + + Ok(obj) + } + + fn search_object(&self, name: &str, parent_runpath: &Option) -> Result { + let debug = self.config.debug_flags.contains(DebugFlags::SEARCH); + if debug { + eprintln!("[ld.so]: looking for '{}'", name); + } + + let mut full_path = name.to_string(); + if accessible(&full_path, F_OK).is_ok() { + if debug { + eprintln!("[ld.so]: found at '{}'!", full_path); + } + return Ok(full_path); + } else { + let mut search_paths = Vec::new(); + if let Some(runpath) = parent_runpath { + search_paths.extend(runpath.split(PATH_SEP)); + } + if let Some(ld_path) = self.config.library_path.as_ref() { + search_paths.extend(ld_path.split(PATH_SEP)); + } + search_paths.push("/lib"); + for part in search_paths.iter() { + full_path = format!("{}/{}", part, name); + if debug { + eprintln!("[ld.so]: trying path '{}'", full_path); + } + if accessible(&full_path, F_OK).is_ok() { + if debug { + eprintln!("[ld.so]: found at '{}'!", full_path); + } + return Ok(full_path); + } + } + } + + if debug { + eprintln!("[ld.so]: failed to locate '{}'", name); + } + + Err(DlError::NotFound) + } + + fn read_file(&self, path: &str) -> Result { + let debug = self.config.debug_flags.contains(DebugFlags::SEARCH); + + let path_c = CString::new(path).map_err(|err| { + if debug { + eprintln!("[ld.so]: invalid path '{}': {}", path, err) + } + + DlError::NotFound + })?; + + // TODO: Caches may silently fail within multiple users (try to leverage capabilities?) + // TODO: No way to specify weak cache or pruning the cache manually + + #[cfg(feature = "ld_so_cache")] + let file = { + let mut mtime_sec = 0; + let mut mtime_nsec = 0; + let mut source_size = 0; + + let src_fd = Sys::open(CStr::borrow(&path_c), fcntl::O_RDONLY | fcntl::O_CLOEXEC, 0) + .map_err(|err| { + if debug { + eprintln!("[ld.so]: failed to open '{}': {}", path, err) + } + DlError::NotFound + })?; + let mut st = stat::default(); + if Sys::fstat(src_fd, Out::from_mut(&mut st)).is_ok() { + mtime_sec = st.st_mtim.tv_sec; + mtime_nsec = st.st_mtim.tv_nsec; + source_size = st.st_size as usize; + } + + let shm_path_str = format!( + "/scheme/shm/ld.so.cache.{}.{}.{}\0", + path.replace('/', "_"), + mtime_sec, + mtime_nsec + ); + let shm_path = unsafe { CStr::from_bytes_with_nul_unchecked(shm_path_str.as_bytes()) }; + + let mut shm_exists = false; + + if let Ok(shm_fd) = Sys::open(shm_path, fcntl::O_RDONLY, 0) { + let mut shm_stat = stat::default(); + if Sys::fstat(shm_fd, Out::from_mut(&mut shm_stat)).is_ok() { + shm_exists = true; + if shm_stat.st_size > 0 { + if let Ok(mmap_file) = MmapFile::anonymous(source_size) { + let mut offset = 0; + let buf = mmap_file.as_mut_slice(); + while offset < source_size { + match Sys::read(shm_fd, &mut buf[offset..]) { + Ok(0) | Err(_) => break, + Ok(n) => offset += n, + } + } + let _ = Sys::close(shm_fd); + return Ok(mmap_file); + } + let _ = Sys::close(shm_fd); + } + } + let _ = Sys::close(shm_fd); + } + + let file = MmapFile::from_fd(src_fd, source_size).map_err(|err| { + if debug { + eprintln!("[ld.so]: failed to map '{}': {}", path, err) + } + DlError::NotFound + })?; + + if !shm_exists { + let _ = Sys::open(shm_path, fcntl::O_CREAT | fcntl::O_RDWR, 0o600).map(Sys::close); + } else if let Ok(shm_fd) = Sys::open(shm_path, fcntl::O_RDWR, 0o600) { + let _ = Sys::ftruncate(shm_fd, source_size as i64); + let _ = Sys::write(shm_fd, file.data()); + let _ = Sys::close(shm_fd); + } + + file + }; + #[cfg(not(feature = "ld_so_cache"))] + let file = { + let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC; + MmapFile::open(CStr::borrow(&path_c), flags).map_err(|err| { + if debug { + eprintln!("[ld.so]: failed to open '{}': {}", path, err) + } + + DlError::NotFound + })? + }; + + Ok(file) + } + + fn run_init(&self, obj: &DSO) { + use crate::platform::{self, types::*}; + + if let Some((symbol, SymbolBinding::Global)) = obj.get_sym("__relibc_init_environ") { + unsafe { + symbol + .as_ptr() + .cast::<*mut *mut c_char>() + .write(platform::environ); + } + } + + obj.run_init(); + } + + fn scope_debug(&self) { + if self.config.debug_flags.contains(DebugFlags::SCOPES) { + println!("[ld.so]: =========== SCOPES =========="); + GLOBAL_SCOPE.read().debug(); + for obj in self.objects.values() { + obj.scope().debug(); + } + println!("[ld.so]: =============================="); + } + } +} + +// GOT[1] = object_id +// GOT[2] = __plt_resolve_trampoline +// +// The stubs in .plt will push the relocation index and the object pointer onto +// the stack and jump to [`__plt_resolve_trampoline`]. The trampoline will then +// call this function to resolve the symbol and update the respective GOT entry. +// The trampoline will then jump to the resolved symbol. +// +// FIXME(andypython): 32-bit +#[cfg(target_pointer_width = "64")] +extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) -> *mut c_void { + let obj = unsafe { &*obj }; + let obj_base = obj.mmap.as_ptr() as usize; + let jmprel = obj.dynamic.jmprel; + + let rela = unsafe { &*(jmprel as *const Rela).add(relocation_index as usize) }; + assert_eq!(rela.r_type(NativeEndian, false), elf::R_X86_64_JUMP_SLOT); + + let sym = obj + .dynamic + .symbol(rela.symbol(NativeEndian, false).unwrap()) + .expect("symbol not found"); + assert_ne!(sym.st_name(NativeEndian), 0); + + let name = core::str::from_utf8( + obj.dynamic + .dynstrtab + .get(sym.st_name(NativeEndian)) + .unwrap(), + ) + .expect("non utf8 symbol name"); + + let resolved = resolve_sym(name, &[&GLOBAL_SCOPE.read(), obj.scope()]) + .map(|(sym, _, _)| sym) + .unwrap_or_else(|| panic!("symbol '{name}' not found")) + .as_ptr(); + + let ptr = if obj.pie { + (obj_base as u64 + rela.r_offset(NativeEndian)) as *mut u64 + } else { + rela.r_offset(NativeEndian) as *mut u64 + }; + #[cfg(feature = "trace_tls")] + log::trace!("@plt: {} -> *mut {:p}", name, ptr); + + unsafe { *ptr = resolved as u64 } + resolved +} + +unsafe extern "C" { + pub(super) fn __plt_resolve_trampoline() -> usize; +} + +#[cfg(target_arch = "x86_64")] +core::arch::global_asm!( + " +.global __plt_resolve_trampoline +.hidden __plt_resolve_trampoline +__plt_resolve_trampoline: + push rsi + push rdi + + mov rdi, qword ptr [rsp + 0x10] + mov rsi, qword ptr [rsp + 0x18] + + // stash the floating point argument registers + sub rsp, 128 + movdqu [rsp + 0x00], xmm0 + movdqu [rsp + 0x10], xmm1 + movdqu [rsp + 0x20], xmm2 + movdqu [rsp + 0x30], xmm3 + movdqu [rsp + 0x40], xmm4 + movdqu [rsp + 0x50], xmm5 + movdqu [rsp + 0x60], xmm6 + movdqu [rsp + 0x70], xmm7 + + push rax + push rcx + push rdx + push r8 + push r9 + push r10 + + push rbp + mov rbp, rsp + and rsp, 0xfffffffffffffff0 + call {__plt_resolve_inner} + mov r11, rax + mov rsp, rbp + pop rbp + + pop r10 + pop r9 + pop r8 + pop rdx + pop rcx + pop rax + + movdqu xmm7, [rsp + 0x70] + movdqu xmm6, [rsp + 0x60] + movdqu xmm5, [rsp + 0x50] + movdqu xmm4, [rsp + 0x40] + movdqu xmm3, [rsp + 0x30] + movdqu xmm2, [rsp + 0x20] + movdqu xmm1, [rsp + 0x10] + movdqu xmm0, [rsp + 0x00] + add rsp, 128 + + pop rdi + pop rsi + + add rsp, 0x10 + jmp r11 + + ud2 +.size __plt_resolve_trampoline, . - __plt_resolve_trampoline +", + __plt_resolve_inner = sym __plt_resolve_inner +); + +#[cfg(target_arch = "x86")] +core::arch::global_asm!( + " +.global __plt_resolve_trampoline +.hidden __plt_resolve_trampoline +__plt_resolve_trampoline: + ud2 +.size __plt_resolve_trampoline, . - __plt_resolve_trampoline + " +); + +#[cfg(target_arch = "aarch64")] +core::arch::global_asm!( + " +.global __plt_resolve_trampoline +.hidden __plt_resolve_trampoline +__plt_resolve_trampoline: + udf #0 +.size __plt_resolve_trampoline, . - __plt_resolve_trampoline + " +); + +#[cfg(target_arch = "riscv64")] +core::arch::global_asm!( + " +.global __plt_resolve_trampoline +.hidden __plt_resolve_trampoline +__plt_resolve_trampoline: + unimp +.size __plt_resolve_trampoline, . - __plt_resolve_trampoline + " +); diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs new file mode 100644 index 0000000000..750f218987 --- /dev/null +++ b/src/ld_so/mod.rs @@ -0,0 +1,212 @@ +//! Dynamic loading and linking. + +// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is +// dropped from src/lib.rs. +#![warn(warnings, unused_variables)] + +use core::{mem, ptr}; +use object::{ + Endianness, + elf::{self, ProgramHeader32, ProgramHeader64}, + read::elf::ProgramHeader, +}; + +use self::tcb::{Master, Tcb}; +use crate::{ + header::sys_auxv::{AT_NULL, AT_PHDR, AT_PHENT, AT_PHNUM}, + platform::{Pal, Sys}, + start::Stack, +}; + +pub const PATH_SEP: char = ':'; + +mod access; +pub mod callbacks; +pub mod debug; +mod dso; +pub mod linker; +pub mod start; +pub mod tcb; + +pub use generic_rt::{ExpectTlsFree, panic_notls}; + +static mut STATIC_TCB_MASTER: Master = Master { + ptr: ptr::null_mut(), + image_size: 0, + segment_size: 0, + offset: 0, +}; + +#[inline(never)] +fn static_init( + sp: &'static Stack, + #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuardUpper, +) { + const SIZEOF_PHDR64: usize = mem::size_of::>(); + const SIZEOF_PHDR32: usize = mem::size_of::>(); + + let mut phdr_opt = None; + let mut phent_opt = None; + let mut phnum_opt = None; + + let mut auxv = sp.auxv(); + loop { + let (kind, value) = unsafe { *auxv }; + if kind == AT_NULL { + break; + } + + match kind { + AT_PHDR => phdr_opt = Some(value), + AT_PHENT => phent_opt = Some(value), + AT_PHNUM => phnum_opt = Some(value), + _ => (), + } + + auxv = unsafe { auxv.add(1) }; + } + + let phdr = phdr_opt.expect_notls("failed to find AT_PHDR"); + let phent = phent_opt.expect_notls("failed to find AT_PHENT"); + let phnum = phnum_opt.expect_notls("failed to find AT_PHNUM"); + + for i in 0..phnum { + let ph_addr = phdr + phent * i; + let endian = Endianness::default(); + let (p_align, p_filesz, p_memsz, p_type, p_vaddr) = match phent { + SIZEOF_PHDR64 => unsafe { + let ph = &*(ph_addr as *const ProgramHeader64); + ( + ph.p_align(endian) as usize, + ph.p_filesz(endian) as usize, + ph.p_memsz(endian) as usize, + ph.p_type(endian), + ph.p_vaddr(endian) as usize, + ) + }, + + SIZEOF_PHDR32 => unsafe { + let ph = &*(ph_addr as *const ProgramHeader32); + ( + ph.p_align(endian) as usize, + ph.p_filesz(endian) as usize, + ph.p_memsz(endian) as usize, + ph.p_type(endian), + ph.p_vaddr(endian) as usize, + ) + }, + _ => panic_notls(format_args!("unknown AT_PHENT size {}", phent)), + }; + + let page_size = Sys::getpagesize(); + let voff = p_vaddr % page_size; + // let vaddr = ph.p_vaddr as usize - voff; + let vsize = (p_memsz + voff).div_ceil(page_size) * page_size; + + if p_type == elf::PT_TLS { + let valign = if p_align > 0 { + p_memsz.div_ceil(p_align) * p_align + } else { + p_memsz + }; + + unsafe { + STATIC_TCB_MASTER.ptr = p_vaddr as *const u8; + STATIC_TCB_MASTER.image_size = p_filesz; + STATIC_TCB_MASTER.offset = valign; + + let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB"); + tcb.masters_ptr = ptr::addr_of_mut!(STATIC_TCB_MASTER); + tcb.masters_len = mem::size_of::(); + tcb.copy_masters() + .expect_notls("failed to copy TLS master data"); + tcb.activate( + #[cfg(target_os = "redox")] + Some(thr_fd), + ); + } + + //TODO: Warning on multiple TLS sections? + return; + } + } +} + +#[cfg(any(target_os = "linux", target_os = "redox"))] +pub unsafe fn init( + sp: &'static Stack, + #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuardUpper, +) { + let tp: usize; + + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + { + const ARCH_GET_FS: usize = 0x1003; + let mut val = 0usize; + syscall!(ARCH_PRCTL, ARCH_GET_FS, &raw mut val); + tp = val; + } + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!( + "mrs {}, tpidr_el0", + out(reg) tp, + ); + } + #[cfg(all(target_os = "redox", target_arch = "x86"))] + { + let mut env = syscall::EnvRegisters::default(); + + { + let file = thr_fd + .dup(b"regs/env") + .expect_notls("failed to open handle for process registers"); + + file.read(&mut env).expect_notls("failed to read gsbase"); + } + + tp = env.gsbase as usize; + } + #[cfg(all(target_os = "redox", target_arch = "x86_64"))] + { + let mut env = syscall::EnvRegisters::default(); + + { + let file = thr_fd + .dup(b"regs/env") + .expect_notls("failed to open handle for process registers"); + + file.read(&mut env).expect_notls("failed to read fsbase"); + } + + tp = env.fsbase as usize; + } + #[cfg(all(target_os = "redox", target_arch = "riscv64"))] + unsafe { + core::arch::asm!( + "mv {}, tp", + out(reg) tp, + ); + } + + if tp == 0 { + static_init( + sp, + #[cfg(target_os = "redox")] + thr_fd, + ); + } else { + // The thread fd must already be present in the already existing TCB. Don't close it. + #[cfg(target_os = "redox")] + core::mem::forget(thr_fd); + } +} + +pub unsafe fn fini() { + if let Some(tcb) = unsafe { Tcb::current() } + && !tcb.linker_ptr.is_null() + { + let linker = unsafe { (*tcb.linker_ptr).lock() }; + linker.fini(); + } +} diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs new file mode 100644 index 0000000000..f20778e2ab --- /dev/null +++ b/src/ld_so/start.rs @@ -0,0 +1,440 @@ +// Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs + +use core::slice; + +use alloc::{ + borrow::ToOwned, + boxed::Box, + collections::BTreeMap, + string::{String, ToString}, + vec::Vec, +}; +use object::{ + NativeEndian, + elf::{self, PT_DYNAMIC, PT_PHDR}, + read::elf::{Dyn as _, ProgramHeader as _}, +}; + +use crate::{ + c_str::CStr, + header::{ + elf::{AT_BASE, AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM}, + unistd, + }, + ld_so::{ + dso::{ + DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rel, Rela, Relocation, + RelocationKind, Relr, apply_relr, + }, + linker::DebugFlags, + }, + platform::{auxv_iter, get_auxvs, types::c_char}, + start::Stack, + sync::mutex::Mutex, +}; + +use super::{ + PATH_SEP, + access::accessible, + debug::_r_debug, + linker::{Config, Linker}, + tcb::Tcb, +}; + +use generic_rt::ExpectTlsFree; + +#[cfg(target_pointer_width = "32")] +pub const SIZEOF_EHDR: usize = 52; + +#[cfg(target_pointer_width = "64")] +pub const SIZEOF_EHDR: usize = 64; + +unsafe fn get_argv(mut ptr: *const usize) -> (Vec, *const usize) { + //traverse the stack and collect argument vector + let mut argv = Vec::new(); + while unsafe { *ptr != 0 } { + let arg = unsafe { *ptr }; + match unsafe { CStr::from_ptr(arg as *const c_char).to_str() } { + Ok(arg_str) => argv.push(arg_str.to_owned()), + _ => { + eprintln!("ld.so: failed to parse argv[{}]", argv.len()); + unistd::_exit(1); + } + } + ptr = unsafe { ptr.add(1) }; + } + + (argv, ptr) +} + +unsafe fn get_env(mut ptr: *const usize) -> (BTreeMap, *const usize) { + //traverse the stack and collect argument environment variables + let mut envs = BTreeMap::new(); + while unsafe { *ptr != 0 } { + let env = unsafe { *ptr }; + if let Ok(arg_str) = unsafe { CStr::from_ptr(env as *const c_char).to_str() } { + let mut parts = arg_str.splitn(2, '='); + if let Some(key) = parts.next() + && let Some(value) = parts.next() + { + envs.insert(key.to_owned(), value.to_owned()); + } + } + ptr = unsafe { ptr.add(1) }; + } + + (envs, ptr) +} + +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn adjust_stack(sp: &'static mut Stack) { + let mut argv = sp.argv() as *mut usize; + + // Move arguments + loop { + let next_argv = argv.add(1); + let arg = *next_argv; + *argv = arg; + argv = next_argv; + if arg == 0 { + break; + } + } + + // Move environment + loop { + let next_argv = argv.add(1); + let arg = *next_argv; + *argv = arg; + argv = next_argv; + if arg == 0 { + break; + } + } + + // Move auxiliary vectors + loop { + let next_argv = argv.add(1); + let kind = *next_argv; + *argv = kind; + argv = next_argv; + let next_argv = argv.add(1); + let value = *next_argv; + *argv = value; + argv = next_argv; + if kind == 0 { + break; + } + } + sp.argc -= 1; +} + +fn resolve_path_name( + name_or_path: &str, + envs: &BTreeMap, +) -> Option<(String, String)> { + if accessible(name_or_path, unistd::F_OK).is_ok() { + return Some(( + name_or_path.to_string(), + name_or_path + .split("/") + .collect::>() + .last() + .unwrap() + .to_string(), + )); + } + if name_or_path.split("/").collect::>().len() != 1 { + return None; + } + + let env_path = envs.get("PATH")?; + for part in env_path.split(PATH_SEP) { + let path = if part.is_empty() { + format!("./{}", name_or_path) + } else { + format!("{}/{}", part, name_or_path) + }; + if accessible(&path, unistd::F_OK).is_ok() { + return Some((path.to_string(), name_or_path.to_string())); + } + } + None +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn relibc_ld_so_start( + sp: &'static mut Stack, + ld_entry: usize, + dynamic: *const Dyn, +) -> usize { + // Relocate ourselves. + // + // This function is very delicate as it must **not** contain relocations itself. References to + // external symbols **cannot** be made until `stage2()` so, this function might not be very + // elegant. + // + // At this stage the TCB is not setup either so `expect_notls` must be used instead of `expect` + // and `unwrap`. + let mut at_phdr = None; + let mut at_phnum = None; + let mut at_phent = None; + let mut at_base = None; + let mut at_entry = None; + for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::()) } { + match kind { + AT_PHDR => at_phdr = Some(value as *const ProgramHeader), + AT_PHNUM => at_phnum = Some(value), + AT_PHENT => at_phent = Some(value), + AT_BASE => at_base = Some(value), + AT_ENTRY => at_entry = Some(value), + _ => {} + } + } + + let at_phdr = at_phdr.expect_notls("`AT_PHDR` must be present"); + let at_phnum = at_phnum.expect_notls("`AT_PHNUM` must be present if `AT_PHDR` is"); + let at_phent = at_phent.expect_notls("`AT_PHENT` must be present if `AT_PHDR` is"); + assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::()); + let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) }; + + let at_entry = at_entry.expect_notls("`AT_ENTRY` must be present"); + let at_base = at_base.unwrap_or_default(); + + let self_base = if at_base != 0 { + at_base + } else { + let ph = phdrs + .iter() + .find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC) + .unwrap(); + unsafe { dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize } + }; + + let is_manual = at_entry == ld_entry; // Whether the dynamic linker was invoked as a command. + + let mut i = dynamic; + let mut rela_ptr = None; + let mut rela_len = None; + let mut relr_ptr = None; + let mut relr_len = None; + let mut rel_ptr = None; + let mut rel_len = None; + loop { + let entry = unsafe { &*i }; + let val = entry.d_val(NativeEndian); + let ptr = val as *const u8; + match entry.d_tag(NativeEndian) as u32 { + elf::DT_NULL => break, + elf::DT_RELA => rela_ptr = Some(ptr.cast::()), + elf::DT_RELASZ => rela_len = Some(val as usize / size_of::()), + elf::DT_RELAENT => { + assert_eq!(val as usize, size_of::(),); + } + elf::DT_REL => rel_ptr = Some(ptr.cast::()), + elf::DT_RELSZ => rel_len = Some(val as usize / size_of::()), + elf::DT_RELENT => { + assert_eq!(val as usize, size_of::()); + } + DT_RELR => relr_ptr = Some(ptr.cast::()), + DT_RELRSZ => relr_len = Some(val as usize / size_of::()), + DT_RELRENT => { + assert_eq!(val as usize, size_of::()); + } + _ => {} + } + i = unsafe { i.add(1) }; + } + + unsafe fn get_array<'a, T>( + ptr: Option<*const T>, + len: Option, + base_addr: usize, + ) -> &'a [T] { + if let Some(ptr) = ptr { + let len = len.expect_notls("dynamic entry was present without it's corresponding size"); + unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) } + } else { + &[] + } + } + + fn do_relocs<'a, T>(relocs: &'a [T], self_base: usize) + where + Relocation: From<&'a T>, + { + for reloc in relocs { + let reloc: Relocation = reloc.into(); + let ptr = (reloc.offset + self_base) as *mut usize; + if reloc.kind == RelocationKind::RELATIVE { + unsafe { *ptr = self_base + reloc.addend.unwrap_or_default() }; + } + } + } + + let rela = unsafe { get_array::(rela_ptr, rela_len, self_base) }; + let rel = unsafe { get_array::(rel_ptr, rel_len, self_base) }; + do_relocs(rela, self_base); + do_relocs(rel, self_base); + + unsafe { + let relr = get_array(relr_ptr, relr_len, self_base); + apply_relr(self_base as *const u8, relr); + } + + let mut base_addr = None; + if !is_manual { + // if we are not running in manual mode, then the main + // program is already loaded by the kernel and we want + // to use it. on redox, we treat it the same. + for ph in phdrs.iter() { + if ph.p_type(NativeEndian) == PT_PHDR { + assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once"); + base_addr = Some(unsafe { + phdrs + .as_ptr() + .cast::() + .sub(ph.p_vaddr(NativeEndian) as usize) + } as usize); + } + } + } + + stage2(sp, self_base, is_manual, base_addr) +} + +fn stage2( + sp: &'static mut Stack, + self_base: usize, + is_manual: bool, + base_addr: Option, +) -> usize { + // Setup TCB for ourselves. + unsafe { + #[cfg(target_os = "redox")] + let auxv = sp.auxv().cast(); + #[cfg(target_os = "redox")] + let thr_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_THR_FD) + .expect_notls("no thread fd present"); + + let tcb = Tcb::new(0).expect_notls("[ld.so]: failed to allocate bootstrap TCB"); + tcb.activate( + #[cfg(target_os = "redox")] + Some( + redox_rt::proc::FdGuard::new(thr_fd) + .to_upper() + .expect_notls("failed to move thread fd to upper table"), + ), + ); + #[cfg(target_os = "redox")] + { + let proc_fd = + crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_PROC_FD) + .expect_notls("no proc fd present"); + + let ns_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_NS_FD) + .filter(|&fd| fd != usize::MAX) + .map(|fd| { + redox_rt::proc::FdGuard::new(fd) + .to_upper() + .expect_notls("failed to move ns fd to upper table") + }); + + redox_rt::initialize( + redox_rt::proc::FdGuard::new(proc_fd) + .to_upper() + .expect_notls("failed to move proc fd to upper table"), + ns_fd, + ); + redox_rt::signal::setup_sighandler(&tcb.os_specific, true); + } + } + + // We get the arguments, the environment, and the auxilary vector + let (argv, envs, auxv) = unsafe { + let argv_start = sp.argv() as *mut usize; + let (argv, argv_end) = get_argv(argv_start); + let (envs, envs_end) = get_env(argv_end.add(1)); + let auxv = get_auxvs(envs_end.add(1)); + (argv, envs, auxv) + }; + + unsafe { + crate::platform::OUR_ENVIRON.unsafe_set( + envs.iter() + .map(|(k, v)| { + let mut var = Vec::with_capacity(k.len() + v.len() + 2); + var.extend(k.as_bytes()); + var.push(b'='); + var.extend(v.as_bytes()); + var.push(b'\0'); + let mut var = var.into_boxed_slice(); + let ptr = var.as_mut_ptr(); + core::mem::forget(var); + ptr.cast() + }) + .chain(core::iter::once(core::ptr::null_mut())) + .collect::>(), + ); + + crate::platform::environ = crate::platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr(); + } + + // we might need global lock for this kind of stuff + _r_debug.lock().r_ldbase = self_base; + + let name_or_path = if is_manual { + // ld.so is run directly by user and not via execve() or similar systemcall + println!("argv: {:#?}", argv); + println!("envs: {:#?}", envs); + println!("auxv: {:#x?}", auxv); + + if sp.argc < 2 { + eprintln!("ld.so [executable] [arguments...]"); + unistd::_exit(1); + } + unsafe { adjust_stack(sp) }; + argv[1].to_string() + } else { + argv[0].to_string() + }; + + // TODO: Fix memory leak, although minimal. + #[cfg(target_os = "redox")] + unsafe { + crate::platform::init_inner(auxv); + } + + let (path, _name) = match resolve_path_name(&name_or_path, &envs) { + Some((p, n)) => (p, n), + None => { + eprintln!("[ld.so]: failed to locate '{name_or_path}'"); + unistd::_exit(1); + } + }; + + let config = Config::from_env(&envs); + if config.debug_flags.contains(DebugFlags::LOAD) { + println!("[ld.so]: relocated self at {self_base:#x}!"); + if let Some(base_addr) = base_addr { + println!("[ld.so]: executable has been already loaded at {base_addr:#x?}"); + } + } + + let mut linker = Linker::new(config); + let entry = match linker.load_program(&path, base_addr) { + Ok(entry) => entry, + Err(err) => { + eprintln!("[ld.so]: failed to link '{path}': {err:?}"); + eprintln!("[ld.so]: enable debug output with `LD_DEBUG=all` for more information"); + unistd::_exit(1); + } + }; + if let Some(tcb) = unsafe { Tcb::current() } { + tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker))); + } + if is_manual { + eprintln!("[ld.so]: entry '{path}': {entry:#x}"); + } + entry +} diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs new file mode 100644 index 0000000000..2f90ad8cb6 --- /dev/null +++ b/src/ld_so/tcb.rs @@ -0,0 +1,386 @@ +use alloc::vec::Vec; +use core::{ + cell::UnsafeCell, + mem, + ops::{Deref, DerefMut}, + ptr, slice, + sync::atomic::AtomicBool, +}; +use generic_rt::GenericTcb; + +use crate::{ + header::sys_mman, + ld_so::linker::Linker, + platform::{Dlmalloc, Pal, Sys}, + pthread::{OsTid, Pthread}, + sync::{mutex::Mutex, waitval::Waitval}, +}; + +use super::linker::DlError; + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct Master { + /// Pointer to initial data + pub ptr: *const u8, + /// Length of initial data in bytes + pub image_size: usize, + pub segment_size: usize, + /// Offset in TLS to copy initial data to + pub offset: usize, +} + +impl Master { + /// The initial data for this TLS region + pub unsafe fn data(&self) -> &'static [u8] { + unsafe { slice::from_raw_parts(self.ptr, self.image_size) } + } +} + +#[cfg(target_os = "linux")] +pub type OsSpecific = (); + +#[cfg(target_os = "redox")] +pub type OsSpecific = redox_rt::signal::RtSigarea; + +#[derive(Debug)] +#[repr(C)] +// FIXME: Only return &Tcb, and use interior mutability, since it contains the Pthread struct +pub struct Tcb { + pub generic: GenericTcb, + /// Pointer to a list of initial TLS data + pub masters_ptr: *mut Master, + /// Size of the masters list in bytes (multiple of `mem::size_of::()`) + pub masters_len: usize, + /// Index of last copied Master + pub num_copied_masters: usize, + /// Pointer to dynamic linker + pub linker_ptr: *const Mutex, + /// pointer to rust memory allocator structure + pub mspace: *const Mutex, + /// Underlying pthread_t struct, pthread_self() returns &self.pthread + pub pthread: Pthread, + + // Dynamic TLS Vector + pub dtv_ptr: *mut *mut u8, + // Number of DTV entries. + pub dtv_len: usize, +} + +#[cfg(target_os = "redox")] +const _: () = { + if mem::size_of::() > syscall::PAGE_SIZE { + panic!("too large TCB!"); + } +}; + +impl Tcb { + /// Create a new TCB + /// + /// `size` is the size of the TLS in bytes. + #[allow(unsafe_op_in_unsafe_fn)] + pub unsafe fn new(size: usize) -> Result<&'static mut Self, DlError> { + let page_size = Sys::getpagesize(); + let (_abi_page, tls, tcb_page) = Self::os_new(size.next_multiple_of(page_size))?; + + let tcb_ptr = tcb_page.as_mut_ptr().cast::(); + ptr::write( + tcb_ptr, + Self { + generic: GenericTcb { + tls_end: tls.as_mut_ptr().add(tls.len()), + tls_len: tls.len(), + tcb_ptr: tcb_ptr.cast(), + tcb_len: tcb_page.len(), + os_specific: OsSpecific::default(), + }, + masters_ptr: ptr::null_mut(), + masters_len: 0, + num_copied_masters: 0, + linker_ptr: ptr::null(), + mspace: ptr::null(), + pthread: Pthread { + waitval: Waitval::new(), + flags: Default::default(), + has_enabled_cancelation: AtomicBool::new(false), + has_queued_cancelation: AtomicBool::new(false), + stack_base: core::ptr::null_mut(), + stack_size: 0, + os_tid: UnsafeCell::new(OsTid::default()), + }, + + dtv_ptr: ptr::null_mut(), + dtv_len: 0, + }, + ); + + Ok(&mut *tcb_ptr) + } + + /// Get the current TCB + pub unsafe fn current() -> Option<&'static mut Self> { + unsafe { Some(&mut *GenericTcb::::current_ptr()?.cast()) } + } + + /// A slice for all of the TLS data + pub unsafe fn tls(&self) -> Option<&'static mut [u8]> { + if self.tls_end.is_null() || self.tls_len == 0 { + None + } else { + unsafe { + let tls_start = self.tls_end.sub(self.tls_len); + Some(slice::from_raw_parts_mut(tls_start, self.tls_len)) + } + } + } + + /// The initial images for TLS + pub fn masters(&self) -> Option<&'static mut [Master]> { + if self.masters_ptr.is_null() || self.masters_len == 0 { + None + } else { + Some(unsafe { + slice::from_raw_parts_mut( + self.masters_ptr, + self.masters_len / mem::size_of::(), + ) + }) + } + } + + /// Copy data from masters + pub unsafe fn copy_masters(&mut self) -> Result<(), DlError> { + //TODO: Complain if masters or tls exist without the other + if let Some(tls) = unsafe { self.tls() } + && let Some(masters) = self.masters() + { + for master in masters + .iter() + .skip(self.num_copied_masters) + .filter(|master| master.image_size != 0) + { + let range = if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + // x86{_64} TLS layout is backwards + self.tls_len - master.offset..self.tls_len - master.offset + master.image_size + } else { + master.offset..master.offset + master.image_size + }; + if let Some(tls_data) = tls.get_mut(range) { + let data = unsafe { master.data() }; + #[cfg(feature = "trace_tls")] + log::trace!( + "tls master: {:p}, {:#x}: {:p}, {:#x}", + data.as_ptr(), + data.len(), + tls_data.as_mut_ptr(), + tls_data.len() + ); + tls_data.copy_from_slice(data); + } else { + return Err(DlError::Malformed); + } + } + self.num_copied_masters = masters.len(); + } + + Ok(()) + } + + /// The initial images for TLS + pub unsafe fn append_masters(&mut self, mut new_masters: Vec) { + if self.masters_ptr.is_null() { + self.masters_ptr = new_masters.as_mut_ptr(); + self.masters_len = new_masters.len() * mem::size_of::(); + mem::forget(new_masters); + } else { + // XXX: [`Vec::from_raw_parts`] cannot be used here as the masters were originally + // allocated by the ld.so allocator and that would violate that function's invariants. + let mut masters = self.masters().unwrap().to_vec(); + masters.extend(new_masters); + + self.masters_ptr = masters.as_mut_ptr(); + self.masters_len = masters.len() * mem::size_of::(); + mem::forget(masters); + } + } + + /// Activate TLS + pub unsafe fn activate( + &mut self, + #[cfg(target_os = "redox")] thr_fd: Option, + ) { + unsafe { + Self::os_arch_activate( + &self.os_specific, + self.tls_end as usize, + self.tls_len, + #[cfg(target_os = "redox")] + thr_fd, + ) + }; + } + + pub fn setup_dtv(&mut self, n: usize) { + if self.dtv_ptr.is_null() { + let mut dtv = vec![ptr::null_mut(); n]; + + if let Some(masters) = self.masters() { + for (i, master) in masters.iter().enumerate() { + let tls = unsafe { self.tls().unwrap() }; + let offset = if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + // x86{_64} TLS layout is backwards + self.tls_len - master.offset + } else { + master.offset + }; + + dtv[i] = unsafe { tls.as_mut_ptr().add(offset) }; + } + } + + let (ptr, len, _) = dtv.into_raw_parts(); + + self.dtv_ptr = ptr; + self.dtv_len = len; + } else { + // Resize DTV. + // + // XXX: [`Vec::from_raw_parts`] cannot be used here as the DTV was originally allocated + // by the ld.so allocator and that would violate that function's invariants. + let mut dtv = self.dtv_mut().to_vec(); + dtv.resize(n, ptr::null_mut()); + + let (ptr, len, _) = dtv.into_raw_parts(); + self.dtv_ptr = ptr; + self.dtv_len = len; + } + } + + pub fn dtv_mut(&mut self) -> &'static mut [*mut u8] { + if self.dtv_len != 0 { + unsafe { slice::from_raw_parts_mut(self.dtv_ptr, self.dtv_len) } + } else { + &mut [] + } + } + + /// Mapping with correct flags for TCB and TLS + #[allow(unsafe_op_in_unsafe_fn)] + unsafe fn map(size: usize) -> Result<&'static mut [u8], DlError> { + let ptr = Sys::mmap( + ptr::null_mut(), + size, + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE, + -1, + 0, + ) + .map_err(|_| DlError::Oom)?; + + ptr::write_bytes(ptr.cast::(), 0, size); + Ok(slice::from_raw_parts_mut(ptr.cast::(), size)) + } + + /// OS specific code to create a new TLS and TCB - Linux and Redox + /// + /// Memory layout: + /// + /// ```text + /// 0 page_size size (size + page_size * 2) + /// |----------|---------------------------|----------| + /// +++++++++++++++++++++++++++++++++++++++++++++++++++ + /// | ABI Page | TLS | TCB Page | + /// +++++++++++++++++++++++++++++++++++++++++++++++++++ + /// ^ $tp (aarch64) ^ $tp (x86_64) + /// ``` + /// + /// `$tp` refers to the architecture specific thread pointer. + /// + /// **Note**: On x86{_64}, the TLS layout is backwards (i.e. the first byte of the TLS is at + /// the end of the TLS region). + /// + /// ABI page layout for aarch64: + /// ```text + /// 0 4096 + /// +---------------------+ + /// | ABI Page | + /// +---------------------+ + /// ^ + /// | + /// +-------> (page_size - 16): pointer to the start of the TCB page + /// ``` + /// + /// ABI page layout for riscv64: + /// + /// ```text + /// 0 4096 + /// +---------------------+ + /// | ABI Page | + /// +---------------------+ + /// ^ + /// | + /// +-------> (page_size - 8): pointer to the start of the TCB page + /// ``` + /// + /// For x86_64, the ABI page is not used. + #[cfg(any(target_os = "linux", target_os = "redox"))] + unsafe fn os_new( + size: usize, + ) -> Result<(&'static mut [u8], &'static mut [u8], &'static mut [u8]), DlError> { + let page_size = Sys::getpagesize(); + let abi_tls_tcb = unsafe { Self::map(page_size + size + page_size)? }; + let (abi, tls_tcb) = abi_tls_tcb.split_at_mut(page_size); + let (tls, tcb) = tls_tcb.split_at_mut(size); + Ok((abi, tls, tcb)) + } + + /// OS and architecture specific code to activate TLS - Linux x86_64 + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + unsafe fn os_arch_activate(_os: &(), tls_end: usize, _tls_len: usize) { + const ARCH_SET_FS: usize = 0x1002; + unsafe { + syscall!(ARCH_PRCTL, ARCH_SET_FS, tls_end); + } + } + + #[cfg(all(target_os = "linux", target_arch = "aarch64"))] + unsafe fn os_arch_activate(_os: &(), tls_end: usize, tls_len: usize) { + // Uses ABI page + let abi_ptr = tls_end - tls_len - 16; + unsafe { + core::ptr::write(abi_ptr as *mut usize, tls_end); + core::arch::asm!( + "msr tpidr_el0, {}", + in(reg) abi_ptr, + ); + } + } + + #[cfg(target_os = "redox")] + unsafe fn os_arch_activate( + os: &OsSpecific, + tls_end: usize, + tls_len: usize, + thr_fd: Option, + ) { + unsafe { + if let Some(thr_fd) = thr_fd { + os.thr_fd.get().write(Some(thr_fd)); + } + redox_rt::tcb_activate(os, tls_end, tls_len) + } + } +} + +impl Deref for Tcb { + type Target = GenericTcb; + + fn deref(&self) -> &Self::Target { + &self.generic + } +} +impl DerefMut for Tcb { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.generic + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000..ea853da6e4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,117 @@ +//! POSIX C library, implemented in Rust. +//! +//! This crate exists to provide a standard libc as its public API. This is +//! largely provided by automatically generated bindings to the functions and +//! data structures in the [`header`] module. +//! +//! Currently, Linux and Redox syscall backends are supported. + +#![no_std] +#![feature(alloc_error_handler)] +#![feature(allocator_api)] +#![feature(c_variadic)] +#![feature(core_intrinsics)] +#![feature(macro_derive)] +#![feature(maybe_uninit_slice)] +#![feature(lang_items)] +#![feature(linkage)] +#![feature(pointer_is_aligned_to)] +#![feature(ptr_as_uninit)] +#![feature(slice_ptr_get)] +#![feature(stmt_expr_attributes)] +#![feature(sync_unsafe_cell)] +#![feature(thread_local)] +#![feature(vec_into_raw_parts)] +#![feature(negative_impls)] + +#[macro_use] +extern crate alloc; +extern crate cbitset; +extern crate memchr; +extern crate posix_regex; +extern crate rand; + +#[cfg(target_os = "linux")] +#[macro_use] +extern crate sc; + +#[cfg(target_os = "redox")] +extern crate syscall; + +#[macro_use] +mod macros; +pub mod c_str; +pub mod c_vec; +pub mod cxa; +pub mod db; +pub mod error; +pub mod fs; +pub mod header; +pub mod io; +pub mod iter; +pub mod ld_so; +pub mod out; +pub mod platform; +pub mod pthread; +pub mod raw_cell; +pub mod start; +pub mod sync; + +use crate::platform::{Allocator, NEWALLOCATOR}; + +#[global_allocator] +static ALLOCATOR: Allocator = NEWALLOCATOR; + +#[unsafe(no_mangle)] +pub extern "C" fn relibc_panic(pi: &::core::panic::PanicInfo) -> ! { + use core::fmt::Write; + + let mut w = platform::FileWriter::new(2); + let _ = w.write_fmt(format_args!("RELIBC PANIC: {}\n", pi)); + + core::intrinsics::abort(); +} + +#[cfg(not(test))] +#[panic_handler] +#[linkage = "weak"] +pub fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! { + relibc_panic(pi) +} + +#[cfg(not(test))] +#[lang = "eh_personality"] +#[linkage = "weak"] +pub extern "C" fn rust_eh_personality() {} + +#[cfg(not(test))] +#[alloc_error_handler] +#[linkage = "weak"] +#[allow(improper_ctypes_definitions)] +#[unsafe(no_mangle)] +pub extern "C" fn rust_oom(layout: ::core::alloc::Layout) -> ! { + // Layout not FFI-safe? + use core::fmt::Write; + + let mut w = platform::FileWriter::new(2); + let _ = w.write_fmt(format_args!( + "RELIBC OOM: {} bytes aligned to {} bytes\n", + layout.size(), + layout.align() + )); + + core::intrinsics::abort(); +} + +#[cfg(not(test))] +#[allow(non_snake_case)] +#[linkage = "weak"] +#[unsafe(no_mangle)] +pub extern "C" fn _Unwind_Resume() -> ! { + use core::fmt::Write; + + let mut w = platform::FileWriter::new(2); + let _ = w.write_str("_Unwind_Resume\n"); + + core::intrinsics::abort(); +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000..72b4e414db --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,585 @@ +/// Print to stdout +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => {{ + use core::fmt::Write; + let _ = $crate::platform::FileWriter::new(1).write_fmt(format_args!($($arg)*)); + }}; +} + +/// Print with new line to stdout. +/// Deprecated, consider using log::info instead +#[macro_export] +macro_rules! println { + () => { + $crate::print!("\n") + }; + ($($arg:tt)*) => { + $crate::print!("{}\n", format_args!($($arg)*)) + }; +} + +/// Print to stderr +#[macro_export] +macro_rules! eprint { + ($($arg:tt)*) => {{ + use core::fmt::Write; + let _ = $crate::platform::FileWriter::new(2).write_fmt(format_args!($($arg)*)); + }}; +} + +/// Print with new line to stderr. +/// Deprecated, consider using log::info instead +#[macro_export] +macro_rules! eprintln { + () => { + $crate::eprint!("\n") + }; + ($($arg:tt)*) => { + $crate::eprint!("{}\n", format_args!($($arg)*)) + }; +} + +pub const ISSUE_URL: &str = "https://gitlab.redox-os.org/redox-os/relibc/-/issues/"; + +// Skippable todo!(issue, fmt) +#[macro_export] +macro_rules! todo_skip { + ($issue:expr, $($arg:tt)*) => { + if $issue != 0 { + log::info!("TODO ({}{}): {}", $crate::macros::ISSUE_URL, $issue, format_args!($($arg)*)) + } else { + log::info!("TODO: {}", format_args!($($arg)*)) + } + }; +} + +// Recoverable error todo!(issue, fmt, err) +#[macro_export] +macro_rules! todo_error { + ($issue:expr, $err:expr, $($arg:tt)*) => { + if $issue != 0 { + log::error!("TODO ({}{}): {}: {}", $crate::macros::ISSUE_URL, $issue, format_args!($($arg)*), $err) + } else { + log::error!("TODO: {}: {:?}", format_args!($($arg)*), $err) + } + }; +} + +// Unrecoverable error todo!(issue, fmt) +#[macro_export] +macro_rules! todo_panic { + ($issue:expr, $($arg:tt)*) => { + if $issue != 0 { + todo!("{} ({}{})", format_args!($($arg)*), $crate::macros::ISSUE_URL, $issue) + } else { + todo!("{}", format_args!($($arg)*)) + } + }; +} + +#[macro_export] +#[cfg(feature = "no_trace")] +macro_rules! trace_expr { + ($expr:expr, $($arg:tt)*) => { + $expr + }; +} + +#[macro_export] +#[cfg(not(feature = "no_trace"))] +macro_rules! trace_expr { + ($expr:expr, $($arg:tt)*) => ({ + use $crate::header::errno::STR_ERROR; + use $crate::platform; + + log::trace!("{}", format_args!($($arg)*)); + + let trace_old_errno = platform::ERRNO.get(); + platform::ERRNO.set(0); + + let ret = $expr; + + let trace_errno = platform::ERRNO.get() as isize; + if trace_errno == 0 { + platform::ERRNO.set(trace_old_errno); + } + + let trace_strerror = if trace_errno >= 0 && trace_errno < STR_ERROR.len() as isize { + STR_ERROR[trace_errno as usize] + } else { + "Unknown error" + }; + + log::trace!("{} = {} ({}, {})", format_args!($($arg)*), ret, trace_errno, trace_strerror); + + ret + }); +} + +#[macro_export] +macro_rules! skipws { + ($ptr:expr) => { + while isspace(unsafe { *$ptr }) != 0 { + $ptr = unsafe { $ptr.add(1) }; + } + }; +} + +#[macro_export] +macro_rules! strtou_impl { + ($type:ident, $ptr:expr, $base:expr) => { + strtou_impl!($type, $ptr, $base, false) + }; + ($type:ident, $ptr:expr, $base:expr, $negative:expr) => {{ + let mut base = $base; + + if (base == 16 || base == 0) + && unsafe { *$ptr } == '0' as wchar_t + && (unsafe { *$ptr.add(1) } == 'x' as wchar_t + || unsafe { *$ptr.add(1) } == 'X' as wchar_t) + { + $ptr = unsafe { $ptr.add(2) }; + base = 16; + } + + if base == 0 { + base = if unsafe { *$ptr } == '0' as wchar_t { + 8 + } else { + 10 + }; + }; + + let mut result: $type = 0; + while let Some(digit) = + char::from_u32(unsafe { *$ptr } as u32).and_then(|c| c.to_digit(base as u32)) + { + let new = result.checked_mul(base as $type).and_then(|result| { + if $negative { + #[cfg(target_arch = "x86")] + { + result.checked_sub( + $type::try_from(digit).expect("single digit never overflows"), + ) + } + #[cfg(not(target_arch = "x86"))] + { + result.checked_sub($type::from(digit)) + } + } else { + #[cfg(target_arch = "x86")] + { + result.checked_add( + $type::try_from(digit).expect("single digit never overflows"), + ) + } + #[cfg(not(target_arch = "x86"))] + { + result.checked_add($type::from(digit)) + } + } + }); + result = match new { + Some(new) => new, + None => { + platform::ERRNO.set(ERANGE); + return !0; + } + }; + + $ptr = unsafe { $ptr.add(1) }; + } + result + }}; +} + +#[macro_export] +macro_rules! strto_impl { + // this variant is used by inttypes and stdlib + ( + $rettype:ty, $signed:expr, $maxval:expr, $minval:expr, $s:ident, $endptr:ident, $base:ident + ) => {{ + // ensure these are constants + const CHECK_SIGN: bool = $signed; + const MAX_VAL: $rettype = $maxval; + const MIN_VAL: $rettype = $minval; + + let set_endptr = |idx: isize| { + if !$endptr.is_null() { + // This is stupid, but apparently strto* functions want + // const input but mut output, yet the man page says + // "stores the address of the first invalid character in *endptr" + // so obviously it doesn't want us to clone it. + unsafe { + *$endptr = $s.offset(idx).cast_mut(); + } + } + }; + + let invalid_input = || { + platform::ERRNO.set(EINVAL); + set_endptr(0); + }; + + // only valid bases are 2 through 36 + if $base != 0 && !(2..=36).contains(&$base) { + invalid_input(); + return 0; + } + + let mut idx = 0; + + // skip any whitespace at the beginning of the string + while ctype::isspace(c_int::from(unsafe { *$s.offset(idx) })) != 0 { + idx += 1; + } + + // check for +/- + let positive = match is_positive(unsafe { *$s.offset(idx) }) { + Some((pos, i)) => { + idx += i; + pos + } + None => { + invalid_input(); + return 0; + } + }; + + // convert the string to a number + let num_str = unsafe { $s.offset(idx) }; + let res = match $base { + 0 => unsafe { detect_base(num_str) }.and_then(|($base, i)| { + idx += i; + unsafe { convert_integer(num_str.offset(i), $base) } + }), + 8 => unsafe { convert_octal(num_str) }, + 16 => unsafe { convert_hex(num_str) }, + _ => unsafe { convert_integer(num_str, $base) }, + }; + + // check for error parsing octal/hex prefix + // also check to ensure a number was indeed parsed + let (num, i, overflow) = match res { + Some(res) => res, + None => { + invalid_input(); + return 0; + } + }; + idx += i; + + let overflow = if CHECK_SIGN { + overflow || (num as c_long).is_negative() + } else { + overflow + }; + // account for the sign + let num = num as $rettype; + let num = if overflow { + platform::ERRNO.set(ERANGE); + if CHECK_SIGN { + if positive { MAX_VAL } else { MIN_VAL } + } else { + MAX_VAL + } + } else { + if positive { + num + } else { + // not using -num to keep the compiler happy + num.overflowing_neg().0 + } + }; + + set_endptr(idx); + + num + }}; + // this variant is used by wchar (also wcstoimax and wcstoumax from inttypes) + ($type:ident, $ptr:expr, $base:expr) => {{ + let negative = unsafe { *$ptr } == '-' as wchar_t; + if negative { + $ptr = unsafe { $ptr.add(1) }; + } + strtou_impl!($type, $ptr, $base, negative) + }}; +} + +#[macro_export] +macro_rules! strto_float_impl { + ($type:ident, $s:expr, $endptr:expr) => {{ + let mut s = $s; + let endptr = $endptr; + + while ctype::isspace(c_int::from(unsafe{*s})) != 0 { + s = unsafe{ s.offset(1)}; + } + + let mut result: $type = 0.0; + let mut exponent: Option<$type> = None; + let mut radix = 10; + + let result_sign = match unsafe{*s} as u8 { + b'-' => { + s = unsafe{s.offset(1)}; + -1.0 + } + b'+' => { + s = unsafe{s.offset(1)}; + 1.0 + } + _ => 1.0, + }; + + let rust_s = unsafe{CStr::from_ptr(s)}.to_string_lossy(); + + // detect NaN, Inf + if rust_s.to_lowercase().starts_with("inf") { + result = $type::INFINITY; + s = unsafe{s.offset(3)}; + } else if rust_s.to_lowercase().starts_with("nan") { + // we cannot signal negative NaN in LLVM backed languages + // https://github.com/rust-lang/rust/issues/73328 , https://github.com/rust-lang/rust/issues/81261 + result = $type::NAN; + s = unsafe{s.offset(3)}; + } else { + if unsafe{*s} as u8 == b'0' && unsafe{*s.offset(1)} as u8 == b'x' { + s = unsafe{s.offset(2)}; + radix = 16; + } + + while let Some(digit) = (unsafe{*s} as u8 as char).to_digit(radix) { + result *= radix as $type; + result += digit as $type; + s = unsafe{s.offset(1)}; + } + + if unsafe{*s} as u8 == b'.' { + s = unsafe{s.offset(1)}; + + let mut i = 1.0; + while let Some(digit) = (unsafe{*s} as u8 as char).to_digit(radix) { + i *= radix as $type; + result += digit as $type / i; + s = unsafe{s.offset(1)}; + } + } + + let s_before_exponent = s; + + exponent = match (unsafe{*s} as u8, radix) { + (b'e' | b'E', 10) | (b'p' | b'P', 16) => { + s = unsafe{s.offset(1)}; + + let is_exponent_positive = match unsafe{*s} as u8 { + b'-' => { + s = unsafe{s.offset(1)}; + false + } + b'+' => { + s = unsafe{s.offset(1)}; + true + } + _ => true, + }; + + // Exponent digits are always in base 10. + if (unsafe{*s} as u8 as char).is_digit(10) { + let mut exponent_value = 0; + + while let Some(digit) = (unsafe{*s} as u8 as char).to_digit(10) { + exponent_value *= 10; + exponent_value += digit; + s = unsafe{s.offset(1)}; + } + + let exponent_base = match radix { + 10 => 10u128, + 16 => 2u128, + _ => unreachable!(), + }; + + if is_exponent_positive { + Some(exponent_base.pow(exponent_value) as $type) + } else { + Some(1.0 / (exponent_base.pow(exponent_value) as $type)) + } + } else { + // Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback + s = s_before_exponent; + None + } + } + _ => None, + }; + } + + if !endptr.is_null() { + // This is stupid, but apparently strto* functions want + // const input but mut output, yet the man page says + // "stores the address of the first invalid character in *endptr" + // so obviously it doesn't want us to clone it. + unsafe{*endptr = s.cast_mut()}; + } + + if let Some(exponent) = exponent { + result_sign * result * exponent + } else { + result_sign * result + } + }}; +} + +/// Project an `Out` to `struct X { field: Out }`. +/// +/// It is allowed to include only a subset of the struct's fields. The struct must implement +/// `OutProject`. +#[macro_export] +macro_rules! out_project { + { + let $struct:ty { $($field:ident : $fieldty:ty),*$(,)? } = $src:ident; + } => { + // Verify $src actually has type Out<$struct>. Also verify it implements `OutProject`. This + // excludes + // + // - the case where $src is Out<&Struct>, where it would be very UB to just construct a + // writable reference to $src.$field, or a smart pointer + // - the case where there are unaligned fields where it would be UB to call ptr::write to + // them (requiring packed structs) + { + fn ensure_type(_t: &$crate::out::Out) {} + ensure_type::<$struct>(&$src); + } + // Verify there are no duplicate struct fields. This is not strictly necessary as Out lacks + // the noalias requirement, but forbidding the same field to occur multiple times would + // allow both cases. The compiler will reject any struct that reuses the same identifier. + const _: () = { + $( + if ::core::mem::offset_of!($struct, $field) % ::core::mem::align_of::<$fieldty>() != 0 { + panic!(concat!("unaligned field ", stringify!($field), " of struct ", stringify!($struct), ".")); + } + )* + struct S { + $( + $field: $fieldty + ),* + } + }; + + // Finally, create an Out<$fieldty> for each field. + $( + // getting the pointer to $field is safe + let $field = unsafe { &raw mut (*$crate::out::Out::<_>::as_mut_ptr(&mut $src)).$field }; + )* + $( + let mut $field: $crate::out::Out<$fieldty> = unsafe { + // SAFETY: the only guarantee is that the pointer is valid and writable for the + // duration of 'b where $src: Out<'b, T>. But if so, and T is a struct, that + // must also be true for all the struct fields. + $crate::out::Out::with_lifetime_of( + $crate::out::Out::nonnull($field), + &$src, + ) + }; + )* + } +} +#[macro_export] +macro_rules! OutProject { + derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident { + $( + $(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty + ),*$(,)? + } } => { + // SAFETY: As simple as it is, OutProject is valid for any struct, and the pattern we have + // matched above ensures $name is one. + unsafe impl $crate::out::OutProject for $name {} + } +} +#[macro_export] +#[cfg(not(feature = "check_against_libc_crate"))] +macro_rules! CheckVsLibcCrate { + derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident { + $( + $(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty + ),*$(,)? + } } => { + } +} + +// TODO: probably exists nice nightly features that allow conflicting impls. Then we wouldn't need +// much of this redundant code just to say A == B -> B == A and say A == B -> *mut A == *mut B. +pub trait LibcTypeEquals {} +//impl LibcTypeEquals for () {} +impl LibcTypeEquals<*mut A, *mut B> for () where (): LibcTypeEquals {} +impl LibcTypeEquals<*const A, *const B> for () where (): LibcTypeEquals {} +impl LibcTypeEquals<[A; N], [B; N]> for () where (): LibcTypeEquals {} +macro_rules! for_primitive_int( + ($i:ident) => { + impl LibcTypeEquals<$i, $i> for () {} + } +); +for_primitive_int!(u8); +for_primitive_int!(u16); +for_primitive_int!(u32); +for_primitive_int!(u64); +for_primitive_int!(u128); +for_primitive_int!(usize); +for_primitive_int!(i8); +for_primitive_int!(i16); +for_primitive_int!(i32); +for_primitive_int!(i64); +for_primitive_int!(i128); +for_primitive_int!(isize); +impl LibcTypeEquals for () {} +#[cfg(feature = "check_against_libc_crate")] +impl LibcTypeEquals<__libc_only_for_layout_checks::c_void, crate::platform::types::c_void> for () {} +#[cfg(feature = "check_against_libc_crate")] +impl LibcTypeEquals for () {} + +//impl LibcTypeEquals<__libc_only_for_layout_checks::c_void> + +/// Derive macro which checks that structs here are defined the same as in the libc crate. Perhaps +/// not sufficiently rigorous to soundly cast between the types, but should catch most mistakes. +#[macro_export] +#[cfg(feature = "check_against_libc_crate")] +macro_rules! CheckVsLibcCrate { + // XXX: not sure we can have the name be different from libc::$name without parameters to the + // derive macro + derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident { + $( + $(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty + ),*$(,)? + } } => { + // TODO: check repr(C)? probably possible to match on $attrs + #[allow(dead_code)] + const _: () = { + if ::core::mem::size_of::<$name>() != ::core::mem::size_of::<::__libc_only_for_layout_checks::$name>() { + panic!("struct size mismatch"); + } + if ::core::mem::align_of::<$name>() != ::core::mem::align_of::<::__libc_only_for_layout_checks::$name>() { + panic!("struct alignment mismatch"); + } + $( + if ::core::mem::offset_of!($name, $field) != ::core::mem::offset_of!(__libc_only_for_layout_checks::$name, $field) { + panic!("struct field offset mismatch"); + } + )* + }; + $( + // check all field types are equivalent + #[allow(dead_code)] + const _: () = { + fn ensure_ty(a: A, b: B) where (): $crate::macros::LibcTypeEquals:: {} + fn for_libc(a: $name, b: __libc_only_for_layout_checks::$name) { + let a: $type = panic!("never called"); + ensure_ty(a, b.$field); + } + }; + )* + impl $crate::macros::LibcTypeEquals<$name, __libc_only_for_layout_checks::$name> for () {} + impl $crate::macros::LibcTypeEquals<__libc_only_for_layout_checks::$name, $name> for () {} + } +} diff --git a/src/out.rs b/src/out.rs new file mode 100644 index 0000000000..5729a82b64 --- /dev/null +++ b/src/out.rs @@ -0,0 +1,231 @@ +//! Wrapper for the "out pointer" pattern. +//! +//! This is functionally equivalent to `&Cell>` except the only allowed operation is +//! to write a `T`. Using `MaybeUninit` directly would not have been equally general; a +//! `&mut MaybeUninit` could never then be created from a `&mut T` and passed to safe code, +//! which can safely replace it with `MaybeUninit::uninit` and make the existence of `&mut T` UB. +//! +//! As for the "`&Cell<...>`", this is to be slightly weaker than Rust's normally strict +//! requirement that `&mut` references are never aliased, which can typically not be assumed when +//! getting pointers from C. + +use core::{cell::UnsafeCell, fmt, marker::PhantomData, mem::MaybeUninit, ptr::NonNull}; + +/// Wrapper for write-only "out pointers" that are safe to write to +// TODO: We may want to change this to &mut MaybeUninit, or have a generic parameter deciding +// whether it should be noalias or not +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Out<'a, T: ?Sized> { + ptr: NonNull, + _marker: PhantomData<&'a UnsafeCell>, +} +impl<'a, T: ?Sized> Out<'a, T> { + /// # Safety + /// + /// - pointer must either be NULL, or be valid for the duration of lifetime `'a` + #[inline] + pub unsafe fn nullable(ptr: *mut T) -> Option { + Some(Self { + ptr: NonNull::new(ptr)?, + _marker: PhantomData, + }) + } + /// # Safety + /// + /// - pointer must be valid for the duration of lifetime `'a` + #[inline] + pub unsafe fn nonnull(ptr: *mut T) -> Self { + if cfg!(debug_assertions) { + assert!(!ptr.is_null()); + } + Self { + ptr: unsafe { NonNull::new_unchecked(ptr) }, + _marker: PhantomData, + } + } + #[inline] + pub fn from_mut(r: &'a mut T) -> Self { + // SAFETY: + // + // - `r` will obviously have the same lifetime as Self + // - a Rust reference is obviously valid as a pointer, and the lifetime is tied to that of + // this struct + unsafe { Self::nonnull(r) } + } + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.ptr.as_ptr() + } +} +impl<'a, T> Out<'a, T> { + #[inline] + pub fn from_uninit_mut(r: &'a mut MaybeUninit) -> Self { + // SAFETY: + // + // Same as for from_mut. It's fine if *r is uninitialized, as this wrapper only allows + // writes. + unsafe { Self::nonnull(r.as_mut_ptr()) } + } + #[inline] + pub fn write(&mut self, t: T) { + unsafe { + self.ptr.as_ptr().write(t); + } + } +} +impl<'a, T, const N: usize> Out<'a, [T; N]> { + #[inline] + pub fn as_slice_mut<'b>(&'b mut self) -> Out<'b, [T]> { + unsafe { + let ptr: *mut [T; N] = self.as_mut_ptr(); + Out::from_raw_parts(ptr.cast::(), N) + } + } +} +impl<'a, T> Out<'a, [T]> { + /// # Safety + /// + /// If `len > 0`, `ptr` be valid for `len` elements of `T`, during lifetime `'a`. + pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { + // Empty slices must be non-NULL in Rust, but C typically does not force this for + // pointer-length pairs. + let ptr = if len == 0 { + core::ptr::dangling_mut::() + } else { + ptr + }; + unsafe { Self::nonnull(core::ptr::slice_from_raw_parts_mut(ptr, len)) } + } + pub fn len(&self) -> usize { + self.ptr.as_ptr().len() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + // TODO: Maybe strengthen lifetimes? + #[inline] + pub fn split_at_checked<'b>(&'b mut self, n: usize) -> Option<[Out<'b, [T]>; 2]> { + let l = self.ptr.len(); + if n > l { + return None; + } + Some([ + Out { + ptr: unsafe { + NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut( + self.ptr.as_mut_ptr(), + n, + )) + }, + _marker: PhantomData, + }, + Out { + ptr: unsafe { + NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut( + self.ptr.as_mut_ptr().add(n), + l - n, + )) + }, + _marker: PhantomData, + }, + ]) + } + #[inline] + pub fn copy_from_slice(&mut self, src: &[T]) + where + T: Copy, + { + assert_eq!( + self.ptr.len(), + src.len(), + "Out::copy_from_slice size mismatch" + ); + unsafe { + // SAFETY: + // + // - we have already know from the existence of self that the slice is a valid writable + // pointer + // - src is similarly also a valid readable pointer of the same type + // - because of `T: Copy`, it is valid to copy bytes directly + // - although self.ptr may alias, src must not alias with any writable pointer, and the + // Copy bound ensures T cannot have interior mutability since `UnsafeCell: !Copy` + self.ptr + .as_mut_ptr() + .copy_from_nonoverlapping(src.as_ptr(), src.len()); + } + } + pub fn copy_common_length_from_slice(&mut self, src: &[T]) -> usize + where + T: Copy, + { + let l = src.len().min(self.len()); + self.split_at_checked(l).unwrap()[0].copy_from_slice(&src[..l]); + l + } + // TODO: better API, impl RangeBounds, also fn get(usize) -> Out + pub fn subslice<'b>(&'b mut self, start: usize, end: usize) -> Out<'b, [T]> { + assert!(start <= end); + assert!(end <= self.len()); + unsafe { Self::from_raw_parts(self.as_mut_ptr().as_mut_ptr().add(start), end - start) } + } + pub fn index<'b>(&'b mut self, i: usize) -> Out<'b, T> { + assert!(i <= self.len()); + unsafe { Out::nonnull(self.as_mut_ptr().as_mut_ptr().add(i)) } + } +} +// TODO: use bytemuck +impl Out<'_, [T]> { + pub fn zero(&mut self) { + let l = self.ptr.len(); + unsafe { + // SAFETY: + // - already know the pointer is valid up to its length + // - the Plain trait ensures zero is a valid bit pattern + self.ptr.as_mut_ptr().write_bytes(0, l) + } + } + #[inline] + pub fn cast_slice_to<'b, U>(mut self) -> Out<'b, [U]> + where + T: CastSlice, + { + assert_eq!(self.as_mut_ptr().as_mut_ptr() as usize % align_of::(), 0); + + let byte_length = self.as_mut_ptr().len() * size_of::(); + + unsafe { + Out::from_raw_parts( + self.as_mut_ptr().as_mut_ptr().cast(), + byte_length / size_of::(), + ) + } + } +} +// TODO: use bytemuck +pub unsafe trait CastSlice {} +unsafe impl CastSlice for u8 {} +unsafe impl CastSlice for i8 {} +unsafe impl CastSlice for u8 {} +unsafe impl CastSlice for i8 {} + +impl fmt::Pointer for Out<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:p}", self.ptr) + } +} +impl fmt::Debug for Out<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[Out: {:p}]", self.ptr) + } +} +/// Marker trait for types where it is sound to turn `Out` into `struct { ...: +/// Out<...> }` by simply referencing fields. This is safe for any struct but must not be +/// implemented for `Deref` types so that `Out<&struct { ... }>` is never projected in a way that +/// adds mutability. +pub unsafe trait OutProject {} + +impl<'a, T: ?Sized> Out<'a, T> { + pub unsafe fn with_lifetime_of<'b, U: ?Sized>(mut self, u: &'b U) -> Out<'b, T> { + unsafe { Out::nonnull(self.as_mut_ptr()) } + } +} diff --git a/src/platform/allocator/mod.rs b/src/platform/allocator/mod.rs new file mode 100644 index 0000000000..acdb643787 --- /dev/null +++ b/src/platform/allocator/mod.rs @@ -0,0 +1,121 @@ +use core::{ + alloc::{GlobalAlloc, Layout}, + cell::SyncUnsafeCell, + cmp, + mem::align_of, + ptr::{self, copy_nonoverlapping, write_bytes}, + sync::atomic::{AtomicPtr, Ordering}, +}; + +mod sys; +use super::types::*; +use crate::{ALLOCATOR, sync::Mutex}; +use dlmalloc::DlmallocCApi; + +pub type Dlmalloc = DlmallocCApi; + +#[allow(clippy::declare_interior_mutable_const)] +pub const NEWALLOCATOR: Allocator = Allocator::new(); + +pub struct Allocator { + inner: SyncUnsafeCell>, + pub ptr: AtomicPtr>, +} + +impl Allocator { + #[allow(clippy::new_without_default)] + pub const fn new() -> Self { + Allocator { + inner: SyncUnsafeCell::new(Mutex::new(Dlmalloc::new(sys::System::new()))), + ptr: AtomicPtr::new(ptr::null_mut()), + } + } + + pub fn get(&self) -> *const Mutex { + let ptr = self.ptr.load(Ordering::Acquire); + if !ptr.is_null() { + return ptr; + } + + self.inner.get() + } + + pub fn set(&self, mspace: *const Mutex) { + self.ptr.store(mspace.cast_mut(), Ordering::Release); + } +} + +unsafe impl GlobalAlloc for Allocator { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() <= align_of::() { + unsafe { (*self.get()).lock().malloc(layout.size()) } + } else { + unsafe { (*self.get()).lock().memalign(layout.align(), layout.size()) } + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { (*self.get()).lock().free(ptr) } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + let ptr = unsafe { self.alloc(layout) }; + if !ptr.is_null() && unsafe { (*self.get()).lock().calloc_must_clear(ptr) } { + unsafe { write_bytes(ptr, 0, layout.size()) }; + } + ptr + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= align_of::() { + unsafe { (*self.get()).lock().realloc(ptr, new_size) } + } else { + let new = + unsafe { self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())) }; + let old_size = layout.size(); + + if !new.is_null() { + let size = cmp::min(old_size, new_size); + unsafe { copy_nonoverlapping(ptr, new, size) }; + } + + unsafe { (*self.get()).lock().free(ptr) }; + + new + } + } +} + +pub unsafe fn alloc(size: size_t) -> *mut c_void { + unsafe { (*ALLOCATOR.get()).lock().malloc(size) }.cast() +} + +pub unsafe fn alloc_align(size: size_t, alignment: size_t) -> *mut c_void { + unsafe { (*ALLOCATOR.get()).lock().memalign(alignment, size) }.cast() +} + +pub unsafe fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void { + if ptr.is_null() { + unsafe { (*ALLOCATOR.get()).lock().malloc(size) }.cast() + } else { + unsafe { (*ALLOCATOR.get()).lock().realloc(ptr.cast(), size) }.cast() + } +} + +pub unsafe fn free(ptr: *mut c_void) { + if ptr.is_null() { + return; + } + unsafe { (*ALLOCATOR.get()).lock().free(ptr.cast()) } +} + +pub unsafe fn alloc_usable_size(ptr: *mut c_void) -> size_t { + if ptr.is_null() { + return 0; + } + unsafe { (*ALLOCATOR.get()).lock().usable_size(ptr.cast()) } +} diff --git a/src/platform/allocator/sys.rs b/src/platform/allocator/sys.rs new file mode 100644 index 0000000000..401910d59b --- /dev/null +++ b/src/platform/allocator/sys.rs @@ -0,0 +1,129 @@ +use crate::{ + header::{ + pthread::pthread_atfork, + sys_mman::{self, MREMAP_MAYMOVE}, + }, + platform::{Pal, Sys}, + sync::Mutex, +}; +use core::ptr; + +use dlmalloc::Allocator; + +/// System setting for Redox/Linux +pub struct System { + _priv: (), +} + +impl System { + pub const fn new() -> System { + System { _priv: () } + } +} + +static LOCK: Mutex<()> = Mutex::new(()); + +unsafe impl Allocator for System { + fn alloc(&self, size: usize) -> (*mut u8, usize, u32) { + let Ok(addr) = (unsafe { + Sys::mmap( + ptr::null_mut(), + size, + sys_mman::PROT_WRITE | sys_mman::PROT_READ, + sys_mman::MAP_ANON | sys_mman::MAP_PRIVATE, + -1, + 0, + ) + }) else { + return (ptr::null_mut(), 0, 0); + }; + (addr.cast::(), size, 0) + } + + fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8 { + let flags = if can_move { MREMAP_MAYMOVE } else { 0 }; + let Ok(ptr) = + (unsafe { Sys::mremap(ptr.cast(), oldsize, newsize, flags, ptr::null_mut()) }) + else { + return ptr::null_mut(); + }; + ptr.cast::() + } + + fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool { + unsafe { + if Sys::mremap(ptr.cast(), oldsize, newsize, 0, ptr::null_mut()).is_ok() { + return true; + } + Sys::munmap(ptr.add(newsize).cast(), oldsize - newsize).is_ok() + } + } + + fn free(&self, ptr: *mut u8, size: usize) -> bool { + unsafe { Sys::munmap(ptr.cast(), size).is_ok() } + } + + fn can_release_part(&self, _flags: u32) -> bool { + true + } + + fn allocates_zeros(&self) -> bool { + true + } + + fn page_size(&self) -> usize { + 4096 + } +} + +pub fn acquire_global_lock() { + unsafe { + // SAFETY: No data inside + LOCK.manual_lock(); + } +} + +pub(super) fn release_global_lock() { + unsafe { + // SAFETY: No data inside + LOCK.manual_unlock(); + } +} + +/// Allows the allocator to remain unsable in the child process, +/// after a call to `fork(2)` +/// +/// # Safety +/// +/// if used, this function must be called, +/// before any allocations are made with the global allocator. +pub unsafe fn enable_alloc_after_fork() { + // atfork must only be called once, to avoid a deadlock, + // where the handler attempts to acquire the global lock twice + static mut FORK_PROTECTED: bool = false; + + extern "C" fn _acquire_global_lock() { + acquire_global_lock() + } + + extern "C" fn _release_global_lock() { + release_global_lock() + } + + acquire_global_lock(); + // if a process forks, + // it will acquire the lock before any other thread, + // protecting it from deadlock, + // due to the child being created with only the calling thread. + unsafe { + if !FORK_PROTECTED { + pthread_atfork( + Some(_acquire_global_lock), + Some(_release_global_lock), + Some(_release_global_lock), + ); + FORK_PROTECTED = true; + } + } + release_global_lock(); +} diff --git a/src/platform/auxv_defs.rs b/src/platform/auxv_defs.rs new file mode 100644 index 0000000000..748800aa44 --- /dev/null +++ b/src/platform/auxv_defs.rs @@ -0,0 +1,60 @@ +pub const AT_NULL: usize = 0; /* End of vector */ +pub const AT_IGNORE: usize = 1; /* Entry should be ignored */ +pub const AT_EXECFD: usize = 2; /* File descriptor of program */ +pub const AT_PHDR: usize = 3; /* Program headers for program */ +pub const AT_PHENT: usize = 4; /* Size of program header entry */ +pub const AT_PHNUM: usize = 5; /* Number of program headers */ +pub const AT_PAGESZ: usize = 6; /* System page size */ +pub const AT_BASE: usize = 7; /* Base address of interpreter */ +pub const AT_FLAGS: usize = 8; /* Flags */ +pub const AT_ENTRY: usize = 9; /* Entry point of program */ +pub const AT_NOTELF: usize = 10; /* Program is not ELF */ +pub const AT_UID: usize = 11; /* Real uid */ +pub const AT_EUID: usize = 12; /* Effective uid */ +pub const AT_GID: usize = 13; /* Real gid */ +pub const AT_EGID: usize = 14; /* Effective gid */ +pub const AT_CLKTCK: usize = 17; /* Frequency of times() */ +pub const AT_PLATFORM: usize = 15; /* String identifying platform. */ +pub const AT_HWCAP: usize = 16; /* Machine-dependent hints about */ +pub const AT_FPUCW: usize = 18; /* Used FPU control word. */ +pub const AT_DCACHEBSIZE: usize = 19; /* Data cache block size. */ +pub const AT_ICACHEBSIZE: usize = 20; /* Instruction cache block size. */ +pub const AT_UCACHEBSIZE: usize = 21; /* Unified cache block size. */ +pub const AT_IGNOREPPC: usize = 22; /* Entry should be ignored. */ +pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/ +pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */ +pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/ +pub const AT_EXECFN: usize = 31; /* Filename of executable. */ + +// TODO: Downgrade aux vectors to getauxval constants on Redox, and use a regular struct for +// passing important runtime info between exec (or posix_spawn in the future) calls. + +#[cfg(target_os = "redox")] +// XXX: The name AT_CWD is already used in openat... for a completely different purpose. +pub const AT_REDOX_INITIAL_CWD_PTR: usize = 32; +#[cfg(target_os = "redox")] +pub const AT_REDOX_INITIAL_CWD_LEN: usize = 33; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_INHERITED_SIGIGNMASK: usize = 34; +#[cfg(all(target_os = "redox", target_pointer_width = "32"))] +pub const AT_REDOX_INHERITED_SIGIGNMASK_HI: usize = 35; +#[cfg(target_os = "redox")] +pub const AT_REDOX_INHERITED_SIGPROCMASK: usize = 36; +#[cfg(all(target_os = "redox", target_pointer_width = "32"))] +pub const AT_REDOX_INHERITED_SIGPROCMASK_HI: usize = 37; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_UMASK: usize = 40; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_PROC_FD: usize = 41; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_THR_FD: usize = 42; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_NS_FD: usize = 43; + +#[cfg(target_os = "redox")] +pub const AT_REDOX_CWD_FD: usize = 44; diff --git a/src/platform/linux/epoll.rs b/src/platform/linux/epoll.rs new file mode 100644 index 0000000000..ce5b9be3da --- /dev/null +++ b/src/platform/linux/epoll.rs @@ -0,0 +1,45 @@ +use core::mem; + +use super::{Sys, e_raw}; +use crate::{ + error::Result, + header::{bits_sigset_t::sigset_t, sys_epoll::epoll_event}, + platform::{ + PalEpoll, + types::{c_int, size_t}, + }, +}; + +impl PalEpoll for Sys { + fn epoll_create1(flags: c_int) -> Result { + Ok(unsafe { e_raw(syscall!(EPOLL_CREATE1, flags))? as c_int }) + } + + unsafe fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> Result<()> { + unsafe { + e_raw(syscall!(EPOLL_CTL, epfd, op, fd, event))?; + } + Ok(()) + } + + unsafe fn epoll_pwait( + epfd: c_int, + events: *mut epoll_event, + maxevents: c_int, + timeout: c_int, + sigmask: *const sigset_t, + ) -> Result { + let sigsetsize: size_t = mem::size_of::(); + unsafe { + e_raw(syscall!( + EPOLL_PWAIT, + epfd, + events, + maxevents, + timeout, + sigmask, + sigsetsize + )) + } + } +} diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs new file mode 100644 index 0000000000..995df1b2d3 --- /dev/null +++ b/src/platform/linux/mod.rs @@ -0,0 +1,859 @@ +#[cfg(target_arch = "x86_64")] +use core::arch::asm; + +use super::{Pal, types::*}; +use crate::{ + c_str::CStr, + error::{Errno, Result}, + header::{ + bits_timespec::timespec, + dirent::dirent, + errno::{EINVAL, EIO}, + fcntl::{AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR}, + signal::{SIGCHLD, sigevent}, + sys_resource::{rlimit, rusage}, + sys_select::timeval, + sys_stat::{S_IFIFO, stat}, + sys_statvfs::statvfs, + sys_time::timezone, + sys_utsname::utsname, + time::itimerspec, + unistd::{SEEK_CUR, SEEK_SET}, + }, + ld_so::tcb::OsSpecific, + out::Out, +}; +use core::{num::NonZeroU64, ptr}; +// use header::sys_times::tms; + +mod epoll; +mod ptrace; +mod signal; +mod socket; + +const SYS_CLONE: usize = 56; +const CLONE_VM: usize = 0x0100; +const CLONE_FS: usize = 0x0200; +const CLONE_FILES: usize = 0x0400; +const CLONE_SIGHAND: usize = 0x0800; +const CLONE_THREAD: usize = 0x00010000; + +#[repr(C)] +#[derive(Default)] +struct linux_statfs { + f_type: c_long, /* type of file system (see below) */ + f_bsize: c_long, /* optimal transfer block size */ + f_blocks: fsblkcnt_t, /* total data blocks in file system */ + f_bfree: fsblkcnt_t, /* free blocks in fs */ + f_bavail: fsblkcnt_t, /* free blocks available to unprivileged user */ + f_files: fsfilcnt_t, /* total file nodes in file system */ + f_ffree: fsfilcnt_t, /* free file nodes in fs */ + f_fsid: c_long, /* file system id */ + f_namelen: c_long, /* maximum length of filenames */ + f_frsize: c_long, /* fragment size (since Linux 2.6) */ + f_flags: c_long, + f_spare: [c_long; 4], +} + +// TODO +const ERRNO_MAX: usize = 4095; + +pub fn e_raw(sys: usize) -> Result { + if sys > ERRNO_MAX.wrapping_neg() { + Err(Errno(sys.wrapping_neg() as _)) + } else { + Ok(sys) + } +} + +/// Linux syscall implementation of [`Pal`]. +pub struct Sys; + +impl Sys { + pub unsafe fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> Result { + // TODO: Somehow support varargs to syscall?? + Ok(e_raw(syscall!(IOCTL, fd, request, out))? as c_int) + } + + // fn times(out: *mut tms) -> clock_t { + // unsafe { syscall!(TIMES, out) as clock_t } + // } +} + +impl Pal for Sys { + fn faccessat(fd: c_int, path: CStr, amode: c_int, flags: c_int) -> Result<()> { + e_raw(unsafe { syscall!(FACCESSAT, fd, path.as_ptr(), amode, flags) }).map(|_| ()) + } + + unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void> { + Ok(e_raw(unsafe { syscall!(BRK, addr) })? as *mut c_void) + } + + fn chdir(path: CStr) -> Result<()> { + e_raw(unsafe { syscall!(CHDIR, path.as_ptr()) }).map(|_| ()) + } + + fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()> { + e_raw(unsafe { + syscall!( + FCHOWNAT, + fildes, + path.as_ptr(), + owner as u32, + group as u32, + flags + ) + }) + .map(|_| ()) + } + + fn clock_getres(clk_id: clockid_t, res: Option>) -> Result<()> { + e_raw(unsafe { + syscall!( + CLOCK_GETRES, + clk_id, + res.map_or(core::ptr::null_mut(), |mut p| p.as_mut_ptr()) + ) + }) + .map(|_| ()) + } + + fn clock_gettime(clk_id: clockid_t, mut tp: Out) -> Result<()> { + e_raw(unsafe { syscall!(CLOCK_GETTIME, clk_id, tp.as_mut_ptr()) }).map(|_| ()) + } + + unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()> { + e_raw(syscall!(CLOCK_SETTIME, clk_id, tp)).map(|_| ()) + } + + fn close(fildes: c_int) -> Result<()> { + e_raw(unsafe { syscall!(CLOSE, fildes) }).map(|_| ()) + } + + fn dup(fildes: c_int) -> Result { + e_raw(unsafe { syscall!(DUP, fildes) }).map(|f| f as c_int) + } + + fn dup2(fildes: c_int, fildes2: c_int) -> Result { + e_raw(unsafe { syscall!(DUP3, fildes, fildes2, 0) }).map(|f| f as c_int) + } + + unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> Result<()> { + e_raw(syscall!(EXECVE, path.as_ptr(), argv, envp))?; + unreachable!() + } + unsafe fn fexecve( + fildes: c_int, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> Result<()> { + let empty = b"\0"; + let empty_ptr = empty.as_ptr().cast::(); + e_raw(syscall!( + EXECVEAT, + fildes, + empty_ptr, + argv, + envp, + AT_EMPTY_PATH + ))?; + unreachable!() + } + + fn exit(status: c_int) -> ! { + unsafe { + syscall!(EXIT, status); + } + loop {} + } + unsafe fn exit_thread(_stack_base: *mut (), _stack_size: usize) -> ! { + // TODO + Self::exit(0) + } + + fn fchdir(fildes: c_int) -> Result<()> { + e_raw(unsafe { syscall!(FCHDIR, fildes) }).map(|_| ()) + } + + fn fchmod(fildes: c_int, mode: mode_t) -> Result<()> { + e_raw(unsafe { syscall!(FCHMOD, fildes, mode) }).map(|_| ()) + } + + fn fchmodat(dirfd: c_int, path: Option, mode: mode_t, flags: c_int) -> Result<()> { + e_raw(unsafe { + syscall!( + FCHMODAT, + dirfd, + path.map_or(core::ptr::null(), |p| p.as_ptr()), + mode, + flags + ) + }) + .map(|_| ()) + } + + fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()> { + e_raw(unsafe { syscall!(FCHOWN, fildes, owner, group) }).map(|_| ()) + } + + fn fdatasync(fildes: c_int) -> Result<()> { + e_raw(unsafe { syscall!(FDATASYNC, fildes) }).map(|_| ()) + } + + fn flock(fd: c_int, operation: c_int) -> Result<()> { + e_raw(unsafe { syscall!(FLOCK, fd, operation) }).map(|_| ()) + } + + fn fstat(fildes: c_int, mut buf: Out) -> Result<()> { + let empty = b"\0"; + let empty_ptr = empty.as_ptr().cast::(); + e_raw(unsafe { + syscall!( + NEWFSTATAT, + fildes, + empty_ptr, + buf.as_mut_ptr(), + AT_EMPTY_PATH + ) + }) + .map(|_| ()) + } + + fn fstatat(fildes: c_int, path: Option, mut buf: Out, flags: c_int) -> Result<()> { + e_raw(unsafe { + syscall!( + NEWFSTATAT, + fildes, + path.map_or(core::ptr::null(), |s| s.as_ptr()), + buf.as_mut_ptr(), + flags + ) + }) + .map(|_| ()) + } + + fn fstatvfs(fildes: c_int, mut buf: Out) -> Result<()> { + let buf = buf.as_mut_ptr(); + + let mut kbuf = linux_statfs::default(); + let kbuf_ptr = &raw mut kbuf; + e_raw(unsafe { syscall!(FSTATFS, fildes, kbuf_ptr) })?; + + if !buf.is_null() { + unsafe { + (*buf).f_bsize = kbuf.f_bsize as c_ulong; + (*buf).f_frsize = if kbuf.f_frsize != 0 { + kbuf.f_frsize + } else { + kbuf.f_bsize + } as c_ulong; + (*buf).f_blocks = kbuf.f_blocks; + (*buf).f_bfree = kbuf.f_bfree; + (*buf).f_bavail = kbuf.f_bavail; + (*buf).f_files = kbuf.f_files; + (*buf).f_ffree = kbuf.f_ffree; + (*buf).f_favail = kbuf.f_ffree; + (*buf).f_fsid = kbuf.f_fsid as c_ulong; + (*buf).f_flag = kbuf.f_flags as c_ulong; + (*buf).f_namemax = kbuf.f_namelen as c_ulong; + } + } + Ok(()) + } + + fn fcntl(fildes: c_int, cmd: c_int, arg: c_ulonglong) -> Result { + Ok(e_raw(unsafe { syscall!(FCNTL, fildes, cmd, arg) })? as c_int) + } + + unsafe fn fork() -> Result { + Ok(e_raw(unsafe { syscall!(CLONE, SIGCHLD, 0, 0, 0, 0) })? as pid_t) + } + + fn fpath(fildes: c_int, out: &mut [u8]) -> Result { + let proc_path = format!("/proc/self/fd/{}\0", fildes).into_bytes(); + Self::readlink(CStr::from_bytes_with_nul(&proc_path).unwrap(), out) + } + + fn fsync(fildes: c_int) -> Result<()> { + e_raw(unsafe { syscall!(FSYNC, fildes) }).map(|_| ()) + } + + fn ftruncate(fildes: c_int, length: off_t) -> Result<()> { + e_raw(unsafe { syscall!(FTRUNCATE, fildes, length) }).map(|_| ()) + } + + #[inline] + unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<×pec>) -> Result<()> { + let deadline = deadline.map_or(0, |d| ptr::from_ref(d) as usize); + e_raw(unsafe { + syscall!( + FUTEX, addr, // uaddr + 9, // futex_op: FUTEX_WAIT_BITSET + val, // val + deadline, // timeout: deadline + 0, // uaddr2/val2: 0/NULL + 0xffffffff // val3: FUTEX_BITSET_MATCH_ANY + ) + }) + .map(|_| ()) + } + #[inline] + unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result { + e_raw(unsafe { + syscall!(FUTEX, addr, 1 /* FUTEX_WAKE */, num) + }) + .map(|n| n as u32) + } + + unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()> { + e_raw(unsafe { syscall!(UTIMENSAT, fd, ptr::null::(), times, 0) }).map(|_| ()) + } + + unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()> { + e_raw(unsafe { syscall!(UTIMENSAT, AT_FDCWD, path.as_ptr(), times, 0) }).map(|_| ()) + } + + fn getcwd(mut buf: Out<[u8]>) -> Result<()> { + e_raw(unsafe { + syscall!( + GETCWD, + buf.as_mut_ptr().as_mut_ptr(), + buf.as_mut_ptr().len() + ) + })?; + Ok(()) + } + + fn getdents(fd: c_int, buf: &mut [u8], _off: u64) -> Result { + e_raw(unsafe { syscall!(GETDENTS64, fd, buf.as_mut_ptr(), buf.len()) }) + } + fn dir_seek(fd: c_int, off: u64) -> Result<()> { + e_raw(unsafe { syscall!(LSEEK, fd, off, SEEK_SET) })?; + Ok(()) + } + unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)> { + let dent = this_dent.as_ptr().cast::(); + Some((unsafe { (*dent).d_reclen }, unsafe { (*dent).d_off } as u64)) + } + + fn getegid() -> gid_t { + // Always successful + unsafe { syscall!(GETEGID) as gid_t } + } + + fn geteuid() -> uid_t { + // Always successful + unsafe { syscall!(GETEUID) as uid_t } + } + + fn getgid() -> gid_t { + // Always successful + unsafe { syscall!(GETGID) as gid_t } + } + + fn getgroups(mut list: Out<[gid_t]>) -> Result { + Ok(e_raw(unsafe { + syscall!( + GETGROUPS, + list.len() as c_int, + list.as_mut_ptr().as_mut_ptr() + ) + })? as c_int) + } + + fn getpagesize() -> usize { + 4096 + } + + fn getpgid(pid: pid_t) -> Result { + Ok(e_raw(unsafe { syscall!(GETPGID, pid) })? as pid_t) + } + + fn getpid() -> pid_t { + // Always successful + unsafe { syscall!(GETPID) as pid_t } + } + + fn getppid() -> pid_t { + // Always successful + unsafe { syscall!(GETPPID) as pid_t } + } + + fn getpriority(which: c_int, who: id_t) -> Result { + Ok(e_raw(unsafe { syscall!(GETPRIORITY, which, who) })? as c_int) + } + + fn getrandom(buf: &mut [u8], flags: c_uint) -> Result { + e_raw(unsafe { syscall!(GETRANDOM, buf.as_mut_ptr(), buf.len(), flags) }) + } + + fn getrlimit(resource: c_int, mut rlim: Out) -> Result<()> { + e_raw(unsafe { syscall!(GETRLIMIT, resource, rlim.as_mut_ptr()) }).map(|_| ()) + } + + fn getresgid( + rgid: Option>, + egid: Option>, + sgid: Option>, + ) -> Result<()> { + unsafe { + e_raw(syscall!( + GETRESGID, + rgid.map_or(0, |mut r| r.as_mut_ptr() as usize), + egid.map_or(0, |mut r| r.as_mut_ptr() as usize), + sgid.map_or(0, |mut r| r.as_mut_ptr() as usize) + )) + .map(|_| ()) + } + } + fn getresuid( + ruid: Option>, + euid: Option>, + suid: Option>, + ) -> Result<()> { + unsafe { + e_raw(syscall!( + GETRESUID, + ruid.map_or(0, |mut r| r.as_mut_ptr() as usize), + euid.map_or(0, |mut r| r.as_mut_ptr() as usize), + suid.map_or(0, |mut r| r.as_mut_ptr() as usize) + )) + .map(|_| ()) + } + } + + unsafe fn setrlimit(resource: c_int, rlimit: *const rlimit) -> Result<()> { + e_raw(syscall!(SETRLIMIT, resource, rlimit)).map(|_| ()) + } + + fn getrusage(who: c_int, mut r_usage: Out) -> Result<()> { + e_raw(unsafe { syscall!(GETRUSAGE, who, r_usage.as_mut_ptr()) })?; + Ok(()) + } + + fn getsid(pid: pid_t) -> Result { + Ok(e_raw(unsafe { syscall!(GETSID, pid) })? as pid_t) + } + + fn gettid() -> pid_t { + // Always successful + unsafe { syscall!(GETTID) as pid_t } + } + + fn gettimeofday(mut tp: Out, tzp: Option>) -> Result<()> { + e_raw(unsafe { + syscall!( + GETTIMEOFDAY, + tp.as_mut_ptr(), + tzp.map_or(0, |mut p| p.as_mut_ptr() as usize) + ) + }) + .map(|_| ()) + } + + fn getuid() -> uid_t { + unsafe { syscall!(GETUID) as uid_t } + } + + #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { + e_raw(unsafe { syscall!(LCHOWN, path.as_ptr(), owner, group) }).map(|_| ()) + } + + #[cfg(target_arch = "aarch64")] + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { + e_raw(unsafe { + syscall!( + FCHOWNAT, + AT_FDCWD, + path.as_ptr(), + owner as u32, + group as u32, + crate::header::fcntl::AT_SYMLINK_NOFOLLOW + ) + }) + .map(|_| ()) + } + + fn linkat(fd1: c_int, path1: CStr, fd2: c_int, path2: CStr, flags: c_int) -> Result<()> { + e_raw(unsafe { syscall!(LINKAT, fd1, path1.as_ptr(), fd2, path2.as_ptr(), flags) }) + .map(|_| ()) + } + + fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result { + e_raw(unsafe { syscall!(LSEEK, fildes, offset, whence) }).map(|o| o as off_t) + } + + fn mkdirat(dir_fildes: c_int, path: CStr, mode: mode_t) -> Result<()> { + e_raw(unsafe { syscall!(MKDIRAT, dir_fildes, path.as_ptr(), mode) }).map(|_| ()) + } + + fn mknodat(dir_fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> Result<()> { + // Note: dev_t is c_long (i64) and __kernel_dev_t is u32; So we need to cast it + // and check for overflow + let k_dev: c_uint = dev as c_uint; + if dev_t::from(k_dev) != dev { + return Err(Errno(EINVAL)); + } + + e_raw(unsafe { syscall!(MKNODAT, dir_fildes, path.as_ptr(), mode, k_dev) }).map(|_| ()) + } + + fn mkfifoat(dir_fd: c_int, path: CStr, mode: mode_t) -> Result<()> { + Sys::mknodat(dir_fd, path, mode | S_IFIFO, 0) + } + + fn mkfifo(path: CStr, mode: mode_t) -> Result<()> { + Sys::mknod(path, mode | S_IFIFO, 0) + } + + unsafe fn mlock(addr: *const c_void, len: usize) -> Result<()> { + e_raw(unsafe { syscall!(MLOCK, addr, len) }).map(|_| ()) + } + + unsafe fn mlockall(flags: c_int) -> Result<()> { + e_raw(unsafe { syscall!(MLOCKALL, flags) }).map(|_| ()) + } + + unsafe fn mmap( + addr: *mut c_void, + len: usize, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, + ) -> Result<*mut c_void> { + Ok(e_raw(syscall!(MMAP, addr, len, prot, flags, fildes, off))? as *mut c_void) + } + + unsafe fn mremap( + addr: *mut c_void, + len: usize, + new_len: usize, + flags: c_int, + args: *mut c_void, + ) -> Result<*mut c_void> { + Ok(e_raw(syscall!(MREMAP, addr, len, new_len, flags, args))? as *mut c_void) + } + + unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> Result<()> { + e_raw(syscall!(MPROTECT, addr, len, prot)).map(|_| ()) + } + + unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> { + e_raw(syscall!(MSYNC, addr, len, flags)).map(|_| ()) + } + + unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> { + e_raw(syscall!(MUNLOCK, addr, len)).map(|_| ()) + } + + unsafe fn munlockall() -> Result<()> { + e_raw(unsafe { syscall!(MUNLOCKALL) }).map(|_| ()) + } + + unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()> { + e_raw(syscall!(MUNMAP, addr, len)).map(|_| ()) + } + + unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> { + e_raw(syscall!(MADVISE, addr, len, flags)).map(|_| ()) + } + + unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> { + e_raw(unsafe { syscall!(NANOSLEEP, rqtp, rmtp) }).map(|_| ()) + } + + fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result { + e_raw(unsafe { syscall!(OPENAT, AT_FDCWD, path.as_ptr(), oflag, mode) }) + .map(|fd| fd as c_int) + } + + fn openat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result { + e_raw(unsafe { syscall!(OPENAT, dirfd, path.as_ptr(), oflag, mode) }).map(|fd| fd as c_int) + } + + fn pipe2(mut fildes: Out<[c_int; 2]>, flags: c_int) -> Result<()> { + e_raw(unsafe { syscall!(PIPE2, fildes.as_mut_ptr(), flags) }).map(|_| ()) + } + + fn posix_fallocate(fd: c_int, offset: u64, length: NonZeroU64) -> Result<()> { + let length = length.get(); + e_raw(unsafe { syscall!(FALLOCATE, fd, 0, offset, length) }).map(|_| ()) + } + + fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result { + let current_offset = Self::lseek(fildes, 0, SEEK_CUR)? as u64; + let bytes_read = Self::getdents(fildes, buf, current_offset)?; + if bytes_read == 0 { + return Ok(0); + } + let mut bytes_processed = 0; + let mut next_offset = current_offset; + + while bytes_processed < bytes_read { + let remaining_slice = &buf[bytes_processed..]; + let (reclen, opaque_next) = + unsafe { Self::dent_reclen_offset(remaining_slice, bytes_processed) } + .ok_or(Errno(EIO))?; + if reclen == 0 { + return Err(Errno(EIO)); + } + + bytes_processed += reclen as usize; + next_offset = opaque_next; + } + + Self::lseek(fildes, next_offset as off_t, SEEK_SET)?; + Ok(bytes_read) + } + + #[cfg(target_arch = "x86_64")] + unsafe fn rlct_clone( + stack: *mut usize, + _os_specific: &mut OsSpecific, + ) -> Result { + let flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD; + let pid; + unsafe { + asm!(" + # Call clone syscall + syscall + + # Check if child or parent + test rax, rax + jnz 2f + + # Load registers + pop rax + pop rdi + pop rsi + pop rdx + pop rcx + pop r8 + pop r9 + + # Call entry point + call rax + + # Exit + mov rax, 60 + xor rdi, rdi + syscall + + # Invalid instruction on failure to exit + ud2 + + # Return PID if parent + 2: + ", + inout("rax") SYS_CLONE => pid, + inout("rdi") flags => _, + inout("rsi") stack => _, + inout("rdx") 0 => _, + inout("r10") 0 => _, + inout("r8") 0 => _, + //TODO: out("rbx") _, + out("rcx") _, + out("r9") _, + out("r11") _, + out("r12") _, + out("r13") _, + out("r14") _, + out("r15") _, + ); + } + let tid = e_raw(pid)?; + + Ok(crate::pthread::OsTid { thread_id: tid }) + } + + #[cfg(target_arch = "aarch64")] + unsafe fn rlct_clone( + stack: *mut usize, + _os_specific: &mut OsSpecific, + ) -> Result { + todo!("rlct_clone not implemented for aarch64 yet") + } + + unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<()> { + let tgid = Self::getpid(); + e_raw(unsafe { syscall!(TGKILL, tgid, os_tid.thread_id, signal) }).map(|_| ()) + } + + fn current_os_tid() -> crate::pthread::OsTid { + crate::pthread::OsTid { + thread_id: unsafe { syscall!(GETTID) }, + } + } + + fn read(fildes: c_int, buf: &mut [u8]) -> Result { + e_raw(unsafe { syscall!(READ, fildes, buf.as_mut_ptr(), buf.len()) }) + } + fn pread(fildes: c_int, buf: &mut [u8], off: off_t) -> Result { + e_raw(unsafe { syscall!(PREAD64, fildes, buf.as_mut_ptr(), buf.len(), off) }) + } + + fn readlinkat(dirfd: c_int, pathname: CStr, out: &mut [u8]) -> Result { + e_raw(unsafe { + syscall!( + READLINKAT, + dirfd, + pathname.as_ptr(), + out.as_mut_ptr(), + out.len() + ) + }) + } + + fn renameat(old_dir: c_int, old_path: CStr, new_dir: c_int, new_path: CStr) -> Result<()> { + e_raw(unsafe { + syscall!( + RENAMEAT, + old_dir, + old_path.as_ptr(), + new_dir, + new_path.as_ptr() + ) + }) + .map(|_| ()) + } + + fn renameat2( + old_dir: c_int, + old_path: CStr, + new_dir: c_int, + new_path: CStr, + flags: c_uint, + ) -> Result<()> { + e_raw(unsafe { + syscall!( + RENAMEAT2, + old_dir, + old_path.as_ptr(), + new_dir, + new_path.as_ptr(), + flags + ) + }) + .map(|_| ()) + } + + fn rmdir(path: CStr) -> Result<()> { + e_raw(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), AT_REMOVEDIR) }).map(|_| ()) + } + + fn sched_yield() -> Result<()> { + e_raw(unsafe { syscall!(SCHED_YIELD) }).map(|_| ()) + } + + unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> { + e_raw(unsafe { syscall!(SETGROUPS, size, list) }).map(|_| ()) + } + + fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> { + e_raw(unsafe { syscall!(SETPGID, pid, pgid) }).map(|_| ()) + } + + fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()> { + e_raw(unsafe { syscall!(SETPRIORITY, which, who, prio) }).map(|_| ()) + } + + fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> { + e_raw(unsafe { syscall!(SETRESGID, rgid, egid, sgid) }).map(|_| ()) + } + + fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> { + e_raw(unsafe { syscall!(SETRESUID, ruid, euid, suid) }).map(|_| ()) + } + + fn setsid() -> Result { + e_raw(unsafe { syscall!(SETSID) }).map(|s| s as c_int) + } + + fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> { + e_raw(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), fd, path2.as_ptr()) }).map(|_| ()) + } + + fn sync() -> Result<()> { + e_raw(unsafe { syscall!(SYNC) }).map(|_| ()) + } + + fn timer_create(clock_id: clockid_t, evp: &sigevent, mut timerid: Out) -> Result<()> { + e_raw(unsafe { + syscall!( + TIMER_CREATE, + clock_id, + ptr::addr_of!(evp), + timerid.as_mut_ptr() + ) + }) + .map(|_| ()) + } + + fn timer_delete(timerid: timer_t) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_DELETE, timerid) }).map(|_| ()) + } + + fn timer_gettime(timerid: timer_t, mut value: Out) -> Result<()> { + e_raw(unsafe { syscall!(TIMER_GETTIME, timerid, value.as_mut_ptr()) }).map(|_| ()) + } + + fn timer_settime( + timerid: timer_t, + flags: c_int, + value: &itimerspec, + ovalue: Option>, + ) -> Result<()> { + e_raw(unsafe { + syscall!( + TIMER_SETTIME, + timerid, + flags, + ptr::addr_of!(value), + match ovalue { + None => ptr::null_mut(), + Some(mut o) => o.as_mut_ptr(), + } + ) + }) + .map(|_| ()) + } + + fn umask(mask: mode_t) -> mode_t { + unsafe { syscall!(UMASK, mask) as mode_t } + } + + fn uname(mut utsname: Out) -> Result<()> { + e_raw(unsafe { syscall!(UNAME, utsname.as_mut_ptr(), 0) }).map(|_| ()) + } + + fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()> { + e_raw(unsafe { syscall!(UNLINKAT, fd, path.as_ptr(), flags) }).map(|_| ()) + } + + fn waitpid(pid: pid_t, stat_loc: Option>, options: c_int) -> Result { + e_raw(unsafe { + syscall!( + WAIT4, + pid, + stat_loc.map_or(core::ptr::null_mut(), |mut o| o.as_mut_ptr()), + options, + 0 + ) + }) + .map(|p| p as pid_t) + } + + fn write(fildes: c_int, buf: &[u8]) -> Result { + e_raw(unsafe { syscall!(WRITE, fildes, buf.as_ptr(), buf.len()) }) + } + fn pwrite(fildes: c_int, buf: &[u8], off: off_t) -> Result { + e_raw(unsafe { syscall!(PWRITE64, fildes, buf.as_ptr(), buf.len(), off) }) + } + + fn verify() -> bool { + // GETPID on Linux is 39, which does not exist on Redox + e_raw(unsafe { sc::syscall5(sc::nr::GETPID, !0, !0, !0, !0, !0) }).is_ok() + } +} diff --git a/src/platform/linux/ptrace.rs b/src/platform/linux/ptrace.rs new file mode 100644 index 0000000000..8301ea303b --- /dev/null +++ b/src/platform/linux/ptrace.rs @@ -0,0 +1,16 @@ +use super::{ + super::{PalPtrace, types::*}, + Sys, e_raw, +}; +use crate::error::Result; + +impl PalPtrace for Sys { + unsafe fn ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, + ) -> Result { + Ok(unsafe { e_raw(syscall!(PTRACE, request, pid, addr, data))? as c_int }) + } +} diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs new file mode 100644 index 0000000000..ef748a36e7 --- /dev/null +++ b/src/platform/linux/signal.rs @@ -0,0 +1,205 @@ +use core::{ + mem, + ptr::{self, addr_of}, +}; + +use super::{ + super::{ + PalSignal, + types::{c_int, c_uint, pid_t}, + }, + Sys, e_raw, +}; +#[allow(deprecated)] +use crate::header::sys_time::itimerval; +use crate::{ + error::{Errno, Result}, + header::{ + bits_sigset_t::sigset_t, + bits_timespec::timespec, + signal::{ + SA_RESTORER, SI_QUEUE, SIGALRM, SIGEV_SIGNAL, sigaction, sigevent, siginfo_t, sigval, + stack_t, + }, + time, + }, + out::Out, + platform::{self, Pal, types::timer_t}, +}; + +impl PalSignal for Sys { + #[allow(deprecated)] + fn getitimer(which: c_int, out: &mut itimerval) -> Result<()> { + unsafe { + e_raw(syscall!(GETITIMER, which, ptr::from_mut(out)))?; + } + Ok(()) + } + + fn kill(pid: pid_t, sig: c_int) -> Result<()> { + e_raw(unsafe { syscall!(KILL, pid, sig) })?; + Ok(()) + } + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()> { + let info = siginfo_t { + si_addr: core::ptr::null_mut(), + si_code: SI_QUEUE, + si_errno: 0, + si_pid: 0, // TODO: GETPID? + si_signo: sig, + si_status: 0, + si_uid: 0, // TODO: GETUID? + si_value: val, + }; + e_raw(unsafe { syscall!(RT_SIGQUEUEINFO, pid, sig, addr_of!(info)) }).map(|_| ()) + } + + fn killpg(pgrp: pid_t, sig: c_int) -> Result<()> { + e_raw(unsafe { syscall!(KILL, -(pgrp as isize) as pid_t, sig) })?; + Ok(()) + } + + fn raise(sig: c_int) -> Result<()> { + let tid = e_raw(unsafe { syscall!(GETTID) })? as pid_t; + e_raw(unsafe { syscall!(TKILL, tid, sig) })?; + Ok(()) + } + + #[allow(deprecated)] + fn setitimer(which: c_int, new: &itimerval, old: Option<&mut itimerval>) -> Result<()> { + e_raw(unsafe { + syscall!( + SETITIMER, + which, + ptr::from_ref(new), + old.map_or_else(ptr::null_mut, ptr::from_mut) + ) + })?; + Ok(()) + } + + /// See . + fn alarm(seconds: c_uint) -> c_uint { + let mut signal_event = sigevent { + sigev_value: sigval { sival_int: 0 }, + sigev_signo: SIGALRM as c_int, + sigev_notify: SIGEV_SIGNAL, + sigev_notify_attributes: ptr::null_mut(), + sigev_notify_function: None, + }; + let mut timerid: timer_t = Default::default(); + let timer = unsafe { + time::timer_create( + time::CLOCK_REALTIME, + ptr::from_mut(&mut signal_event), + ptr::from_mut(&mut timerid), + ) + }; + let value = time::itimerspec { + it_value: timespec { + tv_sec: i64::from(seconds), + tv_nsec: 0, + }, + it_interval: timespec { + tv_sec: 0, + tv_nsec: 0, + }, + }; + let mut ovalue = Default::default(); + + let errno_backup = platform::ERRNO.get(); + if Sys::timer_settime(timerid, 0, &value, Some(Out::from_mut(&mut ovalue))).is_err() { + platform::ERRNO.set(errno_backup); + 0 + } else { + ovalue.it_value.tv_sec as c_uint + if ovalue.it_value.tv_nsec > 0 { 1 } else { 0 } + } + } + + fn sigaction( + sig: c_int, + act: Option<&sigaction>, + oact: Option<&mut sigaction>, + ) -> Result<(), Errno> { + unsafe extern "C" { + fn __restore_rt(); + } + let act = act.map(|act| { + let mut act_clone = act.clone(); + act_clone.sa_flags |= SA_RESTORER as c_int; + act_clone.sa_restorer = Some(__restore_rt); + act_clone + }); + e_raw(unsafe { + syscall!( + RT_SIGACTION, + sig, + act.as_ref().map_or_else(ptr::null, ptr::from_ref), + oact.map_or_else(ptr::null_mut, ptr::from_mut), + mem::size_of::() + ) + }) + .map(|_| ()) + } + + unsafe fn sigaltstack(ss: Option<&stack_t>, old_ss: Option<&mut stack_t>) -> Result<()> { + e_raw(syscall!( + SIGALTSTACK, + ss.map_or_else(ptr::null, ptr::from_ref), + old_ss.map_or_else(ptr::null_mut, ptr::from_mut) + )) + .map(|_| ()) + } + + fn sigpending(set: &mut sigset_t) -> Result<()> { + e_raw(unsafe { + syscall!( + RT_SIGPENDING, + ptr::from_mut::(set) as usize, + mem::size_of::() + ) + }) + .map(|_| ()) + } + + fn sigprocmask(how: c_int, set: Option<&sigset_t>, oset: Option<&mut sigset_t>) -> Result<()> { + e_raw(unsafe { + syscall!( + RT_SIGPROCMASK, + how, + set.map_or_else(ptr::null, ptr::from_ref), + oset.map_or_else(ptr::null_mut, ptr::from_mut), + mem::size_of::() + ) + }) + .map(|_| ()) + } + + fn sigsuspend(mask: &sigset_t) -> Errno { + unsafe { + e_raw(syscall!( + RT_SIGSUSPEND, + ptr::from_ref::(mask), + size_of::() + )) + .expect_err("must fail") + } + } + + fn sigtimedwait( + set: &sigset_t, + sig: Option<&mut siginfo_t>, + tp: Option<×pec>, + ) -> Result { + unsafe { + e_raw(syscall!( + RT_SIGTIMEDWAIT, + ptr::from_ref(set), + sig.map_or_else(ptr::null_mut, ptr::from_mut), + tp.map_or_else(ptr::null, ptr::from_ref), + size_of::() + )) + .map(|s| s as c_int) + } + } +} diff --git a/src/platform/linux/socket.rs b/src/platform/linux/socket.rs new file mode 100644 index 0000000000..47aef77141 --- /dev/null +++ b/src/platform/linux/socket.rs @@ -0,0 +1,152 @@ +use super::{Sys, e_raw}; +use crate::{ + error::Result, + header::{ + bits_socklen_t::socklen_t, + sys_socket::{msghdr, sockaddr}, + }, + platform::{ + PalSocket, + types::{c_int, c_void, size_t}, + }, +}; + +impl PalSocket for Sys { + unsafe fn accept( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result { + Ok(e_raw(syscall!(ACCEPT, socket, address, address_len))? as c_int) + } + + unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> Result<()> { + e_raw(syscall!(BIND, socket, address, address_len))?; + Ok(()) + } + + unsafe fn connect( + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, + ) -> Result { + Ok(e_raw(syscall!(CONNECT, socket, address, address_len))? as c_int) + } + + unsafe fn getpeername( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result<()> { + e_raw(syscall!(GETPEERNAME, socket, address, address_len))?; + Ok(()) + } + + unsafe fn getsockname( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result<()> { + e_raw(syscall!(GETSOCKNAME, socket, address, address_len))?; + Ok(()) + } + + unsafe fn getsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *mut c_void, + option_len: *mut socklen_t, + ) -> Result<()> { + e_raw(unsafe { + syscall!( + GETSOCKOPT, + socket, + level, + option_name, + option_value, + option_len + ) + })?; + Ok(()) + } + + fn listen(socket: c_int, backlog: c_int) -> Result<()> { + e_raw(unsafe { syscall!(LISTEN, socket, backlog) })?; + Ok(()) + } + + unsafe fn recvfrom( + socket: c_int, + buf: *mut c_void, + len: size_t, + flags: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result { + e_raw(syscall!( + RECVFROM, + socket, + buf, + len, + flags, + address, + address_len + )) + } + + unsafe fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> Result { + e_raw(syscall!(RECVMSG, socket, msg, flags)) + } + + unsafe fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> Result { + e_raw(syscall!(SENDMSG, socket, msg, flags)) + } + + unsafe fn sendto( + socket: c_int, + buf: *const c_void, + len: size_t, + flags: c_int, + dest_addr: *const sockaddr, + dest_len: socklen_t, + ) -> Result { + e_raw(syscall!( + SENDTO, socket, buf, len, flags, dest_addr, dest_len + )) + } + + unsafe fn setsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *const c_void, + option_len: socklen_t, + ) -> Result<()> { + e_raw(unsafe { + syscall!( + SETSOCKOPT, + socket, + level, + option_name, + option_value, + option_len + ) + })?; + Ok(()) + } + + fn shutdown(socket: c_int, how: c_int) -> Result<()> { + e_raw(unsafe { syscall!(SHUTDOWN, socket, how) })?; + Ok(()) + } + + unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> Result { + Ok(e_raw(syscall!(SOCKET, domain, kind, protocol))? as c_int) + } + + fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> Result<()> { + e_raw(unsafe { syscall!(SOCKETPAIR, domain, kind, protocol, sv.as_mut_ptr()) })?; + Ok(()) + } +} diff --git a/src/platform/logger.rs b/src/platform/logger.rs new file mode 100644 index 0000000000..bdf4dce188 --- /dev/null +++ b/src/platform/logger.rs @@ -0,0 +1,293 @@ +use core::{fmt, str::FromStr}; + +use crate::{c_str::CStr, io::prelude::*, sync::Mutex}; + +use alloc::string::{String, ToString}; +use log::{Metadata, Record}; + +const DEFAULT_LOG_LEVEL: log::LevelFilter = log::LevelFilter::Info; + +pub unsafe fn init() { + let mut logger = RedoxLogger::new(); + let log_env = c"RELIBC_LOG_LEVEL".as_ptr(); + #[cfg(feature = "no_trace")] + let mut trace_warn = false; + unsafe { + if let Some(env) = CStr::from_nullable_ptr(crate::header::stdlib::getenv(log_env)) + && let Ok(level) = log::LevelFilter::from_str(env.to_str().unwrap_or("")) + { + #[cfg(feature = "no_trace")] + if level == log::LevelFilter::Trace { + trace_warn = true; + } + + logger = logger.with_output(OutputBuilder::stderr().with_filter(level).build()); + } + if let Some(name) = CStr::from_nullable_ptr(crate::platform::program_invocation_short_name) + { + logger = logger.with_process_name(name.to_str().unwrap_or("").to_string()); + } + } + if logger.enable().is_err() { + log::error!("Logger already initialized"); + } + + #[cfg(feature = "no_trace")] + if trace_warn { + log::warn!( + "The 'no_trace' feature is enabled but RELIBC_LOG_LEVEL=TRACE, there will be no trace logs" + ); + } +} + +/// Copied from redox_log crate with some modifications, in future we might use it instead? +/// An output that will be logged to. The two major outputs for most Redox system programs are +/// usually the log file, and the global stdout. +pub struct Output { + // the actual endpoint to write to. + endpoint: Mutex>, + + // useful for devices like BufWrite or BufRead. You don't want the log file to never but + // written until the program exists. + flush_on_newline: bool, + + // specifies the maximum log level possible + filter: log::LevelFilter, +} + +impl fmt::Debug for Output { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Output") + .field("endpoint", &"opaque") + .field("flush_on_newline", &self.flush_on_newline) + .field("filter", &self.filter) + .finish() + } +} + +impl Default for Output { + fn default() -> Self { + // Uses default level of max_level_in_use == None a.k.a LogLevel::Info + OutputBuilder::stderr().build() + } +} + +pub struct OutputBuilder { + endpoint: Box, + flush_on_newline: Option, + filter: Option, + ansi: Option, +} +impl OutputBuilder { + /* + pub fn in_redox_logging_scheme( + category: A, + subcategory: B, + logfile: C, + ) -> Result + where + A: AsRef, + B: AsRef, + C: AsRef, + { + if !cfg!(target_os = "redox") { + return Ok(Self::with_endpoint(Vec::new())); + } + + let mut path = PathBuf::from("/scheme/logging/"); + path.push(category.as_ref()); + path.push(subcategory.as_ref()); + path.push(logfile.as_ref()); + path.set_extension("log"); + + if let Some(parent) = path.parent() { + if !parent.exists() { + fs::create_dir_all(parent)?; + } + } + + Ok(Self::with_endpoint(BufWriter::new(File::create( + path, + fcntl::O_CREAT | fcntl::O_CLOEXEC, + 0, + )?))) + } + */ + pub fn stdout() -> Self { + Self::with_endpoint(crate::platform::FileWriter::new(1)) + } + pub fn stderr() -> Self { + Self::with_endpoint(crate::platform::FileWriter::new(2)) + } + + pub fn with_endpoint(endpoint: T) -> Self + where + T: fmt::Write + Send + 'static, + { + Self::with_dyn_endpoint(Box::new(endpoint)) + } + pub fn with_dyn_endpoint(endpoint: Box) -> Self { + Self { + endpoint, + flush_on_newline: None, + filter: None, + ansi: None, + } + } + pub fn flush_on_newline(mut self, flush: bool) -> Self { + self.flush_on_newline = Some(flush); + self + } + pub fn with_filter(mut self, filter: log::LevelFilter) -> Self { + self.filter = Some(filter); + self + } + pub fn build(self) -> Output { + Output { + endpoint: Mutex::new(self.endpoint), + filter: self.filter.unwrap_or(DEFAULT_LOG_LEVEL), + flush_on_newline: self.flush_on_newline.unwrap_or(true), + } + } +} + +#[derive(Debug, Default)] +pub struct RedoxLogger { + output: Output, + min_filter: Option, + max_filter: Option, + max_level_in_use: Option, + min_level_in_use: Option, + process_name: Option, +} + +impl RedoxLogger { + pub fn new() -> Self { + Self::default() + } + fn adjust_output_level( + max_filter: Option, + min_filter: Option, + max_in_use: &mut Option, + min_in_use: &mut Option, + output: &mut Output, + ) { + if let Some(max) = max_filter { + output.filter = core::cmp::max(output.filter, max); + } + if let Some(min) = min_filter { + output.filter = core::cmp::min(output.filter, min); + } + match max_in_use { + &mut Some(ref mut max) => *max = core::cmp::max(output.filter, *max), + max @ &mut None => *max = Some(output.filter), + } + match min_in_use { + &mut Some(ref mut min) => *min = core::cmp::min(output.filter, *min), + min @ &mut None => *min = Some(output.filter), + } + } + pub fn with_output(mut self, mut output: Output) -> Self { + Self::adjust_output_level( + self.max_filter, + self.min_filter, + &mut self.max_level_in_use, + &mut self.min_level_in_use, + &mut output, + ); + self.output = output; + self + } + pub fn with_min_level_override(mut self, min: log::LevelFilter) -> Self { + self.min_filter = Some(min); + let output = &mut self.output; + Self::adjust_output_level( + self.max_filter, + self.min_filter, + &mut self.max_level_in_use, + &mut self.min_level_in_use, + output, + ); + self + } + pub fn with_max_level_override(mut self, max: log::LevelFilter) -> Self { + self.max_filter = Some(max); + let output = &mut self.output; + Self::adjust_output_level( + self.max_filter, + self.min_filter, + &mut self.max_level_in_use, + &mut self.min_level_in_use, + output, + ); + self + } + pub fn with_process_name(mut self, name: String) -> Self { + self.process_name = Some(name); + self + } + pub fn enable(self) -> Result<&'static Self, log::SetLoggerError> { + let leak = Box::leak(Box::new(self)); + log::set_logger(leak)?; + if let Some(max) = leak.max_level_in_use { + log::set_max_level(max); + } else { + log::set_max_level(DEFAULT_LOG_LEVEL); + } + Ok(leak) + } + fn write_record( + record: &Record, + process_name: Option<&str>, + writer: &mut W, + ) -> fmt::Result { + let target = record.module_path().unwrap_or(record.target()); + let level = record.level(); + let message = record.args(); + + let show_lines = true; + let line_number = if show_lines { record.line() } else { None }; + + let process_name = process_name.unwrap_or(""); + let line = &LineFmt(line_number); + writeln!(writer, "[{process_name}@{target}{line} {level}] {message}",) + } +} + +impl log::Log for RedoxLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + self.max_level_in_use + .map(|min| metadata.level() >= min) + .unwrap_or(false) + && self + .min_level_in_use + .map(|max| metadata.level() <= max) + .unwrap_or(false) + } + fn log(&self, record: &Record) { + let output = &self.output; + if record.metadata().level() <= output.filter { + let mut endpoint_guard = output.endpoint.lock(); + + let _ = Self::write_record( + record, + self.process_name.as_deref(), + endpoint_guard.as_mut(), + ); + } + } + fn flush(&self) { + // no-op + } +} + +struct LineFmt(Option); +impl fmt::Display for LineFmt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(line) = self.0 { + write!(f, ":{line}") + } else { + write!(f, "") + } + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs new file mode 100644 index 0000000000..d3a10b92a5 --- /dev/null +++ b/src/platform/mod.rs @@ -0,0 +1,421 @@ +//! Platform abstractions and environment. + +use crate::{ + error::{Errno, ResultExt}, + io::{self, Read, Write}, + raw_cell::RawCell, +}; +use alloc::{boxed::Box, vec::Vec}; +use core::{cell::Cell, fmt, ptr}; + +pub use self::allocator::*; + +mod allocator; + +pub mod logger; + +pub use self::pal::{Pal, PalEpoll, PalPtrace, PalSignal, PalSocket}; + +mod pal; + +pub use self::sys::Sys; + +#[cfg(target_os = "linux")] +#[path = "linux/mod.rs"] +pub(crate) mod sys; + +#[cfg(target_os = "redox")] +#[path = "redox/mod.rs"] +pub(crate) mod sys; + +pub use self::rlb::{Line, RawLineBuffer}; +pub mod rlb; + +#[cfg(target_os = "linux")] +pub mod auxv_defs; + +#[cfg(target_os = "redox")] +pub use redox_rt::auxv_defs; + +use self::types::*; +pub mod types; + +/// The global `errno` variable used internally in relibc. +#[thread_local] +pub static ERRNO: Cell = Cell::new(0); + +/// The `argv` argument available to a program's `main` function. +#[allow(non_upper_case_globals)] +pub static mut argv: *mut *mut c_char = ptr::null_mut(); +#[allow(non_upper_case_globals)] +pub static inner_argv: RawCell> = RawCell::new(Vec::new()); +#[allow(non_upper_case_globals)] +pub static mut program_invocation_name: *mut c_char = ptr::null_mut(); +#[allow(non_upper_case_globals)] +pub static mut program_invocation_short_name: *mut c_char = ptr::null_mut(); + +#[allow(non_upper_case_globals)] +#[unsafe(no_mangle)] +pub static mut environ: *mut *mut c_char = ptr::null_mut(); + +pub static OUR_ENVIRON: RawCell> = RawCell::new(Vec::new()); + +pub fn environ_iter() -> impl Iterator + 'static { + unsafe { + let mut ptrs = environ; + + core::iter::from_fn(move || { + if ptrs.is_null() { + None + } else { + let ptr = ptrs.read(); + if ptr.is_null() { + None + } else { + ptrs = ptrs.add(1); + Some(ptr) + } + } + }) + } +} + +pub trait WriteByte: fmt::Write { + fn write_u8(&mut self, byte: u8) -> fmt::Result; +} + +impl WriteByte for &mut W { + fn write_u8(&mut self, byte: u8) -> fmt::Result { + (**self).write_u8(byte) + } +} + +/// An implementation of [`core::fmt::Write`] for a file descriptor. +pub struct FileWriter(pub c_int, Option); + +impl FileWriter { + pub fn new(fd: c_int) -> Self { + Self(fd, None) + } + + pub fn write(&mut self, buf: &[u8]) -> fmt::Result { + let _ = Sys::write(self.0, buf).map_err(|err| { + self.1 = Some(err); + fmt::Error + })?; + Ok(()) + } +} + +impl fmt::Write for FileWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + if let Ok(()) = self.write(s.as_bytes()) {}; // TODO handle error + Ok(()) + } +} + +impl WriteByte for FileWriter { + fn write_u8(&mut self, byte: u8) -> fmt::Result { + if let Ok(()) = self.write(&[byte]) {}; // TODO handle error + Ok(()) + } +} + +/// An implementation of [`Read`] for a file descriptor. +pub struct FileReader(pub c_int); + +impl FileReader { + // TODO: This is a bad interface. Rustify + pub fn read(&mut self, buf: &mut [u8]) -> isize { + Sys::read(self.0, buf) + .map(|u| u as isize) + .or_minus_one_errno() + } +} + +impl Read for FileReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let i = Sys::read(self.0, buf) + .map(|u| u as isize) + .or_minus_one_errno(); // TODO + if i >= 0 { + Ok(i as usize) + } else { + Err(io::Error::from_raw_os_error(-i as i32)) + } + } +} + +/// An implementation of [`Write`]/[`core::fmt::Write`] for a byte array. +pub struct StringWriter(pub *mut u8, pub usize); +impl Write for StringWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.1 > 1 { + let copy_size = buf.len().min(self.1 - 1); + unsafe { + ptr::copy_nonoverlapping(buf.as_ptr(), self.0, copy_size); + self.1 -= copy_size; + + self.0 = self.0.add(copy_size); + *self.0 = 0; + } + } + + // Pretend the entire slice was written. This is because many functions + // (like snprintf) expects a return value that reflects how many bytes + // *would have* been written. So keeping track of this information is + // good, and then if we want the *actual* written size we can just go + // `cmp::min(written, maxlen)`. + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} +impl fmt::Write for StringWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + // can't fail + self.write(s.as_bytes()).unwrap(); + Ok(()) + } +} +impl WriteByte for StringWriter { + fn write_u8(&mut self, byte: u8) -> fmt::Result { + // can't fail + self.write(&[byte]).unwrap(); + Ok(()) + } +} + +/// An implementation of [`Write`]/[`core::fmt::Write`] for a byte array, +/// without buffer overflow protection. +pub struct UnsafeStringWriter(pub *mut u8); +impl Write for UnsafeStringWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { + ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len()); + self.0 = self.0.add(buf.len()); + *self.0 = b'\0'; + } + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} +impl fmt::Write for UnsafeStringWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + // can't fail + self.write(s.as_bytes()).unwrap(); + Ok(()) + } +} +impl WriteByte for UnsafeStringWriter { + fn write_u8(&mut self, byte: u8) -> fmt::Result { + // can't fail + self.write(&[byte]).unwrap(); + Ok(()) + } +} + +/// An implementation of [`Read`] for a byte array, without buffer over-read +/// protection. +pub struct UnsafeStringReader(pub *const u8); +impl Read for UnsafeStringReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + unsafe { + for (i, inner) in buf.iter_mut().enumerate() { + if *self.0 == 0 { + return Ok(i); + } + + *inner = *self.0; + self.0 = self.0.offset(1); + } + Ok(buf.len()) + } + } +} + +/// A wrapper that keeps track of the number of bytes written with the +/// underlying writer `T`. +pub struct CountingWriter { + pub inner: T, + pub written: usize, +} +impl CountingWriter { + pub fn new(writer: T) -> Self { + Self { + inner: writer, + written: 0, + } + } +} +impl fmt::Write for CountingWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.written += s.len(); + self.inner.write_str(s) + } +} +impl WriteByte for CountingWriter { + fn write_u8(&mut self, byte: u8) -> fmt::Result { + self.written += 1; + self.inner.write_u8(byte) + } +} +impl Write for CountingWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let res = self.inner.write(buf); + if let Ok(written) = res { + self.written += written; + } + res + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match self.inner.write_all(buf) { + Ok(()) => (), + Err(ref err) if err.kind() == io::ErrorKind::WriteZero => (), + Err(err) => return Err(err), + } + self.written += buf.len(); + Ok(()) + } + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + +// TODO: Set a global variable once get_auxvs is called, and then implement getauxval based on +// get_auxv. + +#[cold] +pub unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator + 'a { + struct St(*const usize); + impl Iterator for St { + type Item = [usize; 2]; + + fn next(&mut self) -> Option { + unsafe { + if *self.0 == self::auxv_defs::AT_NULL { + return None; + } + let kind = *self.0; + let value = *self.0.add(1); + self.0 = self.0.add(2); + + Some([kind, value]) + } + } + } + St(ptr) +} + +#[cold] +pub unsafe fn get_auxvs(ptr: *const usize) -> Box<[[usize; 2]]> { + //traverse the stack and collect argument environment variables + let mut auxvs = unsafe { auxv_iter(ptr) }.collect::>(); + + auxvs.sort_unstable_by_key(|[kind, _]| *kind); + auxvs.into_boxed_slice() +} +// TODO: Find an auxv replacement for Redox's execv protocol +#[cold] +pub unsafe fn get_auxv_raw(ptr: *const usize, requested_kind: usize) -> Option { + unsafe { auxv_iter(ptr) } + .find_map(|[kind, value]| Some(value).filter(|_| kind == requested_kind)) +} +pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option { + auxvs + .binary_search_by_key(&key, |[entry_key, _]| *entry_key) + .ok() + .map(|idx| auxvs[idx][1]) +} + +#[cold] +#[cfg(target_os = "redox")] +// SAFETY: Must only be called when only one thread exists. +pub unsafe fn init(auxvs: Box<[[usize; 2]]>) { + use self::auxv_defs::*; + use redox_rt::proc::FdGuard; + + let Some(proc_fd) = get_auxv(&auxvs, AT_REDOX_PROC_FD) else { + panic!("Missing proc and thread fd!"); + }; + let Some(ns_fd) = get_auxv(&auxvs, AT_REDOX_NS_FD) else { + panic!("Missing namespace fd!"); + }; + unsafe { + redox_rt::initialize( + FdGuard::new(proc_fd).to_upper().unwrap(), + if ns_fd == usize::MAX { + None + } else { + Some(FdGuard::new(ns_fd).to_upper().unwrap()) + }, + ); + init_inner(auxvs) + } +} +#[cold] +#[cfg(target_os = "redox")] +pub unsafe fn init_inner(auxvs: Box<[[usize; 2]]>) { + use self::auxv_defs::*; + use crate::header::sys_stat::S_ISVTX; + use redox_rt::proc::FdGuard; + use syscall::MODE_PERM; + + // TODO: Is it safe to assume setup_sighandler has been called at this point? + redox_rt::sys::this_proc_call( + &mut [], + syscall::CallFlags::empty(), + &[redox_protocols::protocol::ProcCall::SyncSigPctl as u64], + ) + .expect("failed to sync signal pctl"); + + if let (Some(cwd_ptr), Some(cwd_len), Some(cwd_fd)) = ( + get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_PTR), + get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_LEN), + get_auxv(&auxvs, AT_REDOX_CWD_FD), + ) { + let cwd_bytes: &'static [u8] = + unsafe { core::slice::from_raw_parts(cwd_ptr as *const u8, cwd_len) }; + if let (Ok(cwd_path), Some(cwd_fd)) = ( + core::str::from_utf8(cwd_bytes), + (cwd_fd != usize::MAX).then(|| { + FdGuard::new(cwd_fd) + .to_upper() + .expect("failed to move cwd fd to upper table") + }), + ) { + self::sys::path::set_cwd_manual(cwd_path.into(), cwd_fd); + } + } + + let mut inherited_sigignmask = 0_u64; + if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGIGNMASK) { + inherited_sigignmask |= mask as u64; + } + #[cfg(target_pointer_width = "32")] + if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGIGNMASK_HI) { + inherited_sigignmask |= (mask as u64) << 32; + } + redox_rt::signal::apply_inherited_sigignmask(inherited_sigignmask); + + let mut inherited_sigprocmask = 0_u64; + + if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGPROCMASK) { + inherited_sigprocmask |= mask as u64; + } + #[cfg(target_pointer_width = "32")] + if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGPROCMASK_HI) { + inherited_sigprocmask |= (mask as u64) << 32; + } + redox_rt::signal::set_sigmask(Some(inherited_sigprocmask), None).unwrap(); + + if let Some(umask) = get_auxv(&auxvs, AT_REDOX_UMASK) { + let _ = + redox_rt::sys::swap_umask((umask as u32) & u32::from(MODE_PERM) & !(S_ISVTX as u32)); + } +} +#[cfg(not(target_os = "redox"))] +pub unsafe fn init(auxvs: Box<[[usize; 2]]>) {} diff --git a/src/platform/pal/epoll.rs b/src/platform/pal/epoll.rs new file mode 100644 index 0000000000..0d417d7df7 --- /dev/null +++ b/src/platform/pal/epoll.rs @@ -0,0 +1,23 @@ +use crate::{ + error::Result, + header::{bits_sigset_t::sigset_t, sys_epoll::epoll_event}, + platform::{Pal, types::c_int}, +}; + +/// Platform abstraction for `epoll` functionality. +pub trait PalEpoll: Pal { + /// Platform implementation of [`epoll_create1()`](crate::header::sys_epoll::epoll_create1) from [`sys/epoll.h`](crate::header::sys_epoll). + fn epoll_create1(flags: c_int) -> Result; + + /// Platform implementation of [`epoll_ctl()`](crate::header::sys_epoll::epoll_ctl) from [`sys/epoll.h`](crate::header::sys_epoll). + unsafe fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> Result<()>; + + /// Platform implementation of [`epoll_pwait()`](crate::header::sys_epoll::epoll_pwait) from [`sys/epoll.h`](crate::header::sys_epoll). + unsafe fn epoll_pwait( + epfd: c_int, + events: *mut epoll_event, + maxevents: c_int, + timeout: c_int, + sigmask: *const sigset_t, + ) -> Result; +} diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs new file mode 100644 index 0000000000..ad31680e0b --- /dev/null +++ b/src/platform/pal/mod.rs @@ -0,0 +1,459 @@ +use core::num::NonZeroU64; + +use super::types::*; +use crate::{ + c_str::CStr, + error::{Errno, Result}, + header::{ + bits_timespec::timespec, + fcntl::{AT_EMPTY_PATH, AT_FDCWD}, + signal::sigevent, + sys_resource::{rlimit, rusage}, + sys_select::timeval, + sys_stat::stat, + sys_statvfs::statvfs, + sys_time::timezone, + sys_utsname::utsname, + time::itimerspec, + }, + ld_so::tcb::OsSpecific, + out::Out, + pthread, +}; + +pub use self::epoll::PalEpoll; +mod epoll; + +pub use self::ptrace::PalPtrace; +mod ptrace; + +pub use self::signal::PalSignal; +mod signal; + +pub use self::socket::PalSocket; +mod socket; + +/// Platform abstraction layer, a platform-agnostic abstraction over syscalls. +pub trait Pal { + /// Platform implementation of [`access()`](crate::header::unistd::access) from [`unistd.h`](crate::header::unistd). + fn access(path: CStr, mode: c_int) -> Result<()> { + Self::faccessat(AT_FDCWD, path, mode, 0) + } + + /// Platform implementation of [`faccessat()`](crate::header::unistd::faccessat) from [`unistd.h`](crate::header::unistd). + fn faccessat(fd: c_int, path: CStr, amode: c_int, flags: c_int) -> Result<()>; + + /// Platform implementation of [`brk()`](crate::header::unistd::brk) from [`unistd.h`](crate::header::unistd). + unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void>; + + /// Platform implementation of [`chdir()`](crate::header::unistd::chdir) from [`unistd.h`](crate::header::unistd). + fn chdir(path: CStr) -> Result<()>; + + /// Platform implementation of [`chmod()`](crate::header::sys_stat::chmod) from [`sys/stat.h`](crate::header::sys_stat). + fn chmod(path: CStr, mode: mode_t) -> Result<()> { + Self::fchmodat(AT_FDCWD, Some(path), mode, 0) + } + + /// Platform implementation of [`chown()`](crate::header::unistd::chown) from [`unistd.h`](crate::header::unistd). + fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { + Self::fchownat(AT_FDCWD, path, owner, group, 0) + } + + /// Platform implementation of [`clock_getres()`](crate::header::time::clock_getres) from [`time.h`](crate::header::time). + fn clock_getres(clk_id: clockid_t, tp: Option>) -> Result<()>; + + // TODO: maybe remove tp and change signature to -> Result? + /// Platform implementation of [`clock_gettime()`](crate::header::time::clock_gettime) from [`time.h`](crate::header::time). + fn clock_gettime(clk_id: clockid_t, tp: Out) -> Result<()>; + + /// Platform implementation of [`clock_settime()`](crate::header::time::clock_settime) from [`time.h`](crate::header::time). + unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()>; + + /// Platform implementation of [`close()`](crate::header::unistd::close) from [`unistd.h`](crate::header::unistd). + fn close(fildes: c_int) -> Result<()>; + + /// Platform implementation of [`dup()`](crate::header::unistd::dup) from [`unistd.h`](crate::header::unistd). + fn dup(fildes: c_int) -> Result; + + /// Platform implementation of [`dup2()`](crate::header::unistd::dup2) from [`unistd.h`](crate::header::unistd). + fn dup2(fildes: c_int, fildes2: c_int) -> Result; + + /// Platform implementation of [`execve()`](crate::header::unistd::execve) from [`unistd.h`](crate::header::unistd). + unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> Result<()>; + + /// Platform implementation of [`fexecve()`](crate::header::unistd::fexecve) from [`unistd.h`](crate::header::unistd). + unsafe fn fexecve( + fildes: c_int, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> Result<()>; + + /// Platform implementation of [`_Exit()`](crate::header::stdlib::_Exit) from [`stdlib.h`](crate::header::stdlib) (or the equivalent [`_exit()`](crate::header::unistd::_exit) from [`unistd.h`](crate::header::unistd)). + fn exit(status: c_int) -> !; + + unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> !; + + /// Platform implementation of [`fchdir()`](crate::header::unistd::fchdir) from [`unistd.h`](crate::header::unistd). + fn fchdir(fildes: c_int) -> Result<()>; + + /// Platform implementation of [`fchmod()`](crate::header::sys_stat::fchmod) from [`sys/stat.h`](crate::header::sys_stat). + fn fchmod(fildes: c_int, mode: mode_t) -> Result<()> { + Self::fchmodat(fildes, Some(c"".into()), mode, AT_EMPTY_PATH) + } + + /// Platform implementation of [`fchmodat()`](crate::header::sys_stat::fchmodat) from [`sys/stat.h`](crate::header::sys_stat). + fn fchmodat(dirfd: c_int, path: Option, mode: mode_t, flags: c_int) -> Result<()>; + + /// Platform implementation of [`fchown()`](crate::header::unistd::fchown) from [`unistd.h`](crate::header::unistd). + fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()> { + Self::fchownat(fildes, c"".into(), owner, group, AT_EMPTY_PATH) + } + + /// Platform implementation of [`fchownat()`](crate::header::unistd::fchownat) from [`unistd.h`](crate::header::unistd). + fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()>; + + /// Platform implementation of [`fdatasync()`](crate::header::unistd::fdatasync) from [`unistd.h`](crate::header::unistd). + fn fdatasync(fildes: c_int) -> Result<()>; + + /// Platform implementation of [`flock()`](crate::header::sys_file::flock) from [`sys/file.h`](crate::header::sys_file). + fn flock(fd: c_int, operation: c_int) -> Result<()>; + + /// Platform implementation of [`fstat()`](crate::header::sys_stat::fstat) from [`sys/stat.h`](crate::header::sys_stat). + fn fstat(fildes: c_int, buf: Out) -> Result<()> { + Self::fstatat(fildes, Some(c"".into()), buf, 0) + } + + /// Platform implementation of [`fstatat()`](crate::header::sys_stat::fstatat) from [`sys/stat.h`](crate::header::sys_stat). + fn fstatat(fildes: c_int, path: Option, buf: Out, flags: c_int) -> Result<()>; + + /// Platform implementation of [`fstatvfs()`](crate::header::sys_statvfs::fstatvfs) from [`sys/statvfs.h`](crate::header::sys_statvfs). + fn fstatvfs(fildes: c_int, buf: Out) -> Result<()>; + + /// Platform implementation of [`fcntl()`](crate::header::fcntl::fcntl) from [`fcntl.h`](crate::header::fcntl). + fn fcntl(fildes: c_int, cmd: c_int, arg: c_ulonglong) -> Result; + + /// Platform implementation of [`_Fork()`](crate::header::unistd::_Fork) from [`unistd.h`](crate::header::unistd). + unsafe fn fork() -> Result; + + fn fpath(fildes: c_int, out: &mut [u8]) -> Result; + + /// Platform implementation of [`fsync()`](crate::header::unistd::fsync) from [`unistd.h`](crate::header::unistd). + fn fsync(fildes: c_int) -> Result<()>; + + /// Platform implementation of [`ftruncate()`](crate::header::unistd::ftruncate) from [`unistd.h`](crate::header::unistd). + fn ftruncate(fildes: c_int, length: off_t) -> Result<()>; + + unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<×pec>) -> Result<()>; + + unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result; + + /// Platform implementation of [`futimens()`](crate::header::sys_stat::futimens) from [`sys/stat.h`](crate::header::sys_stat). + unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()>; + + /// Platform implementation of `utimens()` (TODO) from [`sys/stat.h`](crate::header::sys_stat). + unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()>; + + /// Platform implementation of [`getcwd()`](crate::header::unistd::getcwd) from [`unistd.h`](crate::header::unistd). + fn getcwd(buf: Out<[u8]>) -> Result<()>; + + fn getdents(fd: c_int, buf: &mut [u8], opaque_offset: u64) -> Result; + + fn dir_seek(fd: c_int, opaque_offset: u64) -> Result<()>; + + // SAFETY: This_dent must satisfy platform-specific size and alignment constraints. On Linux, + // this means the buffer came from a valid getdents64 invocation, whereas on Redox, every + // possible this_dent slice is safe (and will be validated). + unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)>; + + // Always successful + /// Platform implementation of [`getegid()`](crate::header::unistd::getegid) from [`unistd.h`](crate::header::unistd). + fn getegid() -> gid_t; + + // Always successful + /// Platform implementation of [`geteuid()`](crate::header::unistd::geteuid) from [`unistd.h`](crate::header::unistd). + fn geteuid() -> uid_t; + + // Always successful + /// Platform implementation of [`getgid()`](crate::header::unistd::getgid) from [`unistd.h`](crate::header::unistd). + fn getgid() -> gid_t; + + /// Platform implementation of [`getgroups()`](crate::header::unistd::getgroups) from [`unistd.h`](crate::header::unistd). + fn getgroups(list: Out<[gid_t]>) -> Result; + + /* Note that this is distinct from the legacy POSIX function + * getpagesize(), which returns a c_int. On some Linux platforms, + * page size may be determined through a syscall ("getpagesize"). */ + fn getpagesize() -> usize; + + /// Platform implementation of [`getpgid()`](crate::header::unistd::getpgid) from [`unistd.h`](crate::header::unistd). + fn getpgid(pid: pid_t) -> Result; + + // Always successful + /// Platform implementation of [`getpid()`](crate::header::unistd::getpid) from [`unistd.h`](crate::header::unistd). + fn getpid() -> pid_t; + + // Always successful + /// Platform implementation of [`getppid()`](crate::header::unistd::getppid) from [`unistd.h`](crate::header::unistd). + fn getppid() -> pid_t; + + /// Platform implementation of [`getpriority()`](crate::header::sys_resource::getpriority) from [`sys/resource.h`](crate::header::sys_resource). + fn getpriority(which: c_int, who: id_t) -> Result; + + /// Platform implementation of [`getrandom()`](crate::header::sys_random::getrandom) from [`sys/random.h`](crate::header::sys_random). + fn getrandom(buf: &mut [u8], flags: c_uint) -> Result; + + /// Platform implementation of [`getresgid()`](crate::header::unistd::getresgid) from [`unistd.h`](crate::header::unistd). + fn getresgid( + rgid: Option>, + egid: Option>, + sgid: Option>, + ) -> Result<()>; + + /// Platform implementation of [`getresuid()`](crate::header::unistd::getresuid) from [`unistd.h`](crate::header::unistd). + fn getresuid( + ruid: Option>, + euid: Option>, + suid: Option>, + ) -> Result<()>; + + /// Platform implementation of [`getrlimit()`](crate::header::sys_resource::getrlimit) from [`sys/resource.h`](crate::header::sys_resource). + fn getrlimit(resource: c_int, rlim: Out) -> Result<()>; + + /// Platform implementation of [`setrlimit()`](crate::header::sys_resource::setrlimit) from [`sys/resource.h`](crate::header::sys_resource). + unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()>; + + /// Platform implementation of [`getrusage()`](crate::header::sys_resource::getrusage) from [`sys/resource.h`](crate::header::sys_resource). + fn getrusage(who: c_int, r_usage: Out) -> Result<()>; + + /// Platform implementation of [`getsid()`](crate::header::unistd::getsid) from [`unistd.h`](crate::header::unistd). + fn getsid(pid: pid_t) -> Result; + + // Always successful + /// Platform implementation of `gettid()` (TODO) from [`unistd.h`](crate::header::unistd). + fn gettid() -> pid_t; + + /// Platform implementation of [`gettimeofday()`](crate::header::sys_time::gettimeofday) from [`sys/time.h`](crate::header::sys_time). + fn gettimeofday(tp: Out, tzp: Option>) -> Result<()>; + + /// Platform implementation of [`getuid()`](crate::header::unistd::getuid) from [`unistd.h`](crate::header::unistd). + fn getuid() -> uid_t; + + /// Platform implementation of [`lchown()`](crate::header::unistd::lchown) from [`unistd.h`](crate::header::unistd). + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()>; + + /// Platform implementation of [`link()`](crate::header::unistd::link) from [`unistd.h`](crate::header::unistd). + fn link(path1: CStr, path2: CStr) -> Result<()> { + Self::linkat(AT_FDCWD, path1, AT_FDCWD, path2, 0) + } + + /// Platform implementation of [`linkat()`](crate::header::unistd::linkat) from [`unistd.h`](crate::header::unistd). + fn linkat(fd1: c_int, oldpath: CStr, fd2: c_int, newpath: CStr, flags: c_int) -> Result<()>; + + /// Platform implementation of [`lseek()`](crate::header::unistd::lseek) from [`unistd.h`](crate::header::unistd). + fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result; + + /// Platform implementation of [`mkdirat()`](crate::header::sys_stat::mkdirat) from [`sys/stat.h`](crate::header::sys_stat). + fn mkdirat(fildes: c_int, path: CStr, mode: mode_t) -> Result<()>; + + /// Platform implementation of [`mkdir()`](crate::header::sys_stat::mkdir) from [`sys/stat.h`](crate::header::sys_stat). + fn mkdir(path: CStr, mode: mode_t) -> Result<()> { + Self::mkdirat(AT_FDCWD, path, mode) + } + + /// Platform implementation of [`mkfifoat()`](crate::header::sys_stat::mkfifoat) from [`sys/stat.h`](crate::header::sys_stat). + fn mkfifoat(dir_fd: c_int, path: CStr, mode: mode_t) -> Result<()>; + + /// Platform implementation of [`mkfifo()`](crate::header::sys_stat::mkfifo) from [`sys/stat.h`](crate::header::sys_stat). + fn mkfifo(path: CStr, mode: mode_t) -> Result<()> { + Self::mkfifoat(AT_FDCWD, path, mode) + } + + /// Platform implementation of [`mknodat()`](crate::header::sys_stat::mknodat) from [`sys/stat.h`](crate::header::sys_stat). + fn mknodat(fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> Result<()>; + + /// Platform implementation of [`mknod()`](crate::header::sys_stat::mknod) from [`sys/stat.h`](crate::header::sys_stat). + fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> Result<()> { + Self::mknodat(AT_FDCWD, path, mode, dev) + } + + /// Platform implementation of [`mlock()`](crate::header::sys_mman::mlock) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn mlock(addr: *const c_void, len: usize) -> Result<()>; + + /// Platform implementation of [`mlockall()`](crate::header::sys_mman::mlockall) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn mlockall(flags: c_int) -> Result<()>; + + /// Platform implementation of [`mmap()`](crate::header::sys_mman::mmap) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn mmap( + addr: *mut c_void, + len: usize, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, + ) -> Result<*mut c_void>; + + /// Platform implementation of [`mremap()`](crate::header::sys_mman::mremap) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn mremap( + addr: *mut c_void, + len: usize, + new_len: usize, + flags: c_int, + args: *mut c_void, + ) -> Result<*mut c_void>; + + /// Platform implementation of [`mprotect()`](crate::header::sys_mman::mprotect) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> Result<()>; + + /// Platform implementation of [`msync()`](crate::header::sys_mman::msync) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()>; + + /// Platform implementation of [`munlock()`](crate::header::sys_mman::munlock) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()>; + + /// Platform implementation of [`madvise()`](crate::header::sys_mman::madvise) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()>; + + /// Platform implementation of [`munlockall()`](crate::header::sys_mman::munlockall) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn munlockall() -> Result<()>; + + /// Platform implementation of [`munmap()`](crate::header::sys_mman::munmap) from [`sys/mman.h`](crate::header::sys_mman). + unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()>; + + /// Platform implementation of [`nanosleep()`](crate::header::time::nanosleep) from [`time.h`](crate::header::time). + unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()>; + + /// Platform implementation of [`open()`](crate::header::fcntl::open) from [`fcntl.h`](crate::header::fcntl). + fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result; + + /// Platform implementation of `openat()` (TODO) from [`fcntl.h`](crate::header::fcntl). + fn openat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result; + + /// Platform implementation of [`pipe2()`](crate::header::unistd::pipe2) from [`unistd.h`](crate::header::unistd). + fn pipe2(fildes: Out<[c_int; 2]>, flags: c_int) -> Result<()>; + + /// Platform implementation of [`posix_fallocate()`](crate::header::fcntl::posix_fallocate) from [`fcntl.h`](crate::header::fcntl). + fn posix_fallocate(fd: c_int, offset: u64, length: NonZeroU64) -> Result<()>; + + /// Platform implementation of [`posix_getdents()`](crate::header::dirent::posix_getdents) from [`dirent.h`](crate::header::dirent). + fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result; + + unsafe fn rlct_clone( + stack: *mut usize, + os_specific: &mut OsSpecific, + ) -> Result; + + unsafe fn rlct_kill(os_tid: pthread::OsTid, signal: usize) -> Result<()>; + + fn current_os_tid() -> pthread::OsTid; + + /// Platform implementation of [`read()`](crate::header::unistd::read) from [`unistd.h`](crate::header::unistd). + fn read(fildes: c_int, buf: &mut [u8]) -> Result; + + /// Platform implementation of [`pread()`](crate::header::unistd::pread) from [`unistd.h`](crate::header::unistd). + fn pread(fildes: c_int, buf: &mut [u8], offset: off_t) -> Result; + + /// Platform implementation of [`readlink()`](crate::header::unistd::readlink) from [`unistd.h`](crate::header::unistd). + fn readlink(pathname: CStr, out: &mut [u8]) -> Result { + Self::readlinkat(AT_FDCWD, pathname, out) + } + + /// Platform implementation of [`readlinkat()`](crate::header::unistd::readlinkat) from [`unistd.h`](crate::header::unistd). + fn readlinkat(dirfd: c_int, pathname: CStr, out: &mut [u8]) -> Result; + + /// Platform implementation of [`rename()`](crate::header::stdio::rename) from [`stdio.h`](crate::header::stdio). + fn rename(old: CStr, new: CStr) -> Result<()> { + Self::renameat(AT_FDCWD, old, AT_FDCWD, new) + } + + /// Platform implementation of [`renameat()`](crate::header::stdio::renameat) from [`stdio.h`](crate::header::stdio). + fn renameat(old_dir: c_int, old_path: CStr, new_dir: c_int, new_path: CStr) -> Result<()> { + Self::renameat2(old_dir, old_path, new_dir, new_path, 0) + } + + /// Platform implementation of [`renameat2()`](crate::header::stdio::renameat2) from [`stdio.h`](crate::header::stdio). + fn renameat2( + old_dir: c_int, + old_path: CStr, + new_dir: c_int, + new_path: CStr, + flags: c_uint, + ) -> Result<()>; + + /// Platform implementation of [`rmdir()`](crate::header::unistd::rmdir) from [`unistd.h`](crate::header::unistd). + fn rmdir(path: CStr) -> Result<()>; + + /// Platform implementation of [`sched_yield()`](crate::header::sched::sched_yield) from [`sched.h`](crate::header::sched). + fn sched_yield() -> Result<()>; + + /// Platform implementation of [`setgroups()`](crate::header::grp::setgroups) from [`grp.h`](crate::header::grp). + unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()>; + + /// Platform implementation of [`setpgid()`](crate::header::unistd::setpgid) from [`unistd.h`](crate::header::unistd). + fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()>; + + /// Platform implementation of [`setpriority()`](crate::header::sys_resource::setpriority) from [`sys/resource.h`](crate::header::sys_resource). + fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()>; + + /// Platform implementation of [`setresgid()`](crate::header::unistd::setresgid) from [`unistd.h`](crate::header::unistd). + fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()>; + + /// Platform implementation of [`setresuid()`](crate::header::unistd::setresuid) from [`unistd.h`](crate::header::unistd). + fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()>; + + /// Platform implementation of [`setsid()`](crate::header::unistd::setsid) from [`unistd.h`](crate::header::unistd). + fn setsid() -> Result; + + /// Platform implementation of [`symlink()`](crate::header::unistd::symlink) from [`unistd.h`](crate::header::unistd). + fn symlink(path1: CStr, path2: CStr) -> Result<()> { + Self::symlinkat(path1, AT_FDCWD, path2) + } + + /// Platform implementation of [`symlinkat()`](crate::header::unistd::symlinkat) from [`unistd.h`](crate::header::unistd). + fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()>; + + /// Platform implementation of [`sync()`](crate::header::unistd::sync) from [`unistd.h`](crate::header::unistd). + fn sync() -> Result<()>; + + /// Platform implementation of [`timer_create()`](crate::header::time::timer_create) from [`time.h`](crate::header::time). + fn timer_create(clock_id: clockid_t, evp: &sigevent, timerid: Out) -> Result<()>; + + /// Platform implementation of [`timer_delete()`](crate::header::time::timer_delete) from [`time.h`](crate::header::time). + fn timer_delete(timerid: timer_t) -> Result<()>; + + /// Platform implementation of [`timer_gettime()`](crate::header::time::timer_gettime) from [`time.h`](crate::header::time). + fn timer_gettime(timerid: timer_t, value: Out) -> Result<()>; + + /// Platform implementation of [`timer_settime()`](crate::header::time::timer_settime) from [`time.h`](crate::header::time). + fn timer_settime( + timerid: timer_t, + flags: c_int, + value: &itimerspec, + ovalue: Option>, + ) -> Result<()>; + + // Always successful + /// Platform implementation of [`umask()`](crate::header::sys_stat::umask) from [`sys/stat.h`](crate::header::sys_stat). + fn umask(mask: mode_t) -> mode_t; + + /// Platform implementation of [`uname()`](crate::header::sys_utsname::uname) from [`sys/utsname.h`](crate::header::sys_utsname). + fn uname(utsname: Out) -> Result<()>; + + /// Platform implementation of [`unlink()`](crate::header::unistd::unlink) from [`unistd.h`](crate::header::unistd). + fn unlink(path: CStr) -> Result<()> { + Self::unlinkat(AT_FDCWD, path, 0) + } + + /// Platform implementation of [`unlinkat()`](crate::header::unistd::unlinkat) from [`unistd.h`](crate::header::unistd). + fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()>; + + /// Platform implementation of [`waitpid()`](crate::header::sys_wait::waitpid) from [`sys/wait.h`](crate::header::sys_wait). + fn waitpid(pid: pid_t, stat_loc: Option>, options: c_int) -> Result; + + /// Platform implementation of [`write()`](crate::header::unistd::write) from [`unistd.h`](crate::header::unistd). + fn write(fildes: c_int, buf: &[u8]) -> Result; + + /// Platform implementation of [`pwrite()`](crate::header::unistd::pwrite) from [`unistd.h`](crate::header::unistd). + fn pwrite(fildes: c_int, buf: &[u8], offset: off_t) -> Result; + + fn verify() -> bool; +} diff --git a/src/platform/pal/ptrace.rs b/src/platform/pal/ptrace.rs new file mode 100644 index 0000000000..d174435c7d --- /dev/null +++ b/src/platform/pal/ptrace.rs @@ -0,0 +1,15 @@ +use crate::{ + error::Result, + platform::{Pal, types::*}, +}; + +/// Platform abstraction for `ptrace` functionality. +pub trait PalPtrace: Pal { + /// Platform implementation of [`ptrace()`](crate::header::sys_ptrace::ptrace) from [`sys/ptrace.h`](crate::header::sys_ptrace). + unsafe fn ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, + ) -> Result; +} diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs new file mode 100644 index 0000000000..1eeba0fa1e --- /dev/null +++ b/src/platform/pal/signal.rs @@ -0,0 +1,62 @@ +use super::super::{ + Pal, + types::{c_int, c_uint, pid_t}, +}; +#[allow(deprecated)] +use crate::header::sys_time::itimerval; +use crate::{ + error::{Errno, Result}, + header::{ + bits_sigset_t::sigset_t, + bits_timespec::timespec, + signal::{sigaction, siginfo_t, sigval, stack_t}, + }, +}; + +/// Platform abstraction of signal-related functionality. +pub trait PalSignal: Pal { + /// Platform implementation of [`getitimer()`](crate::header::sys_time::getitimer) from [`sys/time.h`](crate::header::sys_time). + #[allow(deprecated)] + fn getitimer(which: c_int, out: &mut itimerval) -> Result<()>; + + /// Platform implementation of [`kill()`](crate::header::signal::kill) from [`signal.h`](crate::header::signal). + fn kill(pid: pid_t, sig: c_int) -> Result<()>; + + /// Platform implementation of [`sigqueue()`](crate::header::signal::sigqueue) from [`signal.h`](crate::header::signal). + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()>; + + /// Platform implementation of [`killpg()`](crate::header::signal::killpg) from [`signal.h`](crate::header::signal). + fn killpg(pgrp: pid_t, sig: c_int) -> Result<()>; + + /// Platform implementation of [`raise()`](crate::header::signal::raise) from [`signal.h`](crate::header::signal). + fn raise(sig: c_int) -> Result<()>; + + /// Platform implementation of [`setitimer()`](crate::header::sys_time::setitimer) from [`sys/time.h`](crate::header::sys_time). + #[allow(deprecated)] + fn setitimer(which: c_int, new: &itimerval, old: Option<&mut itimerval>) -> Result<()>; + + /// Platform implementation of [`alarm()`](crate::header::unistd::alarm) from [`unistd.h`](crate::header::unistd). + fn alarm(seconds: c_uint) -> c_uint; + + /// Platform implementation of [`sigaction()`](crate::header::signal::sigaction()) from [`signal.h`](crate::header::signal). + fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> Result<()>; + + /// Platform implementation of [`sigaltstack()`](crate::header::signal::sigaltstack()) from [`signal.h`](crate::header::signal). + unsafe fn sigaltstack(ss: Option<&stack_t>, old_ss: Option<&mut stack_t>) -> Result<()>; + + /// Platform implementation of [`sigpending()`](crate::header::signal::sigpending) from [`signal.h`](crate::header::signal). + fn sigpending(set: &mut sigset_t) -> Result<()>; + + /// Platform implementation of [`sigprocmask()`](crate::header::signal::sigprocmask) from [`signal.h`](crate::header::signal). + fn sigprocmask(how: c_int, set: Option<&sigset_t>, oset: Option<&mut sigset_t>) -> Result<()>; + + /// Platform implementation of [`sigsuspend()`](crate::header::signal::sigsuspend) from [`signal.h`](crate::header::signal). + fn sigsuspend(mask: &sigset_t) -> Errno; // always fails + + /// Platform implementation of [`sigtimedwait()`](crate::header::signal::sigtimedwait) from [`signal.h`](crate::header::signal). + fn sigtimedwait( + set: &sigset_t, + sig: Option<&mut siginfo_t>, + tp: Option<×pec>, + ) -> Result; +} diff --git a/src/platform/pal/socket.rs b/src/platform/pal/socket.rs new file mode 100644 index 0000000000..9d2c29ffbb --- /dev/null +++ b/src/platform/pal/socket.rs @@ -0,0 +1,101 @@ +use crate::{ + error::Result, + header::{ + bits_socklen_t::socklen_t, + sys_socket::{msghdr, sockaddr}, + }, + platform::{ + Pal, + types::{c_int, c_void, size_t}, + }, +}; + +/// Platform abstraction of socket functionality. +pub trait PalSocket: Pal { + /// Platform implementation of [`accept()`](crate::header::sys_socket::accept) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn accept( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result; + + /// Platform implementation of [`bind()`](crate::header::sys_socket::bind) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> Result<()>; + + /// Platform implementation of [`connect()`](crate::header::sys_socket::connect) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn connect( + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, + ) -> Result; + + /// Platform implementation of [`getpeername()`](crate::header::sys_socket::getpeername) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn getpeername( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result<()>; + + /// Platform implementation of [`getsockname()`](crate::header::sys_socket::getsockname) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn getsockname( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result<()>; + + /// Platform implementation of [`getsockopt()`](crate::header::sys_socket::getsockopt) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn getsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *mut c_void, + option_len: *mut socklen_t, + ) -> Result<()>; + + /// Platform implementation of [`listen()`](crate::header::sys_socket::listen) from [`sys/socket.h`](crate::header::sys_socket). + fn listen(socket: c_int, backlog: c_int) -> Result<()>; + + /// Platform implementation of [`recvfrom()`](crate::header::sys_socket::recvfrom) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn recvfrom( + socket: c_int, + buf: *mut c_void, + len: size_t, + flags: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result; + + /// Platform implementation of [`recvmsg()`](crate::header::sys_socket::recvmsg) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> Result; + + /// Platform implementation of [`sendmsg()`](crate::header::sys_socket::sendmsg) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> Result; + + /// Platform implementation of [`sendto()`](crate::header::sys_socket::sendto) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn sendto( + socket: c_int, + buf: *const c_void, + len: size_t, + flags: c_int, + dest_addr: *const sockaddr, + dest_len: socklen_t, + ) -> Result; + + /// Platform implementation of [`setsockopt()`](crate::header::sys_socket::setsockopt) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn setsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *const c_void, + option_len: socklen_t, + ) -> Result<()>; + + /// Platform implementation of [`shutdown()`](crate::header::sys_socket::shutdown) from [`sys/socket.h`](crate::header::sys_socket). + fn shutdown(socket: c_int, how: c_int) -> Result<()>; + + /// Platform implementation of [`socket()`](crate::header::sys_socket::socket) from [`sys/socket.h`](crate::header::sys_socket). + unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> Result; + + /// Platform implementation of [`socketpair()`](crate::header::sys_socket::socketpair) from [`sys/socket.h`](crate::header::sys_socket). + fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> Result<()>; +} diff --git a/src/platform/redox/epoll.rs b/src/platform/redox/epoll.rs new file mode 100644 index 0000000000..060b645767 --- /dev/null +++ b/src/platform/redox/epoll.rs @@ -0,0 +1,176 @@ +use super::{ + super::{Pal, PalEpoll, types::*}, + Sys, +}; + +use crate::{ + error::Errno, + fs::File, + header::{bits_sigset_t::sigset_t, errno::*, fcntl::*, sys_epoll::*}, + io::prelude::*, +}; +use core::{mem, slice}; +use syscall::{ + data::{Event, TimeSpec}, + flag::EVENT_READ, +}; + +fn epoll_to_event_flags(epoll: c_uint) -> syscall::EventFlags { + let mut event_flags = syscall::EventFlags::empty(); + + if epoll & EPOLLIN != 0 { + event_flags |= syscall::EventFlags::EVENT_READ; + } + + if epoll & EPOLLOUT != 0 { + event_flags |= syscall::EventFlags::EVENT_WRITE; + } + + /*TODO: support more EPOLL flags */ + let unsupported = !(EPOLLIN | EPOLLOUT); + if epoll & unsupported != 0 { + log::trace!("epoll unsupported flags 0x{:X}", epoll & unsupported); + } + + event_flags +} + +fn event_flags_to_epoll(flags: syscall::EventFlags) -> c_uint { + let mut epoll = 0; + + if flags.contains(syscall::EventFlags::EVENT_READ) { + epoll |= EPOLLIN; + } + + if flags.contains(syscall::EventFlags::EVENT_WRITE) { + epoll |= EPOLLOUT; + } + + epoll +} + +impl PalEpoll for Sys { + fn epoll_create1(flags: c_int) -> Result { + Sys::open(c"/scheme/event".into(), O_RDWR | flags, 0) + } + + unsafe fn epoll_ctl( + epfd: c_int, + op: c_int, + fd: c_int, + event: *mut epoll_event, + ) -> Result<(), Errno> { + match op { + EPOLL_CTL_ADD | EPOLL_CTL_MOD => { + Sys::write( + epfd, + &Event { + id: fd as usize, + flags: unsafe { epoll_to_event_flags((*event).events) }, + // NOTE: Danger when using something smaller than 64-bit + // systems. If this is needed, use a box or something + data: unsafe { (*event).data.u64 as usize }, + }, + )?; + } + EPOLL_CTL_DEL => { + Sys::write( + epfd, + &Event { + id: fd as usize, + flags: syscall::EventFlags::empty(), + //TODO: Is data required? + data: 0, + }, + )?; + } + _ => return Err(Errno(EINVAL)), + } + Ok(()) + } + + unsafe fn epoll_pwait( + epfd: c_int, + events: *mut epoll_event, + maxevents: c_int, + timeout: c_int, + sigset: *const sigset_t, + ) -> Result { + assert_eq!(mem::size_of::(), mem::size_of::()); + + if maxevents <= 0 { + return Err(Errno(EINVAL)); + } + + let timer_opt = if timeout != -1 { + let mut timer = File::open(c"/scheme/time/4".into(), O_RDWR)?; + Sys::write( + epfd, + &Event { + id: timer.fd as usize, + flags: EVENT_READ, + data: 0, + }, + )?; + + let mut time = TimeSpec::default(); + let _ = timer + .read(&mut time) + .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?; + time.tv_sec += (timeout as i64) / 1000; + time.tv_nsec += (timeout % 1000) * 1000000; + let _ = timer + .write(&time) + .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?; + + Some(timer) + } else { + None + }; + + let callback = || { + let res = syscall::read(epfd as usize, unsafe { + slice::from_raw_parts_mut( + events as *mut u8, + maxevents as usize * mem::size_of::(), + ) + }); + res + }; + + let bytes_read = if sigset.is_null() { + callback() + } else { + // Allowset is inverse of sigset mask + let allowset = !unsafe { *sigset }; + redox_rt::signal::callback_or_signal_async(allowset, callback) + }?; + + let read = bytes_read as usize / mem::size_of::(); + + let mut count = 0; + for i in 0..read { + unsafe { + let event_ptr = events.add(i); + let target_ptr = events.add(count); + let event = *(event_ptr as *mut Event); + if let Some(ref timer) = timer_opt { + if event.id as c_int == timer.fd { + // Do not count timer event + continue; + } + } + *target_ptr = epoll_event { + events: event_flags_to_epoll(event.flags), + data: epoll_data { + u64: event.data as u64, + }, + ..Default::default() + }; + count += 1; + } + } + + Ok(count) + } +} diff --git a/src/platform/redox/event.rs b/src/platform/redox/event.rs new file mode 100644 index 0000000000..b02f641ce9 --- /dev/null +++ b/src/platform/redox/event.rs @@ -0,0 +1,82 @@ +use core::mem::size_of; + +use crate::header::{ + bits_sigset_t::sigset_t, + bits_timespec::timespec, + fcntl::{O_CLOEXEC, O_CREAT, O_RDWR}, +}; + +use super::libredox::RawResult; + +use syscall::{EINVAL, Error, Result}; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_event_queue_create_v1(flags: u32) -> RawResult { + Error::mux((|| { + if flags != 0 { + return Err(Error::new(EINVAL)); + } + Ok(super::libredox::open("/scheme/event", O_CLOEXEC | O_CREAT | O_RDWR, 0o700)? as usize) + })()) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_event_queue_get_events_v1( + queue: usize, + buf: *mut event::raw::RawEventV1, + buf_count: usize, + flags: u32, + _timeout: *const timespec, + _sigset: *const sigset_t, +) -> RawResult { + Error::mux((|| -> Result { + if flags != 0 || buf_count == 0 { + return Err(Error::new(EINVAL)); + } + let mut event = syscall::Event::default(); + let res = syscall::read(queue, &mut event)?; + assert_eq!( + res, + size_of::(), + "EOF not yet defined for event queue reads" + ); + unsafe { + buf.write(event::raw::RawEventV1 { + fd: event.id, + flags: event::raw::EventFlags::from(event.flags).bits(), + user_data: event.data, + }) + }; + + Ok(1) + })()) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_event_queue_ctl_v1( + queue: usize, + fd: usize, + flags: u32, + user_data: usize, +) -> RawResult { + Error::mux((|| -> Result { + let res = syscall::write( + queue, + &syscall::Event { + id: fd, + flags: event::raw::EventFlags::from_bits(flags) + .ok_or(Error::new(EINVAL))? + .into(), + data: user_data, + }, + )?; + assert_eq!( + res, + size_of::(), + "EOF not yet defined for event queue writes" + ); + Ok(0) + })()) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_event_queue_destroy_v1(queue: usize) -> RawResult { + Error::mux(syscall::close(queue)) +} diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs new file mode 100644 index 0000000000..1b4b96bb66 --- /dev/null +++ b/src/platform/redox/exec.rs @@ -0,0 +1,618 @@ +use core::{ + convert::Infallible, + num::{NonZeroU64, NonZeroUsize}, +}; + +use crate::{ + c_str::{CStr, CString}, + fs::File, + header::{limits::PATH_MAX, string::strlen}, + io::{BufReader, SeekFrom, prelude::*}, + platform::types::*, +}; + +use redox_rt::{ + RtTcb, + proc::{ExtraInfo, FdGuard, FdGuardUpper, FexecResult, InterpOverride}, + sys::Resugid, +}; +use syscall::{data::Stat, error::*, flag::*}; + +fn fexec_impl( + exec_file: FdGuardUpper, + path: &[u8], + args: &[&[u8]], + envs: &[&[u8]], + extrainfo: &ExtraInfo, + interp_override: Option, +) -> Result { + let FexecResult::Interp { + path, + interp_override: new_interp_override, + } = redox_rt::proc::fexec_impl( + exec_file, + &RtTcb::current().thread_fd(), + redox_rt::current_proc_fd(), + path, + args, + envs, + extrainfo, + interp_override, + )?; + + // According to elf(5), PT_INTERP requires that the interpreter path be + // null-terminated. Violating this should therefore give the "format error" ENOEXEC. + let path_cstr = CStr::from_bytes_with_nul(&path).map_err(|_| Error::new(ENOEXEC))?; + + return execve( + Executable::AtPath(path_cstr), + ArgEnv::Parsed { args, envs }, + Some(new_interp_override), + ); +} + +pub enum ArgEnv<'a> { + C { + argv: *const *mut c_char, + envp: *const *mut c_char, + }, + Parsed { + args: &'a [&'a [u8]], + envs: &'a [&'a [u8]], + }, +} + +pub enum Executable<'a> { + AtPath(CStr<'a>), + InFd { file: File, arg0: &'a [u8] }, +} + +pub fn execve( + exec: Executable<'_>, + arg_env: ArgEnv, + interp_override: Option, +) -> Result { + // NOTE: We must omit O_CLOEXEC and close manually, otherwise it will be closed before we + // have even read it! + let (mut image_file, stat, arg0) = match exec { + Executable::AtPath(path) => { + let Ok(src_fd) = File::open(path, O_RDONLY as c_int) else { + return Err(Error::new(ENOENT)); + }; + + let mut src_stat = Stat::default(); + redox_rt::sys::fstat(*src_fd as usize, &mut src_stat)?; + + #[cfg(feature = "ld_so_cache")] + let src_fd = { + let mtime_sec = src_stat.st_mtime; + let mtime_nsec = src_stat.st_mtime_nsec; + + let safe_path = path.to_str().unwrap_or("").replace('/', "_"); + let shm_path_owned = format!( + "/scheme/shm/ld.so.cache.{}.{}.{}\0", + safe_path, mtime_sec, mtime_nsec + ); + let shm_path = + unsafe { CStr::from_bytes_with_nul_unchecked(shm_path_owned.as_bytes()) }; + + let mut file_opt = None; + if let Ok(shm_fd) = File::open(shm_path, O_RDONLY as c_int) { + if let Ok(shm_stat) = shm_fd.fstat() + && shm_stat.st_size > 0 + { + file_opt = Some(shm_fd); + } + } + file_opt.unwrap_or(src_fd) + }; + + (src_fd, src_stat, path.to_bytes()) + } + Executable::InFd { file, arg0 } => { + let mut stat = Stat::default(); + redox_rt::sys::fstat(*file as usize, &mut stat)?; + (file, stat, arg0) + } + }; + + // With execve now being implemented in userspace, we need to check ourselves that this + // file is actually executable. While checking for read permission is unnecessary as the + // scheme will not allow us to read otherwise, the execute bit is completely unenforced. + // + // But we do (currently) have the permission to mmap executable memory and fill it with any + // program, even marked non-executable, so really the best we can do is check that nothing is + // executed by accident. + // + // TODO: At some point we might have capabilities limiting the ability to allocate + // executable memory. + + let Resugid { ruid, euid, rgid, .. } = redox_rt::sys::posix_getresugid(); + + // Root (uid 0) bypasses execute permission checks, matching Linux behavior. + // Check both ruid and euid since Linux checks the effective UID. + if ruid != 0 && euid != 0 { + let mode = if ruid == stat.st_uid { + (stat.st_mode >> 3 * 2) & 0o7 + } else if rgid == stat.st_gid { + (stat.st_mode >> 3 * 1) & 0o7 + } else { + stat.st_mode & 0o7 + }; + + if mode & 0o1 == 0o0 { + return Err(Error::new(EACCES)); + } + } + + let cwd: Box<[u8]> = super::path::clone_cwd().unwrap_or_default().into(); + + // Path to interpreter binary and args if found + let (interpreter_path, interpreter_args) = { parse_interpreter(&mut image_file)? }; + + // Total number of arguments which includes the interpreter if interpreted and its args + let mut len = 0; + if interpreter_path.is_some() { + len = 1; + if interpreter_args.is_some() { + len = 2; + } + } + + // Count arguments for `exec` which is different from the interpreter's args + // + // When there's an interpreter, we skip the original `argv[0]` and replace it with the script + // path (`arg0`). + match arg_env { + ArgEnv::C { argv, .. } => unsafe { + let mut count = 0; + let ptr = if interpreter_path.is_some() && !(*argv).is_null() { + argv.add(1) + } else { + argv + }; + + while !(*ptr.add(count)).is_null() { + count += 1; + } + len += count; + }, + ArgEnv::Parsed { args, .. } => { + let skip = if interpreter_path.is_some() { 1 } else { 0 }; + len += args.len().saturating_sub(skip); + } + } + let mut args: Vec<&[u8]> = Vec::with_capacity(len); + + if let Some(interpreter) = &interpreter_path { + image_file = File::open(CStr::borrow(&interpreter), O_RDONLY as c_int) + .map_err(|_| Error::new(ENOENT))?; + + // Push interpreter to arguments + args.push(interpreter.as_bytes()); + + // Push interpreter args, if any, to our main arguments + if let Some(args_ref) = interpreter_args.as_ref() { + args.push(args_ref.as_bytes()); + } + } else { + image_file + .seek(SeekFrom::Start(0)) + .map_err(|_| Error::new(EIO))?; + } + + let (args, envs): (Vec<_>, Vec<_>) = match arg_env { + ArgEnv::C { mut argv, mut envp } => unsafe { + // Arguments + if interpreter_path.is_some() { + args.push(arg0); + if !(*argv).is_null() { + argv = argv.add(1); + } + } + + while !argv.read().is_null() { + let arg = argv.read(); + + let len = strlen(arg); + args.push(core::slice::from_raw_parts(arg as *const u8, len)); + argv = argv.add(1); + } + + // Environment variables + let mut len = 0; + while !envp.add(len).read().is_null() { + len += 1; + } + + let mut envs: Vec<&[u8]> = Vec::with_capacity(len); + while !envp.read().is_null() { + let env = envp.read(); + + let len = strlen(env); + envs.push(core::slice::from_raw_parts(env as *const u8, len)); + envp = envp.add(1); + } + (args, envs) + }, + ArgEnv::Parsed { + args: new_args, + envs, + } => { + if interpreter_path.is_some() { + args.push(arg0); + args.extend(new_args.iter().skip(1)); + } else { + args.extend(new_args); + } + (args, Vec::from(envs)) + } + }; + + // TODO: Convert image_file to FdGuard earlier? + let exec_fd_guard = FdGuard::new(image_file.fd as usize).to_upper().unwrap(); + core::mem::forget(image_file); + + let sigprocmask = redox_rt::signal::get_sigmask().unwrap(); + + let extrainfo = ExtraInfo { + cwd: Some(&cwd), + sigignmask: redox_rt::signal::get_sigignmask_to_inherit(), + sigprocmask, + umask: redox_rt::sys::get_umask(), + thr_fd: RtTcb::current().thread_fd().as_raw_fd(), + proc_fd: redox_rt::current_proc_fd().as_raw_fd(), + ns_fd: redox_rt::current_namespace_fd().ok(), + cwd_fd: super::path::current_dir() + .ok() + .map(|fd| fd.as_ref().unwrap().fd.as_raw_fd()), + }; + + fexec_impl( + exec_fd_guard, + arg0, + &args, + &envs, + &extrainfo, + interp_override, + ) +} + +// Parse the interpreter and its args if `reader` starts with a shebang (#!). +// +// # Return +// * Path to the interpreter and its args, if any +// * `None` if no shebang +// * An error if parsing failed +// +// # Errors +// * E2BIG: The full path of the shebang is greater than [`PATH_MAX`] +// * ENOEXEC: Invalid shebang line, such as a line of all whitespace +// * EIO: Failure reading from `reader` +fn parse_interpreter(image_file: &mut R) -> Result<(Option, Option)> +where + R: Read + Seek, +{ + // Read shebang (for example #!/bin/sh) + let mut read = 0; + let mut shebang = [0; 2]; + + while read < 2 { + match image_file + .read(&mut shebang) + .map_err(|_| Error::new(ENOEXEC))? + { + 0 => break, + i => read += i, + } + } + if shebang != *b"#!" { + return Ok((None, None)); + } + + // BufReader is created after parsing the shebang because it doesn't make sense to buffer + // bytes to read two bytes especially if `image_file` is NOT a script. + let mut reader_ = BufReader::new(image_file); + let reader = &mut reader_; + + // Skip prepended whitespace for interpreter + // Ex: #! /usr/bin/python + let pos = reader + .bytes() + .position(|byte| byte.ok().is_some_and(|byte| !byte.is_ascii_whitespace())) + .and_then(|pos| (pos + 2).try_into().ok()) + // Fail if all whitespace or empty + .ok_or_else(|| Error::new(ENOEXEC))?; + // We read the non-whitespace character which sets reader position one past it. + // Seeking back to that position is essentially free since reads are buffered and it's + // unlikely that there was enough whitespace that we performed multiple reads. + reader + .seek(SeekFrom::Start(pos)) + .map_err(|_| Error::new(EIO))?; + + // Scan the first line once for the mandatory interpreter and optional args. + // This is nicer than using `read_until` or `read_line` because it avoids having to scan the + // data twice to check if there are args. + let mut interp_offset = None; + let mut args_offset = None; + for (i, byte) in reader.bytes().enumerate() { + let byte = byte.map_err(|_| Error::new(EIO))?; + + match (byte, interp_offset, args_offset) { + // No args; only interpreter + (b'\n', None, None) => { + interp_offset = NonZeroUsize::new(i); + break; + } + // Interpreter found, so we're scanning for where the args ends + (b'\n', Some(_), None) => { + args_offset = NonZeroUsize::new(i); + break; + } + // Found args so interpreter ends at `i` + (b' ', None, None) => { + interp_offset = NonZeroUsize::new(i); + } + _ => {} + } + } + + // Interpreter is mandatory since we found #! earlier + let Some(interp_offset) = interp_offset.map(NonZeroUsize::get) else { + return Err(Error::new(ENOEXEC)); + }; + // We need u64s and usizes; converting them now is easier + let Ok(interp_offset_u64) = interp_offset.try_into() else { + return Err(Error::new(E2BIG)); + }; + let args_offset_u64: Option = args_offset + .map(|offset| offset.try_into()) + .transpose() + .map_err(|_| Error::new(E2BIG))?; + + // Spec: full length of the shebang can't exceed max path length + let shebang_len = pos + .checked_add(interp_offset_u64) + .and_then(|len| len.checked_add(args_offset_u64.map(NonZeroU64::get).unwrap_or_default())) + .ok_or_else(|| Error::new(E2BIG))?; + // PATH_MAX is a small number that fits into u64 so `as` is okay + if shebang_len > PATH_MAX as u64 { + return Err(Error::new(E2BIG)); + } + + // Rewind to the beginning of the interpreter. + // As above, this is essentially free because the internal buf size is several times larger + // than PATH_MAX by default, and our shebang_len < PATH_MAX as checked above. + reader + .seek(SeekFrom::Start(pos)) + .map_err(|_| Error::new(E2BIG))?; + + let mut interpreter = Vec::with_capacity(interp_offset); + reader + .take(interp_offset_u64) + .read_to_end(&mut interpreter) + .map_err(|_| Error::new(EIO))?; + + // Read args, but treat as an opaque block to pass to the interpreter. + // Linux and FreeBSD both pass the args as is to the interpreter whereas macOS splits + // the args similar to `/usr/bin/env -S`. + // POSIX leaves the behavior up to the implementation. + // It's simpler to rely on env because well behaved, portable scripts will use + // it to ensure correct operation on Linux/FreeBSD. + // Splitting args ourselves gains little while reinventing env -S + let interpreter_args = if let (Some(offset), Some(offset_u64)) = ( + args_offset.map(NonZeroUsize::get), + args_offset_u64.map(NonZeroU64::get), + ) { + let len = offset - interp_offset; + let len_u64 = offset_u64 - interp_offset_u64; + let mut args = Vec::with_capacity(len); + + reader + .take(len_u64) + .read_to_end(&mut args) + .map_err(|_| Error::new(E2BIG))?; + + // Eat '\n' + reader.consume(1); + + let mut arg_start = 0; + while arg_start < args.len() && (args[arg_start] == b' ' || args[arg_start] == b'\t') { + arg_start += 1; + } + + if arg_start < args.len() { + let args = CString::new(&args[arg_start..]).map_err(|_| Error::new(ENOEXEC))?; + Some(args) + } else { + None + } + } else { + None + }; + + let interpreter = CString::new(interpreter).map_err(|_| Error::new(ENOEXEC))?; + Ok((Some(interpreter), interpreter_args)) +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use super::parse_interpreter; + + // Shebangs without a script attached + const NO_FRILLS: &str = "#!/bin/sh\n"; + const NO_FRILLS_EXPECTED: &str = "/bin/sh"; + + const SPACE_B4_INTERP: &str = "#! /bin/sh\n"; + const SPACE_B4_INTERP_EXPECTED: &str = "/bin/sh"; + + const NO_FRILLS_ENV: &str = "#!/usr/bin/env sh\n"; + const NO_FRILLS_ENV_EXPECTED: &str = "/usr/bin/env"; + const NO_FRILLS_ENV_EXPECTED_ARGS: &str = "sh"; + + const SPACE_B4_ENV: &str = "#! /usr/bin/env sh\n"; + const SPACE_B4_EXPECTED: &str = NO_FRILLS_ENV_EXPECTED; + const SPACE_B4_EXPECTED_ARGS: &str = NO_FRILLS_ENV_EXPECTED_ARGS; + + const MULT_SPACES_B4: &str = "#! /usr/bin/env sh\n"; + const MULT_SPACES_B4_EXPECTED: &str = NO_FRILLS_ENV_EXPECTED; + const MULT_SPACES_B4_EXPECTED_ARGS: &str = NO_FRILLS_ENV_EXPECTED_ARGS; + + // Shebangs with a script attached + // These test that the parser doesn't run off the first line + const NO_FRILLS_W_SCRIPT: &str = r#"#!/bin/sh + echo "Hello from Redox""#; + const NO_FRILLS_W_SCRIPT_EXPECTED: &str = NO_FRILLS_EXPECTED; + + const SPACE_B4_INTERP_W_SCRIPT: &str = r#"#! /bin/sh + echo "Doctor Eigenvalue""#; + const SPACE_B4_INTERP_W_SCRIPT_EXPECTED: &str = NO_FRILLS_EXPECTED; + + const MULT_ARGUMENTS: &str = r#"#! /usr/bin/env -S python -OO + assert False + print("This totally works") + "#; + const MULT_ARGUMENTS_EXPECTED: &str = NO_FRILLS_ENV_EXPECTED; + const MULT_ARGUMENTS_EXPECTED_ARGS: &str = "-S python -OO"; + + // No hashbang conditions + const NO_SHEBANG: &str = "/bin/sh"; + const EMPTY: &str = ""; + + // Error conditions + const SHEBANG_NO_INTERP: &str = "#!"; + const SHEBANG_NO_INTERP_SPACE: &str = "#! "; + const SHEBANG_NO_INTERP_SCRIPT: &str = "#!\necho ${PATH}"; + + fn success(input: &str, expected_interp: &str, expected_args: Option<&str>) { + let mut reader = Cursor::new(input); + let (actual_interp, actual_args) = parse_interpreter(&mut reader) + .unwrap_or_else(|e| panic!("Shebang ({input}) should parse\n\t{e}")); + + let actual_interp = actual_interp + .expect("Expected an interpreter") + .into_string() + .expect("Interpreter is ASCII (valid UTF-8)"); + assert_eq!(expected_interp, actual_interp); + + if let Some(expected_args) = expected_args { + let actual_args = actual_args + .expect("Expected arguments to interpreter") + .into_string() + .expect("Args string is ASCII (valid UTF-8)"); + assert_eq!(expected_args, actual_args); + } + } + + #[test] + fn parse_interpreter_without_space() { + success(NO_FRILLS, NO_FRILLS_EXPECTED, None); + } + + #[test] + fn parse_interpreter_with_space() { + success(SPACE_B4_INTERP, SPACE_B4_INTERP_EXPECTED, None); + } + + #[test] + fn parse_interpreter_with_arg() { + success( + NO_FRILLS_ENV, + NO_FRILLS_ENV_EXPECTED, + Some(NO_FRILLS_ENV_EXPECTED_ARGS), + ); + } + + #[test] + fn parse_interpreter_with_arg_and_space() { + success( + SPACE_B4_ENV, + SPACE_B4_EXPECTED, + Some(SPACE_B4_EXPECTED_ARGS), + ); + } + + #[test] + fn parse_interpreter_with_multiple_spaces() { + success( + MULT_SPACES_B4, + MULT_SPACES_B4_EXPECTED, + Some(MULT_SPACES_B4_EXPECTED_ARGS), + ); + } + + #[test] + fn parse_interpreter_with_script() { + success(NO_FRILLS_W_SCRIPT, NO_FRILLS_W_SCRIPT_EXPECTED, None); + } + + #[test] + fn parse_interpreter_with_script_and_space() { + success( + SPACE_B4_INTERP_W_SCRIPT, + SPACE_B4_INTERP_W_SCRIPT_EXPECTED, + None, + ); + } + + #[test] + fn parse_interpreter_with_script_args_space() { + success( + MULT_ARGUMENTS, + MULT_ARGUMENTS_EXPECTED, + Some(MULT_ARGUMENTS_EXPECTED_ARGS), + ); + } + + #[test] + fn parse_interpreter_no_shebang() { + let mut reader = Cursor::new(NO_SHEBANG); + let (interpreter, args) = + parse_interpreter(&mut reader).expect("Shouldn't fail if file doesn't have a shebang"); + + assert!( + interpreter.is_none(), + "Interpreter should be `None` if shebang isn't present" + ); + assert!( + args.is_none(), + "Args should be empty without an interpreter." + ); + } + + #[test] + fn parse_interpreter_empty() { + let mut reader = Cursor::new(EMPTY); + let (interpreter, args) = + parse_interpreter(&mut reader).expect("Shouldn't fail if file doesn't have a shebang"); + + assert!( + interpreter.is_none(), + "Interpreter should be `None` for empty image" + ); + assert!(args.is_none(), "Args should be empty for empty image"); + } + + #[test] + fn parse_interpreter_no_interpreter_fail() { + let mut reader = Cursor::new(SHEBANG_NO_INTERP); + parse_interpreter(&mut reader) + .expect_err("A hashbang without an interpreter should return an error"); + } + + #[test] + fn parse_interpreter_no_interpreter_space_fail() { + let mut reader = Cursor::new(SHEBANG_NO_INTERP_SPACE); + parse_interpreter(&mut reader) + .expect_err("A hashbang without an interpreter should return an error"); + } + + #[test] + fn parse_interpreter_no_interpreter_script_fail() { + let mut reader = Cursor::new(SHEBANG_NO_INTERP_SCRIPT); + parse_interpreter(&mut reader) + .expect_err("A hashbang without an interpreter should return an error"); + } +} diff --git a/src/platform/redox/extra.rs b/src/platform/redox/extra.rs new file mode 100644 index 0000000000..1e3138ed44 --- /dev/null +++ b/src/platform/redox/extra.rs @@ -0,0 +1,38 @@ +use core::slice; + +use crate::{ + error::{Errno, ResultExt}, + platform::types::*, +}; +use syscall::{F_SETFD, F_SETFL, O_RDONLY, O_WRONLY, error::*}; + +pub use redox_rt::proc::FdGuard; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fpath(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t { + syscall::fpath(fd as usize, unsafe { + slice::from_raw_parts_mut(buf as *mut u8, count) + }) + .map_err(Errno::from) + .map(|l| l as ssize_t) + .or_minus_one_errno() +} + +pub fn pipe2(flags: usize) -> syscall::error::Result<[c_int; 2]> { + let read_flags = flags | O_RDONLY; + let write_flags = flags | O_WRONLY; + let read_fd = FdGuard::open("/scheme/pipe", read_flags)?; + let write_fd = read_fd.dup(b"write")?; + write_fd.fcntl(F_SETFL, write_flags)?; + write_fd.fcntl(F_SETFD, write_flags)?; + + let fds = [ + c_int::try_from(read_fd.as_raw_fd()).map_err(|_| Error::new(EMFILE))?, + c_int::try_from(write_fd.as_raw_fd()).map_err(|_| Error::new(EMFILE))?, + ]; + + read_fd.take(); + write_fd.take(); + + Ok(fds) +} diff --git a/src/platform/redox/libcscheme.rs b/src/platform/redox/libcscheme.rs new file mode 100644 index 0000000000..a089606055 --- /dev/null +++ b/src/platform/redox/libcscheme.rs @@ -0,0 +1,52 @@ +use crate::{c_str::CStr, header::stdlib::getenv, platform::types::*}; +use core::ptr; +use syscall::{EIO, ENOENT, Error, Result, flag::*}; + +pub const LIBC_SCHEME: &'static str = "libc:"; + +const ENV_MAX_LEN: i32 = i32::MAX; + +macro_rules! env_str { + ($lit:expr) => { + #[allow(unused_unsafe)] + { + let val_bytes = unsafe { getenv(concat!($lit, "\0").as_ptr() as *const c_char) }; + if val_bytes != ptr::null_mut() { + if let Ok(val_str) = unsafe { CStr::from_ptr(val_bytes) }.to_str() { + Some(val_str) + } else { + None + } + } else { + None + } + } + }; +} + +pub fn open(path: &str, flags: usize) -> Result { + assert!(path.starts_with(LIBC_SCHEME)); + + if flags & O_SYMLINK != 0 { + return Err(Error::new(ENOENT)); + } + + let basename = match path.strip_prefix(LIBC_SCHEME) { + Some(path) => path.trim_matches('/'), + _ => return Err(Error::new(EIO)), + }; + + // Linux seems to allow you to read from or write to any of /dev/{stdin,stderr,stdout} + match basename { + "stderr" => syscall::dup(2, &[]), + "stdin" => syscall::dup(0, &[]), + "stdout" => syscall::dup(1, &[]), + "tty" => { + if let Some(tty) = env_str!("TTY") { + return redox_rt::sys::open(tty, flags); + } + Err(Error::new(ENOENT)) + } + _ => Err(Error::new(ENOENT)), + } +} diff --git a/src/platform/redox/libredox.rs b/src/platform/redox/libredox.rs new file mode 100644 index 0000000000..2aad40861f --- /dev/null +++ b/src/platform/redox/libredox.rs @@ -0,0 +1,634 @@ +use core::{mem, slice, str}; + +use alloc::vec::Vec; +use ioslice::IoSlice; +use redox_protocols::protocol::{ProcKillTarget, SocketCall, WaitFlags}; +use redox_rt::sys::{WaitpidTarget, posix_read, posix_write, std_fs_call_ro, std_fs_call_wo}; +use syscall::{ + EMFILE, ENAMETOOLONG, ENOSYS, EOPNOTSUPP, Error, Result, StdFsCallKind, + data::StdFsCallMeta, + dirent::{DirentHeader, DirentKind}, +}; + +use crate::{ + header::{ + bits_iovec::iovec, bits_timespec::timespec, errno::EINVAL, signal::sigaction, + sys_stat::UTIME_NOW, + }, + out::Out, + platform::{PalSignal, pal::Pal, types::*}, +}; + +use super::Sys; + +pub type RawResult = usize; + +pub fn open(path: &str, oflag: c_int, mode: mode_t) -> Result { + let usize_fd = super::path::open( + path, + ((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF), + )?; + + c_int::try_from(usize_fd) + .map_err(|_| { + let _ = syscall::close(usize_fd); + Error::new(EMFILE) + }) + .map(|f| f as usize) +} + +pub fn openat(dirfd: c_int, path: &str, oflag: c_int, mode: mode_t) -> Result { + let usize_fd = super::path::openat( + dirfd, + path, + ((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF), + )?; + + c_int::try_from(usize_fd) + .map_err(|_| { + let _ = syscall::close(usize_fd); + Error::new(EMFILE) + }) + .map(|f| f as usize) +} +pub fn fchmod(fd: usize, new_mode: u16) -> Result<()> { + std_fs_call_wo( + fd, + &[], + &StdFsCallMeta::new(StdFsCallKind::Fchmod, new_mode as u64, 0), + )?; + Ok(()) +} +pub fn fchown(fd: usize, new_uid: u32, new_gid: u32) -> Result<()> { + /* std_fs_call + Error::mux(std_fs_call_wo( + fd, + &[], + &StdFsCallMeta::new( + StdFsCallKind::Fchmod, + (new_uid as u64) | ((new_gid as u64) << 32), + 0, + ), + )) + */ + syscall::fchown(fd, new_uid, new_gid)?; + Ok(()) +} +pub fn getdents(fd: usize, buf: &mut [u8], opaque: u64) -> Result { + //println!("GETDENTS {} into ({:p}+{})", fd, buf.as_ptr(), buf.len()); + + const HEADER_SIZE: usize = mem::size_of::(); + + // Use syscall if it exists. + match std_fs_call_ro( + fd, + buf, + &StdFsCallMeta::new(StdFsCallKind::Getdents, opaque, HEADER_SIZE as u64), + ) { + Err(Error { + errno: EOPNOTSUPP | ENOSYS, + }) => (), + other => { + //println!("REAL GETDENTS {:?}", other); + return Ok(other?); + } + } + + // Otherwise, for legacy schemes, assume the buffer is pre-arranged (all schemes do this in + // practice), and just read the name. If multiple names appear, pretend it didn't happen + // and just use the first entry. + + let (header, name) = buf.split_at_mut(mem::size_of::()); + + let bytes_read = Sys::pread(fd as c_int, name, opaque as i64)? as usize; + if bytes_read == 0 { + return Ok(0); + } + + let (name_len, advance) = match name[..bytes_read].iter().position(|c| *c == b'\n') { + Some(idx) => (idx, idx + 1), + + // Insufficient space for NUL byte, or entire entry was not read. Indicate we need a + // larger buffer. + None if bytes_read == name.len() => return Err(Error::new(EINVAL)), + + None => (bytes_read, name.len()), + }; + name[name_len] = b'\0'; + + let record_len = u16::try_from(mem::size_of::() + name_len + 1) + .map_err(|_| Error::new(ENAMETOOLONG))?; + header.copy_from_slice(&DirentHeader { + inode: 0, + next_opaque_id: opaque + advance as u64, + record_len, + kind: DirentKind::Unspecified as u8, + }); + //println!("EMULATED GETDENTS"); + + Ok(record_len.into()) +} +pub unsafe fn fstat(fd: usize, buf: *mut crate::header::sys_stat::stat) -> Result<()> { + let mut redox_buf: syscall::Stat = Default::default(); + redox_rt::sys::fstat(fd, &mut redox_buf)?; + + if let Some(buf) = unsafe { buf.as_mut() } { + buf.st_dev = redox_buf.st_dev as dev_t; + buf.st_ino = redox_buf.st_ino as ino_t; + buf.st_nlink = redox_buf.st_nlink as nlink_t; + buf.st_mode = redox_buf.st_mode as mode_t; + buf.st_uid = redox_buf.st_uid as uid_t; + buf.st_gid = redox_buf.st_gid as gid_t; + // TODO st_rdev + buf.st_rdev = 0; + buf.st_size = redox_buf.st_size as off_t; + buf.st_blksize = redox_buf.st_blksize as blksize_t; + buf.st_blocks = redox_buf.st_blocks as blkcnt_t; + buf.st_atim = timespec { + tv_sec: redox_buf.st_atime as time_t, + tv_nsec: redox_buf.st_atime_nsec as c_long, + }; + buf.st_mtim = timespec { + tv_sec: redox_buf.st_mtime as time_t, + tv_nsec: redox_buf.st_mtime_nsec as c_long, + }; + buf.st_ctim = timespec { + tv_sec: redox_buf.st_ctime as time_t, + tv_nsec: redox_buf.st_ctime_nsec as c_long, + }; + } + Ok(()) +} +pub unsafe fn fstatvfs(fd: usize, buf: *mut crate::header::sys_statvfs::statvfs) -> Result<()> { + let mut kbuf: syscall::StatVfs = Default::default(); + std_fs_call_ro( + fd, + &mut kbuf, + &StdFsCallMeta::new(StdFsCallKind::Fstatvfs, 0, 0), + )?; + + if !buf.is_null() { + unsafe { + (*buf).f_bsize = kbuf.f_bsize as c_ulong; + (*buf).f_frsize = kbuf.f_bsize as c_ulong; + (*buf).f_blocks = kbuf.f_blocks as c_ulong; + (*buf).f_bfree = kbuf.f_bfree as c_ulong; + (*buf).f_bavail = kbuf.f_bavail as c_ulong; + //TODO + (*buf).f_files = 0; + (*buf).f_ffree = 0; + (*buf).f_favail = 0; + (*buf).f_fsid = 0; + (*buf).f_flag = 0; + (*buf).f_namemax = 0; + } + } + Ok(()) +} +pub fn fsync(fd: usize) -> Result<()> { + std_fs_call_wo(fd, &[], &StdFsCallMeta::new(StdFsCallKind::Fsync, 0, 0))?; + Ok(()) +} +pub fn ftruncate(fd: usize, len: usize) -> Result<()> { + std_fs_call_wo( + fd, + &[], + &StdFsCallMeta::new(StdFsCallKind::Ftruncate, len as u64, 0), + )?; + Ok(()) +} +pub unsafe fn futimens(fd: usize, times: *const timespec) -> Result<()> { + let times = if times.is_null() { + // null means set to current time using special UTIME_NOW value (tv_sec is ignored in that case) + [ + syscall::TimeSpec { + tv_sec: 0, + tv_nsec: UTIME_NOW as c_int, + }, + syscall::TimeSpec { + tv_sec: 0, + tv_nsec: UTIME_NOW as c_int, + }, + ] + } else { + unsafe { times.cast::<[timespec; 2]>().read() }.map(|ts| syscall::TimeSpec::from(&ts)) + }; + let redox_buf = unsafe { + slice::from_raw_parts( + times.as_ptr() as *const u8, + times.len() * mem::size_of::(), + ) + }; + std_fs_call_wo( + fd, + redox_buf, + &StdFsCallMeta::new(StdFsCallKind::Futimens, 0, 0), + )?; + Ok(()) +} +pub fn clock_gettime(clock: usize, mut tp: Out) -> Result<()> { + let mut redox_tp = syscall::TimeSpec::default(); + syscall::clock_gettime(clock as usize, &mut redox_tp)?; + tp.write(timespec { + tv_sec: redox_tp.tv_sec as time_t, + tv_nsec: redox_tp.tv_nsec as c_long, + }); + Ok(()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_open_v1( + path_base: *const u8, + path_len: usize, + flags: u32, + mode: u16, +) -> RawResult { + Error::mux(open( + unsafe { str::from_utf8_unchecked(slice::from_raw_parts(path_base, path_len)) }, + flags as c_int, + mode as mode_t, + )) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_openat_v1( + fd: usize, + path_base: *const u8, + path_len: usize, + flags: u32, + fcntl_flags: u32, +) -> RawResult { + Error::mux(syscall::openat( + fd, + unsafe { str::from_utf8_unchecked(slice::from_raw_parts(path_base, path_len)) }, + flags as usize, + fcntl_flags as usize, + )) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_dup_v1(fd: usize, buf: *const u8, len: usize) -> RawResult { + Error::mux(syscall::dup(fd, unsafe { + core::slice::from_raw_parts(buf, len) + })) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_dup2_v1( + old_fd: usize, + new_fd: usize, + buf: *const u8, + len: usize, +) -> RawResult { + Error::mux(syscall::dup2(old_fd, new_fd, unsafe { + core::slice::from_raw_parts(buf, len) + })) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_read_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult { + Error::mux(posix_read(fd, unsafe { + slice::from_raw_parts_mut(dst_base, dst_len) + })) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_write_v1( + fd: usize, + src_base: *const u8, + src_len: usize, +) -> RawResult { + Error::mux(posix_write(fd, unsafe { + slice::from_raw_parts(src_base, src_len) + })) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fchmod_v1(fd: usize, new_mode: u16) -> RawResult { + Error::mux(fchmod(fd, new_mode).map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fchown_v1(fd: usize, new_uid: u32, new_gid: u32) -> RawResult { + Error::mux(fchown(fd, new_uid, new_gid).map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_getdents_v0( + fd: usize, + buf: *mut u8, + buf_len: usize, + opaque: u64, +) -> RawResult { + Error::mux( + Sys::getdents( + fd as c_int, + unsafe { slice::from_raw_parts_mut(buf, buf_len) }, + opaque, + ) + .map_err(Into::into), + ) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fstat_v1( + fd: usize, + stat: *mut crate::header::sys_stat::stat, +) -> RawResult { + Error::mux(unsafe { fstat(fd, stat) }.map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fstatvfs_v1( + fd: usize, + stat: *mut crate::header::sys_statvfs::statvfs, +) -> RawResult { + Error::mux(unsafe { fstatvfs(fd, stat) }.map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fsync_v1(fd: usize) -> RawResult { + Error::mux(fsync(fd).map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fdatasync_v1(fd: usize) -> RawResult { + // TODO + Error::mux(fsync(fd).map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_ftruncate_v0(fd: usize, len: usize) -> RawResult { + Error::mux(ftruncate(fd, len).map(|()| 0)) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_futimens_v1(fd: usize, times: *const timespec) -> RawResult { + Error::mux(unsafe { futimens(fd, times) }.map(|()| 0)) +} +/* TODO: Support unlinkat +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_unlinkat_v0( + fd: usize, + path_base: *const u8, + path_len: usize, + flags: u32, +) -> RawResult { + Error::mux(std_fs_call_wo( + fd, + unsafe { slice::from_raw_parts(path_base, path_len) }, + &StdFsCallMeta::new(StdFsCallKind::Unlinkat, flags as u64, 0), + )) +} +*/ +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_fpath_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult { + Error::mux(syscall::fpath(fd, unsafe { + core::slice::from_raw_parts_mut(dst_base, dst_len) + })) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_close_v1(fd: usize) -> RawResult { + Error::mux(syscall::close(fd)) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_pid_v1() -> RawResult { + redox_rt::sys::posix_getpid() as _ +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_euid_v1() -> RawResult { + redox_rt::sys::posix_getresugid().euid as _ +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_ruid_v1() -> RawResult { + redox_rt::sys::posix_getresugid().ruid as _ +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_egid_v1() -> RawResult { + redox_rt::sys::posix_getresugid().egid as _ +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_rgid_v1() -> RawResult { + redox_rt::sys::posix_getresugid().rgid as _ +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_ens_v0() -> RawResult { + Error::mux(redox_rt::sys::getens()) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_ns_v0() -> RawResult { + Error::mux(redox_rt::sys::getns()) +} +#[allow(improper_ctypes_definitions)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_proc_credentials_v1( + cap_fd: usize, + target_pid: usize, + buf: &mut [u8], // not FFI safe +) -> RawResult { + Error::mux(redox_rt::sys::get_proc_credentials(cap_fd, target_pid, buf)) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_setrens_v1(rns: usize, ens: usize) -> RawResult { + let _ = if ens == 0 { + let null_namespace: [IoSlice; 2] = [IoSlice::new(b"memory"), IoSlice::new(b"pipe")]; + match redox_rt::sys::mkns(&null_namespace) { + Ok(new_ns_fd) => redox_rt::sys::setns(new_ns_fd.take()), + Err(e) => return Error::mux(Err(e)), + } + } else { + redox_rt::sys::setns(ens) + }; + 0 +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_waitpid_v1(pid: usize, status: *mut i32, options: u32) -> RawResult { + let mut sts = 0_usize; + let res = Error::mux(redox_rt::sys::sys_waitpid( + WaitpidTarget::from_posix_arg(pid as isize), + &mut sts, + WaitFlags::from_bits_truncate(options as usize), + )); + unsafe { status.write(sts as i32) }; + res +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_kill_v1(pid: usize, signal: u32) -> RawResult { + Error::mux( + redox_rt::sys::posix_kill(ProcKillTarget::from_raw(pid), signal as usize).map(|()| 0), + ) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_sigaction_v1( + signal: u32, + new: *const sigaction, + old: *mut sigaction, +) -> RawResult { + Error::mux( + Sys::sigaction(signal as c_int, unsafe { new.as_ref() }, unsafe { + old.as_mut() + }) + .map(|()| 0) + .map_err(Into::into), + ) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_sigprocmask_v1( + how: u32, + new: *const u64, + old: *mut u64, +) -> RawResult { + Error::mux( + Sys::sigprocmask(how as c_int, unsafe { new.as_ref() }, unsafe { + old.as_mut() + }) + .map(|()| 0) + .map_err(Into::into), + ) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_mmap_v1( + addr: *mut (), + unaligned_len: usize, + prot: u32, + flags: u32, + fd: usize, + offset: u64, +) -> RawResult { + Error::mux(unsafe { + syscall::fmap( + fd, + &syscall::Map { + address: addr as usize, + offset: offset as usize, + size: unaligned_len, + flags: syscall::MapFlags::from_bits_truncate( + ((prot << 16) | (flags & 0xffff)) as usize, + ), + }, + ) + }) +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_munmap_v1(addr: *mut (), unaligned_len: usize) -> RawResult { + Error::mux(unsafe { syscall::funmap(addr as usize, unaligned_len) }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_clock_gettime_v1(clock: usize, ts: *mut timespec) -> RawResult { + Error::mux(clock_gettime(clock, unsafe { Out::nonnull(ts) }).map(|()| 0)) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_strerror_v1( + buf: *mut u8, + buflen: *mut usize, + error: u32, +) -> RawResult { + let dst = unsafe { core::slice::from_raw_parts_mut(buf, buflen.read()) }; + + Error::mux((|| { + // TODO: Merge syscall::error::STR_ERROR into crate::header::error::? + + let src = syscall::error::STR_ERROR + .get(error as usize) + .ok_or(Error::new(EINVAL))?; + + // This API ensures that the returned buffer is proper UTF-8. Thus, it returns both the + // copied length and the actual length. + + unsafe { buflen.write(src.len()) }; + + let raw_len = core::cmp::min(dst.len(), src.len()); + let len = match core::str::from_utf8(&src.as_bytes()[..raw_len]) { + Ok(_valid) => raw_len, + Err(error) => error.valid_up_to(), + }; + + dst[..len].copy_from_slice(&src.as_bytes()[..len]); + Ok(len) + })()) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_mkns_v1( + names: *const iovec, + num_names: usize, + flags: u32, +) -> RawResult { + Error::mux((|| { + if flags != 0 { + return Err(Error::new(EINVAL)); + } + let raw_iovecs = unsafe { slice::from_raw_parts(names, num_names) }; + let names_ioslice: Vec = raw_iovecs + .iter() + .map(|iov| { + IoSlice::new(unsafe { + slice::from_raw_parts(iov.iov_base as *const u8, iov.iov_len) + }) + }) + .collect(); + redox_rt::sys::mkns(&names_ioslice).map(|fd| fd.take()) + })()) +} + +// ABI-UNSTABLE +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_cur_procfd_v0() -> usize { + redox_rt::current_proc_fd().as_raw_fd() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_cur_thrfd_v0() -> usize { + redox_rt::RtTcb::current().thread_fd().as_raw_fd() +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_sys_call_v0( + fd: usize, + payload: *mut u8, + payload_len: usize, + flags: usize, + metadata: *const u64, + metadata_len: usize, +) -> RawResult { + Error::mux(redox_rt::sys::sys_call( + fd, + unsafe { slice::from_raw_parts_mut(payload, payload_len) }, + syscall::CallFlags::from_bits_retain(flags), + unsafe { slice::from_raw_parts(metadata, metadata_len) }, + )) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_get_socket_token_v0( + fd: usize, + payload: *mut u8, + payload_len: usize, +) -> RawResult { + let metadata = [SocketCall::GetToken as u64]; + Error::mux(redox_rt::sys::sys_call_ro( + fd, + unsafe { slice::from_raw_parts_mut(payload, payload_len) }, + syscall::CallFlags::empty(), + &metadata, + )) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_setns_v0(fd: usize) -> RawResult { + match redox_rt::sys::setns(fd) { + Some(guard) => guard.take(), + None => usize::MAX, + } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn redox_register_scheme_to_ns_v0( + ns_fd: usize, + name_base: *const u8, + name_len: usize, + cap_fd: usize, +) -> RawResult { + Error::mux( + redox_rt::sys::register_scheme_to_ns( + ns_fd, + unsafe { str::from_utf8_unchecked(slice::from_raw_parts(name_base, name_len)) }, + cap_fd, + ) + .map(|()| 0), + ) +} diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs new file mode 100644 index 0000000000..3f2029ceab --- /dev/null +++ b/src/platform/redox/mod.rs @@ -0,0 +1,1763 @@ +use core::{ + convert::TryFrom, + mem::{self, size_of}, + num::NonZeroU64, + ptr, slice, str, +}; +use object::bytes_of_slice_mut; +use redox_protocols::protocol::{WaitFlags, wifstopped}; +use redox_rt::{ + RtTcb, + sys::{Resugid, WaitpidTarget}, +}; +use syscall::{ + self, EILSEQ, ESRCH, Error, MODE_PERM, StdFsCallKind, StdFsCallMeta, + data::{Map, TimeSpec as redox_timespec}, + dirent::DirentHeader, +}; + +use self::{ + exec::Executable, + path::{FileLock, canonicalize, openat2, openat2_path}, +}; +use super::{Pal, Read, types::*}; +use crate::{ + c_str::{CStr, CString}, + error::{Errno, Result}, + fs::File, + header::{ + bits_timespec::timespec, + errno::{ + EBADF, EBADFD, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM, + ENOSYS, EOPNOTSUPP, EPERM, + }, + fcntl::{ + self, AT_EACCESS, AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, + AT_SYMLINK_NOFOLLOW, F_GETLK, F_OFD_GETLK, F_OFD_SETLK, F_RDLCK, F_SETLK, F_SETLKW, + F_UNLCK, F_WRLCK, flock, + }, + limits, + pthread::{pthread_cancel, pthread_create}, + signal::{NSIG, SIGEV_NONE, SIGEV_SIGNAL, SIGEV_THREAD, SIGRTMIN, sigevent}, + stdio::RENAME_NOREPLACE, + sys_file, + sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE}, + sys_random, + sys_resource::{RLIM_INFINITY, RLIMIT_NLIMITS, rlimit, rusage}, + sys_select::timeval, + sys_stat::{S_ISVTX, stat}, + sys_statvfs::statvfs, + sys_time::timezone, + sys_utsname::{UTSLENGTH, utsname}, + time::{TIMER_ABSTIME, itimerspec, timer_internal_t}, + unistd::{F_OK, R_OK, SEEK_CUR, SEEK_SET, W_OK, X_OK}, + }, + io::{self, BufReader, prelude::*}, + ld_so::tcb::OsSpecific, + out::Out, + platform::sys::timer::{timer_routine, timer_update_wake_time}, + sync::rwlock::RwLock, +}; + +pub use redox_rt::proc::FdGuard; + +mod epoll; +mod event; +mod exec; +mod extra; +mod libcscheme; +mod libredox; +pub(crate) mod path; +mod ptrace; +pub(crate) mod signal; +mod socket; +mod timer; + +static mut BRK_CUR: *mut c_void = ptr::null_mut(); +static mut BRK_END: *mut c_void = ptr::null_mut(); + +const PAGE_SIZE: usize = 4096; +fn round_up_to_page_size(val: usize) -> Option { + val.checked_add(PAGE_SIZE) + .map(|val| (val - 1) / PAGE_SIZE * PAGE_SIZE) +} + +fn cvt_uid(id: c_int) -> Result> { + if id == -1 { + return Ok(None); + } + Ok(Some(id.try_into().map_err(|_| Errno(EINVAL))?)) +} + +macro_rules! path_from_c_str { + ($c_str:expr) => {{ + match $c_str.to_str() { + Ok(ok) => ok, + Err(err) => { + ERRNO.set(EINVAL); + return -1; + } + } + }}; +} + +static CLONE_LOCK: RwLock<()> = RwLock::new(()); + +/// Per-process resource limits. Initialized with Linux-compatible defaults. +/// Inherited automatically across fork() (kernel copies address space). +const RLIMIT_DEFAULTS: [rlimit; RLIMIT_NLIMITS as usize] = [ + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_CPU + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_FSIZE + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_DATA + rlimit { rlim_cur: 8 * 1024 * 1024, rlim_max: RLIM_INFINITY }, // RLIMIT_STACK (8 MB soft) + rlimit { rlim_cur: 0, rlim_max: RLIM_INFINITY }, // RLIMIT_CORE + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_RSS + rlimit { rlim_cur: 4096, rlim_max: RLIM_INFINITY }, // RLIMIT_NPROC + rlimit { rlim_cur: 1024, rlim_max: 1024 * 64 }, // RLIMIT_NOFILE + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_MEMLOCK + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_AS + rlimit { rlim_cur: RLIM_INFINITY, rlim_max: RLIM_INFINITY }, // RLIMIT_LOCKS + rlimit { rlim_cur: 4096, rlim_max: RLIM_INFINITY }, // RLIMIT_SIGPENDING + rlimit { rlim_cur: 819200, rlim_max: RLIM_INFINITY }, // RLIMIT_MSGQUEUE + rlimit { rlim_cur: 0, rlim_max: 0 }, // RLIMIT_NICE + rlimit { rlim_cur: 0, rlim_max: 0 }, // RLIMIT_RTPRIO +]; + +/// Runtime resource limits, mutable via setrlimit(). +/// Inherited across fork() (kernel copies address space). +static RLIMIT_TABLE: RwLock<[rlimit; RLIMIT_NLIMITS as usize]> = RwLock::new( + RLIMIT_DEFAULTS +); + +/// Redox syscall implementation of [`Pal`]. +pub struct Sys; + +impl Pal for Sys { + fn access(path: CStr, mode: c_int) -> Result<()> { + Sys::faccessat(AT_FDCWD, path, mode, 0) + } + + fn faccessat(fd: c_int, path: CStr, mode: c_int, flags: c_int) -> Result<()> { + let fd = FdGuard::new(Sys::openat(fd, path, fcntl::O_PATH | fcntl::O_CLOEXEC, 0)? as usize); + + if (flags & !(AT_EACCESS)) != 0 { + return Err(Errno(EINVAL)); + } + + if mode == F_OK { + return Ok(()); + } + + let mut stat = syscall::Stat::default(); + + fd.fstat(&mut stat)?; + + let Resugid { + ruid, + rgid, + euid, + egid, + .. + } = redox_rt::sys::posix_getresugid(); + let (uid, gid) = if (flags & AT_EACCESS) == AT_EACCESS { + (euid, egid) + } else { + (ruid, rgid) + }; + + let perms = (if stat.st_uid == uid { + stat.st_mode >> (3 * 2) + } else if stat.st_gid == gid { + stat.st_mode >> (3 * 1) + } else { + stat.st_mode + }) & 0o7; + if (mode & R_OK == R_OK && perms & 0o4 != 0o4) + || (mode & W_OK == W_OK && perms & 0o2 != 0o2) + || (mode & X_OK == X_OK && perms & 0o1 != 0o1) + { + return Err(Errno(EINVAL)); + } + + Ok(()) + } + + unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void> { + // On first invocation, allocate a buffer for brk + if unsafe { BRK_CUR }.is_null() { + // 4 megabytes of RAM ought to be enough for anybody + const BRK_MAX_SIZE: usize = 4 * 1024 * 1024; + + let allocated = unsafe { + Self::mmap( + ptr::null_mut(), + BRK_MAX_SIZE, + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS, + 0, + 0, + ) + }?; + + unsafe { + BRK_CUR = allocated; + BRK_END = (allocated as *mut u8).add(BRK_MAX_SIZE) as *mut c_void + }; + } + + if addr.is_null() { + // Lookup what previous brk() invocations have set the address to + Ok(unsafe { BRK_CUR }) + } else if unsafe { BRK_CUR } <= addr && addr < unsafe { BRK_END } { + // It's inside buffer, return + unsafe { BRK_CUR = addr }; + Ok(addr) + } else { + // It was outside of valid range + Err(Errno(ENOMEM)) + } + } + + fn chdir(path: CStr) -> Result<()> { + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + path::chdir(path)?; + Ok(()) + } + + fn chmod(path: CStr, mode: mode_t) -> Result<()> { + let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?; + Self::fchmod(*file, mode) + } + + fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { + let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?; + Self::fchown(*file, owner, group) + } + + fn clock_getres(clk_id: clockid_t, res: Option>) -> Result<()> { + let path = format!("/scheme/time/{clk_id}/getres"); + let timerfd = FdGuard::open(&path, syscall::O_RDONLY)?; + let mut redox_res = timespec::default(); + let buffer = unsafe { + slice::from_raw_parts_mut( + &mut redox_res as *mut _ as *mut u8, + mem::size_of::(), + ) + }; + + let bytes_read = redox_rt::sys::posix_read(timerfd.as_raw_fd(), buffer)?; + + if bytes_read < mem::size_of::() { + return Err(Errno(EIO)); + } + + if let Some(mut res) = res { + res.write(redox_res); + } + + Ok(()) + } + + fn clock_gettime(clk_id: clockid_t, tp: Out) -> Result<()> { + libredox::clock_gettime(clk_id as usize, tp)?; + Ok(()) + } + + unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()> { + todo_skip!(0, "clock_settime({}, {:p}): not implemented", clk_id, tp); + Err(Errno(ENOSYS)) + } + + fn close(fd: c_int) -> Result<()> { + syscall::close(fd as usize)?; + Ok(()) + } + + fn dup(fd: c_int) -> Result { + Ok(syscall::dup(fd as usize, &[])? as c_int) + } + + fn dup2(fd1: c_int, fd2: c_int) -> Result { + Ok(syscall::dup2(fd1 as usize, fd2 as usize, &[])? as c_int) + } + + fn exit(status: c_int) -> ! { + let _ = redox_rt::sys::posix_exit(status); + loop {} + } + + unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> Result<()> { + self::exec::execve( + Executable::AtPath(path), + self::exec::ArgEnv::C { argv, envp }, + None, + )?; + unreachable!() + } + unsafe fn fexecve( + fildes: c_int, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> Result<()> { + self::exec::execve( + Executable::InFd { + file: File::new(fildes), + arg0: unsafe { CStr::from_ptr(argv.read()) }.to_bytes(), + }, + self::exec::ArgEnv::C { argv, envp }, + None, + )?; + unreachable!() + } + + fn fchdir(fd: c_int) -> Result<()> { + path::fchdir(fd)?; + Ok(()) + } + + fn fchmod(fd: c_int, mode: mode_t) -> Result<()> { + libredox::fchmod(fd as usize, mode as u16)?; + Ok(()) + } + + fn fchmodat(dirfd: c_int, path: Option, mode: mode_t, flags: c_int) -> Result<()> { + const MASK: c_int = !(fcntl::AT_SYMLINK_NOFOLLOW | fcntl::AT_EMPTY_PATH); + if MASK & flags != 0 { + return Err(Errno(EINVAL)); + } + let mut path = path + .and_then(|cs| str::from_utf8(cs.to_bytes()).ok()) + .ok_or(Errno(ENOENT))?; + + if path.is_empty() { + if flags & AT_EMPTY_PATH == AT_EMPTY_PATH { + if dirfd == AT_FDCWD { + path = "."; + } else { + return Sys::fchmod(dirfd, mode); + } + } else { + // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out. + return Err(Errno(ENOENT)); + } + } + + let file = openat2(dirfd, path, flags, 0)?; + libredox::fchmod(*file as usize, mode as u16)?; + Ok(()) + } + + fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> Result<()> { + libredox::fchown(fd as usize, owner as u32, group as u32)?; + Ok(()) + } + + fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()> { + const MASK: c_int = !(fcntl::AT_SYMLINK_NOFOLLOW | fcntl::AT_EMPTY_PATH); + if MASK & flags != 0 { + return Err(Errno(EINVAL)); + } + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + let file = openat2(fildes, path, flags, 0)?; + libredox::fchown( + *file as usize, + owner.try_into().map_err(|_| Errno(EINVAL))?, + group.try_into().map_err(|_| Errno(EINVAL))?, + )?; + Ok(()) + } + + fn fcntl(fd: c_int, cmd: c_int, args: c_ulonglong) -> Result { + match cmd { + F_SETLK | F_OFD_SETLK => { + let is_ofd = cmd == F_OFD_SETLK; + let flock = unsafe { &mut *(args as *mut flock) }; + + let (start, len) = Self::relative_to_absolute_foffset( + fd as usize, + flock.l_whence, + flock.l_start, + flock.l_len, + )?; + + let start = start as u64 | if is_ofd { 1 << 63 } else { 0 }; + let len = len as u64; + + match flock.l_type as i32 { + F_UNLCK => { + let meta = StdFsCallMeta::new(StdFsCallKind::Unlock, start, len); + syscall::std_fs_call(fd as usize, &mut [], &meta)?; + return Ok(0); + } + + F_RDLCK | F_WRLCK => { + let meta = StdFsCallMeta::new( + StdFsCallKind::Lock, + start, + len | if flock.l_type as i32 == F_WRLCK { + 1 << 63 + } else { + 0 + }, + ); + syscall::std_fs_call(fd as usize, &mut [], &meta)?; + return Ok(0); + } + + _ => return Err(Errno(EINVAL)), + }; + } + + F_GETLK | F_OFD_GETLK => { + let is_ofd = cmd == F_OFD_GETLK; + let flock = unsafe { &mut *(args as *mut flock) }; + + if is_ofd && flock.l_pid != 0 { + log::warn!("POSIX requires `l_pid` to be 0 on input for `F_OFD_GETLK`"); + return Err(Errno(EINVAL)); + } + + let (start, len) = Self::relative_to_absolute_foffset( + fd as usize, + flock.l_whence, + flock.l_start, + flock.l_len, + )?; + + let mut start = start as u64; + if is_ofd { + start |= 1 << 63; + } + + let mut len = len as u64; + if flock.l_type as i32 == F_WRLCK { + len |= 1 << 63; + } + + let meta = StdFsCallMeta::new(StdFsCallKind::GetLock, 0, 0); + let payload = &mut [start, len]; + // `pid` if traditional POSIX otherwise 0 + let val = + match syscall::std_fs_call(fd as usize, bytes_of_slice_mut(payload), &meta) { + // According to POSIX Issue 8: + // > If no lock is found that would prevent this lock from being created, then + // > the structure shall be left unchanged except for the lock type in `l_type` + // > which shall be set to F_UNLCK. + Err(err) if err.errno == ESRCH => { + flock.l_type = F_UNLCK as i16; + return Ok(0); + } + + Ok(val) => val, + Err(err) => return Err(Errno(err.errno)), + }; + + debug_assert_ne!(payload[0] & (1 << 63), 1 << 63); + + if is_ofd { + flock.l_pid = -1; + } else { + flock.l_pid = val as i32; + } + + let len = payload[1] & !(1 << 63); + if payload[1] & (1 << 63) == (1 << 63) { + flock.l_type = F_WRLCK as i16; + } else { + flock.l_type = F_RDLCK as i16; + } + + flock.l_whence = SEEK_SET as _; + flock.l_start = start as i64; + flock.l_len = len as i64; + return Ok(0); + } + + F_SETLKW => log::warn!("F_SETLKW: not yet implemented"), + + _ => {} + } + + Ok(syscall::fcntl(fd as usize, cmd as usize, args as usize)? as c_int) + } + + fn fdatasync(fd: c_int) -> Result<()> { + // TODO: "Needs" syscall update + syscall::fsync(fd as usize)?; + Ok(()) + } + + fn flock(_fd: c_int, _operation: c_int) -> Result<()> { + // TODO: Redox does not have file locking yet + Ok(()) + } + + unsafe fn fork() -> Result { + // TODO: Find way to avoid lock. + let _guard = CLONE_LOCK.write(); + + Ok(redox_rt::proc::fork_impl(&redox_rt::proc::ForkArgs::Managed)? as pid_t) + } + + fn fstat(fildes: c_int, mut buf: Out) -> Result<()> { + unsafe { + libredox::fstat(fildes as usize, buf.as_mut_ptr())?; + } + Ok(()) + } + + fn fstatat(dirfd: c_int, path: Option, buf: Out, flags: c_int) -> Result<()> { + // `path` should be non-null. + let path = path.ok_or(Errno(EFAULT))?; + let mut path = str::from_utf8(path.to_bytes()).ok().ok_or(Errno(EILSEQ))?; + + if path.is_empty() { + if flags & AT_EMPTY_PATH == AT_EMPTY_PATH { + if dirfd == AT_FDCWD { + path = "."; + } else { + return Sys::fstat(dirfd, buf); + } + } else { + // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out. + return Err(Errno(ENOENT)); + } + } + + // Use `O_PATH` to obtain a file descriptor without actually *opening* the file. This + // bypasses permission checks and avoids cases where opening a file is blocking operation + // (e.g., FIFOs). This gives a file descriptor where fstat(2) can be performed (and some + // other meta operations) but nothing else (e.g. read/write). + // + // `O_CLOEXEC` is used to avoid leaking file descriptors to child processes on exec(2). + // + // FIXME: Ideally we would want the file descriptor to not leak on fork(2) too because + // fstatat(2) should not have side effects. However, Redox does not currently support that, + // so we use `CLOEXEC` as a compromise. + // FIXME: Should we handle AT_* flags here or in openat2? + let mut open_flags = fcntl::O_PATH | fcntl::O_CLOEXEC; + if flags & AT_SYMLINK_NOFOLLOW == AT_SYMLINK_NOFOLLOW { + open_flags |= fcntl::O_SYMLINK | fcntl::O_NOFOLLOW; + } + + let file = openat2(dirfd, path, 0, open_flags)?; + // Close the file descriptor after fstat(2) regardless of success or failure. + let fstat_res = Sys::fstat(*file, buf); + let close_res = syscall::close(file.fd as usize); + if let Err(err) = fstat_res { + return Err(err); + } + close_res?; + fstat_res + } + + fn fstatvfs(fildes: c_int, mut buf: Out) -> Result<()> { + unsafe { + libredox::fstatvfs(fildes as usize, buf.as_mut_ptr())?; + } + Ok(()) + } + + fn fsync(fd: c_int) -> Result<()> { + libredox::fsync(fd as usize)?; + Ok(()) + } + + fn ftruncate(fd: c_int, len: off_t) -> Result<()> { + libredox::ftruncate(fd as usize, len as usize)?; + Ok(()) + } + + #[inline] + unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<×pec>) -> Result<()> { + let deadline = deadline.map(|d| syscall::TimeSpec { + tv_sec: d.tv_sec, + tv_nsec: d.tv_nsec as i32, + }); + (unsafe { redox_rt::sys::sys_futex_wait(addr, val, deadline.as_ref()) })?; + Ok(()) + } + #[inline] + unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result { + Ok(unsafe { redox_rt::sys::sys_futex_wake(addr, num) }?) + } + + unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()> { + (unsafe { libredox::futimens(fd as usize, times) })?; + Ok(()) + } + + unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()> { + let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?; + unsafe { Self::futimens(*file, times) } + } + + fn getcwd(buf: Out<[u8]>) -> Result<()> { + path::getcwd(buf)?; + Ok(()) + } + + fn getdents(fd: c_int, buf: &mut [u8], opaque: u64) -> Result { + Ok(libredox::getdents(fd as usize, buf, opaque)?) + } + + fn dir_seek(_fd: c_int, _off: u64) -> Result<()> { + // Redox getdents takes an explicit (opaque) offset, so this is a no-op. + Ok(()) + } + // NOTE: fn is unsafe, but this just means we can assume more things. impl is safe + unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)> { + let mut header = DirentHeader::default(); + header.copy_from_slice(&this_dent.get(..size_of::())?); + + // If scheme does not send a NUL byte, this shouldn't be able to cause UB for the caller. + if this_dent.get(usize::from(header.record_len) - 1) != Some(&b'\0') { + return None; + } + + Some((header.record_len, header.next_opaque_id)) + } + + fn getegid() -> gid_t { + redox_rt::sys::posix_getresugid().egid as gid_t + } + + fn geteuid() -> uid_t { + redox_rt::sys::posix_getresugid().euid as uid_t + } + + fn getgid() -> gid_t { + redox_rt::sys::posix_getresugid().rgid as gid_t + } + + fn getgroups(mut list: Out<[gid_t]>) -> Result { + // FIXME: this operation doesn't scale when group/passwd file grows + + let uid = Self::geteuid(); + let pwd = crate::header::pwd::getpwuid(uid); + + if pwd.is_null() { + return Err(Errno(ENOENT)); + } + + let username = unsafe { CStr::from_ptr((*pwd).pw_name) }; + let username = username.to_bytes_with_nul(); + let mut count = 0; + + unsafe { + use crate::header::grp; + grp::setgrent(); + + while let Some(grp) = grp::getgrent().as_ref() { + let mut i = 0; + let mut found = false; + + while !(*grp.gr_mem.offset(i)).is_null() { + let member = CStr::from_ptr(*grp.gr_mem.offset(i)); + if member.to_bytes_with_nul() == username { + found = true; + break; + } + i += 1; + } + + if found { + if !list.is_empty() && (count as usize) < list.len() { + list.index(count).write(grp.gr_gid); + } + count += 1; + } + } + grp::endgrent(); + } + + if !list.is_empty() && (count as usize) > list.len() { + return Err(Errno(EINVAL)); + } + + Ok(count as i32) + } + + fn getpagesize() -> usize { + PAGE_SIZE + } + + fn getpgid(pid: pid_t) -> Result { + Ok(redox_rt::sys::posix_getpgid(pid as usize)? as pid_t) + } + + fn getpid() -> pid_t { + redox_rt::sys::posix_getpid() as pid_t + } + + fn getppid() -> pid_t { + redox_rt::sys::posix_getppid() as pid_t + } + + fn getpriority(which: c_int, who: id_t) -> Result { + match redox_rt::sys::posix_getpriority(which, who as u32) { + Ok(kernel_prio) => { + let posix_prio = (kernel_prio as i32 * -1) + 40 as i32; + Ok(posix_prio) + } + Err(e) => Err(Errno(e.errno)), + } + } + + fn getrandom(buf: &mut [u8], flags: c_uint) -> Result { + let path = if flags & sys_random::GRND_RANDOM != 0 { + //TODO: /dev/random equivalent + "/scheme/rand" + } else { + "/scheme/rand" + }; + + let mut open_flags = syscall::O_RDONLY | syscall::O_CLOEXEC; + if flags & sys_random::GRND_NONBLOCK != 0 { + open_flags |= syscall::O_NONBLOCK; + } + + //TODO: store fd internally + let fd = FdGuard::open(path, open_flags)?; + Ok(fd.read(buf)?) + } + + fn getresgid( + rgid_out: Option>, + egid_out: Option>, + sgid_out: Option>, + ) -> Result<()> { + let Resugid { + rgid, egid, sgid, .. + } = redox_rt::sys::posix_getresugid(); + if let Some(mut rgid_out) = rgid_out { + rgid_out.write(rgid as _); + } + if let Some(mut egid_out) = egid_out { + egid_out.write(egid as _); + } + if let Some(mut sgid_out) = sgid_out { + sgid_out.write(sgid as _); + } + Ok(()) + } + fn getresuid( + ruid_out: Option>, + euid_out: Option>, + suid_out: Option>, + ) -> Result<()> { + let Resugid { + ruid, euid, suid, .. + } = redox_rt::sys::posix_getresugid(); + if let Some(mut ruid_out) = ruid_out { + ruid_out.write(ruid as _); + } + if let Some(mut euid_out) = euid_out { + euid_out.write(euid as _); + } + if let Some(mut suid_out) = suid_out { + suid_out.write(suid as _); + } + Ok(()) + } + + fn getrlimit(resource: c_int, mut rlim: Out) -> Result<()> { + if resource < 0 || resource >= RLIMIT_NLIMITS as c_int { + return Err(Errno(EINVAL)); + } + let table = RLIMIT_TABLE.read(); + let current = &table[resource as usize]; + rlim.write(rlimit { + rlim_cur: current.rlim_cur, + rlim_max: current.rlim_max, + }); + Ok(()) + } + + unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()> { + if resource < 0 || resource >= RLIMIT_NLIMITS as c_int { + return Err(Errno(EINVAL)); + } + if rlim.is_null() { + return Err(Errno(EFAULT)); + } + let new = unsafe { &*rlim }; + if new.rlim_cur > new.rlim_max { + return Err(Errno(EINVAL)); + } + let mut table = RLIMIT_TABLE.write(); + let old = &table[resource as usize]; + if new.rlim_max > old.rlim_max { + return Err(Errno(EPERM)); + } + table[resource as usize] = rlimit { + rlim_cur: new.rlim_cur, + rlim_max: new.rlim_max, + }; + Ok(()) + } + + fn getrusage(who: c_int, mut r_usage: Out) -> Result<()> { + let clock_id = match who { + 0 /* RUSAGE_SELF */ => 2 /* CLOCK_PROCESS_CPUTIME_ID */, + 1 /* RUSAGE_THREAD */ => 3 /* CLOCK_THREAD_CPUTIME_ID */, + -1 /* RUSAGE_CHILDREN */ => { + r_usage.write(rusage { + ru_utime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_stime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0, + ru_minflt: 0, ru_majflt: 0, ru_nswap: 0, + ru_inblock: 0, ru_oublock: 0, ru_msgsnd: 0, ru_msgrcv: 0, + ru_nsignals: 0, ru_nvcsw: 0, ru_nivcsw: 0, + }); + return Ok(()); + } + _ => return Err(Errno(EINVAL)), + }; + + let mut redox_tp = syscall::TimeSpec::default(); + let clock_result = syscall::clock_gettime(clock_id, &mut redox_tp); + + let (tv_sec, tv_usec) = if clock_result.is_ok() { + let ts: timespec = (&redox_tp).into(); + (ts.tv_sec, (ts.tv_nsec / 1000) as _) + } else { + (0, 0) + }; + + r_usage.write(rusage { + ru_utime: timeval { tv_sec, tv_usec }, + ru_stime: timeval { tv_sec: 0, tv_usec: 0 }, + ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0, + ru_minflt: 0, ru_majflt: 0, ru_nswap: 0, + ru_inblock: 0, ru_oublock: 0, ru_msgsnd: 0, ru_msgrcv: 0, + ru_nsignals: 0, ru_nvcsw: 0, ru_nivcsw: 0, + }); + Ok(()) + } + + fn getsid(pid: pid_t) -> Result { + Ok(redox_rt::sys::posix_getsid(pid as usize)? as _) + } + + fn gettid() -> pid_t { + // This is used by pthread mutexes for reentrant checks and must be nonzero + // and unique for each thread in the same process (but not cross-process) + let thread_fd = Self::current_os_tid().thread_fd; + (thread_fd & !syscall::UPPER_FDTBL_TAG) + .checked_add(1) + .unwrap() + .try_into() + .unwrap() + } + + fn gettimeofday(mut tp: Out, tzp: Option>) -> Result<()> { + let mut redox_tp = redox_timespec::default(); + syscall::clock_gettime(syscall::CLOCK_REALTIME, &mut redox_tp)?; + tp.write(timeval { + tv_sec: redox_tp.tv_sec as time_t, + tv_usec: (redox_tp.tv_nsec / 1000) as suseconds_t, + }); + + if let Some(mut tzp) = tzp { + tzp.write(timezone { + tz_minuteswest: 0, + tz_dsttime: 0, + }); + } + Ok(()) + } + + fn getuid() -> uid_t { + redox_rt::sys::posix_getresugid().ruid as uid_t + } + + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> { + // TODO: Is it correct for regular chown to use O_PATH? On Linux the meaning of that flag + // is to forbid file operations, including fchown. + + // unlike chown, never follow symbolic links + let file = File::open(path, fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW)?; + Self::fchown(*file, owner, group) + } + + fn linkat(fd1: c_int, oldpath: CStr, fd2: c_int, newpath: CStr, flags: c_int) -> Result<()> { + // make sure the flags passed are valid. + // valid states: AT_SYMLINK_FOLLOW, or 0. + if (flags & !(AT_SYMLINK_FOLLOW)) != 0 { + return Err(Errno(EINVAL)); + } + let newpath = newpath.to_str().map_err(|_| Errno(EINVAL))?; + + // By default, we don't follow the symlink if there is one. + // We only follow it if AT_SYMLINK_FOLLOW is passed in flags. + // We represent this by setting O_NOFOLLOW by default, and clearing it + // if AT_SYMLINK_FOLLOW is present. + let mut oflags = fcntl::O_PATH | fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW; + if (flags & AT_SYMLINK_FOLLOW) == AT_SYMLINK_FOLLOW { + oflags &= !fcntl::O_NOFOLLOW; + } + + let file = File::openat(fd1, oldpath, oflags)?; + let newpath = openat2_path(fd2, newpath, 0)?; + syscall::flink(*file as usize, newpath)?; + Ok(()) + } + + fn lseek(fd: c_int, offset: off_t, whence: c_int) -> Result { + Ok(syscall::lseek(fd as usize, offset as isize, whence as usize)? as off_t) + } + + fn mkdirat(dir_fd: c_int, path_name: CStr, mode: mode_t) -> Result<()> { + File::createat( + dir_fd, + path_name, + fcntl::O_DIRECTORY | fcntl::O_EXCL | fcntl::O_CLOEXEC, + 0o777, + )?; + Ok(()) + } + + fn mkfifoat(dir_fd: c_int, path_name: CStr, mode: mode_t) -> Result<()> { + Sys::mknodat( + dir_fd, + path_name, + syscall::MODE_FIFO as mode_t | (mode & 0o777), + 0, + ) + } + + fn mknodat(dir_fd: c_int, path_name: CStr, mode: mode_t, dev: dev_t) -> Result<()> { + File::createat(dir_fd, path_name, fcntl::O_CREAT | fcntl::O_CLOEXEC, mode)?; + Ok(()) + } + + unsafe fn mlock(addr: *const c_void, len: usize) -> Result<()> { + // Redox never swaps + Ok(()) + } + + unsafe fn mlockall(flags: c_int) -> Result<()> { + // Redox never swaps + Ok(()) + } + + unsafe fn mmap( + addr: *mut c_void, + len: usize, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, + ) -> Result<*mut c_void> { + // 0 is invalid per spec + if len == 0 { + return Err(Errno(EINVAL)); + } + let Some(size) = round_up_to_page_size(len) else { + return Err(Errno(ENOMEM)); + }; + + let map = Map { + offset: off as usize, + size, + flags: syscall::MapFlags::from_bits_truncate( + ((prot as usize) << 16) | ((flags as usize) & 0xFFFF), + ), + address: addr as usize, + }; + + Ok(if flags & MAP_ANONYMOUS == MAP_ANONYMOUS { + (unsafe { syscall::fmap(!0, &map) })? + } else { + (unsafe { syscall::fmap(fildes as usize, &map) })? + } as *mut c_void) + } + + unsafe fn mremap( + addr: *mut c_void, + len: usize, + new_len: usize, + flags: c_int, + args: *mut c_void, + ) -> Result<*mut c_void> { + Err(Errno(ENOSYS)) + } + + unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> Result<()> { + let Some(len) = round_up_to_page_size(len) else { + return Err(Errno(ENOMEM)); + }; + let Some(prot) = syscall::MapFlags::from_bits((prot as usize) << 16) else { + return Err(Errno(EINVAL)); + }; + (unsafe { syscall::mprotect(addr as usize, len, prot) })?; + Ok(()) + } + + unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> { + todo_skip!( + 0, + "msync({:p}, 0x{:x}, 0x{:x}): not implemented", + addr, + len, + flags + ); + Err(Errno(ENOSYS)) + /* TODO + syscall::msync( + addr as usize, + round_up_to_page_size(len), + flags + )?; + */ + } + + unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> { + // Redox never swaps + Ok(()) + } + + unsafe fn munlockall() -> Result<()> { + // Redox never swaps + Ok(()) + } + + unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()> { + // 0 is invalid per spec + if len == 0 { + return Err(Errno(EINVAL)); + } + let Some(len) = round_up_to_page_size(len) else { + return Err(Errno(ENOMEM)); + }; + (unsafe { syscall::funmap(addr as usize, len) })?; + Ok(()) + } + + unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> { + todo_skip!( + 0, + "madvise({:p}, 0x{:x}, 0x{:x}): not implemented", + addr, + len, + flags + ); + Err(Errno(ENOSYS)) + } + + unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> { + let redox_rqtp = unsafe { redox_timespec::from(&*rqtp) }; + let mut redox_rmtp: redox_timespec; + if rmtp.is_null() { + redox_rmtp = redox_timespec::default(); + } else { + redox_rmtp = unsafe { redox_timespec::from(&*rmtp) }; + } + match redox_rt::sys::posix_nanosleep(&redox_rqtp, &mut redox_rmtp) { + Ok(_) => Ok(()), + Err(Error { errno: EINTR }) => { + unsafe { + if !rmtp.is_null() { + (*rmtp).tv_sec = redox_rmtp.tv_sec as time_t; + (*rmtp).tv_nsec = redox_rmtp.tv_nsec as c_long; + } + }; + Err(Errno(EINTR)) + } + Err(Error { errno: e }) => Err(Errno(e)), + } + } + + fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result { + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + + // POSIX states that umask should affect the following: + // + // open, openat, creat, mkdir, mkdirat, + // mkfifo, mkfifoat, mknod, mknodat, + // mq_open, and sem_open, + // + // all of which (the ones that exist thus far) currently call this function. + let effective_mode = mode & !(redox_rt::sys::get_umask() as mode_t); + + Ok(libredox::open(path, oflag, effective_mode)? as c_int) + } + + fn openat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result { + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + + // POSIX states that umask should affect the following: + // + // open, openat, creat, mkdir, mkdirat, + // mkfifo, mkfifoat, mknod, mknodat, + // mq_open, and sem_open, + // + // all of which (the ones that exist thus far) currently call this function. + let effective_mode = mode & !(redox_rt::sys::get_umask() as mode_t); + + Ok(libredox::openat(dirfd, path, oflag, effective_mode)? as c_int) + } + + fn pipe2(mut fds: Out<[c_int; 2]>, flags: c_int) -> Result<()> { + fds.write(extra::pipe2(flags as usize)?); + Ok(()) + } + + fn posix_fallocate(fd: c_int, offset: u64, length: NonZeroU64) -> Result<()> { + // Redox doesn't actually have flock yet but presumably the file will need to be locked to + // avoid accidentally truncating it if the length changes. + let _guard = FileLock::lock(fd, sys_file::LOCK_EX)?; + + // posix_fallocate is less nuanced than the Linux syscall fallocate. + // If the byte range is already allocated, posix_fallocate doesn't do any extra work. + // If the byte range is unallocated; free, uninitialized bytes are reserved. + // posix_fallocate does not shrink files. + // + // The main purpose of it is to ensure subsequent writes to a byte range don't fail. + let length = length.get(); + let total_offset = offset.checked_add(length).ok_or(Errno(EFBIG))?; + + let mut stat: stat = unsafe { mem::zeroed() }; + unsafe { libredox::fstat(fd as usize, &mut stat)? }; + let st_size = stat.st_size as u64; + // The difference between total_offset and the file size is the number of bytes to + // allocate. So, if it's negative then the file is already large enough and we don't + // need to do any extra work. + if let Some(total_len) = total_offset + .checked_sub(st_size) + .and_then(|diff| st_size.checked_add(diff)) + { + let total_len: usize = total_len.try_into().map_err(|_| Errno(EFBIG))?; + libredox::ftruncate(fd as usize, total_len)?; + } + + Ok(()) + } + + fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result { + let current_offset = Self::lseek(fildes, 0, SEEK_CUR)? as u64; + let bytes_read = Self::getdents(fildes, buf, current_offset)?; + if bytes_read == 0 { + return Ok(0); + } + let mut bytes_processed = 0; + let mut next_offset = current_offset; + + while bytes_processed < bytes_read { + let remaining_slice = &buf[bytes_processed..]; + let (reclen, opaque_next) = + unsafe { Self::dent_reclen_offset(remaining_slice, bytes_processed) } + .ok_or(Errno(EIO))?; + if reclen == 0 { + return Err(Errno(EIO)); + } + + bytes_processed += reclen as usize; + next_offset = opaque_next; + } + + Self::lseek(fildes, next_offset as off_t, SEEK_SET)?; + Ok(bytes_read) + } + + unsafe fn rlct_clone( + stack: *mut usize, + os_specific: &mut OsSpecific, + ) -> Result { + let _guard = CLONE_LOCK.read(); + let res = unsafe { redox_rt::thread::rlct_clone_impl(stack, os_specific) }; + + res.map(|thread_fd| crate::pthread::OsTid { thread_fd }) + .map_err(|error| Errno(error.errno)) + } + + unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<()> { + redox_rt::sys::posix_kill_thread(os_tid.thread_fd, signal as u32)?; + Ok(()) + } + fn current_os_tid() -> crate::pthread::OsTid { + crate::pthread::OsTid { + thread_fd: RtTcb::current().thread_fd().as_raw_fd(), + } + } + + fn read(fd: c_int, buf: &mut [u8]) -> Result { + let fd = usize::try_from(fd).map_err(|_| Errno(EBADF))?; + Ok(redox_rt::sys::posix_read(fd, buf)?) + } + + fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result { + unsafe { + Ok(syscall::syscall5( + syscall::SYS_READ2, + fd as usize, + buf.as_mut_ptr() as usize, + buf.len(), + offset as usize, + !0, + )?) + } + } + + fn fpath(fildes: c_int, out: &mut [u8]) -> Result { + // Since this is used by realpath, it converts from the old format to the new one for + // compatibility reasons + let mut buf = [0; limits::PATH_MAX]; + let count = syscall::fpath(fildes as usize, &mut buf)?; + + let redox_path = str::from_utf8(&buf[..count]) + .ok() + .and_then(|x| redox_path::RedoxPath::from_absolute(x)) + .ok_or(Errno(EINVAL))?; + + let (scheme, reference) = redox_path.as_parts().ok_or(Errno(EINVAL))?; + + let mut cursor = io::Cursor::new(out); + let res = match scheme.as_ref() { + "file" => write!(cursor, "/{}", reference.as_ref().trim_start_matches('/')), + _ => write!( + cursor, + "/scheme/{}/{}", + scheme.as_ref(), + reference.as_ref().trim_start_matches('/') + ), + }; + match res { + Ok(()) => Ok(cursor.position() as usize), + Err(_err) => Err(Errno(ENAMETOOLONG)), + } + } + + fn readlinkat(dirfd: c_int, path: CStr, out: &mut [u8]) -> Result { + let path = str::from_utf8(path.to_bytes()).map_err(|_| Errno(ENOENT))?; + let file = openat2( + dirfd, + path, + 0, + fcntl::O_RDONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC, + )?; + Sys::read(*file, out) + } + + fn rename(oldpath: CStr, newpath: CStr) -> Result<()> { + let newpath = newpath.to_str().map_err(|_| Errno(EINVAL))?; + let newpath = canonicalize(newpath).map_err(|_| Errno(EINVAL))?; + + let file = File::open( + oldpath, + fcntl::O_NOFOLLOW | fcntl::O_PATH | fcntl::O_CLOEXEC, + )?; + syscall::frename(*file as usize, newpath)?; + Ok(()) + } + + fn renameat(old_dir: c_int, old_path: CStr, new_dir: c_int, new_path: CStr) -> Result<()> { + Sys::renameat2(old_dir, old_path, new_dir, new_path, 0) + } + + fn renameat2( + old_dir: c_int, + old_path: CStr, + new_dir: c_int, + new_path: CStr, + flags: c_uint, + ) -> Result<()> { + const MASK: c_uint = !RENAME_NOREPLACE; + if MASK & flags != 0 { + return Err(Errno(EOPNOTSUPP)); + } + + let new_path = new_path.to_str().map_err(|_| Errno(EINVAL))?; + // Fail if the target exists with RENAME_NOREPLACE. + if flags & RENAME_NOREPLACE != 0 + && let Ok(fd) = + libredox::openat(new_dir, &new_path, fcntl::O_PATH | fcntl::O_CLOEXEC, 0) + .map(FdGuard::new) + { + return Err(Errno(EEXIST)); + } + + let old_path = old_path.to_str().map_err(|_| Errno(EINVAL))?; + // oflags are the same as Sys::rename above. + let source = openat2(old_dir, old_path, 0, fcntl::O_NOFOLLOW | fcntl::O_PATH)?; + + let target = openat2_path(new_dir, new_path, 0)?; + // I'm avoiding Sys::rename to avoid reallocating a CString from a String. + syscall::frename(*source as usize, target) + .map(|_| ()) + .map_err(Into::into) + } + + fn rmdir(path: CStr) -> Result<()> { + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + let canon = canonicalize(path)?; + redox_rt::sys::unlink(&canon, fcntl::AT_REMOVEDIR as usize)?; + Ok(()) + } + + fn sched_yield() -> Result<()> { + syscall::sched_yield()?; + Ok(()) + } + + unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> { + // TODO + todo_skip!(0, "setgroups({}, {:p}): not implemented", size, list); + Err(Errno(ENOSYS)) + } + + fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> { + redox_rt::sys::posix_setpgid(pid as usize, pgid as usize)?; + Ok(()) + } + + fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()> { + let clamped_prio = prio.clamp(-20, 19); + let kernel_prio = (20 + clamped_prio) as u32; + + match redox_rt::sys::posix_setpriority(which, who as u32, kernel_prio) { + Ok(_) => Ok(()), + Err(e) => Err(Errno(e.errno)), + } + } + + fn setsid() -> Result { + Ok(redox_rt::sys::posix_setsid()? as c_int) + } + + fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()> { + redox_rt::sys::posix_setresugid(&Resugid { + ruid: None, + euid: None, + suid: None, + rgid: cvt_uid(rgid)?, + egid: cvt_uid(egid)?, + sgid: cvt_uid(sgid)?, + })?; + Ok(()) + } + + fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()> { + redox_rt::sys::posix_setresugid(&Resugid { + ruid: cvt_uid(ruid)?, + euid: cvt_uid(euid)?, + suid: cvt_uid(suid)?, + rgid: None, + egid: None, + sgid: None, + })?; + Ok(()) + } + + fn symlink(path1: CStr, path2: CStr) -> Result<()> { + Sys::symlinkat(path1, AT_FDCWD, path2) + } + + fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()> { + let mut file = File::createat( + fd, + path2, + fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC, + 0o777, + )?; + + file.write(path1.to_bytes()) + .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?; + + Ok(()) + } + + fn sync() -> Result<()> { + Ok(()) + } + + fn timer_create(clock_id: clockid_t, evp: &sigevent, mut timerid: Out) -> Result<()> { + if evp.sigev_notify == SIGEV_THREAD { + if evp.sigev_notify_function.is_none() { + return Err(Errno(EINVAL)); + } + } else if evp.sigev_notify == SIGEV_SIGNAL { + const n_sig: i32 = NSIG as i32; + const rt_min: i32 = SIGRTMIN as i32; + const rt_max: i32 = SIGRTMIN as i32; + match evp.sigev_signo { + 0..n_sig => {} + rt_min..=rt_max => {} + _ => { + return Err(Errno(EINVAL)); + } + } + } + + let path = format!("/scheme/time/{clock_id}"); + let timerfd = FdGuard::open(&path, syscall::O_RDWR)?; + let eventfd = FdGuard::new(Error::demux(unsafe { + event::redox_event_queue_create_v1(0) + })?); + let caller_thread = Self::current_os_tid(); + + let timer_buf = unsafe { + let timer_buf = Self::mmap( + ptr::null_mut(), + size_of::(), + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS, + 0, + 0, + )?; + + let timer_ptr = timer_buf as *mut timer_internal_t; + let timer_st = &mut *timer_ptr; + + timer_st.clockid = clock_id; + timer_st.timerfd = timerfd.take(); + timer_st.eventfd = eventfd.take(); + timer_st.evp = (*evp).clone(); + timer_st.next_wake_time = itimerspec::default(); + timer_st.thread = ptr::null_mut(); + timer_st.caller_thread = caller_thread; + timer_st.process_pid = 0; + timer_buf + }; + + timerid.write(timer_buf); + + Ok(()) + } + + fn timer_delete(timerid: timer_t) -> Result<()> { + unsafe { + let timer_st = &mut *(timerid as *mut timer_internal_t); + let _ = syscall::close(timer_st.timerfd); + let _ = syscall::close(timer_st.eventfd); + if !timer_st.thread.is_null() { + let _ = pthread_cancel(timer_st.thread); + } + Self::munmap(timerid, size_of::())?; + } + + Ok(()) + } + + fn timer_gettime(timerid: timer_t, mut value: Out) -> Result<()> { + let timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) }; + let mut now = timespec::default(); + Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?; + + if timer_st.evp.sigev_notify == SIGEV_NONE { + if timespec::subtract(timer_st.next_wake_time.it_value.clone(), now.clone()).is_none() { + // error here means the timer is disarmed + let _ = timer_update_wake_time(timer_st); + } + } + + value.write(if timer_st.next_wake_time.it_value.is_default() { + // disarmed + itimerspec::default() + } else { + itimerspec { + it_interval: timer_st.next_wake_time.it_interval.clone(), + it_value: timespec::subtract(timer_st.next_wake_time.it_value.clone(), now) + .unwrap_or_default(), + } + }); + + Ok(()) + } + + fn timer_settime( + timerid: timer_t, + flags: c_int, + value: &itimerspec, + ovalue: Option>, + ) -> Result<()> { + let timer_st = unsafe { &mut *(timerid as *mut timer_internal_t) }; + + if let Some(ovalue) = ovalue { + Self::timer_gettime(timerid, ovalue)?; + } + + let mut now = timespec::default(); + Self::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?; + + //FIXME: make these atomic? + timer_st.next_wake_time = { + let mut val = value.clone(); + if flags & TIMER_ABSTIME == 0 { + val.it_value = timespec::add(now, val.it_value).ok_or(Errno(EINVAL))?; + } + val + }; + + Error::demux(unsafe { + event::redox_event_queue_ctl_v1(timer_st.eventfd, timer_st.timerfd, 1, 0) + })?; + + let buf_to_write = unsafe { + slice::from_raw_parts( + &timer_st.next_wake_time.it_value as *const _ as *const u8, + mem::size_of::(), + ) + }; + + let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?; + + if bytes_written < mem::size_of::() { + return Err(Errno(EIO)); + } + + if timer_st.thread.is_null() { + timer_st.thread = match timer_st.evp.sigev_notify { + SIGEV_THREAD | SIGEV_SIGNAL => { + let mut tid = pthread_t::default(); + let result = unsafe { + pthread_create( + &mut tid as *mut _, + ptr::null(), + timer_routine, + timerid as *mut c_void, + ) + }; + if result != 0 { + return Err(Errno(result)); + } + tid + } + SIGEV_NONE => ptr::null_mut(), + _ => { + return Err(Errno(EINVAL)); + } + }; + } + + Ok(()) + } + + fn umask(mask: mode_t) -> mode_t { + let new_effective_mask = mask & mode_t::from(MODE_PERM) & !S_ISVTX; + (redox_rt::sys::swap_umask(new_effective_mask as u32) as mode_t) & !S_ISVTX + } + + fn uname(mut utsname: Out) -> Result<(), Errno> { + fn gethostname(mut name: Out<[u8]>) -> io::Result<()> { + if name.is_empty() { + return Ok(()); + } + + let mut file = File::open(c"/etc/hostname".into(), fcntl::O_RDONLY | fcntl::O_CLOEXEC)?; + + let mut read = 0; + let name_len = name.len(); + loop { + match file.read_out(name.subslice(read, name_len - 1))? { + 0 => break, + n => read += n, + } + } + name.index(read).write(0); + Ok(()) + } + out_project! { + let utsname { + nodename: [c_char; UTSLENGTH], + sysname: [c_char; UTSLENGTH], + release: [c_char; UTSLENGTH], + machine: [c_char; UTSLENGTH], + version: [c_char; UTSLENGTH], + domainname: [c_char; UTSLENGTH], + } = utsname; + } + + match gethostname(nodename.as_slice_mut().cast_slice_to::()) { + Ok(_) => (), + Err(_) => return Err(Errno(EIO)), + } + + let file_path = c"/scheme/sys/uname".into(); + let mut file = match File::open(file_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) { + Ok(ok) => ok, + Err(_) => return Err(Errno(EIO)), + }; + let mut lines = BufReader::new(&mut file).lines(); + + let mut read_line = |mut dst: Out<[u8]>| { + // TODO: set nul byte without allocating CString + let line = match lines.next() { + Some(Ok(l)) => CString::new(l).map_err(|_| Errno(EIO))?, + None | Some(Err(_)) => return Err(Errno(EIO)), + }; + + let line_slice: &[u8] = line.as_bytes_with_nul(); + if line_slice.len() > UTSLENGTH { + return Err(Errno(EIO)); + } + + dst.copy_common_length_from_slice(line_slice); + Ok(()) + }; + + // The file format is currently as follows: + // \n e.g. "Redox" + // \n e.g. "0.9.0" + // \n e.g. "x86_64" + // \n e.g. "yyyy-mm-ddThh:mm:ssZ" + + // A future file format might add the domainname. + + // nodename is handled above with /etc/hostname, and the domainname is + // currently zeroed out. + + read_line(sysname.as_slice_mut().cast_slice_to::())?; + read_line(release.as_slice_mut().cast_slice_to::())?; + read_line(machine.as_slice_mut().cast_slice_to::())?; + read_line(version.as_slice_mut().cast_slice_to::())?; + + // Redox doesn't provide domainname in uname scheme + //read_line(domainname.as_slice_mut().cast_slice_to::())?; + domainname.as_slice_mut().zero(); + + Ok(()) + } + + fn unlink(path: CStr) -> Result<()> { + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + let canon = canonicalize(path)?; + redox_rt::sys::unlink(&canon, 0)?; + Ok(()) + } + + fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()> { + if (flags & !AT_REMOVEDIR) != 0 { + return Err(Errno(EINVAL)); + } + let path = path.to_str().map_err(|_| Errno(EINVAL))?; + let path = openat2_path(fd, path, 0)?; + let canon = canonicalize(&path)?; + redox_rt::sys::unlink(&canon, flags.try_into().map_err(|_| Errno(EINVAL))?)?; + Ok(()) + } + + fn waitpid(pid: pid_t, stat_loc: Option>, options: c_int) -> Result { + let res = None; + let mut status = 0; + + let options = usize::try_from(options) + .ok() + .and_then(WaitFlags::from_bits) + .ok_or(Errno(EINVAL))?; + + let inner = |status: &mut usize, flags| { + redox_rt::sys::sys_waitpid(WaitpidTarget::from_posix_arg(pid as isize), status, flags) + }; + + // First, allow ptrace to handle waitpid + // TODO: Handle special PIDs here (such as -1) + let state = ptrace::init_state(); + // TODO: Fix ptrace deadlock seen during openposixtestsuite signals tests + // let mut sessions = state.sessions.lock(); + // if let Ok(session) = ptrace::get_session(&mut sessions, pid) { + // if !options.contains(WaitFlags::WNOHANG) { + // let mut _event = PtraceEvent::default(); + // let _ = (&mut &session.tracer).read(&mut _event); + + // res = Some(inner( + // &mut status, + // options | WaitFlags::WNOHANG | WaitFlags::WUNTRACED, + // )); + // if res == Some(Ok(0)) { + // // WNOHANG, just pretend ptrace SIGSTOP:ped this + // status = (redox_rt::protocol::SIGSTOP << 8) | 0x7f; + // assert!(wifstopped(status)); + // assert_eq!(wstopsig(status), redox_rt::protocol::SIGSTOP); + // res = Some(Ok(pid as usize)); + // } + // } + // } + + // If ptrace didn't impact this waitpid, proceed *almost* as + // normal: We still need to add WUNTRACED, but we only return + // it if (and only if) a ptrace traceme was activated during + // the wait. + let res = res.unwrap_or_else(|| { + loop { + let res = inner(&mut status, options | WaitFlags::WUNTRACED); + + // TODO: Also handle special PIDs here + if !wifstopped(status) + || options.contains(WaitFlags::WUNTRACED) + || ptrace::is_traceme(pid) + { + break res; + } + } + }); + + // If stat_loc is non-null, set that and the return + if let Some(mut stat_loc) = stat_loc { + stat_loc.write(status as c_int); + } + + Ok(res? as pid_t) + } + + fn write(fd: c_int, buf: &[u8]) -> Result { + let fd = usize::try_from(fd).map_err(|_| Errno(EBADFD))?; + Ok(redox_rt::sys::posix_write(fd, buf)?) + } + fn pwrite(fd: c_int, buf: &[u8], offset: off_t) -> Result { + unsafe { + Ok(syscall::syscall5( + syscall::SYS_WRITE2, + fd as usize, + buf.as_ptr() as usize, + buf.len(), + offset as usize, + !0, + )?) + } + } + + fn verify() -> bool { + // YIELD on Redox is 20, which is SYS_ARCH_PRCTL on Linux + (unsafe { syscall::syscall5(syscall::number::SYS_YIELD, !0, !0, !0, !0, !0) }).is_ok() + } + + unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> ! { + unsafe { redox_rt::thread::exit_this_thread(stack_base, stack_size) } + } +} + +impl Sys { + fn relative_to_absolute_foffset( + fd: usize, + whence: c_short, + start: off_t, + len: off_t, + ) -> Result<(off_t, off_t)> { + // let file_off = Self::lseek(fd, 0, SEEK_SET)?; + match whence as i32 { + SEEK_SET => { + let (start, len) = if len < 0 { + (start + len, -len) + } else { + (start, len) + }; + + if start < 0 { + return Err(Errno(EINVAL)); + } + + assert!(len >= 0); + Ok((start, len)) + } + // FIXME: andypython: SEEK_CUR, SEEK_END + c => { + log::warn!( + "Sys::relative_to_absolute_foffset: whence={whence} not yet implemented" + ); + Ok((0, 0)) + } + } + } +} diff --git a/src/platform/redox/path.rs b/src/platform/redox/path.rs new file mode 100644 index 0000000000..4d4ae48358 --- /dev/null +++ b/src/platform/redox/path.rs @@ -0,0 +1,522 @@ +use alloc::{ + boxed::Box, + collections::btree_set::BTreeSet, + ffi::CString, + string::{String, ToString}, + vec::Vec, +}; +use core::{ffi::c_int, str}; +use redox_rt::{proc::FdGuardUpper, signal::tmp_disable_signals}; +use syscall::{data::Stat, error::*, flag::*}; + +use super::{FdGuard, Pal, Sys, libcscheme}; +use crate::{ + error::Errno, + fs::File, + header::{fcntl, limits, sys_file}, + out::Out, + sync::rwlock::{ReadGuard, RwLock}, +}; + +pub use redox_path::{RedoxPath, canonicalize_using_cwd}; + +pub fn normalize_path<'a>(path: &'a str) -> Option<(bool, String)> { + let absolute = match RedoxPath::from_absolute(path) { + Some(absolute) => absolute, + None => return Some((true, partially_canonical(path)?)), + }; + let canonical = absolute.canonical()?; + Some((false, canonical.to_string())) +} + +pub fn normalize_scheme_rooted_path<'a>(path: &'a str) -> Option<(bool, String)> { + let absolute = match RedoxPath::from_absolute(path) { + Some(absolute) => absolute, + None => return Some((true, partially_canonical(path)?)), + }; + let canonical = absolute.canonical()?; + Some((false, scheme_rooted_path(&canonical.to_string()).ok()?)) +} + +fn partially_canonical(path: &str) -> Option { + let mut stack = Vec::new(); + let mut paths_to_check = BTreeSet::new(); + let mut up_counts = 0; + + for part in path.split('/') { + if part.is_empty() || part == "." { + continue; + } else if part == ".." { + if let Some(part) = stack.pop() { + paths_to_check.insert( + core::iter::repeat("..") + .take(up_counts) + .chain(stack.clone()) + .chain(core::iter::once(part)) + .collect::>() + .join("/"), + ); + } else { + up_counts += 1; + } + } else { + stack.push(part); + } + } + + for path in paths_to_check { + let _ = current_dir() + .ok()? + .as_ref() + .unwrap() + .fd + .openat(&path, O_STAT, 0) + .ok()?; + } + + Some( + core::iter::repeat("..") + .take(up_counts) + .chain(stack) + .collect::>() + .join("/"), + ) +} + +// POSIX states chdir is both thread-safe and signal-safe. Thus we need to synchronize access to CWD, but at the +// same time forbid signal handlers from running in the meantime, to avoid reentrant deadlock. +pub fn chdir(path: &str) -> Result<()> { + let _siglock = tmp_disable_signals(); + let mut cwd_guard = CWD.write(); + let (is_relative, path) = normalize_path(path).ok_or(Error::new(ENOENT))?; + if is_relative { + let fd = cwd_guard + .as_ref() + .unwrap() + .fd + .openat(&path, O_STAT, 0)? + .to_upper() + .unwrap(); + let mut stat = Stat::default(); + if fd.fstat(&mut stat).is_err() || (stat.st_mode & MODE_TYPE) != MODE_DIR { + return Err(Error::new(ENOTDIR)); + } + + let canon = canonicalize_using_cwd(cwd_guard.as_ref().map(|c| c.path.as_ref()), &path) + .ok_or(Error::new(ENOENT))?; + *cwd_guard = Some(Cwd { + path: canon.into_boxed_str(), + fd, + }); + } else { + let canon_with_scheme = scheme_rooted_path(&path)?; + + let fd = FdGuard::open(&canon_with_scheme, O_STAT)? + .to_upper() + .unwrap(); + let mut stat = Stat::default(); + if fd.fstat(&mut stat).is_err() || (stat.st_mode & MODE_TYPE) != MODE_DIR { + return Err(Error::new(ENOTDIR)); + } + + *cwd_guard = Some(Cwd { + path: path.into_boxed_str(), + fd, + }); + } + + Ok(()) +} + +pub fn fchdir(fd: c_int) -> Result<()> { + let mut buf = [0_u8; limits::PATH_MAX]; + let res = Sys::fpath(fd, &mut buf)?; + + let path = core::str::from_utf8(&buf[..res]) + .map_err(|_| Errno(EINVAL))? + .to_string(); + let fd = FdGuard::new(syscall::fcntl( + fd as usize, + syscall::F_DUPFD, + syscall::UPPER_FDTBL_TAG, + )?) + .to_upper() + .unwrap(); + set_cwd_manual(path.into_boxed_str(), fd); + Ok(()) +} + +// getcwd is similarly both thread-safe and signal-safe. +pub fn getcwd(mut buf: Out<[u8]>) -> Result { + let _siglock = tmp_disable_signals(); + let guard = CWD.read(); + let cwd = guard.as_ref().ok_or(Error::new(ENOENT))?; + let path_bytes = cwd.path.as_bytes(); + + let [mut before, mut after] = buf + .split_at_checked(path_bytes.len()) + .ok_or(Error::new(ERANGE))?; + + before.copy_from_slice(path_bytes); + after.zero(); + + Ok(path_bytes.len()) +} + +// Get Cwd object +pub fn current_dir() -> Result>> { + let _siglock = tmp_disable_signals(); + let guard = CWD.read(); + + if guard.as_ref().is_none() { + return Err(Error::new(ENOENT)); + } + + Ok(guard) +} + +fn scheme_rooted_path(path: &str) -> Result { + let standard_scheme = path == "/scheme" || path.starts_with("/scheme/"); + let legacy_scheme = path + .split("/") + .next() + .map(|c| c.contains(":")) + .unwrap_or(false); + + Ok(if standard_scheme || legacy_scheme { + path.to_string() + } else { + let mut result = format!("/scheme/file{}", path); + + // Trim trailing / to keep path canonical. + if result.as_bytes().last() == Some(&b'/') { + result.pop(); + } + + result + }) +} + +// TODO: How much of this logic should be in redox-path? +fn canonicalize_with_cwd_internal(cwd: Option<&str>, path: &str) -> Result { + let path = canonicalize_using_cwd(cwd, path).ok_or(Error::new(ENOENT))?; + scheme_rooted_path(&path) +} + +pub fn canonicalize(path: &str) -> Result { + let _siglock = tmp_disable_signals(); + let cwd_guard = CWD.read(); + canonicalize_with_cwd_internal(cwd_guard.as_ref().map(|c| c.path.as_ref()), path) +} + +pub struct Cwd { + pub path: Box, + pub fd: FdGuardUpper, +} + +// TODO: arraystring? +static CWD: RwLock> = RwLock::new(None); + +pub fn set_cwd_manual(path: Box, fd: FdGuardUpper) { + let _siglock = tmp_disable_signals(); + *CWD.write() = Some(Cwd { path, fd }); +} + +pub fn clone_cwd() -> Option> { + let _siglock = tmp_disable_signals(); + CWD.read().as_ref().map(|cwd| cwd.path.clone()) +} + +fn open_absolute(path: &str, flags: usize) -> Result { + if path.starts_with(libcscheme::LIBC_SCHEME) { + libcscheme::open(path, flags) + } else { + redox_rt::sys::open(path, flags) + } +} + +fn link_target(fd: FdGuard) -> Result { + let mut resolve_buf = [0_u8; limits::PATH_MAX]; + let count = fd.read(&mut resolve_buf)?; + if count == resolve_buf.len() { + // TODO: make resolve_buf PATH_MAX + 1 bytes? + return Err(Error::new(ENAMETOOLONG)); + } + // If the symbolic link path is non-UTF8, it cannot be opened, and is thus + // considered a "dangling symbolic link". + core::str::from_utf8(&resolve_buf[..count]) + .map_err(|_| Error::new(ENOENT)) + .map(|s| s.to_string()) +} + +fn read_link_content(path: &str, is_relative: bool) -> Result { + let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY; + + let fd = if is_relative { + current_dir()? + .as_ref() + .unwrap() + .fd + .openat(path, resolve_flags, 0)? + } else { + FdGuard::open(path, resolve_flags)? + }; + + link_target(fd) +} + +fn calc_next_abs_path(current_abs: &str, link_target: &str) -> Result { + let parent = get_parent_path(current_abs).ok_or(Error::new(ENOENT))?; + + canonicalize_using_cwd(Some(&parent), link_target).ok_or(Error::new(ENOENT)) +} + +fn resolve_sym_links(mut current_path_string: String, flags: usize) -> Result { + // TODO: SYMLOOP_MAX + const MAX_LEVEL: usize = 64; + // Sym reolve loop + for _ in 0..(MAX_LEVEL - 1) { + match open_absolute(¤t_path_string, flags) { + Ok(fd) => return Ok(fd), + Err(e) if e == Error::new(EXDEV) => { + let link_target = read_link_content(¤t_path_string, false)?; + + current_path_string = calc_next_abs_path(¤t_path_string, &link_target)?; + } + Err(e) => return Err(e), + } + } + + Err(Error::new(ELOOP)) +} + +// TODO: Move to redox-rt, or maybe part of it? +pub fn openat(dirfd: c_int, path: &str, flags: usize) -> Result { + if path.is_empty() && flags as i32 & fcntl::AT_EMPTY_PATH != fcntl::AT_EMPTY_PATH { + return Err(Error::new(ENOENT)); + } + + let (is_relative, path_to_open) = normalize_path(path).ok_or(Error::new(ENOENT))?; + + if !is_relative { + return open(path, flags); + } + + let _siglock = tmp_disable_signals(); + let fcntl_flags = flags & syscall::O_FCNTL_MASK; + // First try + let initial_res = if dirfd == fcntl::AT_FDCWD { + current_dir()? + .as_ref() + .unwrap() + .fd + .openat(&path_to_open, flags, fcntl_flags) + .map(|fd: FdGuard| fd.take()) + } else { + redox_rt::sys::openat(dirfd as usize, &path_to_open, flags, fcntl_flags) + }; + + let current_path_string = match initial_res { + Ok(fd) => return Ok(fd), + Err(e) if e == Error::new(EXDEV) => { + let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY; + + let fd = if dirfd == fcntl::AT_FDCWD { + current_dir()? + .as_ref() + .unwrap() + .fd + .openat(path, resolve_flags, 0)? + } else { + redox_rt::sys::openat(dirfd as usize, &path_to_open, flags, fcntl_flags) + .map(FdGuard::new)? + }; + + let link_target = link_target(fd)?; + + let current_abs = openat2_path(dirfd, path, 0)?; + calc_next_abs_path(¤t_abs, &link_target)? + } + Err(e) => return Err(e), + }; + + resolve_sym_links(current_path_string, flags) +} + +// TODO: Move to redox-rt, or maybe part of it? +pub fn open(path: &str, flags: usize) -> Result { + let _siglock = tmp_disable_signals(); + if path == "" { + return Err(Error::new(ENOENT)); + } + + let (is_relative, canon) = normalize_scheme_rooted_path(path).ok_or(Error::new(ENOENT))?; + + // First try + let initial_res = if is_relative { + let fcntl_flags = flags & syscall::O_FCNTL_MASK; + current_dir()? + .as_ref() + .unwrap() + .fd + .openat(&canon, flags, fcntl_flags) + .map(|fd: FdGuard| fd.take()) + } else { + open_absolute(&canon, flags) + }; + + let current_path_string = match initial_res { + Ok(fd) => return Ok(fd), + Err(e) if e == Error::new(EXDEV) => { + let link_target = read_link_content(&canon, is_relative)?; + + let cwd_guard = CWD.read(); + let current_abs = + canonicalize_using_cwd(cwd_guard.as_ref().map(|c| c.path.as_ref()), &canon) + .ok_or(Error::new(ENOENT))?; + + calc_next_abs_path(¤t_abs, &link_target)? + } + Err(e) => return Err(e), + }; + + // Sym reolve loop + resolve_sym_links(current_path_string, flags) +} + +fn get_parent_path(path: &str) -> Option { + let path = path.strip_suffix('/').unwrap_or(path); + fn parent_opt(path: &str) -> Option<&str> { + path.rfind('/').map(|index| { + if index == 0 { + // Path is something like "/file.txt" or the root "/". + // The parent is the root directory "/". + "/" + } else { + // Path is something like "/a/b/c.txt". + // Take the slice from the beginning up to the last '/'. + &path[..index] + } + }) + } + match RedoxPath::from_absolute(path) { + Some(path) => { + let (scheme, reference) = path.as_parts()?; + let parent_ref = parent_opt(reference.as_ref()).unwrap_or(""); + Some(format!("/scheme/{}/{}", scheme.as_ref(), parent_ref)) + } + None => parent_opt(path).map(String::from), + } +} + +pub fn dir_path_and_fd_path(socket_path: &str) -> Result<(String, String)> { + let _siglock = tmp_disable_signals(); + let cwd_guard = CWD.read(); + let cwd_path = cwd_guard.as_ref().map(|c| c.path.as_ref()); + + let full_path = canonicalize_with_cwd_internal(cwd_path, socket_path)?; + + let redox_path = RedoxPath::from_absolute(&full_path).ok_or(Error::new(EINVAL))?; + let (_, ref_path) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + if ref_path.as_ref().is_empty() { + return Err(Error::new(EINVAL)); + } + if redox_path.is_default_scheme() { + let dir_to_open = get_parent_path(&full_path).ok_or(Error::new(EINVAL))?; + Ok((dir_to_open, ref_path.as_ref().to_string())) + } else { + let full_path = canonicalize_with_cwd_internal(cwd_path, ref_path.as_ref())?; + let redox_path = RedoxPath::from_absolute(&full_path).ok_or(Error::new(EINVAL))?; + let (_, path) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + let dir_to_open = get_parent_path(&full_path).ok_or(Error::new(EINVAL))?; + Ok((dir_to_open, path.as_ref().to_string())) + } +} + +pub struct FileLock(c_int); + +impl FileLock { + pub fn lock(fd: c_int, op: c_int) -> Result { + if op & sys_file::LOCK_SH | sys_file::LOCK_EX == 0 { + return Err(Error::new(EINVAL)); + } + + Sys::flock(fd, op)?; + Ok(Self(fd)) + } + + pub fn unlock(self) -> Result<()> { + Sys::flock(self.0, sys_file::LOCK_UN).map_err(Into::into) + } +} + +impl Drop for FileLock { + fn drop(&mut self) { + let fd = self.0; + self.0 = -1; + let _ = Sys::flock(self.0, sys_file::LOCK_UN); + } +} +/// Resolve `path` under `dirfd`. +/// +/// See [`openat2`] for more information. +pub(super) fn openat2_path(dirfd: c_int, path: &str, at_flags: c_int) -> Result { + // Ideally, the function calling this fn would check AT_EMPTY_PATH and just call fstat or + // whatever with the fd. + if path.is_empty() && at_flags & fcntl::AT_EMPTY_PATH != fcntl::AT_EMPTY_PATH { + return Err(Errno(ENOENT)); + } + + // Absolute paths are passed without processing unless RESOLVE_BENEATH is used. + // canonicalize_using_cwd checks that path is absolute so a third branch that does so here + // isn't needed. + if dirfd == fcntl::AT_FDCWD { + // The special constant AT_FDCWD indicates that we should use the cwd. + let cwd_guard = CWD.read(); + canonicalize_using_cwd(cwd_guard.as_ref().map(|c| c.path.as_ref()), path) + .ok_or(Errno(EBADF)) + } else { + let mut buf = [0; limits::PATH_MAX]; + let len = Sys::fpath(dirfd, &mut buf)?; + // SAFETY: fpath checks then copies valid UTF8. + let dir = unsafe { str::from_utf8_unchecked(&buf[..len]) }; + + canonicalize_using_cwd(Some(dir), path).ok_or(Errno(EBADF)) + } +} + +/// Canonicalize and open `path` with respect to `dirfd`. +/// +/// This unexported openat2 is similar to the Linux syscall but with a different interface. The +/// naming is mostly for convenience - it's not a drop in replacement for openat2. +/// +/// # Arguments +/// * `dirfd` is a directory descriptor to which `path` is resolved. +/// * `path` is a relative or absolute path. Relative paths are resolved in relation to `dirfd` +/// while absolute paths skip `dirfd`. +/// * `at_flags` constrains how `path` is resolved. +/// * `oflags` are flags that are passed to open. +/// +/// # Constants +/// `at_flags`: +/// * AT_EMPTY_PATH returns the path at `dirfd` itself if `path` is empty. If `path` is not +/// empty, it's resolved w.r.t `dirfd` like normal. +/// +/// `dirfd`: +/// `AT_FDCWD` is a special constant for `dirfd` that resolves `path` under the current working +/// directory. +pub(super) fn openat2( + dirfd: c_int, + path: &str, + at_flags: c_int, + oflags: c_int, +) -> Result { + // Translate at flags into open flags; openat will do this on its own most likely. + let oflags = if at_flags & fcntl::AT_SYMLINK_NOFOLLOW == fcntl::AT_SYMLINK_NOFOLLOW { + fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW | fcntl::O_PATH | fcntl::O_SYMLINK | oflags + } else { + fcntl::O_CLOEXEC | oflags + }; + let c_path = CString::new(path).map_err(|_| Errno(EINVAL))?; + File::openat(dirfd, c_path.as_c_str().into(), oflags) +} diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs new file mode 100644 index 0000000000..eef280d46b --- /dev/null +++ b/src/platform/redox/ptrace.rs @@ -0,0 +1,290 @@ +//! Note: This module is not going to be clean. We're not going to be +//! able to follow the specs 100%. Linux ptrace is very, very, +//! different to Redox. Many people agree that Linux ptrace is bad, so +//! we are NOT going to bend our API for the sake of +//! compatibility. So, this module will be a hellhole. + +use super::super::{ERRNO, PalPtrace, Sys, types::*}; +#[cfg(target_arch = "x86_64")] +use super::super::{Pal, PalSignal}; +#[cfg(target_arch = "x86_64")] +use crate::header::arch_x64_user::user_regs_struct; +use crate::{ + c_str::{CStr, CString}, + error::Errno, + fs::File, + header::{ + errno::{self as errnoh, EIO}, + fcntl, + }, + io, + raw_cell::RawCell, + sync::Mutex, +}; +#[cfg(target_arch = "x86_64")] +use crate::{ + header::{signal, sys_ptrace}, + io::prelude::*, +}; + +use alloc::collections::{BTreeMap, btree_map::Entry}; +#[cfg(target_arch = "x86_64")] +use core::mem; +#[cfg(target_arch = "x86_64")] +use syscall; + +pub struct Session { + pub first: bool, + pub fpregs: File, + pub mem: File, + pub regs: File, + pub tracer: File, +} +pub struct State { + pub sessions: Mutex>, +} +impl State { + fn new() -> Self { + Self { + sessions: Mutex::new(BTreeMap::new()), + } + } +} + +#[thread_local] +static STATE: RawCell> = RawCell::new(None); + +pub fn init_state() -> &'static State { + // Safe due to STATE being thread_local (TODO: is it though?) + unsafe { + if STATE.unsafe_ref().is_none() { + STATE.unsafe_set(Some(State::new())); + } + let state_ptr = STATE.unsafe_ref().as_ref().unwrap() as *const State; + &*state_ptr + } +} +pub fn is_traceme(pid: pid_t) -> bool { + // Skip special PIDs (<=0) + if pid <= 0 { + return false; + } + File::open( + CStr::borrow(&CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap()), + fcntl::O_PATH, + ) + .is_ok() +} +pub fn get_session( + sessions: &mut BTreeMap, + pid: pid_t, +) -> io::Result<&mut Session> { + const NEW_FLAGS: c_int = fcntl::O_RDWR | fcntl::O_CLOEXEC; + + match sessions.entry(pid) { + Entry::Vacant(entry) => { + if is_traceme(pid) { + Ok(entry.insert(Session { + first: true, + tracer: File::open( + CStr::borrow(&CString::new(format!("/scheme/proc/{}/trace", pid)).unwrap()), + NEW_FLAGS, + )?, + mem: File::open( + CStr::borrow(&CString::new(format!("/scheme/proc/{}/mem", pid)).unwrap()), + NEW_FLAGS, + )?, + regs: File::open( + CStr::borrow( + &CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap(), + ), + NEW_FLAGS, + )?, + fpregs: File::open( + CStr::borrow( + &CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap(), + ), + NEW_FLAGS, + )?, + })) + } else { + ERRNO.set(errnoh::ESRCH); + Err(io::last_os_error()) + } + } + Entry::Occupied(entry) => Ok(entry.into_mut()), + } +} + +#[cfg(target_arch = "aarch64")] +unsafe fn inner_ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, +) -> io::Result { + //TODO: aarch64 + unimplemented!("inner_ptrace not implemented on aarch64"); +} + +#[cfg(target_arch = "x86")] +unsafe fn inner_ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, +) -> io::Result { + //TODO: x86 + unimplemented!("inner_ptrace not implemented on x86"); +} + +#[cfg(target_arch = "x86_64")] +unsafe fn inner_ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, +) -> io::Result { + let state = init_state(); + + if request == sys_ptrace::PTRACE_TRACEME { + // Mark this child as traced, parent will check for this marker file + let pid = Sys::getpid(); + mem::forget(File::open( + CStr::borrow( + &CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap(), + ), + fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL, + )?); + return Ok(0); + } + + let mut sessions = state.sessions.lock(); + let session = get_session(&mut sessions, pid)?; + + match request { + sys_ptrace::PTRACE_CONT + | sys_ptrace::PTRACE_SINGLESTEP + | sys_ptrace::PTRACE_SYSCALL + | sys_ptrace::PTRACE_SYSEMU + | sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => { + if let Ok(()) = Sys::kill(pid, signal::SIGCONT as _) {}; // TODO handle error + + // TODO: Translate errors + let syscall = syscall::PTRACE_STOP_PRE_SYSCALL | syscall::PTRACE_STOP_POST_SYSCALL; + (&mut &session.tracer).write(&match request { + sys_ptrace::PTRACE_CONT => syscall::PtraceFlags::empty(), + sys_ptrace::PTRACE_SINGLESTEP => syscall::PTRACE_STOP_SINGLESTEP, + // Skip the first post-syscall when connected + sys_ptrace::PTRACE_SYSCALL if session.first => syscall::PTRACE_STOP_PRE_SYSCALL, + sys_ptrace::PTRACE_SYSCALL => syscall, + // Skip the first post-syscall when connected + sys_ptrace::PTRACE_SYSEMU if session.first => { + syscall::PTRACE_FLAG_IGNORE | syscall::PTRACE_STOP_PRE_SYSCALL + } + sys_ptrace::PTRACE_SYSEMU => syscall::PTRACE_FLAG_IGNORE | syscall, + sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => { + syscall::PTRACE_FLAG_IGNORE | syscall::PTRACE_STOP_SINGLESTEP + } + _ => unreachable!("unhandled ptrace request type {}", request), + })?; + + session.first = false; + Ok(0) + } + sys_ptrace::PTRACE_GETREGS => { + let c_regs = unsafe { &mut *(data as *mut user_regs_struct) }; + let mut redox_regs = syscall::IntRegisters::default(); + (&mut &session.regs).read(&mut redox_regs)?; + *c_regs = user_regs_struct { + r15: redox_regs.r15 as _, + r14: redox_regs.r14 as _, + r13: redox_regs.r13 as _, + r12: redox_regs.r12 as _, + rbp: redox_regs.rbp as _, + rbx: redox_regs.rbx as _, + r11: redox_regs.r11 as _, + r10: redox_regs.r10 as _, + r9: redox_regs.r9 as _, + r8: redox_regs.r8 as _, + rax: redox_regs.rax as _, + rcx: redox_regs.rcx as _, + rdx: redox_regs.rdx as _, + rsi: redox_regs.rsi as _, + rdi: redox_regs.rdi as _, + orig_rax: redox_regs.rax as _, // redox_regs.orig_rax as _, + rip: redox_regs.rip as _, + cs: redox_regs.cs as _, + eflags: redox_regs.rflags as _, + rsp: redox_regs.rsp as _, + ss: redox_regs.ss as _, + fs_base: 0, // fs_base: redox_regs.fs_base as _, + gs_base: 0, // gs_base: redox_regs.gs_base as _, + ds: 0, // ds: redox_regs.ds as _, + es: 0, // es: redox_regs.es as _, + fs: 0, // fs: redox_regs.fs as _, + gs: 0, // gs: redox_regs.gs as _, + }; + Ok(0) + } + sys_ptrace::PTRACE_SETREGS => { + let c_regs = unsafe { &*(data as *mut user_regs_struct) }; + let redox_regs = syscall::IntRegisters { + r15: c_regs.r15 as _, + r14: c_regs.r14 as _, + r13: c_regs.r13 as _, + r12: c_regs.r12 as _, + rbp: c_regs.rbp as _, + rbx: c_regs.rbx as _, + r11: c_regs.r11 as _, + r10: c_regs.r10 as _, + r9: c_regs.r9 as _, + r8: c_regs.r8 as _, + rax: c_regs.orig_rax as _, // c_regs.rax as _, + rcx: c_regs.rcx as _, + rdx: c_regs.rdx as _, + rsi: c_regs.rsi as _, + rdi: c_regs.rdi as _, + // orig_rax: c_regs.orig_rax as _, + rip: c_regs.rip as _, + cs: c_regs.cs as _, + rflags: c_regs.eflags as _, + rsp: c_regs.rsp as _, + ss: c_regs.ss as _, + // fs_base: c_regs.fs_base as _, + // gs_base: c_regs.gs_base as _, + // ds: c_regs.ds as _, + // es: c_regs.es as _, + // fs: c_regs.fs as _, + // gs: c_regs.gs as _, + }; + (&mut &session.regs).write(&redox_regs)?; + Ok(0) + } + _ => unimplemented!(), + } +} + +#[cfg(target_arch = "riscv64")] +fn inner_ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, +) -> io::Result { + //TODO: riscv64 + unimplemented!("inner_ptrace not implemented on riscv64"); +} + +impl PalPtrace for Sys { + #[allow(unused_unsafe)] // keeping inner unsafe fails x86_64, removing fails riscv cross-build + unsafe fn ptrace( + request: c_int, + pid: pid_t, + addr: *mut c_void, + data: *mut c_void, + ) -> Result { + unsafe { inner_ptrace(request, pid, addr, data) } + .map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO))) + } +} diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs new file mode 100644 index 0000000000..faa03ea09e --- /dev/null +++ b/src/platform/redox/signal.rs @@ -0,0 +1,362 @@ +use super::{ + super::{Pal, PalSignal, types::*}, + Sys, +}; +#[allow(deprecated)] +use crate::header::sys_time::{ITIMER_REAL, itimerval}; +use crate::{ + error::{Errno, Result}, + header::{ + bits_sigset_t::sigset_t, + bits_timespec::timespec, + errno::{EINVAL, ENOSYS}, + signal::{ + SIG_BLOCK, SIG_DFL, SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SIGALRM, SIGEV_SIGNAL, + SS_DISABLE, SS_ONSTACK, sigaction, sigevent, siginfo_t, sigval, stack_t, ucontext_t, + }, + time::{itimerspec, timer_internal_t}, + }, + out::Out, + sync::Mutex, +}; +use core::mem::offset_of; +use redox_protocols::protocol::ProcKillTarget; +use redox_rt::signal::{ + PosixStackt, SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler, +}; + +/// Wrapper for timer_t that implements Send (the timer_t pointer is a process- +/// wide mmap'd allocation that outlives any single thread). +struct AlarmTimer(timer_t); +// SAFETY: The timer_t pointer refers to an mmap'd timer_internal_t that is +// only accessed under the ALARM_TIMER mutex lock. +unsafe impl Send for AlarmTimer {} + +/// Process-global singleton timer used by alarm(). Protected by a mutex to +/// ensure only one alarm is active at a time (POSIX requirement). +static ALARM_TIMER: Mutex> = Mutex::new(None); + +const _: () = { + #[track_caller] + const fn assert_eq(a: usize, b: usize) { + if a != b { + panic!("compile-time struct verification failed"); + } + } + assert_eq(offset_of!(ucontext_t, uc_link), offset_of!(SigStack, link)); + assert_eq( + offset_of!(ucontext_t, uc_stack), + offset_of!(SigStack, old_stack), + ); + assert_eq( + offset_of!(ucontext_t, uc_sigmask), + offset_of!(SigStack, old_mask), + ); + assert_eq( + offset_of!(ucontext_t, uc_mcontext), + offset_of!(SigStack, regs), + ); +}; + +impl PalSignal for Sys { + #[allow(deprecated)] + fn getitimer(which: c_int, out: &mut itimerval) -> Result<()> { + let path = match which { + ITIMER_REAL => "/scheme/itimer/1", + _ => return Err(Errno(EINVAL)), + }; + // TODO: implement setitimer + // let fd = FdGuard::new(redox_rt::sys::open(path, syscall::O_RDONLY | syscall::O_CLOEXEC)?); + // let count = syscall::read(*fd, &mut spec)?; + + let spec = syscall::ITimerSpec::default(); + out.it_interval.tv_sec = spec.it_interval.tv_sec as time_t; + out.it_interval.tv_usec = spec.it_interval.tv_nsec / 1000; + out.it_value.tv_sec = spec.it_value.tv_sec as time_t; + out.it_value.tv_usec = spec.it_value.tv_nsec / 1000; + + Ok(()) + } + + fn kill(pid: pid_t, sig: c_int) -> Result<()> { + redox_rt::sys::posix_kill(ProcKillTarget::from_raw(pid as usize), sig as usize)?; + Ok(()) + } + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()> { + Ok(redox_rt::sys::posix_sigqueue( + pid as usize, + sig as usize, + unsafe { val.sival_ptr } as usize, + )?) + } + + fn killpg(pgrp: pid_t, sig: c_int) -> Result<()> { + if pgrp == 1 { + return Err(Errno(EINVAL)); + } + Self::kill(-pgrp, sig) + } + + fn raise(sig: c_int) -> Result<()> { + // TODO: Bypass kernel? + unsafe { Self::rlct_kill(Self::current_os_tid(), sig as _) } + } + + #[allow(deprecated)] + fn setitimer(which: c_int, _new: &itimerval, old: Option<&mut itimerval>) -> Result<()> { + // TODO: setitimer is no longer part of POSIX and should not be implemented in Redox + // Change the platform-independent implementation to use POSIX timers. + // For Redox, the timer should probably use "/scheme/time" + todo_skip!(0, "setitimer not implemented"); + Err(Errno(ENOSYS)) + } + + fn sigaction( + sig: c_int, + c_act: Option<&sigaction>, + c_oact: Option<&mut sigaction>, + ) -> Result<(), Errno> { + let sig = u8::try_from(sig).map_err(|_| syscall::Error::new(syscall::EINVAL))?; + + let new_action = c_act.map(|c_act| { + let handler = c_act.sa_handler.map_or(0, |f| f as usize); + + let kind = if handler == SIG_DFL { + SigactionKind::Default + } else if handler == SIG_IGN { + SigactionKind::Ignore + } else { + SigactionKind::Handled { + handler: if c_act.sa_flags & crate::header::signal::SA_SIGINFO as c_int != 0 { + SignalHandler { + sigaction: unsafe { core::mem::transmute(c_act.sa_handler) }, + } + } else { + SignalHandler { + handler: c_act.sa_handler, + } + }, + } + }; + + Sigaction { + kind, + mask: c_act.sa_mask, + flags: SigactionFlags::from_bits_retain(c_act.sa_flags as u32), + } + }); + let mut old_action = c_oact.as_ref().map(|_| Sigaction::default()); + + redox_rt::signal::sigaction(sig, new_action.as_ref(), old_action.as_mut())?; + + if let (Some(c_oact), Some(old_action)) = (c_oact, old_action) { + *c_oact = match old_action.kind { + SigactionKind::Ignore => sigaction { + sa_handler: unsafe { core::mem::transmute(SIG_IGN) }, + sa_flags: 0, + sa_restorer: None, + sa_mask: 0, + }, + SigactionKind::Default => sigaction { + sa_handler: unsafe { core::mem::transmute(SIG_DFL) }, + sa_flags: 0, + sa_restorer: None, + sa_mask: 0, + }, + SigactionKind::Handled { handler } => sigaction { + sa_handler: if old_action.flags.contains(SigactionFlags::SIGINFO) { + unsafe { core::mem::transmute(handler.sigaction) } + } else { + unsafe { handler.handler } + }, + sa_restorer: None, + sa_flags: old_action.flags.bits() as c_int, + sa_mask: old_action.mask, + }, + }; + } + Ok(()) + } + + unsafe fn sigaltstack( + new_c: Option<&stack_t>, + old_c: Option<&mut stack_t>, + ) -> Result<(), Errno> { + let new = new_c + .map(|c_stack| { + let flags = usize::try_from(c_stack.ss_flags).map_err(|_| Errno(EINVAL))?; + if flags != flags & (SS_DISABLE | SS_ONSTACK) { + return Err(Errno(EINVAL)); + } + + Ok(if flags & SS_DISABLE == SS_DISABLE { + Sigaltstack::Disabled + } else { + Sigaltstack::Enabled { + onstack: false, + base: c_stack.ss_sp.cast(), + size: c_stack.ss_size, + } + }) + }) + .transpose()?; + + let mut old = old_c.as_ref().map(|_| Sigaltstack::default()); + (unsafe { redox_rt::signal::sigaltstack(new.as_ref(), old.as_mut()) })?; + + if let (Some(old_c_stack), Some(old)) = (old_c, old) { + let c_stack = PosixStackt::from(old); + *old_c_stack = stack_t { + ss_sp: c_stack.sp.cast(), + ss_size: c_stack.size, + ss_flags: c_stack.flags, + }; + } + Ok(()) + } + + fn sigpending(set: &mut sigset_t) -> Result<(), Errno> { + *set = redox_rt::signal::currently_pending_blocked(); + Ok(()) + } + + fn sigprocmask( + how: c_int, + set: Option<&sigset_t>, + oset: Option<&mut sigset_t>, + ) -> Result<(), Errno> { + match how { + _ if set.is_none() => { + if let Some(oset) = oset { + *oset = redox_rt::signal::get_sigmask()?; + } + } + SIG_SETMASK => redox_rt::signal::set_sigmask(set.copied(), oset)?, + SIG_BLOCK => redox_rt::signal::or_sigmask(set.copied(), oset)?, + SIG_UNBLOCK => redox_rt::signal::andn_sigmask(set.copied(), oset)?, + + _ => return Err(Errno(EINVAL)), + } + Ok(()) + } + + fn sigsuspend(mask: &sigset_t) -> Errno { + match redox_rt::signal::await_signal_async(!*mask) { + Ok(_) => unreachable!(), + Err(err) => err.into(), + } + } + + fn sigtimedwait( + set: &sigset_t, + info_out: Option<&mut siginfo_t>, + timeout: Option<×pec>, + ) -> Result { + // TODO: deadline-based API + let timeout = timeout.map(|timeout| syscall::TimeSpec { + tv_sec: timeout.tv_sec, + tv_nsec: timeout.tv_nsec as _, + }); + let info = redox_rt::signal::await_signal_sync(*set, timeout.as_ref())?.into(); + if let Some(out) = info_out { + *out = info; + } + Ok(info.si_signo) + } + + /// Thread-based alarm() that Recycles the existing POSIX timer machinery + /// (timerfd + eventfd + pthread) with process-level signal delivery. + /// + /// Internally works with a timespec to allow sub-second timers in the + /// future (e.g. ualarm) as i've been asked, though the public API only exposes whole seconds. + fn alarm(seconds: c_uint) -> c_uint { + alarm_timespec(timespec { + tv_sec: seconds as time_t, + tv_nsec: 0, + }) + } +} + +/// Internal helper that arms/disarms the process-global alarm timer. +/// Accepts a full timespec so sub-second timers (ualarm) can reuse this later. +/// Returns the number of seconds remaining on the previous alarm (rounded up), +/// or 0 if there was no previous alarm. +/// +/// TODO: This implementation does not survive `exec()`. POSIX requires that a +/// pending alarm be preserved across exec (the timer continues counting down +/// in the new process image as i understand). +fn alarm_timespec(duration: timespec) -> c_uint { + let mut guard = ALARM_TIMER.lock(); + + // Determine remaining time on any existing alarm + let remaining = if let Some(ref alarm) = *guard { + let mut cur = itimerspec::default(); + if Sys::timer_gettime(alarm.0, Out::from_mut(&mut cur)).is_ok() { + let secs = cur.it_value.tv_sec as c_uint; + if cur.it_value.tv_nsec > 0 { + secs + 1 // POSIX: round up + } else { + secs + } + } else { + 0 + } + } else { + 0 + }; + + let disarm = duration.tv_sec == 0 && duration.tv_nsec == 0; + + if disarm { + // alarm(0): cancel any pending alarm + if let Some(ref alarm) = *guard { + let zero = itimerspec::default(); + let _ = Sys::timer_settime(alarm.0, 0, &zero, None); + } + return remaining; + } + + // Lazily create the singleton timer if it doesn't exist yet + if guard.is_none() { + let evp = sigevent { + sigev_value: sigval { + sival_ptr: core::ptr::null_mut(), + }, + sigev_signo: SIGALRM as c_int, + sigev_notify: SIGEV_SIGNAL, + sigev_notify_function: None, + sigev_notify_attributes: core::ptr::null_mut(), + }; + + let mut timer_id: timer_t = core::ptr::null_mut(); + if Sys::timer_create( + crate::header::time::CLOCK_REALTIME, + &evp, + Out::from_mut(&mut timer_id), + ) + .is_err() + { + return remaining; + } + + // Enable process-wide signal delivery instead of thread-specific + let timer_st = unsafe { &mut *(timer_id as *mut timer_internal_t) }; + timer_st.process_pid = Sys::getpid(); + + *guard = Some(AlarmTimer(timer_id)); + } + + let timer_id = guard + .as_ref() + .expect("alarm timer must exist after lazy init") + .0; + + // Arm the timer as a one-shot (no interval) + let spec = itimerspec { + it_value: duration, + it_interval: timespec::default(), + }; + let _ = Sys::timer_settime(timer_id, 0, &spec, None); + + remaining +} diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs new file mode 100644 index 0000000000..d0fbe9d010 --- /dev/null +++ b/src/platform/redox/socket.rs @@ -0,0 +1,1215 @@ +use alloc::vec::Vec; +use core::{cmp, mem, ptr, slice, str}; +use redox_protocols::protocol::{FsCall, SocketCall}; +use redox_rt::proc::FdGuard; +use syscall::{self, flag::*}; + +use super::{ + super::{Pal, PalSocket, types::*}, + Sys, + path::dir_path_and_fd_path, +}; +use crate::{ + error::{Errno, Result}, + header::{ + arpa_inet::inet_aton, + bits_iovec::iovec, + bits_safamily_t::sa_family_t, + bits_socklen_t::socklen_t, + errno::{ + EAFNOSUPPORT, EDOM, EFAULT, EINVAL, EMSGSIZE, ENOMEM, ENOSYS, ENOTSOCK, EOPNOTSUPP, + EPROTONOSUPPORT, + }, + netinet_in::{in_addr, in_port_t, sockaddr_in}, + string::strnlen, + sys_select::timeval, + sys_socket::{ + CMSG_ALIGN, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE, cmsghdr, + constants::*, msghdr, sockaddr, ucred, + }, + sys_un::sockaddr_un, + }, +}; + +unsafe fn bind_or_connect( + op: SocketCall, + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, +) -> Result { + if (address_len as usize) < mem::size_of::() { + return Err(Errno(EINVAL)); + } + + let path = match unsafe { (*address).sa_family } as c_int { + AF_INET => { + if (address_len as usize) != mem::size_of::() { + return Err(Errno(EINVAL)); + } + + let data = unsafe { &*(address as *const sockaddr_in) }; + let addr = unsafe { + slice::from_raw_parts( + &data.sin_addr.s_addr as *const _ as *const u8, + mem::size_of_val(&data.sin_addr.s_addr), + ) + }; + let port = in_port_t::from_be(data.sin_port); + + match op { + SocketCall::Bind => { + format!("/{}.{}.{}.{}:{}", addr[0], addr[1], addr[2], addr[3], port) + } + SocketCall::Connect => { + format!("{}.{}.{}.{}:{}", addr[0], addr[1], addr[2], addr[3], port) + } + _ => unreachable!(), + } + } + AF_UNIX => { + log::warn!("bind/connect with AF_UNIX were replaced with SYS_CALL."); + return Err(Errno(EAFNOSUPPORT)); + } + AF_UNSPEC => match op { + SocketCall::Bind => { + // Bind is not a valid socket call for AF_UNSPEC + return Err(Errno(EAFNOSUPPORT)); + } + SocketCall::Connect => { + // When a connect is made using AF_UNSPEC TCP and UDP need to disconnect from the default peer + format!("disconnect") + } + _ => unreachable!(), + }, + _ => return Err(Errno(EAFNOSUPPORT)), + }; + let fd = syscall::dup(socket as usize, path.as_bytes())?; + Ok(fd) +} + +pub unsafe fn bind_or_connect_into( + op: SocketCall, + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, +) -> Result { + // Duplicate the socket, and then duplicate the copy back to the original fd + let fd = FdGuard::new(unsafe { bind_or_connect(op, socket, address, address_len) }?); + syscall::dup2(fd.as_raw_fd(), socket as usize, &[])?; + Ok(0) +} + +unsafe fn inner_af_unix(buf: &[u8], address: *mut sockaddr, address_len: *mut socklen_t) { + let data = unsafe { &mut *(address as *mut sockaddr_un) }; + + data.sun_family = AF_UNIX as c_ushort; + + let path = unsafe { + slice::from_raw_parts_mut(&mut data.sun_path as *mut _ as *mut u8, data.sun_path.len()) + }; + + let len = cmp::min(path.len(), buf.len()); + path[..len].copy_from_slice(&buf[..len]); + if len < path.len() { + path[len] = 0; + } + + unsafe { *address_len = len as socklen_t }; +} + +unsafe fn inner_af_inet( + local: bool, + buf: &[u8], + address: *mut sockaddr, + address_len: *mut socklen_t, +) { + let mut parts = buf.split(|c| *c == b'/'); + if local { + // Skip the remote part + parts.next(); + } + let mut unparsed_addr = Vec::from(parts.next().expect("missing address")); + + let sep = memchr::memchr(b':', &unparsed_addr).expect("missing port"); + let (raw_addr, rest) = unparsed_addr.split_at_mut(sep); + let (colon, raw_port) = rest.split_at_mut(1); + let port = str::from_utf8(raw_port) + .expect("non-utf8 port") + .parse() + .expect("invalid port"); + + // Make address be followed by a NUL-byte + colon[0] = b'\0'; + + log::trace!("address: {:?}, port: {:?}", str::from_utf8(&raw_addr), port); + + let mut addr = in_addr::default(); + assert_eq!( + unsafe { inet_aton(raw_addr.as_ptr() as *mut c_char, &mut addr) }, + 1, + "inet_aton might be broken, failed to parse netstack address" + ); + + let ret = sockaddr_in { + sin_family: AF_INET as sa_family_t, + sin_port: in_port_t::to_be(port), + sin_addr: addr, + + ..sockaddr_in::default() + }; + let len = cmp::min(unsafe { *address_len } as usize, mem::size_of_val(&ret)); + + unsafe { + ptr::copy_nonoverlapping(&ret as *const _ as *const u8, address as *mut u8, len); + *address_len = len as socklen_t; + } +} + +unsafe fn inner_get_name_inner( + local: bool, + address: *mut sockaddr, + address_len: *mut socklen_t, + buf: &[u8], +) -> Result<()> { + if buf.starts_with(b"tcp:") || buf.starts_with(b"udp:") { + unsafe { inner_af_inet(local, &buf[4..], address, address_len) }; + } else if buf.starts_with(b"/scheme/tcp/") || buf.starts_with(b"/scheme/udp/") { + unsafe { inner_af_inet(local, &buf[12..], address, address_len) }; + } else if buf.starts_with(b"chan:") { + unsafe { inner_af_unix(&buf[5..], address, address_len) }; + } else if buf.starts_with(b"/scheme/chan/") { + unsafe { inner_af_unix(&buf[13..], address, address_len) }; + } else if buf.starts_with(b"/scheme/uds_stream/") { + unsafe { inner_af_unix(&buf[19..], address, address_len) }; + } else if buf.starts_with(b"/scheme/uds_dgram/") { + unsafe { inner_af_unix(&buf[18..], address, address_len) }; + } else { + // Socket doesn't belong to any scheme + log::trace!( + "socket {:?} doesn't match either tcp, udp or chan schemes", + str::from_utf8(buf) + ); + return Err(Errno(ENOTSOCK)); + } + Ok(()) +} + +fn socket_domain_type(socket: c_int) -> Result<(c_int, c_int)> { + let mut buf = [0; 256]; + let len = syscall::fpath(socket as usize, &mut buf)?; + Ok( + if buf.starts_with(b"tcp:") || buf.starts_with(b"/scheme/tcp/") { + (AF_INET, SOCK_STREAM) + } else if buf.starts_with(b"udp:") || buf.starts_with(b"/scheme/udp/") { + (AF_INET, SOCK_DGRAM) + } else if buf.starts_with(b"/scheme/uds_stream/") { + (AF_UNIX, SOCK_STREAM) + } else if buf.starts_with(b"/scheme/uds_dgram/") { + (AF_UNIX, SOCK_DGRAM) + } else { + return Err(Errno(ENOTSOCK)); + }, + ) +} + +fn socket_kind(mut kind: c_int) -> (c_int, usize) { + let mut flags = O_RDWR; + if kind & SOCK_NONBLOCK == SOCK_NONBLOCK { + kind &= !SOCK_NONBLOCK; + flags |= O_NONBLOCK; + } + if kind & SOCK_CLOEXEC == SOCK_CLOEXEC { + kind &= !SOCK_CLOEXEC; + flags |= O_CLOEXEC; + } + (kind, flags) +} + +unsafe fn serialize_payload_to_stream( + msg_stream: &mut Vec, + iovs: &[iovec], + whole_iov_size: usize, +) -> Result { + msg_stream.extend_from_slice(&whole_iov_size.to_le_bytes()); + + for iov in iovs { + if iov.iov_len > 0 { + if iov.iov_base.is_null() { + return Err(Errno(EFAULT)); + } + let source_slice: &[u8] = + unsafe { slice::from_raw_parts(iov.iov_base as *const u8, iov.iov_len) }; + msg_stream.extend_from_slice(source_slice); + } + } + Ok(whole_iov_size) +} + +unsafe fn serialize_ancillary_data_to_stream( + msg: *const msghdr, + mhdr: &msghdr, + socket: c_int, + msg_stream: &mut Vec, +) -> Result<()> { + if mhdr.msg_control.is_null() { + return Err(Errno(EINVAL)); + } + + let mut cmsg: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(msg) }; + let mut cmsg_count = 0; + while !cmsg.is_null() { + cmsg_count += 1; + let current_cmsg = unsafe { &*cmsg }; + let min_cmsg_len = unsafe { CMSG_ALIGN(mem::size_of::()) }; + if current_cmsg.cmsg_len < min_cmsg_len { + return Err(Errno(EINVAL)); + } + + // cmsg entry format: [level(i32)][type(i32)][data_len(usize)][data] + msg_stream.extend_from_slice(¤t_cmsg.cmsg_level.to_le_bytes()); + msg_stream.extend_from_slice(¤t_cmsg.cmsg_type.to_le_bytes()); + + match (current_cmsg.cmsg_level, current_cmsg.cmsg_type) { + (SOL_SOCKET, SCM_RIGHTS) => { + let data_len = current_cmsg.cmsg_len - min_cmsg_len; + if data_len % mem::size_of::() != 0 { + return Err(Errno(EINVAL)); + } + let fd_count = data_len / mem::size_of::(); + + if fd_count > 0 { + let fds_ptr = unsafe { CMSG_DATA(cmsg) } as *const c_int; + let c_fds = unsafe { slice::from_raw_parts(fds_ptr, fd_count) }; + let fds_usize: Vec = c_fds.iter().map(|&fd| fd as usize).collect(); + let fds_slice = unsafe { + slice::from_raw_parts( + fds_usize.as_ptr() as *const u8, + fds_usize.len() * mem::size_of::(), + ) + }; + redox_rt::sys::sys_call_wo(socket as usize, &fds_slice, CallFlags::FD, &[])?; + } + + // Serialize to ancillary_data_stream. + // Our intermediate format: data_len is size of fd_count (usize), data is fd_count (usize) + let data_for_stream_len = mem::size_of::(); + let data_for_stream_payload = (fd_count as usize).to_le_bytes(); + + msg_stream.extend_from_slice(&(data_for_stream_len as usize).to_le_bytes()); + msg_stream.extend_from_slice(&data_for_stream_payload); + } + (SOL_SOCKET, SCM_CREDENTIALS) => { + // Our intermediate format: data_len is 0, no data payload + let data_for_stream_len = 0usize; + msg_stream.extend_from_slice(&(data_for_stream_len as usize).to_le_bytes()); + } + _ => { + return Err(Errno(EOPNOTSUPP)); + } + } + cmsg = unsafe { CMSG_NXTHDR(msg, cmsg) }; + } + Ok(()) +} + +unsafe fn deserialize_name_from_stream( + mhdr: &mut msghdr, + msg_stream: &[u8], + cursor: &mut usize, +) -> Result<()> { + // Read name_len from stream + let name_len_in_stream = read_num::(&msg_stream[*cursor..])?; + let name_len = cmp::min(name_len_in_stream, mhdr.msg_namelen as usize); + *cursor += mem::size_of::(); + + if name_len > 0 { + if *cursor + name_len > msg_stream.len() { + return Err(Errno(EMSGSIZE)); + } + if !mhdr.msg_name.is_null() && mhdr.msg_namelen > 0 { + let name_buffer = &msg_stream[*cursor..*cursor + name_len_in_stream]; + (unsafe { + inner_get_name_inner( + false, + mhdr.msg_name as *mut sockaddr, + &mut mhdr.msg_namelen, + name_buffer, + ) + })?; + } + *cursor += name_len_in_stream; + } else { + // If name_len is 0, set msg_namelen to 0 + mhdr.msg_namelen = 0; + } + Ok(()) +} + +unsafe fn deserialize_payload_from_stream( + mhdr: &mut msghdr, + msg_stream: &[u8], + iovs: &[iovec], + whole_iov_size: usize, + cursor: &mut usize, + test: u8, +) -> Result { + let full_payload_len_from_scheme = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + // Determine actual payload data available in the stream + let payload_len_to_read = cmp::min(full_payload_len_from_scheme, whole_iov_size); + let payload_data_from_stream = &msg_stream[*cursor..*cursor + payload_len_to_read]; + *cursor += payload_len_to_read; + + let mut total_bytes_written: usize = 0; + if !iovs.is_empty() && payload_len_to_read > 0 { + let mut source_bytes_consumed: usize = 0; + for iov in iovs { + if iov.iov_len == 0 { + continue; + } + if iov.iov_base.is_null() { + return Err(Errno(EFAULT)); + } + + let source_bytes_remaining = payload_data_from_stream + .len() + .saturating_sub(source_bytes_consumed); + if source_bytes_remaining == 0 { + break; + } + + let bytes_to_write = cmp::min(iov.iov_len, source_bytes_remaining); + if bytes_to_write > 0 { + let dest_slice: &mut [u8] = + unsafe { slice::from_raw_parts_mut(iov.iov_base as *mut u8, iov.iov_len) }; + + let source_sub_slice = &payload_data_from_stream + [source_bytes_consumed..source_bytes_consumed + bytes_to_write]; + dest_slice[..bytes_to_write].copy_from_slice(source_sub_slice); + total_bytes_written += bytes_to_write; + source_bytes_consumed += bytes_to_write; + } + } + } + + if full_payload_len_from_scheme > whole_iov_size { + mhdr.msg_flags |= MSG_TRUNC; + } + + Ok(total_bytes_written) +} + +unsafe fn deserialize_ancillary_data_from_stream( + mhdr: &mut msghdr, + socket: c_int, + msg_stream: &[u8], + cursor: &mut usize, + cmsg_space_provided: usize, + flags: c_int, +) -> Result<()> { + let mut current_cmsg_ptr_in_user_buf = if !mhdr.msg_control.is_null() && cmsg_space_provided > 0 + { + unsafe { CMSG_FIRSTHDR(mhdr) } + } else { + ptr::null_mut() + }; + let mut remaining_user_cmsg_buf_len = cmsg_space_provided; + let mut total_csmg_bytes_written_to_user_buf: usize = 0; + + while *cursor < msg_stream.len() { + const CMSG_HEADER_LEN_IN_STREAM: usize = + mem::size_of::() * 2 + mem::size_of::(); + if *cursor + CMSG_HEADER_LEN_IN_STREAM > msg_stream.len() { + if msg_stream[*cursor..].iter().any(|&b| b != 0) { + mhdr.msg_flags |= MSG_CTRUNC; + } + break; + } + + // cmsg entry format: [level(i32)][type(i32)][data_len(usize)][data] + let cmsg_level = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + let cmsg_type = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + let cmsg_data_len_in_stream = read_num::(&msg_stream[*cursor..])?; + *cursor += mem::size_of::(); + + if *cursor + cmsg_data_len_in_stream > msg_stream.len() { + mhdr.msg_flags |= MSG_CTRUNC; + break; + } + + let cmsg_data_from_stream = &msg_stream[*cursor..*cursor + cmsg_data_len_in_stream]; + *cursor += cmsg_data_len_in_stream; + + let mut temp_posix_cmsg_data_buf: Vec = Vec::new(); + + let actual_posix_cmsg_data_len = match (cmsg_level, cmsg_type) { + (SOL_SOCKET, SCM_RIGHTS) => { + if cmsg_data_len_in_stream != mem::size_of::() { + return Err(Errno(EINVAL)); + } + let fd_count = read_num::(&cmsg_data_from_stream)?; + + let mut fds_usize = vec![0usize; fd_count]; + + let fds_bytes = unsafe { + slice::from_raw_parts_mut( + fds_usize.as_mut_ptr() as *mut u8, + fds_usize.len() * mem::size_of::(), + ) + }; + + let mut call_flags = CallFlags::FD; + if flags & MSG_CMSG_CLOEXEC == MSG_CMSG_CLOEXEC { + call_flags |= CallFlags::FD_CLOEXEC; + } + + redox_rt::sys::sys_call_ro(socket as usize, fds_bytes, call_flags, &[])?; + + for fd in fds_usize { + temp_posix_cmsg_data_buf.extend_from_slice(&(fd as c_int).to_le_bytes()); + } + temp_posix_cmsg_data_buf.len() + } + (SOL_SOCKET, SCM_CREDENTIALS) => { + if cmsg_data_len_in_stream + != mem::size_of::() + mem::size_of::() + mem::size_of::() + { + return Err(Errno(EINVAL)); + } + + let pid = read_num::(&cmsg_data_from_stream)?; + let uid_offset = mem::size_of::(); + let uid = read_num::(&cmsg_data_from_stream[uid_offset..])?; + let gid_offset = uid_offset + mem::size_of::(); + let gid = read_num::(&cmsg_data_from_stream[gid_offset..])?; + let cred = ucred { pid, uid, gid }; + + temp_posix_cmsg_data_buf.extend_from_slice(unsafe { + slice::from_raw_parts( + &cred as *const ucred as *const u8, + mem::size_of::(), + ) + }); + temp_posix_cmsg_data_buf.len() + } + _ => { + return Err(Errno(EINVAL)); + } + }; + + let space_needed_for_posix_cmsg = + unsafe { CMSG_SPACE(actual_posix_cmsg_data_len as u32) } as usize; + + if !current_cmsg_ptr_in_user_buf.is_null() + && remaining_user_cmsg_buf_len >= space_needed_for_posix_cmsg + { + let cmsg_ref = unsafe { &mut *current_cmsg_ptr_in_user_buf }; + cmsg_ref.cmsg_len = unsafe { CMSG_LEN(actual_posix_cmsg_data_len as u32) } as usize; + cmsg_ref.cmsg_level = cmsg_level; + cmsg_ref.cmsg_type = cmsg_type; + + let data_ptr_in_user_cmsg = unsafe { CMSG_DATA(cmsg_ref) }; + unsafe { + ptr::copy_nonoverlapping( + temp_posix_cmsg_data_buf.as_ptr(), + data_ptr_in_user_cmsg as *mut u8, + actual_posix_cmsg_data_len, + ) + }; + + let aligned_len_written = unsafe { CMSG_ALIGN(cmsg_ref.cmsg_len) }; + total_csmg_bytes_written_to_user_buf += aligned_len_written; + remaining_user_cmsg_buf_len -= aligned_len_written; + current_cmsg_ptr_in_user_buf = + unsafe { CMSG_NXTHDR(mhdr, current_cmsg_ptr_in_user_buf) }; + } else { + mhdr.msg_flags |= MSG_CTRUNC; + break; + } + } + mhdr.msg_controllen = total_csmg_bytes_written_to_user_buf; + Ok(()) +} + +impl PalSocket for Sys { + unsafe fn accept( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result { + let stream = syscall::dup(socket as usize, b"listen")?; + if address != ptr::null_mut() && address_len != ptr::null_mut() { + if let Err(err) = unsafe { Self::getpeername(stream as c_int, address, address_len) } { + let _ = syscall::close(stream); + return Err(err); + } + } + Ok(stream as c_int) + } + + unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> Result<()> { + match unsafe { (*address).sa_family } as c_int { + AF_INET => { + (unsafe { bind_or_connect_into(SocketCall::Bind, socket, address, address_len) })?; + } + AF_UNIX => { + let data = unsafe { &*(address as *const sockaddr_un) }; + + // NOTE: It's UB to access data in given address that exceeds + // the given address length. + + let maxlen = cmp::min( + // Max path length of the full-sized struct + data.sun_path.len(), + // Length inferred from given addrlen + address_len as usize - data.path_offset(), + ); + let len = cmp::min( + // The maximum length of the address + maxlen, + // The first NUL byte, if any + unsafe { strnlen(&data.sun_path as *const _, maxlen as size_t) }, + ); + + let addr = + unsafe { slice::from_raw_parts(&data.sun_path as *const _ as *const u8, len) }; + let path = format!("{}", str::from_utf8(addr).unwrap()); + log::trace!("bind(): path: {:?}", path); + + let (dir_path, fd_path) = dir_path_and_fd_path(&path)?; + + redox_rt::sys::sys_call_wo( + socket as usize, + fd_path.as_bytes(), + CallFlags::empty(), + &[SocketCall::Bind as u64], + )?; + + let fs_bind_result = (|| -> Result<()> { + let dirfd = FdGuard::open( + &dir_path, + syscall::O_RDONLY | syscall::O_DIRECTORY | syscall::O_CLOEXEC, + )?; + let fd_to_send = FdGuard::new(syscall::dup(socket as usize, &[])?); + syscall::sendfd(dirfd.as_raw_fd(), fd_to_send.as_raw_fd(), 0, 0)?; + Ok(()) + })(); + + if let Err(original_error) = fs_bind_result { + if let Err(unbind_error) = redox_rt::sys::sys_call_wo( + socket as usize, + &[], + CallFlags::empty(), + &[SocketCall::Unbind as u64], + ) { + todo_error!( + 0, + unbind_error, + "bind: CRITICAL: failed to unbind socket after a failed transaction" + ); + } + + return Err(original_error); + } + } + _ => { + return Err(Errno(EAFNOSUPPORT)); + } + }; + + Ok(()) + } + + unsafe fn connect( + socket: c_int, + address: *const sockaddr, + address_len: socklen_t, + ) -> Result { + match unsafe { (*address).sa_family } as c_int { + AF_INET => unsafe { + bind_or_connect_into(SocketCall::Connect, socket, address, address_len) + }, + AF_UNIX => { + let data = unsafe { &*(address as *const sockaddr_un) }; + + // NOTE: It's UB to access data in given address that exceeds + // the given address length. + + let maxlen = cmp::min( + // Max path length of the full-sized struct + data.sun_path.len(), + // Length inferred from given addrlen + address_len as usize - data.path_offset(), + ); + let len = cmp::min( + // The maximum length of the address + maxlen, + // The first NUL byte, if any + unsafe { strnlen(&data.sun_path as *const _, maxlen as size_t) }, + ); + + let addr = + unsafe { slice::from_raw_parts(&data.sun_path as *const _ as *const u8, len) }; + let path = format!("{}", str::from_utf8(addr).unwrap()); + log::trace!("connect(): path: {:?}", path); + + let (_, fd_path) = dir_path_and_fd_path(&path)?; + + let target_path = format!("/{fd_path}"); + let socket_file_fd = FdGuard::open(&target_path, syscall::O_RDWR)?; + + const TOKEN_BUF_SIZE: usize = 16; + + let mut token_buf = [0u8; TOKEN_BUF_SIZE]; + + redox_rt::sys::sys_call_ro( + socket_file_fd.as_raw_fd(), + &mut token_buf, + CallFlags::empty(), + &[FsCall::Connect as u64], + )?; + + redox_rt::sys::sys_call_wo( + socket as usize, + &token_buf, + CallFlags::empty(), + &[SocketCall::Connect as u64], + )?; + Result::::Ok(0) + } + AF_UNSPEC => unsafe { + bind_or_connect_into(SocketCall::Connect, socket, address, address_len) + }, + _ => Err(Errno(EAFNOSUPPORT)), + } + } + + unsafe fn getpeername( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result<()> { + let mut buf = [0; 256]; + let len = redox_rt::sys::sys_call_ro( + socket as usize, + &mut buf, + CallFlags::empty(), + &[SocketCall::GetPeerName as u64], + )?; + + unsafe { inner_get_name_inner(false, address, address_len, &buf[..len]) } + } + + unsafe fn getsockname( + socket: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result<()> { + let mut buf = [0; 256]; + let len = syscall::fpath(socket as usize, &mut buf)?; + + unsafe { inner_get_name_inner(true, address, address_len, &buf[..len]) } + } + + unsafe fn getsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *mut c_void, + option_len_ptr: *mut socklen_t, + ) -> Result<()> { + if option_len_ptr.is_null() { + return Err(Errno(EFAULT)); + } + let option_len = (unsafe { *option_len_ptr }) as usize; + + let option_c_int = || -> Result<&mut c_int> { + if option_value.is_null() { + return Err(Errno(EFAULT)); + } + + if option_len < mem::size_of::() { + return Err(Errno(EINVAL)); + } + + Ok(unsafe { &mut *(option_value as *mut c_int) }) + }; + + match level { + SOL_SOCKET => match option_name { + SO_DOMAIN => { + let option = option_c_int()?; + *option = socket_domain_type(socket)?.0; + unsafe { *option_len_ptr = mem::size_of::() as socklen_t }; + return Ok(()); + } + SO_ERROR => { + let option = option_c_int()?; + //TODO: Socket nonblock connection error + *option = 0; + unsafe { *option_len_ptr = mem::size_of::() as socklen_t }; + return Ok(()); + } + SO_TYPE => { + let option = option_c_int()?; + *option = socket_domain_type(socket)?.1; + unsafe { *option_len_ptr = mem::size_of::() as socklen_t }; + return Ok(()); + } + _ => { + let metadata = [SocketCall::GetSockOpt as u64, option_name as u64]; + let payload = + unsafe { slice::from_raw_parts_mut(option_value as *mut u8, option_len) }; + let call_flags = CallFlags::empty(); + unsafe { + *option_len_ptr = redox_rt::sys::sys_call_ro( + socket as usize, + payload, + CallFlags::empty(), + &metadata, + )? as socklen_t; + } + return Ok(()); + } + }, + crate::header::sys_socket::constants::IPPROTO_TCP => { + let metadata = [SocketCall::GetSockOpt as u64, option_name as u64]; + let payload = + unsafe { slice::from_raw_parts_mut(option_value as *mut u8, option_len) }; + let call_flags = CallFlags::empty(); + unsafe { + *option_len_ptr = redox_rt::sys::sys_call_ro( + socket as usize, + payload, + CallFlags::empty(), + &metadata, + )? as socklen_t; + } + return Ok(()); + } + _ => (), + } + + todo_skip!( + 0, + "getsockopt({}, {}, {}, {:p}, {:p})", + socket, + level, + option_name, + option_value, + option_len_ptr + ); + Err(Errno(ENOSYS)) + } + + fn listen(socket: c_int, backlog: c_int) -> Result<()> { + // Redox has no need to listen + Ok(()) + } + + unsafe fn recvfrom( + socket: c_int, + buf: *mut c_void, + len: size_t, + flags: c_int, + address: *mut sockaddr, + address_len: *mut socklen_t, + ) -> Result { + if address.is_null() && flags == 0 { + Self::read(socket, unsafe { + slice::from_raw_parts_mut(buf as *mut u8, len) + }) + } else { + // Convert to recvmsg + let mut iov = iovec { + iov_base: buf, + iov_len: len, + }; + let mut msg = msghdr { + msg_name: address as *mut c_void, + msg_namelen: if !address_len.is_null() { + unsafe { *address_len } + } else { + 0 + }, + msg_iov: &mut iov, + msg_iovlen: 1, + msg_control: ptr::null_mut(), + msg_controllen: 0, + msg_flags: 0, + }; + let count = unsafe { Self::recvmsg(socket, &mut msg, flags) }?; + if !address_len.is_null() { + unsafe { *address_len = msg.msg_namelen }; + } + return Ok(count); + } + } + + unsafe fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> Result { + if msg.is_null() { + return Err(Errno(EINVAL)); + } + let mut mhdr = unsafe { &mut *msg }; + let iovs_slice: &[iovec] = if mhdr.msg_iov.is_null() || mhdr.msg_iovlen == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(mhdr.msg_iov, mhdr.msg_iovlen as usize) } + }; + let whole_iov_size: usize = iovs_slice.iter().map(|iov| iov.iov_len).sum(); + + let mut msg_stream: Vec = Vec::new(); + + // Prepare space for the message stream. + // [name_len(usize)][name_buffer] + // [payload_len(usize)][payload_data_buffer] + // [ancillary_stream_buffer] + let expected_stream_size = { + 64 //reserve extra space for the scheme path + + mem::size_of::() // name_len + + mhdr.msg_namelen as usize // name_buffer + + mem::size_of::() // payload_len + + whole_iov_size // payload_data_buffer + + mem::size_of::() // control_len + + mhdr.msg_controllen as usize // ancillary_stream_buffer + }; + msg_stream + .try_reserve_exact(expected_stream_size) + .map_err(|_| Errno(ENOMEM))?; + msg_stream.resize(expected_stream_size, 0); + + // Write the information about the msghdr + let mut cursor: usize = 0; + msg_stream[cursor..cursor + mem::size_of::()] + .copy_from_slice(&(mhdr.msg_namelen as usize).to_le_bytes()); + cursor += mem::size_of::(); + msg_stream[cursor..cursor + mem::size_of::()] + .copy_from_slice(&(whole_iov_size).to_le_bytes()); + cursor += mem::size_of::(); + msg_stream[cursor..cursor + mem::size_of::()] + .copy_from_slice(&(mhdr.msg_controllen as usize).to_le_bytes()); + + // Read the message stream. + let metadata = [SocketCall::RecvMsg as u64, flags as u64]; + let call_flags = CallFlags::empty(); + let actual_read_len = + redox_rt::sys::sys_call_rw(socket as usize, &mut msg_stream, call_flags, &metadata)?; + msg_stream.truncate(actual_read_len); + + cursor = 0; + let cmsg_space_provided_by_user = mhdr.msg_controllen; + mhdr.msg_flags = 0; + + // Read sender name. + (unsafe { deserialize_name_from_stream(&mut mhdr, &msg_stream, &mut cursor) })?; + + // Read payload data. + let actual_payload_bytes_written_to_iov = unsafe { + deserialize_payload_from_stream( + &mut mhdr, + &msg_stream, + iovs_slice, + whole_iov_size, + &mut cursor, + 0u8, + ) + }?; + + // Reconstruct the ancillary data in the user-provided buffer. + let has_cmsg_buffer = !mhdr.msg_control.is_null() && cmsg_space_provided_by_user > 0; + let has_ancillary_data = cursor < msg_stream.len(); + if has_cmsg_buffer && has_ancillary_data { + (unsafe { + deserialize_ancillary_data_from_stream( + mhdr, + socket, + &msg_stream, + &mut cursor, + cmsg_space_provided_by_user as usize, + flags, + ) + })?; + } else { + mhdr.msg_controllen = 0; // No ancillary data + } + Ok(actual_payload_bytes_written_to_iov) + } + + unsafe fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> Result { + if msg.is_null() { + return Err(Errno(EINVAL)); + } + let mhdr = unsafe { &*msg }; + + // Reserve space for the message stream. + // [payload_len(usize)][payload_data_buffer] + // [ancillary_stream_buffer] + let iovs_slice: &[iovec] = if mhdr.msg_iov.is_null() || mhdr.msg_iovlen == 0 { + &[] + } else { + unsafe { slice::from_raw_parts(mhdr.msg_iov, mhdr.msg_iovlen as usize) } + }; + + let mut msg_stream: Vec = Vec::new(); + let whole_iov_size: usize = iovs_slice.iter().map(|iov| iov.iov_len).sum(); + msg_stream + .try_reserve_exact( + mem::size_of::() // payload_len + + whole_iov_size // payload_data_buffer + + mhdr.msg_controllen as usize, // ancillary_stream_buffer + ) + .map_err(|_| Errno(ENOMEM))?; + + // Write the message to the msg_stream. + let mut actual_payload_bytes_serialized = 0; + if !mhdr.msg_iov.is_null() && mhdr.msg_iovlen > 0 { + actual_payload_bytes_serialized = unsafe { + serialize_payload_to_stream(&mut msg_stream, &iovs_slice, whole_iov_size) + }?; + } + // Process Control Messages from msghdr and serialize them. + if mhdr.msg_controllen > 0 { + (unsafe { serialize_ancillary_data_to_stream(msg, mhdr, socket, &mut msg_stream) })?; + } + + // Send the message stream. + let metadata = [SocketCall::SendMsg as u64, flags as u64]; + let call_flags = CallFlags::empty(); + let written = redox_rt::sys::sys_call_rw( + socket as usize, + msg_stream.as_mut_slice(), + call_flags, + &metadata, + )?; + + Ok(actual_payload_bytes_serialized) + } + + unsafe fn sendto( + socket: c_int, + buf: *const c_void, + len: size_t, + flags: c_int, + dest_addr: *const sockaddr, + dest_len: socklen_t, + ) -> Result { + if flags != 0 { + // Convert to sendmsg + let mut iov = iovec { + iov_base: buf as *mut c_void, + iov_len: len, + }; + let msg = msghdr { + msg_name: dest_addr as *mut c_void, + msg_namelen: dest_len, + msg_iov: &mut iov, + msg_iovlen: 1, + msg_control: ptr::null_mut(), + msg_controllen: 0, + msg_flags: 0, + }; + return unsafe { Self::sendmsg(socket, &msg, flags) }; + } + if dest_addr == ptr::null() || dest_len == 0 { + Self::write(socket, unsafe { + slice::from_raw_parts(buf as *const u8, len) + }) + } else { + let fd = FdGuard::new(unsafe { + bind_or_connect(SocketCall::Connect, socket, dest_addr, dest_len) + }?); + Self::write(fd.as_c_fd().unwrap(), unsafe { + slice::from_raw_parts(buf as *const u8, len) + }) + } + } + + unsafe fn setsockopt( + socket: c_int, + level: c_int, + option_name: c_int, + option_value: *const c_void, + option_len: socklen_t, + ) -> Result<()> { + let set_timeout = |timeout_name: &[u8]| -> Result<()> { + if option_value.is_null() { + return Err(Errno(EFAULT)); + } + + if (option_len as usize) < mem::size_of::() { + return Err(Errno(EINVAL)); + } + + let timeval = unsafe { &*(option_value as *const timeval) }; + + let fd = FdGuard::new(syscall::dup(socket as usize, timeout_name)?); + + let Some(tv_nsec) = timeval.tv_usec.checked_mul(1000) else { + return Err(Errno(EDOM)); + }; + + let timespec = syscall::TimeSpec { + tv_sec: timeval.tv_sec as i64, + tv_nsec, + }; + + Self::write(fd.as_c_fd().unwrap(), ×pec)?; + Ok(()) + }; + + match level { + SOL_SOCKET => match option_name { + SO_RCVTIMEO => return set_timeout(b"read_timeout"), + SO_SNDTIMEO => return set_timeout(b"write_timeout"), + _ => { + let metadata = [SocketCall::SetSockOpt as u64, option_name as u64]; + let payload = unsafe { + slice::from_raw_parts(option_value as *const u8, option_len as usize) + }; + let call_flags = CallFlags::empty(); + redox_rt::sys::sys_call_wo( + socket as usize, + payload, + CallFlags::empty(), + &metadata, + )?; + return Ok(()); + } + }, + crate::header::sys_socket::constants::IPPROTO_TCP => { + let metadata = [SocketCall::SetSockOpt as u64, option_name as u64]; + let payload = unsafe { + slice::from_raw_parts(option_value as *const u8, option_len as usize) + }; + redox_rt::sys::sys_call_wo( + socket as usize, + payload, + CallFlags::empty(), + &metadata, + )?; + return Ok(()); + } + _ => (), + } + + todo_skip!( + 0, + "setsockopt({}, {}, {}, {:p}, {}) - unknown option", + socket, + level, + option_name, + option_value, + option_len + ); + Ok(()) + } + + fn shutdown(socket: c_int, how: c_int) -> Result<()> { + let metadata = [SocketCall::Shutdown as u64, how as u64]; + redox_rt::sys::sys_call_wo(socket as usize, &[], CallFlags::empty(), &metadata)?; + Ok(()) + } + + unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> Result { + if domain != AF_INET && domain != AF_UNIX { + return Err(Errno(EAFNOSUPPORT)); + } + // if protocol != 0 { + // ERRNO.set(syscall::EPROTONOSUPPORT); + // return -1; + // } + + let (kind, flags) = socket_kind(kind); + + // The tcp: and udp: schemes allow using no path, + // and later specifying one using `dup`. + Ok(match (domain, kind) { + (AF_INET, SOCK_STREAM) => redox_rt::sys::open("/scheme/tcp", flags)? as c_int, + (AF_INET, SOCK_DGRAM) => redox_rt::sys::open("/scheme/udp", flags)? as c_int, + (AF_UNIX, SOCK_STREAM) => { + redox_rt::sys::open("/scheme/uds_stream", flags | O_CREAT)? as c_int + } + (AF_UNIX, SOCK_DGRAM) => { + redox_rt::sys::open("/scheme/uds_dgram", flags | O_CREAT)? as c_int + } + _ => return Err(Errno(EPROTONOSUPPORT)), + }) + } + + fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> Result<()> { + let (kind, flags) = socket_kind(kind); + + match (domain, kind) { + (AF_UNIX, SOCK_STREAM) => { + let listener = FdGuard::open("/scheme/uds_stream", flags | O_CREAT)?; + + // For now, uds_stream: lets connects be instant, and instead blocks + // on any I/O performed. So we don't need to mark this as + // nonblocking. + + let fd0 = listener.dup(b"connect")?; + let fd1 = listener.dup(b"listen")?; + + sv[0] = fd0.take() as c_int; + sv[1] = fd1.take() as c_int; + Ok(()) + } + (AF_UNIX, SOCK_DGRAM) => { + let listener = FdGuard::open("/scheme/uds_dgram", flags | O_CREAT)?; + + // For now, uds_dgram: lets connects be instant, and instead blocks + // on any I/O performed. So we don't need to mark this as + // nonblocking. + + let fd0 = listener.dup(b"connect")?; + + sv[0] = fd0.take() as c_int; + sv[1] = listener.take() as c_int; + Ok(()) + } + _ => { + todo_skip!( + 0, + "socketpair({}, {}, {}, {:p})", + domain, + kind, + protocol, + sv.as_mut_ptr() + ); + Err(Errno(EPROTONOSUPPORT)) + } + } + } +} + +fn read_num(buffer: &[u8]) -> Result +where + T: NumFromBytes, +{ + T::from_le_bytes_slice(buffer) +} +trait NumFromBytes: Sized { + fn from_le_bytes_slice(buffer: &[u8]) -> Result; +} +impl NumFromBytes for i32 { + fn from_le_bytes_slice(buffer: &[u8]) -> Result { + Ok(i32::from_le_bytes( + buffer + .get(..mem::size_of::()) + .and_then(|slice| slice.try_into().ok()) + .ok_or_else(|| Errno(EFAULT))?, + )) + } +} +impl NumFromBytes for usize { + fn from_le_bytes_slice(buffer: &[u8]) -> Result { + Ok(usize::from_le_bytes( + buffer + .get(..mem::size_of::()) + .and_then(|slice| slice.try_into().ok()) + .ok_or_else(|| Errno(EFAULT))?, + )) + } +} diff --git a/src/platform/redox/timer.rs b/src/platform/redox/timer.rs new file mode 100644 index 0000000000..4ca03d67fd --- /dev/null +++ b/src/platform/redox/timer.rs @@ -0,0 +1,103 @@ +use syscall::Error; + +use crate::{ + error::{Errno, Result}, + header::{ + bits_timespec::timespec, + errno::EIO, + signal::{SIGEV_SIGNAL, SIGEV_THREAD}, + time::timer_internal_t, + }, + out::Out, + platform::{Pal, Sys, sys::event, types::c_void}, +}; +use core::{ + mem::{MaybeUninit, size_of}, + ptr, slice, +}; + +pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void { + let timer_st = unsafe { &mut *(arg as *mut timer_internal_t) }; + + loop { + let mut buf = MaybeUninit::uninit(); + let res = Error::demux(unsafe { + event::redox_event_queue_get_events_v1( + timer_st.eventfd, + buf.as_mut_ptr(), + 1, + 0, + core::ptr::null(), + core::ptr::null(), + ) + }); + if let Ok(res) = res { + assert_eq!(res, 1, "EOF is not yet well defined for event queues"); + } else { + break; + } + + if timer_st.evp.sigev_notify == SIGEV_THREAD { + if let Some(fun) = timer_st.evp.sigev_notify_function { + fun(timer_st.evp.sigev_value); + } + } else if timer_st.evp.sigev_notify == SIGEV_SIGNAL { + // TODO: This will deliver signal to process, which is required for alarm() + // Until it can bypass the exec() boundary, do not uncomment this code + // if timer_st.process_pid != 0 && Sys::kill(timer_st.process_pid, timer_st.evp.sigev_signo).is_err() { break; } else + if unsafe { Sys::rlct_kill(timer_st.caller_thread, timer_st.evp.sigev_signo as _) } + .is_err() + { + break; + } + } + + if timer_next_event(timer_st).is_err() { + break; + } + } + + timer_st.thread = ptr::null_mut(); + ptr::null_mut() +} + +fn timer_next_event(timer_st: &mut timer_internal_t) -> Result<()> { + timer_update_wake_time(timer_st)?; + let buf_to_write = unsafe { + Error::demux(event::redox_event_queue_ctl_v1( + timer_st.eventfd, + timer_st.timerfd, + 1, + 0, + ))?; + + slice::from_raw_parts( + &timer_st.next_wake_time.it_value as *const _ as *const u8, + size_of::(), + ) + }; + let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?; + if bytes_written < size_of::() { + return Err(Errno(EIO)); + } + Ok(()) +} + +pub(crate) fn timer_update_wake_time(timer_st: &mut timer_internal_t) -> Result<()> { + timer_st.next_wake_time.it_value = if timer_st.next_wake_time.it_interval.is_default() { + timespec::default() + } else { + let mut now = timespec::default(); + Sys::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?; + let next_time = match timespec::add(now, timer_st.next_wake_time.it_interval.clone()) { + Some(a) => a, + None => timespec::default(), + }; + + next_time + }; + if timer_st.next_wake_time.it_value.is_default() { + return Err(Errno(0)); + } + Ok(()) +} diff --git a/src/platform/rlb.rs b/src/platform/rlb.rs new file mode 100644 index 0000000000..6ceb9d6bf4 --- /dev/null +++ b/src/platform/rlb.rs @@ -0,0 +1,106 @@ +use alloc::vec::Vec; + +use crate::platform::{Pal, Sys, types::*}; + +use crate::{ + error::ResultExt, + header::unistd::{SEEK_SET, lseek}, +}; +/// Implements an `Iterator` which returns on either newline or EOF. +#[derive(Clone)] +pub struct RawLineBuffer { + pub fd: c_int, + buf: Vec, + newline: Option, + read: usize, +} + +#[derive(PartialEq)] +pub enum Line<'a> { + Error, + EOF, + Some(&'a [u8]), +} + +impl RawLineBuffer { + pub const fn new(fd: c_int) -> Self { + Self { + fd, + buf: Vec::new(), + newline: None, + read: 0, + } + } + + // Can't use iterators because we want to return a reference. + // See https://stackoverflow.com/a/30422716/5069285 + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> Line<'_> { + // Remove last line + if let Some(newline) = self.newline { + self.buf.drain(..=newline); + } + + loop { + // Exit if newline was read already + self.newline = self.buf.iter().position(|b| *b == b'\n'); + + if self.newline.is_some() { + break; + } + + let len = self.buf.len(); + + if len >= self.buf.capacity() { + self.buf.reserve(1024); + } + + // Create buffer of what's left in the vector, uninitialized memory + unsafe { + let capacity = self.buf.capacity(); + self.buf.set_len(capacity); + } + + let read = Sys::read(self.fd, &mut self.buf[len..]) + .map(|u| u as isize) + .or_minus_one_errno(); + + let read_usize = read.max(0) as usize; + + // Remove all uninitialized memory that wasn't read + unsafe { + self.buf.set_len(len + read_usize); + } + + self.read += read_usize; + + if read == 0 { + return if self.buf.is_empty() { + Line::EOF + } else { + Line::Some(&self.buf) + }; + } + if read < 0 { + return Line::Error; + } + } + + let newline = self.newline.unwrap(); // safe because it doesn't break the loop otherwise + Line::Some(&self.buf[..newline]) + } + + /// Return the byte position of the start of the line + pub fn line_pos(&self) -> usize { + self.read - self.buf.len() + } + + /// Seek to a byte position in the file + pub fn seek(&mut self, pos: usize) -> off_t { + let ret = lseek(self.fd, pos as off_t, SEEK_SET); + if ret != !0 { + self.read = pos; + } + ret + } +} diff --git a/src/platform/types.rs b/src/platform/types.rs new file mode 100644 index 0000000000..3d14bb29a1 --- /dev/null +++ b/src/platform/types.rs @@ -0,0 +1,161 @@ +//! C data types for this platform. + +// Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help enable +// more optimization opportunities around it recognizing things like +// malloc/free. +/// The `void` type in C. +#[repr(u8)] +pub enum c_void { + // Two dummy variants so the #[repr] attribute can be used. + #[doc(hidden)] + __variant1, + #[doc(hidden)] + __variant2, +} + +/// The `int8_t` type provided in `stdint.h`, see . +pub type int8_t = i8; +/// The `int16_t` type provided in `stdint.h`, see . +pub type int16_t = i16; +/// The `int32_t` type provided in `stdint.h`, see . +pub type int32_t = i32; +/// The `int64_t` type provided in `stdint.h`, see . +pub type int64_t = i64; +/// The `uint8_t` type provided in `stdint.h`, see . +pub type uint8_t = u8; +/// The `uint16_t` type provided in `stdint.h`, see . +pub type uint16_t = u16; +/// The `uint32_t` type provided in `stdint.h`, see . +pub type uint32_t = u32; +/// The `uint64_t` type provided in `stdint.h`, see . +pub type uint64_t = u64; + +/// The `signed char` type in C. +pub type c_schar = i8; +/// The `unsigned char` type in C. +pub type c_uchar = u8; +/// The `short` type in C. +pub type c_short = i16; +/// The `unsigned short` type in C. +pub type c_ushort = u16; +/// The `int` type in C. +pub type c_int = i32; +/// The `unsigned int` type in C. +pub type c_uint = u32; +/// The `float` type in C. +pub type c_float = f32; +/// The `double` type in C. +pub type c_double = f64; +/// The `long long` type in C. +pub type c_longlong = i64; +/// The `unsigned long long` type in C. +pub type c_ulonglong = u64; +/// The `intmax_t` type provided in `stdint.h`, see . +pub type intmax_t = i64; +/// The `uintmax_t` type provided in `stdint.h`, see . +pub type uintmax_t = u64; + +/// The `size_t` type provided in `stddef.h`, see . +pub type size_t = usize; +/// The `ptrdiff_t` type provided in `stddef.h`, see . +pub type ptrdiff_t = isize; +/// The `intptr_t` type provided in `stdint.h`, see . +pub type intptr_t = isize; +/// The `uintptr_t` type provided in `stdint.h`, see . +pub type uintptr_t = usize; +/// The `ssize_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type ssize_t = isize; + +/// The `char` type in C. +pub type c_char = core::ffi::c_char; +/// The `long` type in C. +#[cfg(target_pointer_width = "32")] +pub type c_long = i32; +/// The `unsigned long` type in C. +#[cfg(target_pointer_width = "32")] +pub type c_ulong = u32; +/// The `long` type in C. +#[cfg(target_pointer_width = "64")] +pub type c_long = i64; +/// The `unsigned long` type in C. +#[cfg(target_pointer_width = "64")] +pub type c_ulong = u64; + +/// The `wchar_t` type provided in `stddef.h`, see . +pub type wchar_t = i32; +/// The `wint_t` type provided in [`wchar.h`](crate::header::wchar). +pub type wint_t = u32; + +/// The `regoff_t` type provided in [`regex.h`](crate::header::regex). +pub type regoff_t = size_t; +/// The `off_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type off_t = c_longlong; +/// The `mode_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type mode_t = c_int; +/// The `time_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type time_t = c_longlong; +/// The `pid_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type pid_t = c_int; +/// The `id_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type id_t = c_uint; +/// The `gid_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type gid_t = c_int; +/// The `uid_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type uid_t = c_int; +/// The `dev_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type dev_t = c_ulonglong; +/// The `ino_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type ino_t = c_ulonglong; +/// The `reclen_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type reclen_t = c_ushort; +/// The `nlink_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type nlink_t = c_ulong; +/// The `blksize_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type blksize_t = c_long; +/// The `blkcnt_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type blkcnt_t = c_longlong; + +/// The `fsblkcnt_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type fsblkcnt_t = c_ulong; +/// The `fsfilcnt_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type fsfilcnt_t = c_ulong; + +/// The `useconds_t` type provided in [`sys/types.h`](crate::header::sys_types) prior to Issue 7. +#[deprecated] +pub type useconds_t = c_uint; +/// The `suseconds_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[cfg(target_os = "linux")] +pub type suseconds_t = c_long; +// TODO: Should we break this to c_long as well? This also breaks timeval as well +// but it will be consistent with timespec.tv_nsec (note that syscall already uses c_int) +/// The `suseconds_t` type provided in [`sys/types.h`](crate::header::sys_types). +#[cfg(not(target_os = "linux"))] +pub type suseconds_t = c_int; + +/// The `clock_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type clock_t = c_long; +/// The `clockid_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type clockid_t = c_int; +/// The `timer_t` type provided in [`sys/types.h`](crate::header::sys_types). +pub type timer_t = *mut c_void; + +// A C long double is 96 bit in x86, 128 bit in other 64-bit targets +// However, both in x86 and x86_64 is actually f80 padded which rust has no underlying support, +// while aarch64 (and possibly riscv64) support full f128 type but behind a feature gate. +// Until rust supporting them, relibc will lose precision to get them working, plus: +// All read operation to this type must be converted from "relibc_ldtod". +// All write operation to this type must be converted with "relibc_dtold". +/// The `long double` type in C. +#[cfg(target_pointer_width = "64")] +pub type c_longdouble = u128; +/// The `long double` type in C. +#[cfg(target_pointer_width = "32")] +pub type c_longdouble = [u32; 3]; + +pub use crate::header::bits_pthread::*; + +/// The `max_align_t` type provided in `stddef.h`, see . +#[repr(C, align(16))] +pub struct max_align_t { + _priv: [f64; 4], +} diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs new file mode 100644 index 0000000000..8243a48079 --- /dev/null +++ b/src/pthread/mod.rs @@ -0,0 +1,440 @@ +//! Relibc Threads, or RLCT. + +use core::{ + cell::UnsafeCell, + ptr, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, +}; + +use alloc::collections::BTreeMap; + +use crate::{ + error::Errno, + header::{errno::*, pthread as header, sched::sched_param, sys_mman}, + ld_so::{ExpectTlsFree, tcb::Tcb}, + platform::{Pal, Sys, types::*}, +}; + +use crate::sync::{Mutex, waitval::Waitval}; + +/// Called only by the main thread, as part of relibc_start. +#[allow(unused_mut)] +pub unsafe fn init() { + let mut thread = Pthread { + waitval: Waitval::new(), + has_enabled_cancelation: AtomicBool::new(false), + has_queued_cancelation: AtomicBool::new(false), + flags: PthreadFlags::empty().bits().into(), + + //index: FIRST_THREAD_IDX, + + // TODO: set these values on Linux as well + stack_base: ptr::null_mut(), + stack_size: 0, + + os_tid: UnsafeCell::new(Sys::current_os_tid()), + }; + + #[cfg(target_os = "redox")] + { + //TODO: what is the best way to get these values? + use redox_rt::arch::{STACK_SIZE, STACK_TOP}; + thread.stack_base = (STACK_TOP - STACK_SIZE) as *mut c_void; + thread.stack_size = STACK_SIZE; + } + + unsafe { Tcb::current() } + .expect_notls("no TCB present for main thread") + .pthread = thread; +} + +//static NEXT_INDEX: AtomicU32 = AtomicU32::new(FIRST_THREAD_IDX + 1); +//const FIRST_THREAD_IDX: usize = 1; + +pub unsafe fn terminate_from_main_thread() { + for (_, tcb) in OS_TID_TO_PTHREAD.lock().iter() { + let _ = unsafe { cancel(&(*tcb.0).pthread) }; + } +} + +bitflags::bitflags! { + pub struct PthreadFlags: usize { + const DETACHED = 1; + } +} + +#[derive(Debug)] +pub struct Pthread { + pub(crate) waitval: Waitval, + pub(crate) has_queued_cancelation: AtomicBool, + pub(crate) has_enabled_cancelation: AtomicBool, + pub(crate) flags: AtomicUsize, + + pub(crate) stack_base: *mut c_void, + pub(crate) stack_size: usize, + + pub os_tid: UnsafeCell, +} + +#[derive(Clone, Copy, Debug, Default, Ord, Eq, PartialOrd, PartialEq)] +pub struct OsTid { + #[cfg(target_os = "redox")] + pub thread_fd: usize, + #[cfg(target_os = "linux")] + pub thread_id: usize, +} + +unsafe impl Send for Pthread {} +unsafe impl Sync for Pthread {} + +#[derive(Clone, Copy, Debug)] +pub struct Retval(pub *mut c_void); + +struct MmapGuard { + page_start: *mut c_void, + mmap_size: usize, +} +impl Drop for MmapGuard { + fn drop(&mut self) { + unsafe { + let _ = Sys::munmap(self.page_start, self.mmap_size); + } + } +} + +#[allow(unused_mut)] +pub(crate) unsafe fn create( + attrs: Option<&header::RlctAttr>, + start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void, + arg: *mut c_void, +) -> Result { + let attrs = attrs.cloned().unwrap_or_default(); + + let mut current_sigmask = 0_u64; + #[cfg(target_os = "redox")] + { + current_sigmask = + redox_rt::signal::get_sigmask().expect("failed to obtain sigprocmask for caller"); + } + + // Create a locked mutex, unlocked by the thread after it has started. + let synchronization_mutex = unsafe { Mutex::locked(current_sigmask) }; + let synchronization_mutex = &synchronization_mutex; + + let stack_size = attrs.stacksize.next_multiple_of(Sys::getpagesize()); + + let stack_base = if attrs.stack != 0 { + attrs.stack as *mut c_void + } else { + let ret = unsafe { + sys_mman::mmap( + core::ptr::null_mut(), + stack_size, + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + sys_mman::MAP_PRIVATE | sys_mman::MAP_ANONYMOUS, + -1, + 0, + ) + }; + if ret as isize == -1 { + // "Insufficient resources" + return Err(Errno(EAGAIN)); + } + ret + }; + + let mut flags = PthreadFlags::empty(); + match i32::from(attrs.detachstate) { + header::PTHREAD_CREATE_DETACHED => flags |= PthreadFlags::DETACHED, + header::PTHREAD_CREATE_JOINABLE => (), + + other => unreachable!("unknown detachstate {}", other), + } + + let stack_raii = MmapGuard { + page_start: stack_base, + mmap_size: stack_size, + }; + + let current_tcb = unsafe { Tcb::current() }.expect("no TCB!"); + let new_tcb = unsafe { Tcb::new(current_tcb.tls_len) }.map_err(|_| Errno(ENOMEM))?; + new_tcb.pthread.flags = flags.bits().into(); + new_tcb.pthread.stack_base = stack_base; + new_tcb.pthread.stack_size = stack_size; + + new_tcb.masters_ptr = current_tcb.masters_ptr; + new_tcb.masters_len = current_tcb.masters_len; + new_tcb.linker_ptr = current_tcb.linker_ptr; + new_tcb.mspace = current_tcb.mspace; + + let stack_end = unsafe { stack_base.add(stack_size) }; + let mut stack = stack_end.cast::(); + { + let mut push = |value: usize| { + stack = unsafe { stack.sub(1) }; + unsafe { stack.write(value) }; + }; + + if cfg!(target_arch = "aarch64") { + // Aarch64 requires the stack to be 16 byte aligned after + // the call instruction, unlike x86 which requires it to be + // aligned before the call instruction. As such push an + // extra word on the stack to align the stack to 16 bytes. + push(0); + } + push(0); + push(0); + push(ptr::from_ref(synchronization_mutex) as usize); + push(ptr::from_mut(new_tcb) as usize); + + push(arg as usize); + push(start_routine as usize); + + push(new_thread_shim as *const () as usize); + } + + let Ok(os_tid) = (unsafe { Sys::rlct_clone(stack, &mut new_tcb.os_specific) }) else { + return Err(Errno(EAGAIN)); + }; + core::mem::forget(stack_raii); + + let _ = synchronization_mutex.lock(); + + OS_TID_TO_PTHREAD + .lock() + .insert(os_tid, ForceSendSync(new_tcb)); + + Ok(&raw const new_tcb.pthread as *mut _) +} + +/// A shim to wrap thread entry points in logic to set up TLS, for example +unsafe extern "C" fn new_thread_shim( + entry_point: unsafe extern "C" fn(*mut c_void) -> *mut c_void, + arg: *mut c_void, + tcb: *mut Tcb, + synchronization_mutex: *const Mutex, +) -> ! { + let tcb = unsafe { tcb.as_mut() }.expect_notls("non-null TLS is required"); + + #[cfg(not(target_os = "redox"))] + { + unsafe { tcb.activate() }; + } + #[cfg(target_os = "redox")] + { + // `thr_fd` in `tcb` is set by [`Sys::rlct_clone`] *before* jumping to + // the entry point of the new thread. + unsafe { + tcb.activate(None); + } + redox_rt::signal::setup_sighandler(&tcb.os_specific, false); + } + + let procmask = unsafe { (&*synchronization_mutex).as_ptr().read() }; + + unsafe { tcb.copy_masters() }.unwrap(); + + unsafe { (*tcb).pthread.os_tid.get().write(Sys::current_os_tid()) }; + + unsafe { (&*synchronization_mutex).manual_unlock() }; + + #[cfg(target_os = "redox")] + { + redox_rt::signal::set_sigmask(Some(procmask), None) + .expect("failed to set procmask in child thread"); + } + + let retval = unsafe { entry_point(arg) }; + + unsafe { exit_current_thread(Retval(retval)) } +} +pub unsafe fn join(thread: &Pthread) -> Result { + // We don't have to return EDEADLK, but unlike e.g. pthread_t lifetime checking, it's a + // relatively easy check. + if core::ptr::eq( + thread, + current_thread().expect("current thread not present"), + ) { + return Err(Errno(EDEADLK)); + } + + // Waitval starts locked, and is unlocked when the thread finishes. + let retval = *thread.waitval.wait(); + + // We have now awaited the thread and received its return value. POSIX states that the + // pthread_t of this thread, will no longer be valid. In practice, we can thus deallocate the + // thread state. + + unsafe { dealloc_thread(thread) }; + + Ok(retval) +} + +pub unsafe fn detach(thread: &Pthread) -> Result<(), Errno> { + thread + .flags + .fetch_or(PthreadFlags::DETACHED.bits(), Ordering::AcqRel); + Ok(()) +} + +pub fn current_thread() -> Option<&'static Pthread> { + unsafe { Tcb::current().map(|p| &p.pthread) } +} + +pub unsafe fn testcancel() { + let this_thread = current_thread().expect("current thread not present"); + + if this_thread.has_queued_cancelation.load(Ordering::Acquire) + && this_thread.has_enabled_cancelation.load(Ordering::Acquire) + { + unsafe { cancel_current_thread() }; + } +} + +pub unsafe fn exit_current_thread(retval: Retval) -> ! { + // Run pthread_cleanup_push/pthread_cleanup_pop destructors. + unsafe { header::run_destructor_stack() }; + + unsafe { header::tls::run_all_destructors() }; + + let this = current_thread().expect("failed to obtain current thread when exiting"); + let stack_base = this.stack_base; + let stack_size = this.stack_size; + + if this.flags.load(Ordering::Acquire) & PthreadFlags::DETACHED.bits() != 0 { + // When detached, the thread state no longer makes any sense, and can immediately be + // deallocated. + unsafe { dealloc_thread(this) }; + } else { + // When joinable, the return value should be made available to other threads. + unsafe { this.waitval.post(retval) }; + } + + unsafe { Sys::exit_thread(stack_base.cast(), stack_size) } +} + +unsafe fn dealloc_thread(thread: &Pthread) { + // TODO: How should this be handled on Linux? + unsafe { + OS_TID_TO_PTHREAD.lock().remove(&thread.os_tid.get().read()); + } +} +pub const SIGRT_RLCT_CANCEL: usize = 33; +pub const SIGRT_RLCT_TIMER: usize = 34; + +unsafe extern "C" fn cancel_sighandler(_: c_int) { + unsafe { cancel_current_thread() }; +} +unsafe fn cancel_current_thread() { + // Terminate the thread + unsafe { exit_current_thread(Retval(header::PTHREAD_CANCELED)) }; +} + +pub unsafe fn cancel(thread: &Pthread) -> Result<(), Errno> { + // TODO: What order should these atomic bools be accessed in? + thread.has_queued_cancelation.store(true, Ordering::Release); + + if thread.has_enabled_cancelation.load(Ordering::Acquire) { + (unsafe { Sys::rlct_kill(thread.os_tid.get().read(), SIGRT_RLCT_CANCEL) })?; + } + + Ok(()) +} + +pub fn set_sched_param( + _thread: &Pthread, + _policy: c_int, + _param: &sched_param, +) -> Result<(), Errno> { + // TODO + Ok(()) +} +pub fn set_sched_priority(_thread: &Pthread, _prio: c_int) -> Result<(), Errno> { + // TODO + Ok(()) +} +pub fn set_cancel_state(state: c_int) -> Result { + let this_thread = current_thread().expect("current thread not present"); + + let was_cancelable = match state { + header::PTHREAD_CANCEL_ENABLE => { + let old = this_thread + .has_enabled_cancelation + .swap(true, Ordering::Release); + + if this_thread.has_queued_cancelation.load(Ordering::Acquire) { + unsafe { + cancel_current_thread(); + } + } + old + } + header::PTHREAD_CANCEL_DISABLE => this_thread + .has_enabled_cancelation + .swap(false, Ordering::Release), + + _ => return Err(Errno(EINVAL)), + }; + + Ok(match was_cancelable { + true => header::PTHREAD_CANCEL_ENABLE, + false => header::PTHREAD_CANCEL_DISABLE, + }) +} +pub fn set_cancel_type(ty: c_int) -> Result { + let this_thread = current_thread().expect("current thread not present"); + + // TODO + match ty { + header::PTHREAD_CANCEL_DEFERRED => (), + header::PTHREAD_CANCEL_ASYNCHRONOUS => (), + + _ => return Err(Errno(EINVAL)), + } + Ok(header::PTHREAD_CANCEL_DEFERRED) +} +pub fn get_cpu_clkid(thread: &Pthread) -> Result { + // TODO + Err(Errno(ENOENT)) +} +pub fn get_sched_param(thread: &Pthread) -> Result<(clockid_t, sched_param), Errno> { + todo!() +} + +// TODO: Hash map? +// TODO: RwLock to improve perf? +static OS_TID_TO_PTHREAD: Mutex>> = + Mutex::new(BTreeMap::new()); + +#[derive(Clone, Copy)] +struct ForceSendSync(T); +unsafe impl Send for ForceSendSync {} +unsafe impl Sync for ForceSendSync {} + +/*pub(crate) fn current_thread_index() -> u32 { + current_thread().expect("current thread not present").index +}*/ + +#[derive(Clone, Copy, Default, Debug)] +pub enum Pshared { + #[default] + Private, + + Shared, +} +impl Pshared { + pub const fn from_raw(raw: c_int) -> Option { + Some(match raw { + header::PTHREAD_PROCESS_PRIVATE => Self::Private, + header::PTHREAD_PROCESS_SHARED => Self::Shared, + + _ => return None, + }) + } + pub const fn raw(self) -> c_int { + match self { + Self::Private => header::PTHREAD_PROCESS_PRIVATE, + Self::Shared => header::PTHREAD_PROCESS_SHARED, + } + } +} diff --git a/src/raw_cell.rs b/src/raw_cell.rs new file mode 100644 index 0000000000..998fccdca0 --- /dev/null +++ b/src/raw_cell.rs @@ -0,0 +1,50 @@ +use core::cell::UnsafeCell; + +/// Wrapper over `UnsafeCell` that can directly be used in statics, where all modifications require +/// unsafe. +#[repr(transparent)] +pub struct RawCell { + inner: UnsafeCell, +} +impl RawCell { + #[inline] + pub const fn new(t: T) -> Self { + Self { + inner: UnsafeCell::new(t), + } + } + #[inline] + pub fn as_mut_ptr(&self) -> *mut T { + self.inner.get() + } + #[inline] + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() + } + #[inline] + pub fn into_inner(self) -> T { + self.inner.into_inner() + } + #[inline] + pub unsafe fn unsafe_ref(&self) -> &T { + unsafe { &*self.inner.get() } + } + #[inline] + pub unsafe fn unsafe_set(&self, t: T) { + unsafe { *self.inner.get() = t }; + } + #[inline] + pub unsafe fn unsafe_mut(&self) -> &mut T { + unsafe { &mut *self.inner.get() } + } +} + +// SAFETY: Sync requires that no safe interface be allowed to act on &self in a way that is +// undefined behavior when accessed concurrently. The interface above only allows get, set, and +// as_mut_ptr, where the former two that access memory are unsafe anyway. +unsafe impl Sync for RawCell {} + +const _: () = { + // Check that RawCell works for non-Sync types. + static X: RawCell<*mut ()> = RawCell::new(core::ptr::null_mut()); +}; diff --git a/src/start.rs b/src/start.rs new file mode 100644 index 0000000000..63d404607b --- /dev/null +++ b/src/start.rs @@ -0,0 +1,259 @@ +//! Startup code. + +use alloc::{boxed::Box, vec::Vec}; +use core::{intrinsics, ptr}; + +#[cfg(target_os = "redox")] +use generic_rt::ExpectTlsFree; + +use crate::{ + ALLOCATOR, + header::{libgen, stdio, stdlib}, + ld_so::{self, linker::Linker}, + platform::{self, Pal, Sys, get_auxvs, types::*}, + sync::mutex::Mutex, +}; + +#[repr(C)] +pub struct Stack { + pub argc: isize, + pub argv0: *const c_char, +} + +impl Stack { + pub fn argv(&self) -> *const *const c_char { + ptr::from_ref(&self.argv0) + } + + pub fn envp(&self) -> *const *const c_char { + unsafe { self.argv().offset(self.argc + 1) } + } + + pub fn auxv(&self) -> *const (usize, usize) { + unsafe { + let mut envp = self.envp(); + while !(*envp).is_null() { + envp = envp.add(1); + } + envp.add(1).cast::<(usize, usize)>() + } + } +} + +unsafe fn copy_string_array(array: *const *const c_char, len: usize) -> Vec<*mut c_char> { + use crate::header::string::strlen; + + let mut vec = Vec::with_capacity(len + 1); + let mut lengths = Vec::with_capacity(len); + let mut size = 0; + for i in 0..len { + let item = unsafe { *array.add(i) }; + lengths.push(unsafe { strlen(item) } + 1); + size += lengths[i]; + } + + // Programs unfortunately rely on the strings being contiguous in memory. For example: + // https://github.com/libuv/libuv/blob/12d0dd48e3c6baf1e2f0d9f85f11f0ef58285d6f/src/unix/proctitle.c#L87 + let mut offset = 0; + let buf = unsafe { platform::alloc(size).cast::() }; + + for i in 0..len { + let dest_buf = unsafe { buf.add(offset) }; + let item = unsafe { *array.add(i) }; + let len = lengths[i]; + + unsafe { + ptr::copy_nonoverlapping(item, dest_buf, len); + } + + vec.push(dest_buf); + offset += len; + } + vec.push(ptr::null_mut()); + vec +} + +// Since Redox and Linux are so similar, it is easy to accidentally run a binary from one on the +// other. This will test that the current system is compatible with the current binary +#[unsafe(no_mangle)] +pub unsafe fn relibc_verify_host() { + if !Sys::verify() { + intrinsics::abort(); + } +} +#[unsafe(link_section = ".init_array")] +#[used] +static INIT_ARRAY: [extern "C" fn(); 1] = [init_array]; + +static mut INIT_COMPLETE: bool = false; + +#[used] +#[unsafe(no_mangle)] +static mut __relibc_init_environ: *mut *mut c_char = ptr::null_mut(); + +fn alloc_init() { + unsafe { + if INIT_COMPLETE { + return; + } + } + unsafe { + if let Some(tcb) = ld_so::tcb::Tcb::current() + && !tcb.mspace.is_null() + { + ALLOCATOR.set(tcb.mspace); + } + } +} + +extern "C" fn init_array() { + // The thing is that we cannot guarantee if + // init_array runs first or if relibc_start runs first + // Still whoever gets to run first must initialize rust + // memory allocator before doing anything else. + + unsafe { + if INIT_COMPLETE { + return; + } + } + + alloc_init(); + io_init(); + + unsafe { + if platform::environ.is_null() { + platform::environ = __relibc_init_environ; + } + } + + unsafe { + crate::pthread::init(); + INIT_COMPLETE = true + } +} + +fn io_init() { + unsafe { + // Initialize stdin/stdout/stderr. + // TODO: const fn initialization of FILE + stdio::stdin = stdio::default_stdin().get(); + stdio::stdout = stdio::default_stdout().get(); + stdio::stderr = stdio::default_stderr().get(); + } +} + +#[inline(never)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn relibc_start_v1( + sp: &'static Stack, + main: unsafe extern "C" fn( + argc: isize, + argv: *mut *mut c_char, + envp: *mut *mut c_char, + ) -> c_int, +) -> ! { + unsafe extern "C" { + static __preinit_array_start: extern "C" fn(); + static __preinit_array_end: extern "C" fn(); + static __init_array_start: extern "C" fn(); + static __init_array_end: extern "C" fn(); + } + + // Ensure correct host system before executing more system calls + unsafe { relibc_verify_host() }; + + #[cfg(target_os = "redox")] + let thr_fd = redox_rt::proc::FdGuard::new( + unsafe { + crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD) + } + .expect_notls("no thread fd present"), + ) + .to_upper() + .expect_notls("failed to move thread fd to upper table"); + + // Initialize TLS, if necessary + unsafe { + ld_so::init( + sp, + #[cfg(target_os = "redox")] + thr_fd, + ) + }; + + // Set up the right allocator... + // if any memory rust based memory allocation happen before this step .. we are doomed. + alloc_init(); + + if let Some(tcb) = unsafe { ld_so::tcb::Tcb::current() } { + // Update TCB mspace + if tcb.mspace.is_null() { + tcb.mspace = ALLOCATOR.get(); + } + + // Set linker pointer if necessary + if tcb.linker_ptr.is_null() { + //TODO: get ld path + let linker = Linker::new(ld_so::linker::Config::default()); + //TODO: load root object + tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker))); + } + #[cfg(target_os = "redox")] + redox_rt::signal::setup_sighandler(&tcb.os_specific, true); + } + + // Set up argc and argv + let argc = sp.argc; + let argv = sp.argv(); + unsafe { platform::inner_argv.unsafe_set(copy_string_array(argv, argc as usize)) }; + unsafe { platform::argv = platform::inner_argv.unsafe_mut().as_mut_ptr() }; + // Special code for program_invocation_name and program_invocation_short_name + if let Some(arg) = unsafe { platform::inner_argv.unsafe_ref() }.first() { + unsafe { platform::program_invocation_name = *arg }; + unsafe { platform::program_invocation_short_name = libgen::basename(*arg) }; + } + // We check for NULL here since ld.so might already have initialized it for us, and we don't + // want to overwrite it if constructors in .init_array of dependency libraries have called + // setenv. + if unsafe { platform::environ }.is_null() { + // Set up envp + let envp = sp.envp(); + let mut len = 0; + while !(unsafe { *envp.add(len) }).is_null() { + len += 1; + } + unsafe { platform::OUR_ENVIRON.unsafe_set(copy_string_array(envp, len)) }; + unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() }; + } + + let auxvs = unsafe { get_auxvs(sp.auxv().cast()) }; + unsafe { crate::platform::init(auxvs) }; + init_array(); + unsafe { crate::platform::logger::init() }; + + // Run preinit array + { + let mut f = unsafe { &__preinit_array_start } as *const _; + #[allow(clippy::op_ref)] + while f < &raw const __preinit_array_end { + (unsafe { *f })(); + f = unsafe { f.offset(1) }; + } + } + + // Run init array + { + let mut f = unsafe { &__init_array_start } as *const _; + #[allow(clippy::op_ref)] + while f < &raw const __init_array_end { + (unsafe { *f })(); + f = unsafe { f.offset(1) }; + } + } + + // not argv or envp, because programs like bash try to modify this *const* pointer :| + unsafe { stdlib::exit(main(argc, platform::argv, platform::environ)) }; + + unreachable!(); +} diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs new file mode 100644 index 0000000000..6204a235ef --- /dev/null +++ b/src/sync/barrier.rs @@ -0,0 +1,85 @@ +use core::num::NonZeroU32; + +pub struct Barrier { + original_count: NonZeroU32, + // 4 + lock: crate::sync::Mutex, + // 16 + cvar: crate::header::pthread::RlctCond, + // 24 +} +#[derive(Debug)] +struct Inner { + count: u32, + // TODO: Overflows might be problematic... 64-bit? + gen_id: u32, +} + +pub enum WaitResult { + Waited, + NotifiedAll, +} + +impl Barrier { + pub fn new(count: NonZeroU32) -> Self { + Self { + original_count: count, + lock: crate::sync::Mutex::new(Inner { + count: 0, + gen_id: 0, + }), + cvar: crate::header::pthread::RlctCond::new(), + } + } + pub fn wait(&self) -> WaitResult { + let mut guard = self.lock.lock(); + let gen_id = guard.gen_id; + + guard.count += 1; + + if guard.count == self.original_count.get() { + guard.gen_id = guard.gen_id.wrapping_add(1); + guard.count = 0; + if let Ok(()) = self.cvar.broadcast() {}; // TODO handle error + + drop(guard); + + WaitResult::NotifiedAll + } else { + while guard.gen_id == gen_id { + guard = self.cvar.wait_inner_typedmutex(guard); + } + + WaitResult::Waited + } + /* + let mut guard = self.lock.lock(); + let Inner { count, gen_id } = *guard; + + let last = self.original_count.get() - 1; + + if count == last { + eprintln!("last {:?}", *guard); + guard.gen_id = guard.gen_id.wrapping_add(1); + guard.count = 0; + + drop(guard); + + self.cvar.broadcast(); + + WaitResult::NotifiedAll + } else { + guard.count += 1; + + while guard.count != last && guard.gen_id == gen_id { + eprintln!("before {:?}", *guard); + guard = self.cvar.wait_inner_typedmutex(guard); + eprintln!("after {:?}", *guard); + } + + WaitResult::Waited + } + */ + } +} +static LOCK: crate::sync::Mutex<()> = crate::sync::Mutex::new(()); diff --git a/src/sync/barrier.rs.unused b/src/sync/barrier.rs.unused new file mode 100644 index 0000000000..c07c9d3ca3 --- /dev/null +++ b/src/sync/barrier.rs.unused @@ -0,0 +1,91 @@ +use core::cmp; +use core::num::NonZeroU32; +use core::sync::atomic::{AtomicU32 as AtomicUint, Ordering}; + +pub struct Barrier { + waited_count: AtomicUint, + notified_count: AtomicUint, + cycles_count: AtomicUint, + original_count: NonZeroU32, +} + +pub enum WaitResult { + Waited, + NotifiedAll, +} + +impl Barrier { + pub fn new(count: NonZeroU32) -> Self { + Self { + waited_count: AtomicUint::new(0), + notified_count: AtomicUint::new(0), + cycles_count: AtomicUint::new(0), + original_count: count, + } + } + pub fn wait(&self) -> WaitResult { + // The barrier wait operation can be divided into two parts: (1) incrementing the wait count where + // N-1 waiters wait and one notifies the rest, and (2) notifying all threads that have been + // waiting. + let original_count = self.original_count.get(); + let mut new = self.waited_count.fetch_add(1, Ordering::Acquire) + 1; + let original_cycle_count = self.cycles_count.load(Ordering::Acquire); + + loop { + let result = match Ord::cmp(&new, &original_count) { + cmp::Ordering::Less => { + // new < original_count, i.e. we were one of the threads that incremented the + // counter, and will return without SERIAL_THREAD later, but need to continue + // waiting for the last waiter to notify the others. + + loop { + let count = self.waited_count.load(Ordering::Acquire); + + if count >= original_count { break } + + let _ = crate::sync::futex_wait(&self.waited_count, count, None); + } + + WaitResult::Waited + } + cmp::Ordering::Equal => { + // new == original_count, i.e. we were the one thread doing the last increment, and we + // will be responsible for waking up all other waiters. + + crate::sync::futex_wake(&self.waited_count, original_count as i32 - 1); + + WaitResult::NotifiedAll + } + cmp::Ordering::Greater => { + let mut next_cycle_count; + + loop { + next_cycle_count = self.cycles_count.load(Ordering::Acquire); + + if next_cycle_count != original_cycle_count { + break; + } + + crate::sync::futex_wait(&self.cycles_count, next_cycle_count, None); + } + let difference = next_cycle_count.wrapping_sub(original_cycle_count); + + new = new.saturating_sub(difference * original_cycle_count); + continue; + } + }; + + if self.notified_count.fetch_add(1, Ordering::AcqRel) + 1 == original_count { + self.notified_count.store(0, Ordering::Relaxed); + // Cycle count can be incremented nonatomically here, as this branch can only be + // reached once until waited_count is decremented again. + self.cycles_count.store(self.cycles_count.load(Ordering::Acquire).wrapping_add(1), Ordering::Release); + + let _ = self.waited_count.fetch_sub(original_count, Ordering::Relaxed); + + let _ = crate::sync::futex_wake(&self.cycles_count, i32::MAX); + } + return result; + } + } +} diff --git a/src/sync/cond.rs b/src/sync/cond.rs new file mode 100644 index 0000000000..19f9868a19 --- /dev/null +++ b/src/sync/cond.rs @@ -0,0 +1,134 @@ +// Used design from https://www.remlab.net/op/futex-condvar.shtml + +use crate::{ + error::Errno, + header::{ + bits_timespec::timespec, + errno::{EINVAL, ETIMEDOUT}, + pthread::*, + time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic}, + }, + platform::types::clockid_t, +}; + +use core::sync::atomic::{AtomicU32 as AtomicUint, Ordering}; + +#[derive(Clone, Copy)] +pub struct CondAttr { + pub clock: clockid_t, + pub pshared: i32, +} + +impl Default for CondAttr { + fn default() -> Self { + Self { + // defaults according to POSIX + clock: CLOCK_REALTIME, // for timedwait + pshared: PTHREAD_PROCESS_PRIVATE, // TODO + } + } +} + +pub struct Cond { + cur: AtomicUint, + prev: AtomicUint, +} + +type Result = core::result::Result; + +impl Default for Cond { + fn default() -> Self { + Self::new() + } +} + +impl Cond { + pub fn new() -> Self { + Self { + cur: AtomicUint::new(0), + prev: AtomicUint::new(0), + } + } + fn wake(&self, count: i32) -> Result<(), Errno> { + // This is formally correct as long as we don't have more than u32::MAX threads. + let prev = self.prev.load(Ordering::Relaxed); + self.cur.store(prev.wrapping_add(1), Ordering::Relaxed); + + crate::sync::futex_wake(&self.cur, count); + Ok(()) + } + pub fn broadcast(&self) -> Result<(), Errno> { + self.wake(i32::MAX) + } + pub fn signal(&self) -> Result<(), Errno> { + self.broadcast() + //self.wake(1) + } + pub fn clockwait( + &self, + mutex: &RlctMutex, + timeout: ×pec, + clock_id: clockid_t, + ) -> Result<(), Errno> { + let relative = match clock_id { + // FUTEX expect monotonic clock + CLOCK_MONOTONIC => timeout.clone(), + CLOCK_REALTIME => timespec_realtime_to_monotonic(timeout.clone())?, + _ => return Err(Errno(EINVAL)), + }; + + self.wait_inner(mutex, Some(&relative)) + } + pub fn timedwait(&self, mutex: &RlctMutex, timeout: ×pec) -> Result<(), Errno> { + // TODO: The clock can be other than CLOCK_REALTIME depends on CondAttr + self.clockwait(mutex, timeout, CLOCK_REALTIME) + } + fn wait_inner(&self, mutex: &RlctMutex, timeout: Option<×pec>) -> Result<(), Errno> { + self.wait_inner_generic(|| mutex.unlock(), || mutex.lock(), timeout) + } + pub fn wait_inner_typedmutex<'lock, T>( + &self, + guard: crate::sync::MutexGuard<'lock, T>, + ) -> crate::sync::MutexGuard<'lock, T> { + let mut newguard = None; + let lock = guard.mutex; + self.wait_inner_generic( + move || { + drop(guard); + Ok(()) + }, + || { + newguard = Some(lock.lock()); + Ok(()) + }, + None, + ) + .unwrap(); + newguard.unwrap() + } + // TODO: FUTEX_REQUEUE + fn wait_inner_generic( + &self, + unlock: impl FnOnce() -> Result<()>, + lock: impl FnOnce() -> Result<()>, + deadline: Option<×pec>, + ) -> Result<(), Errno> { + // TODO: Error checking for certain types (i.e. robust and errorcheck) of mutexes, e.g. if the + // mutex is not locked. + let current = self.cur.load(Ordering::Relaxed); + self.prev.store(current, Ordering::Relaxed); + + unlock()?; + let futex_r = crate::sync::futex_wait(&self.cur, current, deadline); + lock()?; + + match futex_r { + super::FutexWaitResult::Waited => Ok(()), + super::FutexWaitResult::Stale => Ok(()), + super::FutexWaitResult::TimedOut => Err(Errno(ETIMEDOUT)), + } + } + pub fn wait(&self, mutex: &RlctMutex) -> Result<(), Errno> { + self.wait_inner(mutex, None) + } +} diff --git a/src/sync/mod.rs b/src/sync/mod.rs new file mode 100644 index 0000000000..9edda90ece --- /dev/null +++ b/src/sync/mod.rs @@ -0,0 +1,247 @@ +//! Synchronization primitives. + +pub mod barrier; +pub mod cond; +// TODO: Merge with pthread_mutex +pub mod mutex; + +pub mod once; +pub mod pthread_mutex; +pub mod rwlock; +pub mod semaphore; +pub mod waitval; + +pub use self::{ + mutex::{Mutex, MutexGuard}, + once::Once, + semaphore::Semaphore, +}; + +use crate::{ + error::Errno, + header::{ + bits_timespec::timespec, + errno::{EAGAIN, EINTR, ETIMEDOUT}, + }, + out::Out, + platform::{Pal, Sys, types::c_int}, +}; +use core::{ + hint, + mem::MaybeUninit, + ops::Deref, + ptr, + sync::atomic::{AtomicI32, AtomicI32 as AtomicInt, AtomicU32}, +}; + +const FUTEX_WAIT: c_int = 0; +const FUTEX_WAKE: c_int = 1; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum AttemptStatus { + Desired, + Waiting, + Other, +} + +pub trait FutexTy { + fn conv(self) -> u32; +} +pub trait FutexAtomicTy { + type Ty: FutexTy; + + fn ptr(&self) -> *mut Self::Ty; +} +impl FutexTy for u32 { + fn conv(self) -> u32 { + self + } +} +impl FutexTy for i32 { + fn conv(self) -> u32 { + self as u32 + } +} +impl FutexAtomicTy for AtomicU32 { + type Ty = u32; + + fn ptr(&self) -> *mut u32 { + // TODO: Change when Redox's toolchain is updated. This is not about targets, but compiler + // versions! + /* + + #[cfg(target_os = "redox")] + return AtomicU32::as_ptr(self); + + #[cfg(target_os = "linux")] + return AtomicU32::as_mut_ptr(self); + + */ + + // AtomicU32::as_mut_ptr internally calls UnsafeCell::get, which itself simply does (&self + // as *const Self as *mut Self). + ptr::from_ref::(self) as *mut u32 + } +} +impl FutexAtomicTy for AtomicI32 { + type Ty = i32; + + fn ptr(&self) -> *mut i32 { + // TODO + /*#[cfg(target_os = "redox")] + return AtomicI32::as_ptr(self); + + #[cfg(target_os = "linux")] + return AtomicI32::as_mut_ptr(self);*/ + + ptr::from_ref::(self) as *mut i32 + } +} + +pub unsafe fn futex_wake_ptr(ptr: *mut impl FutexTy, n: i32) -> usize { + // TODO: unwrap_unchecked? + unsafe { Sys::futex_wake(ptr.cast(), n as u32) }.unwrap() as usize +} +pub unsafe fn futex_wait_ptr( + ptr: *mut T, + value: T, + deadline_opt: Option<×pec>, +) -> FutexWaitResult { + match unsafe { Sys::futex_wait(ptr.cast(), value.conv(), deadline_opt) } { + Ok(()) | Err(Errno(EINTR)) => FutexWaitResult::Waited, + Err(Errno(EAGAIN)) => FutexWaitResult::Stale, + Err(Errno(ETIMEDOUT)) if deadline_opt.is_some() => FutexWaitResult::TimedOut, + Err(err) => { + todo_error!(0, err, "futex failed"); + FutexWaitResult::Waited + } + } +} +pub fn futex_wake(atomic: &impl FutexAtomicTy, n: i32) -> usize { + unsafe { futex_wake_ptr(atomic.ptr(), n) } +} +pub fn futex_wait( + atomic: &T, + value: T::Ty, + deadline_opt: Option<×pec>, +) -> FutexWaitResult { + unsafe { futex_wait_ptr(atomic.ptr(), value, deadline_opt) } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum FutexWaitResult { + Waited, // possibly spurious + Stale, // outdated value + TimedOut, +} + +pub fn rttime() -> timespec { + unsafe { + let mut time = MaybeUninit::uninit(); + + if let Ok(()) = Sys::clock_gettime( + crate::header::time::CLOCK_REALTIME, + Out::from_uninit_mut(&mut time), + ) {}; // TODO handle error + + time.assume_init() + } +} + +pub fn wait_until_generic(word: &AtomicInt, attempt: F1, mark_long: F2, long: c_int) +where + F1: Fn(&AtomicInt) -> AttemptStatus, + F2: Fn(&AtomicInt) -> AttemptStatus, +{ + // First, try spinning for really short durations + for _ in 0..999 { + hint::spin_loop(); + if attempt(word) == AttemptStatus::Desired { + return; + } + } + + // One last attempt, to initiate "previous" + let mut previous = attempt(word); + + // Ok, that seems to take quite some time. Let's go into a + // longer, more patient, wait. + loop { + if previous == AttemptStatus::Desired { + return; + } + + if + // If we or somebody else already initiated a long + // wait, OR + previous == AttemptStatus::Waiting || + // Otherwise, unless our attempt to initiate a long + // wait informed us that we might be done waiting + mark_long(word) != AttemptStatus::Desired + { + futex_wait(word, long, None); + } + + previous = attempt(word); + } +} + +/// Convenient wrapper around the "futex" system call for +/// synchronization implementations +#[repr(C)] +pub(crate) struct AtomicLock { + pub(crate) atomic: AtomicInt, +} +impl AtomicLock { + pub const fn new(value: c_int) -> Self { + Self { + atomic: AtomicInt::new(value), + } + } + pub fn notify_one(&self) { + futex_wake(&self.atomic, 1); + } + pub fn notify_all(&self) { + futex_wake(&self.atomic, i32::MAX); + } + pub fn wait_if(&self, value: c_int, timeout_opt: Option<×pec>) { + self.wait_if_raw(value, timeout_opt); + } + pub fn wait_if_raw(&self, value: c_int, timeout_opt: Option<×pec>) -> FutexWaitResult { + futex_wait(&self.atomic, value, timeout_opt) + } + + /// A general way to efficiently wait for what might be a long time, using two closures: + /// + /// - `attempt` = Attempt to modify the atomic value to any + /// desired state. + /// - `mark_long` = Attempt to modify the atomic value to sign + /// that it want's to get notified when waiting is done. + /// + /// Both of these closures are allowed to spuriously give a + /// non-success return value, they are used only as optimization + /// hints. However, what counts as a "desired value" may differ + /// per closure. Therefore, `mark_long` can notify a value as + /// "desired" in order to get `attempt` retried immediately. + /// + /// The `long` parameter is the only one which actually cares + /// about the specific value of your atomics. This is needed + /// because it needs to pass this to the futex system call in + /// order to avoid race conditions where the atomic could be + /// modified to the desired value before the call is complete and + /// we receive the wakeup notification. + pub fn wait_until(&self, attempt: F1, mark_long: F2, long: c_int) + where + F1: Fn(&AtomicInt) -> AttemptStatus, + F2: Fn(&AtomicInt) -> AttemptStatus, + { + wait_until_generic(&self.atomic, attempt, mark_long, long) + } +} +impl Deref for AtomicLock { + type Target = AtomicInt; + + fn deref(&self) -> &Self::Target { + &self.atomic + } +} diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs new file mode 100644 index 0000000000..fc8cda2892 --- /dev/null +++ b/src/sync/mutex.rs @@ -0,0 +1,142 @@ +use super::{AtomicLock, AttemptStatus}; +use crate::platform::types::c_int; +use core::{ + cell::UnsafeCell, + ops::{Deref, DerefMut}, + sync::atomic::{AtomicI32 as AtomicInt, Ordering}, +}; + +pub(crate) const UNLOCKED: c_int = 0; +pub(crate) const LOCKED: c_int = 1; +pub(crate) const WAITING: c_int = 2; + +pub struct Mutex { + pub(crate) lock: AtomicLock, + content: UnsafeCell, +} +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +pub(crate) unsafe fn manual_try_lock_generic(word: &AtomicInt) -> bool { + word.compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed) + .is_ok() +} +pub(crate) unsafe fn manual_lock_generic(word: &AtomicInt) { + crate::sync::wait_until_generic( + word, + |lock| { + lock.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed) + .map(|_| AttemptStatus::Desired) + .unwrap_or_else(|e| match e { + WAITING => AttemptStatus::Waiting, + _ => AttemptStatus::Other, + }) + }, + |lock| match lock + // TODO: Ordering + .compare_exchange_weak(LOCKED, WAITING, Ordering::SeqCst, Ordering::SeqCst) + .unwrap_or_else(|e| e) + { + UNLOCKED => AttemptStatus::Desired, + WAITING => AttemptStatus::Waiting, + _ => AttemptStatus::Other, + }, + WAITING, + ); +} +pub(crate) unsafe fn manual_unlock_generic(word: &AtomicInt) { + if word.swap(UNLOCKED, Ordering::Release) == WAITING { + crate::sync::futex_wake(word, i32::MAX); + } +} + +impl Mutex { + /// Create a new mutex + pub const fn new(content: T) -> Self { + Self { + lock: AtomicLock::new(UNLOCKED), + content: UnsafeCell::new(content), + } + } + /// Create a new mutex that is already locked. This is a more + /// efficient way to do the following: + /// ```rust + /// let mut mutex = Mutex::new(()); + /// mutex.manual_lock(); + /// ``` + pub unsafe fn locked(content: T) -> Self { + Self { + lock: AtomicLock::new(LOCKED), + content: UnsafeCell::new(content), + } + } + + /// Tries to lock the mutex, fails if it's already locked. Manual means + /// it's up to you to unlock it after mutex. Returns the last atomic value + /// on failure. You should probably not worry about this, it's used for + /// internal optimizations. + pub unsafe fn manual_try_lock(&self) -> Result<&mut T, c_int> { + if unsafe { manual_try_lock_generic(&self.lock) } { + Ok(unsafe { &mut *self.content.get() }) + } else { + Err(0) + } + } + /// Lock the mutex, returning the inner content. After doing this, it's + /// your responsibility to unlock it after usage. Mostly useful for FFI: + /// Prefer normal .lock() where possible. + pub unsafe fn manual_lock(&self) -> &mut T { + unsafe { manual_lock_generic(&self.lock) }; + unsafe { &mut *self.content.get() } + } + /// Unlock the mutex, if it's locked. + pub unsafe fn manual_unlock(&self) { + unsafe { manual_unlock_generic(&self.lock) } + } + pub fn as_ptr(&self) -> *mut T { + self.content.get() + } + + /// Tries to lock the mutex and returns a guard that automatically unlocks + /// the mutex when it falls out of scope. + pub fn try_lock(&self) -> Option> { + unsafe { + self.manual_try_lock().ok().map(|content| MutexGuard { + mutex: self, + content, + }) + } + } + /// Locks the mutex and returns a guard that automatically unlocks the + /// mutex when it falls out of scope. + pub fn lock(&self) -> MutexGuard<'_, T> { + MutexGuard { + mutex: self, + content: unsafe { self.manual_lock() }, + } + } +} + +pub struct MutexGuard<'a, T: 'a> { + pub(crate) mutex: &'a Mutex, + content: &'a mut T, +} +impl<'a, T> Deref for MutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.content + } +} +impl<'a, T> DerefMut for MutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.content + } +} +impl<'a, T> Drop for MutexGuard<'a, T> { + fn drop(&mut self) { + unsafe { + self.mutex.manual_unlock(); + } + } +} diff --git a/src/sync/once.rs b/src/sync/once.rs new file mode 100644 index 0000000000..293ab84cb9 --- /dev/null +++ b/src/sync/once.rs @@ -0,0 +1,124 @@ +use super::AttemptStatus; +use crate::platform::types::*; +use core::{ + cell::UnsafeCell, + mem::MaybeUninit, + sync::atomic::{AtomicI32 as AtomicInt, Ordering}, +}; + +const UNINITIALIZED: c_int = 0; +const INITIALIZING: c_int = 1; +const WAITING: c_int = 2; +const INITIALIZED: c_int = 3; + +pub struct Once { + status: AtomicInt, + data: UnsafeCell>, +} + +// SAFETY: +// +// Sending a Once is the same as sending a (wrapped) T. +unsafe impl Send for Once {} + +// SAFETY: +// +// For Once to be shared between threads without being unsound, only call_once needs to be safe, at +// the moment. +// +// Send requirement: the thread that gets to run the initializer function, will put a T in the cell +// which can then be accessed by other threads, thus T needs to be send. +// +// Sync requirement: after call_once has been called, it returns the value via &T, which naturally +// forces T to be Sync. +unsafe impl Sync for Once {} + +impl Once { + pub const fn new() -> Self { + Self { + status: AtomicInt::new(UNINITIALIZED), + data: UnsafeCell::new(MaybeUninit::uninit()), + } + } + pub fn call_once(&self, constructor: impl FnOnce() -> T) -> &T { + match self.status.compare_exchange( + UNINITIALIZED, + INITIALIZING, + // SAFETY: Success ordering: if the CAS succeeds, we technically need no + // synchronization besides the Release store to INITIALIZED, and Acquire here forbids + // possible loads in f() to be re-ordered before this CAS. One could argue whether or + // not that is reasonable, but the main point is that the success ordering must be at + // least as strong as the failure ordering. + Ordering::Acquire, + // SAFETY: Failure ordering: if the CAS fails, and status was INITIALIZING | WAITING, + // then Relaxed is sufficient, as it will have to be Acquire-loaded again later. If + // INITIALIZED is encountered however, it will nonatomically read the value in the + // Cell, which necessitates Acquire. + Ordering::Acquire, // TODO: On archs where this matters, use Relaxed and core::sync::atomic::fence? + ) { + Ok(_must_be_uninit) => { + // We now have exclusive access to the cell, let's initiate things! + unsafe { self.data.get().cast::().write(constructor()) }; + + // Mark the data as initialized + if self.status.swap(INITIALIZED, Ordering::Release) == WAITING { + // At least one thread is waiting on this to finish + crate::sync::futex_wake(&self.status, i32::MAX); + } + } + Err(INITIALIZING) | Err(WAITING) => crate::sync::wait_until_generic( + &self.status, + // SAFETY: An Acquire load is necessary for the nonatomic store by the thread + // running the constructor, to become visible. + |status| match status.load(Ordering::Acquire) { + WAITING => AttemptStatus::Waiting, + INITIALIZED => AttemptStatus::Desired, + _ => AttemptStatus::Other, + }, + // SAFETY: Double-Acquire is necessary here as well, because if the CAS fails and + // it was INITIALIZED, the nonatomic write by the constructor thread, must be + // visible. + |status| match status + .compare_exchange_weak( + INITIALIZING, + WAITING, + Ordering::Acquire, + Ordering::Acquire, + ) + .unwrap_or_else(|e| e) + { + WAITING => AttemptStatus::Waiting, + INITIALIZED => AttemptStatus::Desired, + _ => AttemptStatus::Other, + }, + WAITING, + ), + Err(INITIALIZED) => (), + + // TODO: Only for debug builds? + Err(_) => unreachable!("invalid state for Once"), + } + + // At this point the data must be initialized! + unsafe { (&*self.data.get()).assume_init_ref() } + } +} +impl Default for Once { + fn default() -> Self { + Self::new() + } +} +// TODO: Drop doesn't work well in const fn, instead use a wrapper for relibc Rust code that adds +// Drop, and don't use that wrapper when writing the header file impls. +/* +impl Drop for Once { + fn drop(&mut self) { + unsafe { + if *self.status.get_mut() == INITIALIZED { + // SAFETY: It must be initialized, because of the above condition. + self.data.get_mut().assume_init_drop(); + } + } + } +} +*/ diff --git a/src/sync/pthread_mutex.rs b/src/sync/pthread_mutex.rs new file mode 100644 index 0000000000..29bad634ae --- /dev/null +++ b/src/sync/pthread_mutex.rs @@ -0,0 +1,259 @@ +use core::{ + cell::Cell, + sync::atomic::{AtomicU32 as AtomicUint, Ordering}, +}; + +use crate::{ + error::Errno, + header::{bits_timespec::timespec, errno::*, pthread::*}, +}; + +use crate::platform::{Pal, Sys, types::c_int}; + +use super::FutexWaitResult; + +pub struct RlctMutex { + // Actual locking word. + inner: AtomicUint, + recursive_count: AtomicUint, + + ty: Ty, + robust: bool, +} + +const STATE_UNLOCKED: u32 = 0; +const WAITING_BIT: u32 = 1 << 31; +const INDEX_MASK: u32 = !WAITING_BIT; + +// TODO: Lower limit is probably better. +const RECURSIVE_COUNT_MAX_INCLUSIVE: u32 = u32::MAX; +// TODO: How many spins should we do before it becomes more time-economical to enter kernel mode +// via futexes? +const SPIN_COUNT: usize = 0; + +impl RlctMutex { + pub(crate) fn new(attr: &RlctMutexAttr) -> Result { + let RlctMutexAttr { + prioceiling, + protocol, + pshared: _, + robust, + ty, + } = *attr; + + Ok(Self { + inner: AtomicUint::new(STATE_UNLOCKED), + recursive_count: AtomicUint::new(0), + robust: match robust { + PTHREAD_MUTEX_STALLED => false, + PTHREAD_MUTEX_ROBUST => true, + + _ => return Err(Errno(EINVAL)), + }, + ty: match ty { + PTHREAD_MUTEX_DEFAULT => Ty::Def, + PTHREAD_MUTEX_ERRORCHECK => Ty::Errck, + PTHREAD_MUTEX_RECURSIVE => Ty::Recursive, + PTHREAD_MUTEX_NORMAL => Ty::Normal, + + _ => return Err(Errno(EINVAL)), + }, + }) + } + pub fn prioceiling(&self) -> Result { + todo_skip!(0, "pthread_getprioceiling: not implemented"); + Ok(0) + } + pub fn replace_prioceiling(&self, _: c_int) -> Result { + todo_skip!(0, "pthread_setprioceiling: not implemented"); + Ok(0) + } + pub fn make_consistent(&self) -> Result<(), Errno> { + todo_skip!(0, "pthread robust mutexes: not implemented"); + Ok(()) + } + fn lock_inner(&self, deadline: Option<×pec>) -> Result<(), Errno> { + let this_thread = os_tid_invalid_after_fork(); + + //let mut spins_left = SPIN_COUNT; + + loop { + let result = self.inner.compare_exchange_weak( + STATE_UNLOCKED, + this_thread, + Ordering::Acquire, + Ordering::Relaxed, + ); + + match result { + // CAS succeeded + Ok(_) => { + if self.ty == Ty::Recursive { + self.increment_recursive_count()?; + } + return Ok(()); + } + // CAS failed, but the mutex was recursive and we already own the lock. + Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Recursive => { + self.increment_recursive_count()?; + return Ok(()); + } + // CAS failed, but the mutex was error-checking and we already own the lock. + Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Errck => { + return Err(Errno(EAGAIN)); + } + // CAS spuriously failed, simply retry the CAS. TODO: Use core::hint::spin_loop()? + Err(thread) if thread & INDEX_MASK == 0 => { + continue; + } + // CAS failed because some other thread owned the lock. We must now wait. + Err(thread) => { + /*if spins_left > 0 { + // TODO: Faster to spin trying to load the flag, compared to CAS? + spins_left -= 1; + core::hint::spin_loop(); + continue; + } + + spins_left = SPIN_COUNT; + + let inner = self.inner.fetch_or(WAITING_BIT, Ordering::Relaxed); + + if inner == STATE_UNLOCKED { + continue; + }*/ + + // If the mutex is not robust, simply futex_wait until unblocked. + //crate::sync::futex_wait(&self.inner, inner | WAITING_BIT, None); + if crate::sync::futex_wait(&self.inner, thread, deadline) + == FutexWaitResult::TimedOut + { + return Err(Errno(ETIMEDOUT)); + } + } + } + } + } + pub fn lock(&self) -> Result<(), Errno> { + self.lock_inner(None) + } + pub fn lock_with_timeout(&self, deadline: ×pec) -> Result<(), Errno> { + self.lock_inner(Some(deadline)) + } + fn increment_recursive_count(&self) -> Result<(), Errno> { + // We don't have to worry about asynchronous signals here, since pthread_mutex_trylock + // is not async-signal-safe. + // + // TODO: Maybe just use Cell? Send/Sync doesn't matter much anyway, and will be + // protected by the lock itself anyway. + + let prev_recursive_count = self.recursive_count.load(Ordering::Relaxed); + + if prev_recursive_count == RECURSIVE_COUNT_MAX_INCLUSIVE { + return Err(Errno(EAGAIN)); + } + + self.recursive_count + .store(prev_recursive_count + 1, Ordering::Relaxed); + + Ok(()) + } + pub fn try_lock(&self) -> Result<(), Errno> { + let this_thread = os_tid_invalid_after_fork(); + + // TODO: If recursive, omitting CAS may be faster if it is already owned by this thread. + let result = self.inner.compare_exchange( + STATE_UNLOCKED, + this_thread, + Ordering::Acquire, + Ordering::Relaxed, + ); + + if self.ty == Ty::Recursive { + match result { + Err(index) if index & INDEX_MASK != this_thread => return Err(Errno(EBUSY)), + _ => (), + } + + self.increment_recursive_count()?; + + return Ok(()); + } + + match result { + Ok(_) => Ok(()), + Err(index) if index & INDEX_MASK == this_thread && self.ty == Ty::Errck => { + Err(Errno(EDEADLK)) + } + Err(_) => Err(Errno(EBUSY)), + } + } + // Safe because we are not protecting any data. + pub fn unlock(&self) -> Result<(), Errno> { + if self.robust || matches!(self.ty, Ty::Recursive | Ty::Errck) { + if self.inner.load(Ordering::Relaxed) & INDEX_MASK != os_tid_invalid_after_fork() { + return Err(Errno(EPERM)); + } + + // TODO: Is this fence correct? + core::sync::atomic::fence(Ordering::Acquire); + } + + if self.ty == Ty::Recursive { + let next = self.recursive_count.load(Ordering::Relaxed) - 1; + self.recursive_count.store(next, Ordering::Relaxed); + + if next > 0 { + return Ok(()); + } + } + + self.inner.store(STATE_UNLOCKED, Ordering::Release); + crate::sync::futex_wake(&self.inner, i32::MAX); + /*let was_waiting = self.inner.swap(STATE_UNLOCKED, Ordering::Release) & WAITING_BIT != 0; + + if was_waiting { + let _ = crate::sync::futex_wake(&self.inner, 1); + }*/ + + Ok(()) + } +} + +#[repr(u8)] +#[derive(PartialEq)] +enum Ty { + // The only difference between PTHREAD_MUTEX_NORMAL and PTHREAD_MUTEX_DEFAULT appears to be + // that "normal" mutexes deadlock if locked multiple times on the same thread, whereas + // "default" mutexes are UB in that case. So we can treat them as being the same type. + Normal, + Def, + + Errck, + Recursive, +} + +// Children after fork can only call async-signal-safe functions until they exec. +#[thread_local] +static CACHED_OS_TID_INVALID_AFTER_FORK: Cell = Cell::new(0); + +// Assumes TIDs are unique between processes, which I only know is true for Redox. +fn os_tid_invalid_after_fork() -> u32 { + // TODO: Coordinate better if using shared == PTHREAD_PROCESS_SHARED, with up to 2^32 separate + // threads within possibly distinct processes, using the mutex. OS thread IDs on Redox are + // pointer-sized, but relibc and POSIX uses int everywhere. + + let value = CACHED_OS_TID_INVALID_AFTER_FORK.get(); + + if value == 0 { + let tid = Sys::gettid(); + + assert_ne!(tid, -1, "failed to obtain current thread ID"); + + CACHED_OS_TID_INVALID_AFTER_FORK.set(tid as u32); + + tid as u32 + } else { + value + } +} diff --git a/src/sync/reentrant_mutex.rs b/src/sync/reentrant_mutex.rs new file mode 100644 index 0000000000..43e60772d0 --- /dev/null +++ b/src/sync/reentrant_mutex.rs @@ -0,0 +1,27 @@ +use super::{AtomicLock, AttemptStatus}; + +const WAITING_BIT: u32 = 1 << 31; +const UNLOCKED: u32 = 0; +// We now have 2^32 - 1 possible thread ID values + +pub struct ReentrantMutex { + lock: AtomicLock, + content: UnsafeCell, +} +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + +impl ReentrantMutex { + pub const fn new(context: T) -> Self { + Self { + lock: AtomicLock::new(UNLOCKED), + content: UnsafeCell::new(content), + } + } +} +pub struct ReentrantMutexGuard<'a, T: 'a> { + mutex: &'a ReentrantMutex, + content: &'a T, +} +impl<'a, T> Deref for MutexGuard { +} diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs new file mode 100644 index 0000000000..c93fe05dc9 --- /dev/null +++ b/src/sync/rwlock.rs @@ -0,0 +1,318 @@ +use core::{ + cell::UnsafeCell, + fmt, ops, + sync::atomic::{AtomicU32, Ordering}, +}; + +use crate::{ + error::{Errno, Result}, + header::{ + bits_timespec::timespec, + errno::{EINVAL, ETIMEDOUT}, + time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic}, + }, + platform::types::clockid_t, + pthread::Pshared, +}; + +pub struct InnerRwLock { + state: AtomicU32, +} +// PTHREAD_RWLOCK_INITIALIZER is defined as "all zeroes". + +const WAITING_WR: u32 = 1 << (u32::BITS - 1); +const COUNT_MASK: u32 = WAITING_WR - 1; +const EXCLUSIVE: u32 = COUNT_MASK; + +// TODO: Optimize for short waits and long waits, using AtomicLock::wait_until, but still +// supporting timeouts. +// TODO: Add futex ops that use bitmasks. + +impl InnerRwLock { + pub const fn new(_pshared: Pshared) -> Self { + Self { + state: AtomicU32::new(0), + } + } + fn translate_timeout(deadline: Option<(×pec, i32)>) -> Result, Errno> { + let relative = match deadline { + // FUTEX expect monotonic clock + Some((abstime, CLOCK_MONOTONIC)) => Some(abstime.clone()), + Some((abstime, CLOCK_REALTIME)) => { + Some(timespec_realtime_to_monotonic(abstime.clone())?) + } + None => None, + _ => { + return Err(Errno(EINVAL)); + } + }; + Ok(relative) + } + pub fn acquire_write_lock( + &self, + deadline: Option<(×pec, clockid_t)>, + ) -> Result<(), Errno> { + let relative = Self::translate_timeout(deadline)?; + let mut waiting_wr = self.state.load(Ordering::Relaxed) & WAITING_WR; + + loop { + match self.state.compare_exchange_weak( + waiting_wr, + EXCLUSIVE, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => break, + Err(actual) => { + let expected = actual; + let expected = if actual & COUNT_MASK != EXCLUSIVE { + // Set the exclusive bit, but only if we're waiting for readers, to avoid + // reader starvation by overprioritizing write locks. + self.state.fetch_or(WAITING_WR, Ordering::Relaxed); + + actual | WAITING_WR + } else { + actual + }; + waiting_wr = expected & WAITING_WR; + + if actual & COUNT_MASK > 0 { + match crate::sync::futex_wait(&self.state, expected, relative.as_ref()) { + super::FutexWaitResult::TimedOut => return Err(Errno(ETIMEDOUT)), + _ => {} + } + } else { + // We must avoid blocking indefinitely in our `futex_wait()`, in this case + // where it's possible that `self.state == expected` but our futex might + // never be woken again, because it's possible that all other threads + // already did their `futex_wake()` before we would've done our + // `futex_wait()`. + } + } + } + } + + Ok(()) + } + pub fn acquire_read_lock(&self, deadline: Option<(×pec, clockid_t)>) -> Result<(), Errno> { + let relative = Self::translate_timeout(deadline)?; + while let Err(old) = self.try_acquire_read_lock() { + match crate::sync::futex_wait(&self.state, old, relative.as_ref()) { + super::FutexWaitResult::TimedOut => return Err(Errno(ETIMEDOUT)), + _ => {} + } + } + + Ok(()) + } + pub fn try_acquire_read_lock(&self) -> Result<(), u32> { + let mut cached = self.state.load(Ordering::Acquire); + + loop { + let waiting_wr = cached & WAITING_WR; + let old = if cached & COUNT_MASK == EXCLUSIVE { + 0 + } else { + cached & COUNT_MASK + }; + let new = old + 1; + + // TODO: Return with error code instead? + assert_ne!( + new & COUNT_MASK, + EXCLUSIVE, + "maximum number of rwlock readers reached" + ); + + match self.state.compare_exchange_weak( + (old & COUNT_MASK) | waiting_wr, + new | waiting_wr, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => return Ok(()), + + Err(value) if value & COUNT_MASK == EXCLUSIVE => return Err(value), + Err(value) => { + cached = value; + // TODO: SCHED_YIELD? + core::hint::spin_loop(); + } + } + } + } + pub fn try_acquire_write_lock(&self) -> Result<(), u32> { + let mut waiting_wr = self.state.load(Ordering::Relaxed) & WAITING_WR; + + loop { + match self.state.compare_exchange_weak( + waiting_wr, + EXCLUSIVE, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(_) => return Ok(()), + Err(actual) if actual & COUNT_MASK > 0 => return Err(actual), + Err(can_retry) => { + waiting_wr = can_retry & WAITING_WR; + + core::hint::spin_loop(); + continue; + } + } + } + } + + pub fn unlock(&self) { + let state = self.state.load(Ordering::Relaxed); + + if state & COUNT_MASK == EXCLUSIVE { + // Unlocking a write lock. + + // This discards the writer-waiting bit, in order to ensure some level of fairness + // between read and write locks. + self.state.store(0, Ordering::Release); + + let _ = crate::sync::futex_wake(&self.state, i32::MAX); + } else { + // Unlocking a read lock. Subtract one from the reader count, but preserve the + // WAITING_WR bit. + + if self.state.fetch_sub(1, Ordering::Release) & COUNT_MASK == 1 { + let _ = crate::sync::futex_wake(&self.state, i32::MAX); + } + } + } +} + +pub struct RwLock { + inner: InnerRwLock, + data: UnsafeCell, +} + +unsafe impl Send for RwLock {} +unsafe impl Sync for RwLock {} + +impl RwLock { + pub const fn new(val: T) -> Self { + Self { + inner: InnerRwLock::new(Pshared::Private), + data: UnsafeCell::new(val), + } + } +} + +impl RwLock { + pub fn read(&self) -> ReadGuard<'_, T> { + let _ = self.inner.acquire_read_lock(None); + unsafe { ReadGuard::new(self) } + } + + pub fn write(&self) -> WriteGuard<'_, T> { + let _ = self.inner.acquire_write_lock(None); + unsafe { WriteGuard::new(self) } + } + + pub fn try_read(&self) -> Option> { + if self.inner.try_acquire_read_lock().is_ok() { + Some(unsafe { ReadGuard::new(self) }) + } else { + None + } + } + + pub fn try_write(&self) -> Option> { + if self.inner.try_acquire_write_lock().is_ok() { + Some(unsafe { WriteGuard::new(self) }) + } else { + None + } + } +} + +pub struct ReadGuard<'a, T: ?Sized + 'a> { + lock: &'a RwLock, +} + +impl !Send for ReadGuard<'_, T> {} +unsafe impl Sync for ReadGuard<'_, T> {} + +impl<'a, T: ?Sized> ReadGuard<'a, T> { + unsafe fn new(lock: &'a RwLock) -> Self { + Self { lock } + } +} + +impl<'a, T: ?Sized> ops::Deref for ReadGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: We have shared reference to the data. + unsafe { &*self.lock.data.get() } + } +} + +impl<'a, T: ?Sized> Drop for ReadGuard<'a, T> { + fn drop(&mut self) { + self.lock.inner.unlock(); + } +} + +impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for ReadGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, T: ?Sized + fmt::Display> fmt::Display for ReadGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +pub struct WriteGuard<'a, T: ?Sized + 'a> { + lock: &'a RwLock, +} + +impl !Send for WriteGuard<'_, T> {} +unsafe impl Sync for WriteGuard<'_, T> {} + +impl<'a, T: ?Sized> WriteGuard<'a, T> { + unsafe fn new(lock: &'a RwLock) -> Self { + Self { lock } + } +} + +impl<'a, T: ?Sized> ops::Deref for WriteGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: We have exclusive reference to the data. + unsafe { &*self.lock.data.get() } + } +} + +impl<'a, T: ?Sized> ops::DerefMut for WriteGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: We have exclusive reference to the data. + unsafe { &mut *self.lock.data.get() } + } +} + +impl<'a, T: ?Sized> Drop for WriteGuard<'a, T> { + fn drop(&mut self) { + self.lock.inner.unlock(); + } +} + +impl<'a, T: ?Sized + fmt::Debug> fmt::Debug for WriteGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, T: ?Sized + fmt::Display> fmt::Display for WriteGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} diff --git a/src/sync/semaphore.rs b/src/sync/semaphore.rs new file mode 100644 index 0000000000..ce1496188a --- /dev/null +++ b/src/sync/semaphore.rs @@ -0,0 +1,85 @@ +// From https://www.remlab.net/op/futex-misc.shtml +//TODO: improve implementation + +use crate::{ + header::{ + bits_timespec::timespec, + time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic}, + }, + platform::types::{c_uint, clockid_t}, +}; + +use core::sync::atomic::{AtomicU32, Ordering}; + +pub struct Semaphore { + count: AtomicU32, +} + +impl Semaphore { + pub const fn new(value: c_uint) -> Self { + Self { + count: AtomicU32::new(value), + } + } + + // TODO: Acquire-Release ordering? + + pub fn post(&self, count: c_uint) { + self.count.fetch_add(count, Ordering::SeqCst); + // TODO: notify one? + crate::sync::futex_wake(&self.count, i32::MAX); + } + + pub fn try_wait(&self) -> u32 { + loop { + let value = self.count.load(Ordering::SeqCst); + + if value == 0 { + return 0; + } + + match self.count.compare_exchange_weak( + value, + value - 1, + Ordering::SeqCst, + Ordering::SeqCst, + ) { + Ok(_) => { + // Acquired + return value; + } + Err(_) => (), + } + // Try again (as long as value > 0) + } + } + + pub fn wait(&self, timeout_opt: Option<×pec>, clock_id: clockid_t) -> Result<(), ()> { + loop { + let value = self.try_wait(); + + if value == 0 { + return Ok(()); + } + + if let Some(timeout) = timeout_opt { + let relative = match clock_id { + // FUTEX expect monotonic clock + CLOCK_MONOTONIC => timeout.clone(), + CLOCK_REALTIME => match timespec_realtime_to_monotonic(timeout.clone()) { + Ok(relative) => relative, + Err(_) => return Err(()), + }, + _ => return Err(()), + }; + crate::sync::futex_wait(&self.count, value, Some(&relative)); + } else { + // Use futex to wait for the next change, without a timeout + crate::sync::futex_wait(&self.count, value, None); + } + } + } + pub fn value(&self) -> c_uint { + self.count.load(Ordering::SeqCst) + } +} diff --git a/src/sync/waitval.rs b/src/sync/waitval.rs new file mode 100644 index 0000000000..bb6cc1b6ba --- /dev/null +++ b/src/sync/waitval.rs @@ -0,0 +1,42 @@ +use core::{ + cell::UnsafeCell, + mem::MaybeUninit, + sync::atomic::{AtomicU32 as AtomicUint, Ordering}, +}; + +/// An unsafe "one thread to one thread" synchronization primitive. Used for and modeled after +/// pthread_join only, at the moment. +#[derive(Debug)] +pub struct Waitval { + state: AtomicUint, + value: UnsafeCell>, +} + +unsafe impl Send for Waitval {} +unsafe impl Sync for Waitval {} + +impl Waitval { + #[allow(clippy::new_without_default)] + pub const fn new() -> Self { + Self { + state: AtomicUint::new(0), + value: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + // SAFETY: Caller must ensure both (1) that the value has not yet been initialized, and (2) + // that this is never run by more than one thread simultaneously. + pub unsafe fn post(&self, value: T) { + unsafe { self.value.get().write(MaybeUninit::new(value)) }; + self.state.store(1, Ordering::Release); + crate::sync::futex_wake(&self.state, i32::MAX); + } + + pub fn wait(&self) -> &T { + while self.state.load(Ordering::Acquire) == 0 { + crate::sync::futex_wait(&self.state, 0, None); + } + + unsafe { (*self.value.get()).assume_init_ref() } + } +} diff --git a/stripcore.sh b/stripcore.sh new file mode 100755 index 0000000000..e711ea6409 --- /dev/null +++ b/stripcore.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# This script exists as a workaround for https://github.com/rust-lang/rust/issues/142119 + +set -e + +target=$1 + +if [ -z "$target" ]; then + echo "Usage:\n\t./stripcore.sh TARGET" + exit 1 +fi + +for sym in cbrtf ceilf copysignf fabsf fdimf floorf fmaf fmaxf fminf fmodf rintf roundf sqrtf truncf \ + cbrt ceil copysign fabs fdim floor fmax fmin fmod rint round sqrt trunc; do \ + "$OBJCOPY" --globalize-symbol=$sym --strip-symbol=$sym "$target"; \ +done diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000000..f7f5661096 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,4 @@ +/build_*/ +/bins_*/ +/gen/ +/*.out diff --git a/tests/Cargo.lock b/tests/Cargo.lock new file mode 100644 index 0000000000..40fd7924fc --- /dev/null +++ b/tests/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "relibc-tests" +version = "0.1.0" diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 0000000000..c50afb5f99 --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "relibc-tests" +version = "0.1.0" +authors = ["Jeremy Soller "] +edition = "2024" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000000..98fbc6a0bd --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,183 @@ +include ../config.mk + +IS_REDOX?=0 +IS_STATIC?=0 +BUILD?=./build_$(TARGET) + +include ./Makefile.tests.mk + +CARGO_TEST?=cargo +TEST_RUNNER?= + +.PHONY: all clean run expected verify + +all: build + +clean: + rm -rf $(BUILD)/bins_* $(BUILD)/gen $(BUILD)/target + +build: $(BUILD)/bins_verify/relibc-tests $(BINS) $(EXPECT_BINS) $(EXPECT_INPUT_BINS) support_build + +support_build: $(BUILD)/expected \ + $(BUILD)/Makefile \ + $(BUILD)/Makefile.tests.mk \ + $(BUILD)/example_dir \ + $(BUILD)/select.c \ + $(BUILD)/stdlib/realpath.c \ + $(BUILD)/stdio/fread.in \ + $(BUILD)/stdio/fscanf_offby1.c \ + $(BUILD)/stdio/fscanf.c \ + $(BUILD)/stdio/stdio.in \ + $(BUILD)/stdio/getline.in \ + $(BUILD)/stdio/ungetc_ftell.c \ + $(BUILD)/sys_stat/stat.c \ + $(BUILD)/unistd/link.c \ + $(BUILD)/wchar/fgetwc.in \ + $(BUILD)/wchar/ungetwc.in + +run: build + $(MAKE) run -C $(BUILD) + +run-once: $(BUILD)/bins_verify/relibc-tests $(BUILD)/$(TESTBIN) support_build + $(MAKE) run-once -C $(BUILD) TESTBIN=$(TESTBIN) + +$(BUILD)/bins_verify/relibc-tests: src/main.rs + mkdir -p $(BUILD)/bins_verify + $(CARGO_TEST) build --release --bin relibc-tests --artifact-dir $(BUILD)/bins_verify --target-dir $(BUILD)/target -Z unstable-options + rm -rf $(BUILD)/target + +$(BUILD)/Makefile: Makefile.run.mk + cp -a $< "$@" + +$(BUILD)/Makefile.tests.mk: Makefile.tests.mk + cp -a $< "$@" + +$(BUILD)/expected: expected + rm -rf "$@" + cp -a "$<" $(BUILD)/ + +$(BUILD)/example_dir: example_dir + rm -rf "$@" + cp -a "$<" $(BUILD)/ + +$(BUILD)/%.in: %.in + @mkdir -p "$$(dirname "$@")" + cp "$*.in" "$@" + +$(BUILD)/%.c: %.c + @mkdir -p "$$(dirname "$@")" + cp "$*.c" "$@" + +FLAGS=\ + -std=c11 \ + -fno-builtin \ + -fno-stack-protector \ + -Wall \ + -Wextra \ + -Werror \ + -Wno-deprecated-declarations \ + -pedantic \ + -g \ + -I . + +STATIC_FLAGS=\ + -static + +DYNAMIC_FLAGS=\ + -Wl,--enable-new-dtags \ + -Wl,-export-dynamic + +SYSROOT?=$(abspath ../sysroot/$(TARGET)/) +SYSROOT_TARGET?=$(SYSROOT) + +$(SYSROOT): + $(MAKE) -C .. sysroot + +NATIVE_LIBC?=0 +ifeq ($(NATIVE_LIBC),0) +FLAGS+=\ + -nostdinc \ + -nostdlib \ + -isystem $(SYSROOT)/include \ + $(SYSROOT)/lib/crt0.o \ + $(SYSROOT)/lib/crti.o \ + $(SYSROOT)/lib/crtn.o + +ifeq ($(TARGET),aarch64-unknown-redox) +FLAGS+=-mno-outline-atomics +endif + +STATIC_FLAGS+=\ + $(SYSROOT)/lib/libc.a + +ifeq ($(IS_REDOX),0) +DYNAMIC_FLAGS+=\ + -Wl,-dynamic-linker=$(SYSROOT_TARGET)/$(LD_SO_PATH) \ + -Wl,-rpath=$(SYSROOT_TARGET)/lib:\$$ORIGIN \ + -L $(SYSROOT)/lib \ + -lc \ + -fpic +else +# TODO: Make -dynamic-linker works +DYNAMIC_FLAGS+=\ + -Wl,-rpath=$(SYSROOT_TARGET)/lib:\$$ORIGIN \ + -L $(SYSROOT)/lib \ + -lc \ + -fpic +endif + +DEPS=../sysroot/$(TARGET) + +else # ifeq ($(NATIVE_LIBC),1) +DYNAMIC_FLAGS+=\ + -Wl,-rpath=\$$ORIGIN +ifeq ($(IS_REDOX),0) +# glibc +FLAGS+=\ + -D_POSIX_C_SOURCE=200809L \ + -D_DEFAULT_SOURCE \ + -D_XOPEN_SOURCE=700 \ + -D_GNU_SOURCE \ + -lcrypt \ + -lm +else +# hosted redox, no extra options needed +endif +endif + +$(BUILD)/bins_static/%: %.c $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS) + +$(BUILD)/bins_expect_input/%: %.c %.exp $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" $(FLAGS) $(STATIC_FLAGS) + cp "$*.exp" $(addsuffix .exp,"$@") + +$(BUILD)/bins_dynamic/%.so: %.c $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" -shared -fpic $(FLAGS) $(DYNAMIC_FLAGS) + +# foobar depends on foo +$(BUILD)/bins_dynamic/libfoobar.so: libfoobar.c $(BUILD)/bins_dynamic/libfoo.so $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" -shared -fpic $(FLAGS) $(DYNAMIC_FLAGS) -L $(BUILD)/bins_dynamic -lfoo + +$(BUILD)/bins_dynamic/dlfcn: dlfcn.c $(BUILD)/bins_dynamic/sharedlib.so $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS) + +$(BUILD)/bins_dynamic/dlopen_scopes: dlopen_scopes.c $(BUILD)/bins_dynamic/libfoobar.so $(BUILD)/bins_dynamic/libfoo.so $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS) + +$(BUILD)/bins_dynamic/%: %.c $(DEPS) + mkdir -p "$$(dirname "$@")" + @$(CC) "$<" -o "$@" $(FLAGS) $(DYNAMIC_FLAGS) + +$(BUILD)/gen/includes: includes.c $(DEPS) + rm -rf $@.tmp $@ + mkdir -p $@.tmp + @$(CC) "$<" -H $(FLAGS) $(DYNAMIC_FLAGS) 2> $@.tmp/graph.txt + sed -i 's|$(SYSROOT)/include/||g' $@.tmp/graph.txt + mv $@.tmp $@ diff --git a/tests/Makefile.run.mk b/tests/Makefile.run.mk new file mode 100644 index 0000000000..31e93b92e4 --- /dev/null +++ b/tests/Makefile.run.mk @@ -0,0 +1,33 @@ +BUILD?=. +ifeq ($(shell uname),Redox) +IS_REDOX=1 +else +IS_REDOX=0 +endif + +include ./Makefile.tests.mk + +CARGO_TEST?=cargo +TEST_RUNNER?= + +.PHONY: all clean run run-once + +all: run + +clean: + rm -rf $(BUILD)/gen + +run: $(BUILD)/bins_verify/relibc-tests $(BINS) $(EXPECT_BINS) $(EXPECT_INPUT_BINS) + @echo "\033[1;36;49mRunning tests\033[0m" + $(TEST_RUNNER) $< $(patsubst %,-s%,$(BINS)) $(EXPECT_BINS) + for exp in $(EXPECT_INPUT_BINS); \ + do \ + echo "# expect $$(readlink -e $${exp}.exp) $$(readlink -e $${exp}) #"; \ + expect "$$(readlink -e $${exp}.exp)" "$$(readlink -e $${exp})" test args || exit $$?; \ + done + +run-once: $(BUILD)/bins_verify/relibc-tests $(BUILD)/$(TESTBIN) + @echo "\033[1;36;49mRunning single test $(TESTBIN) $*\033[0m" + @if [ -f "expected/$(TESTBIN).stdout" ]; then \ + $(TEST_RUNNER) $< $(BUILD)/$(TESTBIN); \ + else $(TEST_RUNNER) $< -s$(BUILD)/$(TESTBIN); fi diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk new file mode 100644 index 0000000000..2c9eb45152 --- /dev/null +++ b/tests/Makefile.tests.mk @@ -0,0 +1,358 @@ + +# If compiling for Redox, IS_REDOX must be 1 +ifeq ($(IS_REDOX),1) +FAILING_TESTS= +else +# Wrong modified time +FAILING_TESTS := futimens +# Crash, mmap issue +FAILING_TESTS += malloc/usable_size +# Not a FIFO +FAILING_TESTS += mkfifo +# Waitpid had EINTR +FAILING_TESTS += sigchld +# not triggering ERANGE +FAILING_TESTS += stdlib/ptsname +# Hang +FAILING_TESTS += sys_epoll/epollet +# Kernel hit todo! +FAILING_TESTS += sys_mman/fmap +# Hang +FAILING_TESTS += sys_socket/unixpeername +# Task failed successfully? +FAILING_TESTS += signals/pthread_kill-child +# Got EBADF +FAILING_TESTS += unistd/isatty +# Got EINVAL +FAILING_TESTS += unistd/link +# Signal kept unmasked +FAILING_TESTS += sigqueue +# Unwrap hit, written as TODO +FAILING_TESTS += pthread/customstack +# Returning garbage values +FAILING_TESTS += sys_resource/getrusage +# No times.h header +#FAILING_TESTS += time/times +# Outdated test +#FAILING_TESTS += netdb/netdb + +endif + +# Binaries that should generate the same output every time +EXPECT_NAMES=\ + alloca \ + arpainet \ + assert \ + ctype \ + crypt/blowfish \ + crypt/md5 \ + crypt/pbkdf2 \ + crypt/scrypt \ + crypt/sha256 \ + crypt/sha512 \ + dirent/fdopendir \ + dirent/scandir \ + endian \ + err \ + errno \ + error \ + fcntl/create \ + fcntl/fcntl \ + fcntl/open \ + fcntl/posix_fallocate \ + features \ + fnmatch \ + glob \ + iso646 \ + libgen \ + locale/duplocale \ + locale/newlocale \ + locale/setlocale \ + math \ + regex \ + select \ + setjmp \ + sigaction \ + sigaltstack \ + signal \ + stdio/all \ + stdio/buffer \ + stdio/dprintf \ + stdio/fgets \ + stdio/fputs \ + stdio/fread \ + stdio/freopen \ + stdio/fseek \ + stdio/fwrite \ + stdio/getc_unget \ + stdio/getline \ + stdio/mutex \ + stdio/popen \ + stdio/printf \ + stdio/putc_unlocked \ + stdio/rename \ + stdio/renameat \ + stdio/scanf \ + stdio/setvbuf \ + stdio/sprintf \ + stdio/printf_space_pad \ + stdio/ungetc_ftell \ + stdio/ungetc_multiple \ + stdio/fscanf_offby1 \ + stdio/fscanf \ + stdio/printf_neg_pad \ + stdlib/a64l \ + stdlib/alloc \ + stdlib/atof \ + stdlib/atoi \ + stdlib/div \ + stdlib/getsubopt \ + stdlib/mkostemps \ + stdlib/qsort \ + stdlib/rand \ + stdlib/rand48 \ + stdlib/random \ + stdlib/strtod \ + stdlib/strtol \ + stdlib/strtoul \ + stdlib/system \ + string/mem \ + string/memcpy \ + string/memmem \ + string/strcat \ + string/strchr \ + string/strchrnul \ + string/strcpy \ + string/strcspn \ + string/strlen \ + string/strncmp \ + string/strpbrk \ + string/strrchr \ + string/strspn \ + string/strstr \ + string/strtok \ + string/strtok_r \ + string/strsep \ + string/strsignal \ + string/stpcpy \ + string/stpncpy \ + strings \ + sys_socket/recv \ + sys_socket/recvfrom \ + sys_socket/unixrecv \ + sys_socket/unixrecvfrom \ + sys_socket/unixsocketpair \ + sys_stat/chmod \ + sys_stat/lstat \ + sys_stat/fstatat \ + sys_syslog/syslog \ + time/asctime \ + time/constants \ + time/gmtime \ + time/localtime \ + time/localtime_r \ + time/macros \ + time/mktime \ + time/strftime \ + time/strptime \ + time/time \ + time/timegm \ + time/tzset \ + unistd/access \ + unistd/alarm \ + unistd/constants \ + unistd/confstr \ + unistd/dup \ + unistd/exec \ + unistd/fchdir \ + unistd/fork \ + unistd/fsync \ + unistd/ftruncate \ + unistd/getopt \ + unistd/getopt_long \ + unistd/pipe \ + unistd/readlinkat \ + unistd/rmdir \ + unistd/sleep \ + unistd/swab \ + unistd/write \ + wchar/fgetwc \ + wchar/fwide \ + wchar/mbrtowc \ + wchar/mbsrtowcs \ + wchar/printf-on-wchars \ + wchar/putwchar \ + wchar/wscanf \ + wchar/ungetwc \ + wchar/wprintf \ + wchar/wcrtomb \ + wchar/wcpcpy \ + wchar/wcpncpy \ + wchar/wcschr \ + wchar/wcscspn \ + wchar/wcsdup \ + wchar/wcsrchr \ + wchar/wcsrtombs \ + wchar/wcsstr \ + wchar/wcstod \ + wchar/wcstok \ + wchar/wcstol \ + wchar/wcstoimax \ + wchar/wcstoumax \ + wchar/wcscasecmp \ + wchar/wcsncasecmp \ + wchar/wcsnlen \ + wchar/wcsnrtombs \ + wchar/wcswidth \ + wctype/towlower \ + wctype/towupper \ + mknod \ + mknodat + +# Binaries that may generate varied output +VARIED_NAMES=\ + dirent/main \ + dirent/posix_getdents \ + includes \ + kill-waitpid \ + limits \ + net/if \ + netdb/getaddrinfo \ + netdb/getaddrinfo_null \ + pthread/timedwait \ + pty/forkpty \ + psignal \ + pwd \ + sa_restart \ + signals/kill-self \ + signals/kill0-self \ + signals/kill-invalid \ + signals/kill-permission \ + signals/killpg-esrch \ + signals/killpg-invalid \ + signals/killpg0-self \ + signals/kill-group \ + signals/kill-child \ + signals/killpg-child \ + signals/killpg-self \ + signals/pthread_kill-invalid \ + signals/pthread_kill-self \ + signals/pthread_kill0-self \ + signals/raise-compliance \ + signals/sigismember-invalid \ + signals/sigismember-valid \ + signals/sigaddset-add \ + signals/sigdelset-delete \ + signals/signal-h \ + signals/signal-h-2 \ + signals/signal-handle_return \ + signals/signal-handler \ + signals/signal-handler2 \ + signals/signal-ignore \ + signals/signal-invalid \ + signals/signal-uncatchable \ + signals/sigprocmask-3 \ + signals/sigprocmask-4 \ + signals/sigprocmask-5 \ + signals/sigprocmask-6 \ + signals/sigprocmask-7 \ + signals/sigprocmask-8 \ + signals/sigprocmask-9 \ + signals/sigprocmask-10 \ + signals/sigprocmask-11 \ + signals/sigpause-invalid \ + signals/sigpause-revert \ + signals/sigpause-suspend \ + signals/sigprocmask-blocksingle \ + signals/sigrelse-1 \ + signals/sigrelse-2 \ + signals/sigrelse-3 \ + signals/sigset-1 \ + signals/sigset-10 \ + signals/sigset-2 \ + signals/sigset-3 \ + signals/sigset-4 \ + signals/sigset-5 \ + signals/sigset-9 \ + stdio/ctermid \ + stdio/tempnam \ + stdio/tmpnam \ + stdlib/bsearch \ + stdlib/mktemp \ + stdlib/realpath \ + sys_epoll/epoll \ + sys_mman/mmap \ + sys_resource/constants \ + sys_socket/getpeername \ + sys_stat/stat \ + sys_statvfs/statvfs \ + sys_utsname/uname \ + time/gettimeofday \ + unistd/chdir \ + unistd/getcwd \ + unistd/gethostname \ + unistd/getid \ + unistd/getpagesize \ + unistd/pathconf \ + unistd/setid \ + unistd/sysconf \ + pthread/main \ + pthread/cleanup \ + pthread/exit \ + pthread/extjoin \ + pthread/once \ + pthread/barrier \ + pthread/rwlock_trylock \ + pthread/rwlock_randtest \ + pthread/mutex_recursive \ + pthread/timeout \ + pthread/tls \ + grp/getgrouplist \ + grp/getgroups \ + grp/getgrgid_r \ + grp/getgrnam_r \ + grp/gr_iter \ + waitid \ + waitpid \ + waitpid_multiple \ + $(FAILING_TESTS) + + +# Tests that only working with when ld.so exist +DYNAMIC_ONLY_EXPECT_NAMES=\ + dlfcn \ + dlopen_scopes + +# Tests that may produce different result when ld.so absent +STATIC_CHECK_EXPECT_NAMES=\ + args \ + constructor \ + destructor \ + stdlib/env \ + unistd/brk \ + tls + +# Tests run with `expect` (require a .c file and an .exp file +# that takes the produced binary as the first argument) +EXPECT_INPUT_NAMES=\ + unistd/getpass + +# TODO: Dynamic linking doesn't work with NATIVE_LIBC=0 +ifeq ($(IS_STATIC),1) +BINS+=$(patsubst %,$(BUILD)/bins_static/%,$(VARIED_NAMES)) +EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_static/%,$(EXPECT_NAMES)) +EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_static/%,$(STATIC_CHECK_EXPECT_NAMES)) +ifeq ($(IS_REDOX),0) +EXPECT_INPUT_BINS=$(patsubst %,$(BUILD)/bins_expect_input/%,$(EXPECT_INPUT_NAMES)) +endif +else +BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(VARIED_NAMES)) +EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_static/%,$(STATIC_CHECK_EXPECT_NAMES)) +EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(EXPECT_NAMES)) +EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(STATIC_CHECK_EXPECT_NAMES)) +EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(DYNAMIC_ONLY_EXPECT_NAMES)) +ifeq ($(IS_REDOX),0) +# TODO: redoxer does not have "expect" binary +EXPECT_INPUT_BINS=$(patsubst %,$(BUILD)/bins_expect_input/%,$(EXPECT_INPUT_NAMES)) +endif +endif diff --git a/tests/alloca.c b/tests/alloca.c new file mode 100644 index 0000000000..0e4a2d4902 --- /dev/null +++ b/tests/alloca.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *str = (char *) alloca(17); + + memset(str, 'A', 16); + str[16] = '\0'; + + printf("%s\n", str); +} diff --git a/tests/args.c b/tests/args.c new file mode 100644 index 0000000000..9095e3a451 --- /dev/null +++ b/tests/args.c @@ -0,0 +1,12 @@ +#include +#include + +#include "test_helpers.h" + +int main(int argc, char *argv[]) { + for(int i = 1; i < argc; i++) { + write(STDOUT_FILENO, argv[i], strlen(argv[i])); + write(STDOUT_FILENO, " ", 1); + } + write(STDOUT_FILENO, "\n", 1); +} diff --git a/tests/arpainet.c b/tests/arpainet.c new file mode 100644 index 0000000000..4b9f4a4067 --- /dev/null +++ b/tests/arpainet.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + uint32_t hl = 0xBADFACED; + uint32_t nl = htonl(hl); + assert(nl == 0xEDACDFBA); + hl = ntohl(nl); + assert(hl == 0xBADFACED); + + uint16_t hs = 0xDEAD; + uint16_t ns = htons(hs); + assert(ns == 0xADDE); + hs = ntohs(ns); + assert(hs == 0xDEAD); + + const char* addr_str = "8.8.4.4"; + struct in_addr* addr = malloc(sizeof addr); + inet_aton(addr_str, addr); + assert(strcmp(inet_ntoa(*addr), addr_str) == 0); +} diff --git a/tests/assert.c b/tests/assert.c new file mode 100644 index 0000000000..6049a38e17 --- /dev/null +++ b/tests/assert.c @@ -0,0 +1,19 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + assert(1 == 1); + assert(1 + 1 == 2); + puts("yay!"); + + if (assert(0 == 0), 1) { + puts("groovy!"); + } + + //This fails, but I can't test it because that'd + //make the test fail lol + //assert(42 == 1337); +} diff --git a/tests/constructor.c b/tests/constructor.c new file mode 100644 index 0000000000..ddde65029f --- /dev/null +++ b/tests/constructor.c @@ -0,0 +1,22 @@ +#include +#include "test_helpers.h" + +__attribute__((constructor)) +void constructor_no_priority(void) { + puts("constructor (no priority)"); +} + +#define TEST(__priority) \ + __attribute__((constructor(__priority))) \ + void constructor_priority_##__priority(void) { \ + puts("constructor ("#__priority")"); \ + } + +TEST(101) +TEST(102) +TEST(103) +TEST(104) + +int main(void) { + puts("main"); +} diff --git a/tests/crypt/blowfish.c b/tests/crypt/blowfish.c new file mode 100644 index 0000000000..0a5b62e34b --- /dev/null +++ b/tests/crypt/blowfish.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +int main() +{ + char *expected_output = "$2y$12$CJr4uRfziaGp4CWIBk0fB.I2tCOHYe3pomaWbC53/92p"; + char *result = crypt("correctbatteryhorsestapler", "$2y$12$L6Bc/AlTQHyd9liGgGEZyO"); + assert(strcmp(result, expected_output) == 0); + + // Invalid salt for Blowfish + result = crypt("correctbatteryhorsestapler", "$2t$12$L6Bc/AlTQHyd9liGgGEZyO"); + assert(result == NULL); + + expected_output = "$2a$4$IAwt7hxuME3DekssMMTWU.xnJub2Xn45xK/oP.wWt3UC"; + result = crypt("password", "$2a$04$UuTkLRZZ6QofpDOlMz32Mu"); + assert(strcmp(result, expected_output) == 0); + + // Invalid salt for Blowfish + result = "$2b$10$testtesttesttes"; + result = crypt("correctbatteryhorsestapler", "$2y$12$L6Bc/AlTQHyd9liGgGEZyO"); + assert(result != NULL); + + return 0; +} diff --git a/tests/crypt/md5.c b/tests/crypt/md5.c new file mode 100644 index 0000000000..4251d70f9d --- /dev/null +++ b/tests/crypt/md5.c @@ -0,0 +1,38 @@ +/* Copyright (C) 1991-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +int main () { + const char salt[] = "$1$saltstring"; + char *cp; + int result = 0; + + cp = crypt ("Hello world!", salt); + + /* MD5 is disabled in FIPS mode. */ + if (cp) { + result |= strcmp ("$1$saltstri$YMyguxXMBpd2TEZ.vS/3q1", cp); + if (!result) + printf("Success!\n"); + } + + return result; +} \ No newline at end of file diff --git a/tests/crypt/pbkdf2.c b/tests/crypt/pbkdf2.c new file mode 100644 index 0000000000..f3e9a3cfbc --- /dev/null +++ b/tests/crypt/pbkdf2.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int main() +{ + char *expected_output = "$8$3e8$salt$ZyTZgT5Pyp4CKdom6q6zHg0QrrAQO4ptWYCpWz4gu16"; + char *result = crypt("pleaseletmein", "$8$3e8$salt"); + assert(strcmp(result, expected_output) == 0); + + // No salt + result = crypt("pleaseletmein", "$8$3e8$"); + assert(result != NULL); + + // Invalid encoded number for rounds + result = crypt("pleaseletmein", "$8$$salt"); + assert(result == NULL); + + // Invalid encoded number for rounds + result = crypt("pleaseletmein", "$8$.$salt"); + assert(result == NULL); + + return 0; +} diff --git a/tests/crypt/scrypt.c b/tests/crypt/scrypt.c new file mode 100644 index 0000000000..d6f70778ce --- /dev/null +++ b/tests/crypt/scrypt.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int main() +{ + char *expected_output = "$7$C6..../....$SodiumChloride$aAM7wxp7ayfEF.ZLedy2490m85oOR228oZTB7jPbmdG"; + char *result = crypt("pleaseletmein", "$7$C6..../....SodiumChloride"); + assert(strcmp(result, expected_output) == 0); + + // No salt + result = crypt("pleaseletmein", "$7$C6..../...."); + assert(result != NULL); + + // Invalid encoded number for r + result = crypt("pleaseletmein", "$7$C6.../....SodiumChloride"); + assert(result == NULL); + + // Invalid encoded number for p + result = crypt("pleaseletmein", "$7$C6..../...SodiumChloride"); + assert(result == NULL); + + return 0; +} diff --git a/tests/crypt/sha256.c b/tests/crypt/sha256.c new file mode 100644 index 0000000000..6e9c3ffa47 --- /dev/null +++ b/tests/crypt/sha256.c @@ -0,0 +1,67 @@ +/* Copyright (C) 1991-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static const struct +{ + const char *salt; + const char *input; + const char *expected; +} tests[] = +{ + { "$5$saltstring", "Hello world!", + "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5" }, + { "$5$rounds=10000$saltstringsaltstring", "Hello world!", + "$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2." + "opqey6IcA" }, + { "$5$rounds=5000$toolongsaltstring", "This is just a test", + "$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8" + "mGRcvxa5" }, + { "$5$rounds=1400$anotherlongsaltstring", + "a very much longer text to encrypt. This one even stretches over more" + "than one line.", + "$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12" + "oP84Bnq1" }, + { "$5$rounds=77777$short", + "we have a short salt string but not a short password", + "$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/" }, + { "$5$rounds=123456$asaltof16chars..", "a short string", + "$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/" + "cZKmF/wJvD" }, + { "$5$rounds=10$roundstoolow", "the minimum number is still observed", + "$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL97" + "2bIC" }, +}; + +const int ntests = sizeof(tests) / sizeof(tests[0]); + +int main (void) { + int result = 0; + + for (int i = 0; i < ntests; ++i){ + char *cp = crypt (tests[i].input, tests[i].salt); + if (strcmp (cp, tests[i].expected) != 0) { + printf ("test %d: expected \"%s\", got \"%s\"\n", i, tests[i].expected, cp); + result = 1; + } + } + return result; +} \ No newline at end of file diff --git a/tests/crypt/sha512.c b/tests/crypt/sha512.c new file mode 100644 index 0000000000..efab1e3162 --- /dev/null +++ b/tests/crypt/sha512.c @@ -0,0 +1,72 @@ +/* Copyright (C) 1991-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static const struct +{ + const char *salt; + const char *input; + const char *expected; +} tests[] = +{ + { "$6$saltstring", "Hello world!", + "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" + "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1" }, + { "$6$rounds=10000$saltstringsaltstring", "Hello world!", + "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sb" + "HbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." }, + { "$6$rounds=5000$toolongsaltstring", "This is just a test", + "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQ" + "zQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" }, + { "$6$rounds=1400$anotherlongsaltstring", + "a very much longer text to encrypt. This one even stretches over more" + "than one line.", + "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wP" + "vMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" }, + { "$6$rounds=77777$short", + "we have a short salt string but not a short password", + "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0g" + "ge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0" }, + { "$6$rounds=123456$asaltof16chars..", "a short string", + "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwc" + "elCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" }, + { "$6$rounds=10$roundstoolow", "the minimum number is still observed", + "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1x" + "hLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." }, +}; + +const int ntests = sizeof(tests) / sizeof(tests[0]); + +int main(void) { + int result = 0; + int i; + + for (i = 0; i < ntests; ++i) { + char * cp = crypt(tests[i].input, tests[i].salt); + + if (strcmp(cp, tests[i].expected) != 0) { + printf("test %d: expected \"%s\", got \"%s\"\n", i, tests[i].expected, cp); + result = 1; + } + } + + return result; +} \ No newline at end of file diff --git a/tests/ctype.c b/tests/ctype.c new file mode 100644 index 0000000000..0f6aa1d1bc --- /dev/null +++ b/tests/ctype.c @@ -0,0 +1,334 @@ +#include +#include +#include + +#include "test_helpers.h" + +struct test_case { + int c; + int isalnum; + int isalpha; + int isascii; + int isblank; + int iscntrl; + int isdigit; + int isgraph; + int islower; + int isprint; + int ispunct; + int isspace; + int isupper; + int isxdigit; + int toascii; + int tolower; + int toupper; +} test_cases[] = { + // a a a b c d g l p p s u x t t t + // l l s l n i r o r u p p d o o o + // n p c a t g a w i n a p i a l u + // u h i n r i p e n c c e g s o p + // m a i k l t h r t t e r i c w p + { 0x00, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00 }, + { 0x01, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x01 }, + { 0x02, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, 0x02, 0x02 }, + { 0x03, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0x03, 0x03 }, + { 0x04, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0x04, 0x04 }, + { 0x05, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, 0x05, 0x05 }, + { 0x06, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x06, 0x06 }, + { 0x07, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, 0x07, 0x07 }, + { 0x08, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08 }, + { 0x09, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0x09, 0x09, 0x09 }, + { 0x0A, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0x0A, 0x0A, 0x0A }, + { 0x0B, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0x0B, 0x0B, 0x0B }, + { 0x0C, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0x0C, 0x0C, 0x0C }, + { 0x0D, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0x0D, 0x0D, 0x0D }, + { 0x0E, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0E, 0x0E, 0x0E }, + { 0x0F, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x0F, 0x0F, 0x0F }, + { 0x10, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10 }, + { 0x11, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x11, 0x11 }, + { 0x12, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x12, 0x12 }, + { 0x13, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x13, 0x13, 0x13 }, + { 0x14, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x14, 0x14 }, + { 0x15, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x15, 0x15, 0x15 }, + { 0x16, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x16, 0x16, 0x16 }, + { 0x17, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x17, 0x17, 0x17 }, + { 0x18, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x18, 0x18 }, + { 0x19, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x19, 0x19, 0x19 }, + { 0x1A, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x1A, 0x1A, 0x1A }, + { 0x1B, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x1B, 0x1B, 0x1B }, + { 0x1C, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x1C, 0x1C, 0x1C }, + { 0x1D, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x1D, 0x1D, 0x1D }, + { 0x1E, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x1E, 0x1E, 0x1E }, + { 0x1F, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x1F, 0x1F, 0x1F }, + { ' ', 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, ' ', ' ', ' ' }, + { '!', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '!', '!', '!' }, + { '"', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '"', '"', '"' }, + { '#', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '#', '#', '#' }, + { '$', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '$', '$', '$' }, + { '%', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '%', '%', '%' }, + { '&', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '&', '&', '&' }, + { '\'', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '\'', '\'', '\'' }, + { '(', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '(', '(', '(' }, + { ')', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, ')', ')', ')' }, + { '*', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '*', '*', '*' }, + { '+', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '+', '+', '+' }, + { ',', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, ',', ',', ',' }, + { '-', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '-', '-', '-' }, + { '.', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '.', '.', '.' }, + { '/', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '/', '/', '/' }, + { '0', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '0', '0', '0' }, + { '1', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '1', '1', '1' }, + { '2', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '2', '2', '2' }, + { '3', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '3', '3', '3' }, + { '4', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '4', '4', '4' }, + { '5', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '5', '5', '5' }, + { '6', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '6', '6', '6' }, + { '7', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '7', '7', '7' }, + { '8', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '8', '8', '8' }, + { '9', 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, '9', '9', '9' }, + { ':', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, ':', ':', ':' }, + { ';', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, ';', ';', ';' }, + { '<', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '<', '<', '<' }, + { '=', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '=', '=', '=' }, + { '>', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '>', '>', '>' }, + { '?', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '?', '?', '?' }, + { '@', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '@', '@', '@' }, + { 'A', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 'A', 'a', 'A' }, + { 'B', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 'B', 'b', 'B' }, + { 'C', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 'C', 'c', 'C' }, + { 'D', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 'D', 'd', 'D' }, + { 'E', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 'E', 'e', 'E' }, + { 'F', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 'F', 'f', 'F' }, + { 'G', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'G', 'g', 'G' }, + { 'H', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'H', 'h', 'H' }, + { 'I', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'I', 'i', 'I' }, + { 'J', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'J', 'j', 'J' }, + { 'K', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'K', 'k', 'K' }, + { 'L', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'L', 'l', 'L' }, + { 'M', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'M', 'm', 'M' }, + { 'N', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'N', 'n', 'N' }, + { 'O', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'O', 'o', 'O' }, + { 'P', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'P', 'p', 'P' }, + { 'Q', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'Q', 'q', 'Q' }, + { 'R', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'R', 'r', 'R' }, + { 'S', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'S', 's', 'S' }, + { 'T', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'T', 't', 'T' }, + { 'U', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'U', 'u', 'U' }, + { 'V', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'V', 'v', 'V' }, + { 'W', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'W', 'w', 'W' }, + { 'X', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'X', 'x', 'X' }, + { 'Y', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'Y', 'y', 'Y' }, + { 'Z', 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 'Z', 'z', 'Z' }, + { '[', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '[', '[', '[' }, + { '\\', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '\\', '\\', '\\' }, + { ']', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, ']', ']', ']' }, + { '^', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '^', '^', '^' }, + { '_', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '_', '_', '_' }, + { '`', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '`', '`', '`' }, + { 'a', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 'a', 'a', 'A' }, + { 'b', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 'b', 'b', 'B' }, + { 'c', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 'c', 'c', 'C' }, + { 'd', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 'd', 'd', 'D' }, + { 'e', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 'e', 'e', 'E' }, + { 'f', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 'f', 'f', 'F' }, + { 'g', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'g', 'g', 'G' }, + { 'h', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'h', 'h', 'H' }, + { 'i', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'i', 'i', 'I' }, + { 'j', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'j', 'j', 'J' }, + { 'k', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'k', 'k', 'K' }, + { 'l', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'l', 'l', 'L' }, + { 'm', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'm', 'm', 'M' }, + { 'n', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'n', 'n', 'N' }, + { 'o', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'o', 'o', 'O' }, + { 'p', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'p', 'p', 'P' }, + { 'q', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'q', 'q', 'Q' }, + { 'r', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'r', 'r', 'R' }, + { 's', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 's', 's', 'S' }, + { 't', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 't', 't', 'T' }, + { 'u', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'u', 'u', 'U' }, + { 'v', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'v', 'v', 'V' }, + { 'w', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'w', 'w', 'W' }, + { 'x', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'x', 'x', 'X' }, + { 'y', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'y', 'y', 'Y' }, + { 'z', 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 'z', 'z', 'Z' }, + { '{', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '{', '{', '{' }, + { '|', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '|', '|', '|' }, + { '}', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '}', '}', '}' }, + { '~', 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, '~', '~', '~' }, + { 0x7F, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x7F, 0x7F, 0x7F }, + { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x80, 0x80 }, + { 0x81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x81, 0x81 }, + { 0x82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, 0x82, 0x82 }, + { 0x83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0x83, 0x83 }, + { 0x84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04, 0x84, 0x84 }, + { 0x85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x05, 0x85, 0x85 }, + { 0x86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x06, 0x86, 0x86 }, + { 0x87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x07, 0x87, 0x87 }, + { 0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x88, 0x88 }, + { 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x09, 0x89, 0x89 }, + { 0x8A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A, 0x8A, 0x8A }, + { 0x8B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0B, 0x8B, 0x8B }, + { 0x8C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0C, 0x8C, 0x8C }, + { 0x8D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0D, 0x8D, 0x8D }, + { 0x8E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0E, 0x8E, 0x8E }, + { 0x8F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0F, 0x8F, 0x8F }, + { 0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x90, 0x90 }, + { 0x91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x11, 0x91, 0x91 }, + { 0x92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12, 0x92, 0x92 }, + { 0x93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x13, 0x93, 0x93 }, + { 0x94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x94, 0x94 }, + { 0x95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x15, 0x95, 0x95 }, + { 0x96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x16, 0x96, 0x96 }, + { 0x97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x17, 0x97, 0x97 }, + { 0x98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x98, 0x98 }, + { 0x99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x19, 0x99, 0x99 }, + { 0x9A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1A, 0x9A, 0x9A }, + { 0x9B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1B, 0x9B, 0x9B }, + { 0x9C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1C, 0x9C, 0x9C }, + { 0x9D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1D, 0x9D, 0x9D }, + { 0x9E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1E, 0x9E, 0x9E }, + { 0x9F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1F, 0x9F, 0x9F }, + { 0xA0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 0xA0, 0xA0 }, + { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '!', 0xA1, 0xA1 }, + { 0xA2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '"', 0xA2, 0xA2 }, + { 0xA3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '#', 0xA3, 0xA3 }, + { 0xA4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '$', 0xA4, 0xA4 }, + { 0xA5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '%', 0xA5, 0xA5 }, + { 0xA6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '&', 0xA6, 0xA6 }, + { 0xA7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\'', 0xA7, 0xA7 }, + { 0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '(', 0xA8, 0xA8 }, + { 0xA9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ')', 0xA9, 0xA9 }, + { 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '*', 0xAA, 0xAA }, + { 0xAB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '+', 0xAB, 0xAB }, + { 0xAC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ',', 0xAC, 0xAC }, + { 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0xAD, 0xAD }, + { 0xAE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '.', 0xAE, 0xAE }, + { 0xAF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', 0xAF, 0xAF }, + { 0xB0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '0', 0xB0, 0xB0 }, + { 0xB1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '1', 0xB1, 0xB1 }, + { 0xB2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '2', 0xB2, 0xB2 }, + { 0xB3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '3', 0xB3, 0xB3 }, + { 0xB4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '4', 0xB4, 0xB4 }, + { 0xB5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '5', 0xB5, 0xB5 }, + { 0xB6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '6', 0xB6, 0xB6 }, + { 0xB7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', 0xB7, 0xB7 }, + { 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '8', 0xB8, 0xB8 }, + { 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '9', 0xB9, 0xB9 }, + { 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ':', 0xBA, 0xBA }, + { 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ';', 0xBB, 0xBB }, + { 0xBC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '<', 0xBC, 0xBC }, + { 0xBD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '=', 0xBD, 0xBD }, + { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '>', 0xBE, 0xBE }, + { 0xBF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '?', 0xBF, 0xBF }, + { 0xC0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '@', 0xC0, 0xC0 }, + { 0xC1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'A', 0xC1, 0xC1 }, + { 0xC2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'B', 0xC2, 0xC2 }, + { 0xC3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'C', 0xC3, 0xC3 }, + { 0xC4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'D', 0xC4, 0xC4 }, + { 0xC5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'E', 0xC5, 0xC5 }, + { 0xC6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'F', 0xC6, 0xC6 }, + { 0xC7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'G', 0xC7, 0xC7 }, + { 0xC8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'H', 0xC8, 0xC8 }, + { 0xC9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'I', 0xC9, 0xC9 }, + { 0xCA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'J', 0xCA, 0xCA }, + { 0xCB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'K', 0xCB, 0xCB }, + { 0xCC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'L', 0xCC, 0xCC }, + { 0xCD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'M', 0xCD, 0xCD }, + { 0xCE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'N', 0xCE, 0xCE }, + { 0xCF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'O', 0xCF, 0xCF }, + { 0xD0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'P', 0xD0, 0xD0 }, + { 0xD1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Q', 0xD1, 0xD1 }, + { 0xD2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'R', 0xD2, 0xD2 }, + { 0xD3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'S', 0xD3, 0xD3 }, + { 0xD4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'T', 0xD4, 0xD4 }, + { 0xD5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'U', 0xD5, 0xD5 }, + { 0xD6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'V', 0xD6, 0xD6 }, + { 0xD7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'W', 0xD7, 0xD7 }, + { 0xD8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'X', 0xD8, 0xD8 }, + { 0xD9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Y', 0xD9, 0xD9 }, + { 0xDA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Z', 0xDA, 0xDA }, + { 0xDB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '[', 0xDB, 0xDB }, + { 0xDC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0xDC, 0xDC }, + { 0xDD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ']', 0xDD, 0xDD }, + { 0xDE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '^', 0xDE, 0xDE }, + { 0xDF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '_', 0xDF, 0xDF }, + { 0xE0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '`', 0xE0, 0xE0 }, + { 0xE1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'a', 0xE1, 0xE1 }, + { 0xE2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'b', 0xE2, 0xE2 }, + { 0xE3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'c', 0xE3, 0xE3 }, + { 0xE4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'd', 0xE4, 0xE4 }, + { 0xE5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'e', 0xE5, 0xE5 }, + { 0xE6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'f', 0xE6, 0xE6 }, + { 0xE7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'g', 0xE7, 0xE7 }, + { 0xE8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'h', 0xE8, 0xE8 }, + { 0xE9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'i', 0xE9, 0xE9 }, + { 0xEA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'j', 0xEA, 0xEA }, + { 0xEB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'k', 0xEB, 0xEB }, + { 0xEC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'l', 0xEC, 0xEC }, + { 0xED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'm', 0xED, 0xED }, + { 0xEE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'n', 0xEE, 0xEE }, + { 0xEF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'o', 0xEF, 0xEF }, + { 0xF0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'p', 0xF0, 0xF0 }, + { 0xF1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'q', 0xF1, 0xF1 }, + { 0xF2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'r', 0xF2, 0xF2 }, + { 0xF3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 's', 0xF3, 0xF3 }, + { 0xF4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 't', 0xF4, 0xF4 }, + { 0xF5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'u', 0xF5, 0xF5 }, + { 0xF6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'v', 0xF6, 0xF6 }, + { 0xF7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'w', 0xF7, 0xF7 }, + { 0xF8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'x', 0xF8, 0xF8 }, + { 0xF9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'y', 0xF9, 0xF9 }, + { 0xFA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'z', 0xFA, 0xFA }, + { 0xFB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{', 0xFB, 0xFB }, + { 0xFC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '|', 0xFC, 0xFC }, + { 0xFD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '}', 0xFD, 0xFD }, + { 0xFE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '~', 0xFE, 0xFE }, + { 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7F, 0xFF, 0xFF }, + // SUSv2 states that these functions should also accept EOF. + { EOF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7F, EOF, EOF } +}; + +size_t num_test_cases = sizeof(test_cases) / sizeof(struct test_case); + +#define CHECK_TEST(tc, fn, retval) \ + do { \ + if (fn(tc.c) != tc.fn) { \ + retval = EXIT_FAILURE; \ + printf("Unexpected result: " #fn "('%c') != %d // Char value: %d\n", tc.c, tc.fn, tc.c); \ + } \ + } while (0) + +int main(void) { + int retval = EXIT_SUCCESS; + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + for(size_t i = 0; i < num_test_cases; ++i) { + struct test_case tc = test_cases[i]; + CHECK_TEST(tc, isalnum, retval); + CHECK_TEST(tc, isalpha, retval); + CHECK_TEST(tc, isascii, retval); + CHECK_TEST(tc, isblank, retval); + CHECK_TEST(tc, iscntrl, retval); + CHECK_TEST(tc, isdigit, retval); + CHECK_TEST(tc, isgraph, retval); + CHECK_TEST(tc, islower, retval); + CHECK_TEST(tc, isprint, retval); + CHECK_TEST(tc, ispunct, retval); + CHECK_TEST(tc, isspace, retval); + CHECK_TEST(tc, isupper, retval); + CHECK_TEST(tc, isxdigit, retval); + CHECK_TEST(tc, toascii, retval); + CHECK_TEST(tc, tolower, retval); + CHECK_TEST(tc, toupper, retval); + } + #pragma GCC diagnostic pop + + if (retval == EXIT_SUCCESS) { + printf("Success: %d\n", retval); + } else { + printf("Failure: %d\n", retval); + } + + return retval; +} diff --git a/tests/destructor.c b/tests/destructor.c new file mode 100644 index 0000000000..234cf2ed42 --- /dev/null +++ b/tests/destructor.c @@ -0,0 +1,22 @@ +#include +#include "test_helpers.h" + +__attribute__((destructor)) +void destructor_no_priority(void) { + puts("destructor (no priority)"); +} + +#define TEST(__priority) \ + __attribute__((destructor(__priority))) \ + void destructor_priority_##__priority(void) { \ + puts("destructor ("#__priority")"); \ + } + +TEST(101) +TEST(102) +TEST(103) +TEST(104) + +int main(void) { + puts("main"); +} diff --git a/tests/dirent/fdopendir.c b/tests/dirent/fdopendir.c new file mode 100644 index 0000000000..268dda2283 --- /dev/null +++ b/tests/dirent/fdopendir.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +__attribute__((nonnull)) +static bool check_dot(struct dirent* dirent) { + const char* dots[2] = { + ".", + ".." + }; + + for (size_t i = 0; i < 2; i++) { + if (strcmp(dots[i], dirent->d_name) == 0) { + return true; + } + } + + return false; +} + +int main(void) { + int status = EXIT_FAILURE; + + char template[] = "/tmp/fdotest.XXXXXX"; + if (!mkdtemp(template)) { + perror("mkdtemp"); + goto bye; + } + + const char* movies[] = { + "big_lebowski", + "blade_runner", + "grand_budapest_hotel", + "taxi_driver" + }; + const size_t movies_len = sizeof(movies)/sizeof(char*); + char paths[sizeof(movies)/sizeof(char*)][PATH_MAX] = {0}; + for (size_t i = 0; i < movies_len; ++i) { + // Concat the path + const size_t len = sizeof(template) - 1; + char buf[PATH_MAX] = {0}; + memcpy(buf, template, len); + buf[len] = '/'; + + memcpy(&buf[len + 1], movies[i], strlen(movies[i])); + memcpy(paths[i], buf, PATH_MAX); + + // And now create the file + int fd = open(buf, O_CREAT); + if (fd == -1) { + perror("open"); + goto rmfiles; + } + close(fd); + } + + // FIXME: Redox requires read perms for the dir while Linux/BSD don't. + int dir = open(template, O_DIRECTORY | O_RDONLY); + if (dir == -1) { + perror("open"); + goto rmfiles; + } + + DIR* iter = fdopendir(dir); + if (!iter) { + perror("fdopendir"); + goto closedirfd; + } + + for (size_t i = 0; i < movies_len; ++i) { + errno = 0; + + struct dirent* dirent = readdir(iter); + if (!dirent) { + if (errno) { + perror("readdir"); + } + fprintf( + stderr, + "Expected entry #%lu but directory stream is complete\n", + i + ); + + goto closediriter; + } + + // Skip . and .. + if (check_dot(dirent)) { + continue; + } + + // Check that the entry matches one of the names. + // readdir's order is indeterministic and looping over the names + // is simpler than qsort for a test. + for (size_t j = 0; j < movies_len; ++j) { + if (strcmp(movies[j], dirent->d_name) == 0) { + goto continue_outer; + } + } + + fprintf( + stderr, + "Unexpected entry: %s\n", + dirent->d_name + ); + goto closediriter; + + continue_outer: + continue; + } + +// fdclosedir is a BSD extension +#ifndef __GLIBC__ + // fdclosedir returns ownership of the original fd. + int returned_fd = fdclosedir(iter); + // Internally, both closedir and fdclosedir consume the boxed DIR. + iter = NULL; + if (returned_fd != dir) { + fputs("fdclosedir returned the wrong descriptor\n", stderr); + goto closedirfd; + } + + // Check that the file descriptor is still valid. + struct stat stat = {0}; + if (fstatat(returned_fd, "", &stat, AT_EMPTY_PATH) == -1) { + perror("fstatat"); + fputs("fdclosedir shouldn't have closed the fd\n", stderr); + goto closedirfd; + } +#endif + + status = EXIT_SUCCESS; +closediriter: + if (iter) { + closedir(iter); + } +closedirfd: + close(dir); +rmfiles: + for (size_t i = 0; i < movies_len; ++i) { + if (strnlen(paths[i], PATH_MAX) > 4) { + unlink(paths[i]); + } + } + rmdir(template); +bye: + return status; +} diff --git a/tests/dirent/main.c b/tests/dirent/main.c new file mode 100644 index 0000000000..b524ed1e98 --- /dev/null +++ b/tests/dirent/main.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("%lu\n", sizeof(struct dirent)); + + DIR* dir = opendir("example_dir/"); + ERROR_IF(opendir, dir, == NULL); + + struct dirent* entry; + + //int tell = 0; + + for (char counter = 0; (entry = readdir(dir)); counter += 1) { + puts(entry->d_name); + + //if (counter == 4) { + // tell = telldir(dir); + //} + } + + puts("--- Testing rewind ---"); + rewinddir(dir); + entry = readdir(dir); + assert(entry != NULL); + puts(entry->d_name); + + // puts("--- Testing seek ---"); + // // Why this doesn't cause it to actually go to the 4th element is beyond + // // me, but glibc acts the same way. + // seekdir(dir, tell); + // entry = readdir(dir); + // puts(entry->d_name); + + int c = closedir(dir); + ERROR_IF(closedir, c, == -1); + UNEXP_IF(closedir, c, != 0); +} diff --git a/tests/dirent/posix_getdents.c b/tests/dirent/posix_getdents.c new file mode 100644 index 0000000000..ee62be96ff --- /dev/null +++ b/tests/dirent/posix_getdents.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define BUFFER_SIZE 4096 + +#ifndef __GLIBC__ +void read_and_print_directory(int fd) { + char buffer[BUFFER_SIZE]; + long nread; + long bpos; + struct posix_dent *d; + + for (;;) { + nread = posix_getdents(fd, buffer, BUFFER_SIZE, 0); + ERROR_IF(posix_getdents, nread, == -1); + UNEXP_IF(posix_getdents, nread, < 0); + + if (nread == 0) { + break; + } + + for (bpos = 0; bpos < nread;) { + d = (struct posix_dent *) (buffer + bpos); + printf(" ino = %-10lu name = %s\n", (unsigned long)d->d_ino, d->d_name); + bpos += d->d_reclen; + } + } +} + +int main(void) { + int fd = open("example_dir/", O_RDONLY | O_DIRECTORY); + ERROR_IF(open, fd, == -1); + read_and_print_directory(fd); + + off_t seek_result = lseek(fd, 0, SEEK_SET); + ERROR_IF(lseek, seek_result, == -1); + read_and_print_directory(fd); + + close(fd); +} +#else + +int main(void) { + // glibc doesn't support posix_getdents & O_DIRECTORY +} +#endif diff --git a/tests/dirent/scandir.c b/tests/dirent/scandir.c new file mode 100644 index 0000000000..c530abe48f --- /dev/null +++ b/tests/dirent/scandir.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int filter(const struct dirent* dirent) { + return strstr(dirent->d_name, "3") == NULL; +} + +int main(void) { + struct dirent** array; + + int len = scandir("example_dir/", &array, filter, alphasort); + ERROR_IF(scandir, len, == -1); + UNEXP_IF(scandir, len, < 0); + + for(int i = 0; i < len; i += 1) { + // TODO: Redox does not yet provide . or .. - so filter them out + // in order to make output match on all systems + if ( + strcmp(array[i]->d_name, ".") != 0 && + strcmp(array[i]->d_name, "..") != 0 + ) { + puts(array[i]->d_name); + } + free(array[i]); + } + free(array); +} diff --git a/tests/dlfcn.c b/tests/dlfcn.c new file mode 100644 index 0000000000..ed81a7e0e5 --- /dev/null +++ b/tests/dlfcn.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include + +#define SHARED_LIB "sharedlib.so" + +int add(int a, int b) { return a + b; } + +void test_dlopen_null() { + void *handle = dlopen(NULL, RTLD_LAZY); + if (!handle) { + printf("dlopen(NULL) failed: %s\n", dlerror()); + exit(1); + } + + int (*f)(int, int); + *(void **)(&f) = dlsym(handle, "add"); + + if (!f) { + printf("dlsym(handle, add) failed: %s\n", dlerror()); + exit(2); + } + int a = 22; + int b = 33; + printf("add(%d, %d) = %d\n", a, b, f(a, b)); + dlclose(handle); +} + +void test_dlopen_libc() { + void *handle = dlopen("libc.so.6", RTLD_LAZY); + if (!handle) { + printf("dlopen(libc.so.6) failed\n"); + exit(1); + } + + int (*f)(const char *); + *(void **)(&f) = dlsym(handle, "puts"); + + if (!f) { + printf("dlsym(handle, puts) failed\n"); + exit(2); + } + f("puts from dlopened libc"); + dlclose(handle); +} + +void test_dlsym_function() { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY); + if (!handle) { + printf("dlopen(sharedlib.so) failed\n"); + exit(1); + } + + void (*f)(); + *(void **)(&f) = dlsym(handle, "print"); + + if (!f) { + printf("dlsym(handle, print) failed\n"); + exit(2); + } + f(); + dlclose(handle); +} + +void test_dlsym_global_var() { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY); + if (!handle) { + printf("dlopen(sharedlib.so) failed\n"); + exit(1); + } + int *global_var = dlsym(handle, "global_var"); + if (!global_var) { + printf("dlsym(handle, global_var) failed\n"); + exit(2); + } + printf("main: global_var == %d\n", *global_var); + dlclose(handle); +} + +void test_dlsym_tls_var() { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY); + if (!handle) { + printf("dlopen(sharedlib.so) failed\n"); + exit(1); + } + int *tls_var = dlsym(handle, "tls_var"); + if (!tls_var) { + printf("dlsym(handle, tls_var) failed\n"); + exit(2); + } + printf("main: tls_var == %d\n", *tls_var); + dlclose(handle); +} + +void test_dlunload(void) { + void *handle = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_LOCAL); + void *handle2 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_LOCAL); + assert(handle == handle2 && handle); + assert(!dlclose(handle)); + void *handle3 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD); + assert(handle3 == handle2); + assert(!dlclose(handle3)); + assert(!dlclose(handle2)); + void *handle4 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD); + assert(handle4 == NULL); + + void *handle5 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_GLOBAL); + assert(handle5); + assert(!dlclose(handle5)); + void *handle6 = dlopen(SHARED_LIB, RTLD_LAZY | RTLD_NOLOAD); + assert(handle6 == NULL); +} + +int main() { + test_dlopen_null(); + test_dlopen_libc(); + test_dlsym_function(); + test_dlsym_global_var(); + test_dlsym_tls_var(); + test_dlunload(); +} diff --git a/tests/dlopen_scopes.c b/tests/dlopen_scopes.c new file mode 100644 index 0000000000..3d8de7d74c --- /dev/null +++ b/tests/dlopen_scopes.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +int main(void) { + void *handle = dlopen("libfoobar.so", RTLD_LAZY | RTLD_LOCAL); + if (!handle) { + printf("dlopen(libfoobar.so): %s\n", dlerror()); + return EXIT_FAILURE; + } + + assert(dlsym(handle, "BAR") != NULL); + assert(dlsym(handle, "FOO") != NULL); + // not in the global scope + assert(dlsym(RTLD_DEFAULT, "BAR") == NULL); + assert(dlsym(RTLD_DEFAULT, "FOO") == NULL); + + void *self = dlopen(NULL, RTLD_LAZY | RTLD_LOCAL); + if (!self) { + printf("dlopen(NULL): %s\n", dlerror()); + return EXIT_FAILURE; + } + + assert(dlsym(self, "FOO") == NULL); + assert(dlsym(self, "BAR") == NULL); + + assert(dlclose(self) == 0); + + // Promote the library to the global scope. + assert(dlopen("libfoobar.so", /* RTLD_NOLOAD |*/ RTLD_NOW | RTLD_GLOBAL)); + + assert(dlsym(RTLD_DEFAULT, "FOO") != NULL); + assert(dlsym(RTLD_DEFAULT, "BAR") != NULL); + + return EXIT_SUCCESS; +} diff --git a/tests/endian.c b/tests/endian.c new file mode 100644 index 0000000000..2eeb7cdab0 --- /dev/null +++ b/tests/endian.c @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "test_helpers.h" + +void to_be(uintmax_t in, uint8_t *out, size_t size) { + for (size_t i = 0; i < size; i++) { + out[i] = (in >> 8*(size - 1 - i)) & 0xff; + } +} + +void to_le(uintmax_t in, uint8_t *out, size_t size) { + for (size_t i = 0; i < size; i++) { + out[i] = (in >> 8*i) & 0xff; + } +} + +int main() { + uint16_t zero_u16 = 0; + assert(be16toh(zero_u16) == zero_u16); + assert(htobe16(zero_u16) == zero_u16); + assert(htole16(zero_u16) == zero_u16); + assert(le16toh(zero_u16) == zero_u16); + + uint16_t u16_ne = 0x0123; + uint16_t u16_be, u16_le; + to_be(u16_ne, (uint8_t *)&u16_be, sizeof(uint16_t)); + to_le(u16_ne, (uint8_t *)&u16_le, sizeof(uint16_t)); + assert(be16toh(u16_be) == u16_ne); + assert(htobe16(u16_ne) == u16_be); + assert(htole16(u16_ne) == u16_le); + assert(le16toh(u16_le) == u16_ne); + + uint32_t zero_u32 = 0; + assert(be32toh(zero_u32) == zero_u32); + assert(htobe32(zero_u32) == zero_u32); + assert(htole32(zero_u32) == zero_u32); + assert(le32toh(zero_u32) == zero_u32); + + uint32_t u32_ne = 0x01234567; + uint32_t u32_be, u32_le; + to_be(u32_ne, (uint8_t *)&u32_be, sizeof(uint32_t)); + to_le(u32_ne, (uint8_t *)&u32_le, sizeof(uint32_t)); + assert(be32toh(u32_be) == u32_ne); + assert(htobe32(u32_ne) == u32_be); + assert(htole32(u32_ne) == u32_le); + assert(le32toh(u32_le) == u32_ne); + + uint64_t zero_u64 = 0; + assert(be64toh(zero_u64) == zero_u64); + assert(htobe64(zero_u64) == zero_u64); + assert(htole64(zero_u64) == zero_u64); + assert(le64toh(zero_u64) == zero_u64); + + uint64_t u64_ne = 0x0123456789ABCDEF; + uint64_t u64_be, u64_le; + to_be(u64_ne, (uint8_t *)&u64_be, sizeof(uint64_t)); + to_le(u64_ne, (uint8_t *)&u64_le, sizeof(uint64_t)); + assert(be64toh(u64_be) == u64_ne); + assert(htobe64(u64_ne) == u64_be); + assert(htole64(u64_ne) == u64_le); + assert(le64toh(u64_le) == u64_ne); + + /* Test that the BYTE_ORDER, LITTLE_ENDIAN and BIG_ENDIAN macros are available */ + /* It is in principle possible to have further endiannesses, like PDP_ENDIAN */ + if (u64_ne == u64_le) { + assert(BYTE_ORDER == LITTLE_ENDIAN); + assert(BYTE_ORDER != BIG_ENDIAN); + } + if (u64_ne == u64_be) { + assert(BYTE_ORDER == BIG_ENDIAN); + assert(BYTE_ORDER != LITTLE_ENDIAN); + } +} diff --git a/tests/err.c b/tests/err.c new file mode 100644 index 0000000000..eda95d1743 --- /dev/null +++ b/tests/err.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +// does not compile with glibc +#ifndef __GLIBC__ + +__attribute__((nonnull(2))) +static void vwarn_test(int code, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + vwarnc(code, fmt, ap); + va_end(ap); +} + +static void log_to_stdout(void) { + err_set_file(stdout); + warnc(ENOENT, "Dang it, Bobby."); + err_set_file(NULL); +} + +void user_callback(int code) { + printf("Exiting due to error code: %d\n", code); +} + +int main(void) { + err_set_exit(user_callback); + + // Set errno to a known value for verifiable messages + // (Also, "Owner died" is just too funny not to use) + errno = EOWNERDEAD; + + warn("Ran out of coffee"); + warnx("%s pulled out your ethernet cable", "Cat"); + warnc(EACCES, "Eat %d cookies", 42); + + vwarn_test(EBADE, "Potato, %s", "krumpli"); + + // Set the sink to stdout then back to stderr + log_to_stdout(); + warnc(EPERM, + "I'm sorry, Dave. I'm afraid I can't do that." + ); + + // As long as one err function works they should all work since + // two functions handle everything internally. + errc(EXIT_SUCCESS, EUSERS, "Bye. It's crowded."); + + + // Unreachable + puts("err did not exit"); + return EXIT_FAILURE; +} + +#else +int main(void) { + exit(0); +} +#endif diff --git a/tests/errno.c b/tests/errno.c new file mode 100644 index 0000000000..29c7df5265 --- /dev/null +++ b/tests/errno.c @@ -0,0 +1,680 @@ +#include +#include +#include +#include "test_helpers.h" + +int main(int argc, char **argv) { + // Recent POSIX requires E2BIG, EACCESS etc. to be available as macros +#ifdef E2BIG + puts("E2BIG macro available"); +#else + puts("E2BIG macro missing"); +#endif /* E2BIG */ +#ifdef EACCES + puts("EACCES macro available"); +#else + puts("EACCES macro missing"); +#endif /* EACCES */ +#ifdef EADDRINUSE + puts("EADDRINUSE macro available"); +#else + puts("EADDRINUSE macro missing"); +#endif /* EADDRINUSE */ +#ifdef EADDRNOTAVAIL + puts("EADDRNOTAVAIL macro available"); +#else + puts("EADDRNOTAVAIL macro missing"); +#endif /* EADDRNOTAVAIL */ +#ifdef EADV + puts("EADV macro available"); +#else + puts("EADV macro missing"); +#endif /* EADV */ +#ifdef EAFNOSUPPORT + puts("EAFNOSUPPORT macro available"); +#else + puts("EAFNOSUPPORT macro missing"); +#endif /* EAFNOSUPPORT */ +#ifdef EAGAIN + puts("EAGAIN macro available"); +#else + puts("EAGAIN macro missing"); +#endif /* EAGAIN */ +#ifdef EALREADY + puts("EALREADY macro available"); +#else + puts("EALREADY macro missing"); +#endif /* EALREADY */ +#ifdef EBADE + puts("EBADE macro available"); +#else + puts("EBADE macro missing"); +#endif /* EBADE */ +#ifdef EBADF + puts("EBADF macro available"); +#else + puts("EBADF macro missing"); +#endif /* EBADF */ +#ifdef EBADFD + puts("EBADFD macro available"); +#else + puts("EBADFD macro missing"); +#endif /* EBADFD */ +#ifdef EBADMSG + puts("EBADMSG macro available"); +#else + puts("EBADMSG macro missing"); +#endif /* EBADMSG */ +#ifdef EBADR + puts("EBADR macro available"); +#else + puts("EBADR macro missing"); +#endif /* EBADR */ +#ifdef EBADRQC + puts("EBADRQC macro available"); +#else + puts("EBADRQC macro missing"); +#endif /* EBADRQC */ +#ifdef EBADSLT + puts("EBADSLT macro available"); +#else + puts("EBADSLT macro missing"); +#endif /* EBADSLT */ +#ifdef EBFONT + puts("EBFONT macro available"); +#else + puts("EBFONT macro missing"); +#endif /* EBFONT */ +#ifdef EBUSY + puts("EBUSY macro available"); +#else + puts("EBUSY macro missing"); +#endif /* EBUSY */ +#ifdef ECANCELED + puts("ECANCELED macro available"); +#else + puts("ECANCELED macro missing"); +#endif /* ECANCELED */ +#ifdef ECHILD + puts("ECHILD macro available"); +#else + puts("ECHILD macro missing"); +#endif /* ECHILD */ +#ifdef ECHRNG + puts("ECHRNG macro available"); +#else + puts("ECHRNG macro missing"); +#endif /* ECHRNG */ +#ifdef ECOMM + puts("ECOMM macro available"); +#else + puts("ECOMM macro missing"); +#endif /* ECOMM */ +#ifdef ECONNABORTED + puts("ECONNABORTED macro available"); +#else + puts("ECONNABORTED macro missing"); +#endif /* ECONNABORTED */ +#ifdef ECONNREFUSED + puts("ECONNREFUSED macro available"); +#else + puts("ECONNREFUSED macro missing"); +#endif /* ECONNREFUSED */ +#ifdef ECONNRESET + puts("ECONNRESET macro available"); +#else + puts("ECONNRESET macro missing"); +#endif /* ECONNRESET */ +#ifdef EDEADLK + puts("EDEADLK macro available"); +#else + puts("EDEADLK macro missing"); +#endif /* EDEADLK */ +#ifdef EDEADLOCK + puts("EDEADLOCK macro available"); +#else + puts("EDEADLOCK macro missing"); +#endif /* EDEADLOCK */ +#ifdef EDESTADDRREQ + puts("EDESTADDRREQ macro available"); +#else + puts("EDESTADDRREQ macro missing"); +#endif /* EDESTADDRREQ */ +#ifdef EDOM + puts("EDOM macro available"); +#else + puts("EDOM macro missing"); +#endif /* EDOM */ +#ifdef EDOTDOT + puts("EDOTDOT macro available"); +#else + puts("EDOTDOT macro missing"); +#endif /* EDOTDOT */ +#ifdef EDQUOT + puts("EDQUOT macro available"); +#else + puts("EDQUOT macro missing"); +#endif /* EDQUOT */ +#ifdef EEXIST + puts("EEXIST macro available"); +#else + puts("EEXIST macro missing"); +#endif /* EEXIST */ +#ifdef EFAULT + puts("EFAULT macro available"); +#else + puts("EFAULT macro missing"); +#endif /* EFAULT */ +#ifdef EFBIG + puts("EFBIG macro available"); +#else + puts("EFBIG macro missing"); +#endif /* EFBIG */ +#ifdef EHOSTDOWN + puts("EHOSTDOWN macro available"); +#else + puts("EHOSTDOWN macro missing"); +#endif /* EHOSTDOWN */ +#ifdef EHOSTUNREACH + puts("EHOSTUNREACH macro available"); +#else + puts("EHOSTUNREACH macro missing"); +#endif /* EHOSTUNREACH */ +#ifdef EIDRM + puts("EIDRM macro available"); +#else + puts("EIDRM macro missing"); +#endif /* EIDRM */ +#ifdef EILSEQ + puts("EILSEQ macro available"); +#else + puts("EILSEQ macro missing"); +#endif /* EILSEQ */ +#ifdef EINPROGRESS + puts("EINPROGRESS macro available"); +#else + puts("EINPROGRESS macro missing"); +#endif /* EINPROGRESS */ +#ifdef EINTR + puts("EINTR macro available"); +#else + puts("EINTR macro missing"); +#endif /* EINTR */ +#ifdef EINVAL + puts("EINVAL macro available"); +#else + puts("EINVAL macro missing"); +#endif /* EINVAL */ +#ifdef EIO + puts("EIO macro available"); +#else + puts("EIO macro missing"); +#endif /* EIO */ +#ifdef EISCONN + puts("EISCONN macro available"); +#else + puts("EISCONN macro missing"); +#endif /* EISCONN */ +#ifdef EISDIR + puts("EISDIR macro available"); +#else + puts("EISDIR macro missing"); +#endif /* EISDIR */ +#ifdef EISNAM + puts("EISNAM macro available"); +#else + puts("EISNAM macro missing"); +#endif /* EISNAM */ +#ifdef EKEYEXPIRED + puts("EKEYEXPIRED macro available"); +#else + puts("EKEYEXPIRED macro missing"); +#endif /* EKEYEXPIRED */ +#ifdef EKEYREJECTED + puts("EKEYREJECTED macro available"); +#else + puts("EKEYREJECTED macro missing"); +#endif /* EKEYREJECTED */ +#ifdef EKEYREVOKED + puts("EKEYREVOKED macro available"); +#else + puts("EKEYREVOKED macro missing"); +#endif /* EKEYREVOKED */ +#ifdef EL2HLT + puts("EL2HLT macro available"); +#else + puts("EL2HLT macro missing"); +#endif /* EL2HLT */ +#ifdef EL2NSYNC + puts("EL2NSYNC macro available"); +#else + puts("EL2NSYNC macro missing"); +#endif /* EL2NSYNC */ +#ifdef EL3HLT + puts("EL3HLT macro available"); +#else + puts("EL3HLT macro missing"); +#endif /* EL3HLT */ +#ifdef EL3RST + puts("EL3RST macro available"); +#else + puts("EL3RST macro missing"); +#endif /* EL3RST */ +#ifdef ELIBACC + puts("ELIBACC macro available"); +#else + puts("ELIBACC macro missing"); +#endif /* ELIBACC */ +#ifdef ELIBBAD + puts("ELIBBAD macro available"); +#else + puts("ELIBBAD macro missing"); +#endif /* ELIBBAD */ +#ifdef ELIBEXEC + puts("ELIBEXEC macro available"); +#else + puts("ELIBEXEC macro missing"); +#endif /* ELIBEXEC */ +#ifdef ELIBMAX + puts("ELIBMAX macro available"); +#else + puts("ELIBMAX macro missing"); +#endif /* ELIBMAX */ +#ifdef ELIBSCN + puts("ELIBSCN macro available"); +#else + puts("ELIBSCN macro missing"); +#endif /* ELIBSCN */ +#ifdef ELNRNG + puts("ELNRNG macro available"); +#else + puts("ELNRNG macro missing"); +#endif /* ELNRNG */ +#ifdef ELOOP + puts("ELOOP macro available"); +#else + puts("ELOOP macro missing"); +#endif /* ELOOP */ +#ifdef EMEDIUMTYPE + puts("EMEDIUMTYPE macro available"); +#else + puts("EMEDIUMTYPE macro missing"); +#endif /* EMEDIUMTYPE */ +#ifdef EMFILE + puts("EMFILE macro available"); +#else + puts("EMFILE macro missing"); +#endif /* EMFILE */ +#ifdef EMLINK + puts("EMLINK macro available"); +#else + puts("EMLINK macro missing"); +#endif /* EMLINK */ +#ifdef EMSGSIZE + puts("EMSGSIZE macro available"); +#else + puts("EMSGSIZE macro missing"); +#endif /* EMSGSIZE */ +#ifdef EMULTIHOP + puts("EMULTIHOP macro available"); +#else + puts("EMULTIHOP macro missing"); +#endif /* EMULTIHOP */ +#ifdef ENAMETOOLONG + puts("ENAMETOOLONG macro available"); +#else + puts("ENAMETOOLONG macro missing"); +#endif /* ENAMETOOLONG */ +#ifdef ENAVAIL + puts("ENAVAIL macro available"); +#else + puts("ENAVAIL macro missing"); +#endif /* ENAVAIL */ +#ifdef ENETDOWN + puts("ENETDOWN macro available"); +#else + puts("ENETDOWN macro missing"); +#endif /* ENETDOWN */ +#ifdef ENETRESET + puts("ENETRESET macro available"); +#else + puts("ENETRESET macro missing"); +#endif /* ENETRESET */ +#ifdef ENETUNREACH + puts("ENETUNREACH macro available"); +#else + puts("ENETUNREACH macro missing"); +#endif /* ENETUNREACH */ +#ifdef ENFILE + puts("ENFILE macro available"); +#else + puts("ENFILE macro missing"); +#endif /* ENFILE */ +#ifdef ENOANO + puts("ENOANO macro available"); +#else + puts("ENOANO macro missing"); +#endif /* ENOANO */ +#ifdef ENOBUFS + puts("ENOBUFS macro available"); +#else + puts("ENOBUFS macro missing"); +#endif /* ENOBUFS */ +#ifdef ENOCSI + puts("ENOCSI macro available"); +#else + puts("ENOCSI macro missing"); +#endif /* ENOCSI */ +#ifdef ENODATA + puts("ENODATA macro available"); +#else + puts("ENODATA macro missing"); +#endif /* ENODATA */ +#ifdef ENODEV + puts("ENODEV macro available"); +#else + puts("ENODEV macro missing"); +#endif /* ENODEV */ +#ifdef ENOENT + puts("ENOENT macro available"); +#else + puts("ENOENT macro missing"); +#endif /* ENOENT */ +#ifdef ENOEXEC + puts("ENOEXEC macro available"); +#else + puts("ENOEXEC macro missing"); +#endif /* ENOEXEC */ +#ifdef ENOKEY + puts("ENOKEY macro available"); +#else + puts("ENOKEY macro missing"); +#endif /* ENOKEY */ +#ifdef ENOLCK + puts("ENOLCK macro available"); +#else + puts("ENOLCK macro missing"); +#endif /* ENOLCK */ +#ifdef ENOLINK + puts("ENOLINK macro available"); +#else + puts("ENOLINK macro missing"); +#endif /* ENOLINK */ +#ifdef ENOMEDIUM + puts("ENOMEDIUM macro available"); +#else + puts("ENOMEDIUM macro missing"); +#endif /* ENOMEDIUM */ +#ifdef ENOMEM + puts("ENOMEM macro available"); +#else + puts("ENOMEM macro missing"); +#endif /* ENOMEM */ +#ifdef ENOMSG + puts("ENOMSG macro available"); +#else + puts("ENOMSG macro missing"); +#endif /* ENOMSG */ +#ifdef ENONET + puts("ENONET macro available"); +#else + puts("ENONET macro missing"); +#endif /* ENONET */ +#ifdef ENOPKG + puts("ENOPKG macro available"); +#else + puts("ENOPKG macro missing"); +#endif /* ENOPKG */ +#ifdef ENOPROTOOPT + puts("ENOPROTOOPT macro available"); +#else + puts("ENOPROTOOPT macro missing"); +#endif /* ENOPROTOOPT */ +#ifdef ENOSPC + puts("ENOSPC macro available"); +#else + puts("ENOSPC macro missing"); +#endif /* ENOSPC */ +#ifdef ENOSR + puts("ENOSR macro available"); +#else + puts("ENOSR macro missing"); +#endif /* ENOSR */ +#ifdef ENOSTR + puts("ENOSTR macro available"); +#else + puts("ENOSTR macro missing"); +#endif /* ENOSTR */ +#ifdef ENOSYS + puts("ENOSYS macro available"); +#else + puts("ENOSYS macro missing"); +#endif /* ENOSYS */ +#ifdef ENOTBLK + puts("ENOTBLK macro available"); +#else + puts("ENOTBLK macro missing"); +#endif /* ENOTBLK */ +#ifdef ENOTCONN + puts("ENOTCONN macro available"); +#else + puts("ENOTCONN macro missing"); +#endif /* ENOTCONN */ +#ifdef ENOTDIR + puts("ENOTDIR macro available"); +#else + puts("ENOTDIR macro missing"); +#endif /* ENOTDIR */ +#ifdef ENOTEMPTY + puts("ENOTEMPTY macro available"); +#else + puts("ENOTEMPTY macro missing"); +#endif /* ENOTEMPTY */ +#ifdef ENOTNAM + puts("ENOTNAM macro available"); +#else + puts("ENOTNAM macro missing"); +#endif /* ENOTNAM */ +#ifdef ENOTRECOVERABLE + puts("ENOTRECOVERABLE macro available"); +#else + puts("ENOTRECOVERABLE macro missing"); +#endif /* ENOTRECOVERABLE */ +#ifdef ENOTSOCK + puts("ENOTSOCK macro available"); +#else + puts("ENOTSOCK macro missing"); +#endif /* ENOTSOCK */ +#ifdef ENOTSUP + puts("ENOTSUP macro available"); +#else + puts("ENOTSUP macro missing"); +#endif /* ENOTSUP */ +#ifdef ENOTTY + puts("ENOTTY macro available"); +#else + puts("ENOTTY macro missing"); +#endif /* ENOTTY */ +#ifdef ENOTUNIQ + puts("ENOTUNIQ macro available"); +#else + puts("ENOTUNIQ macro missing"); +#endif /* ENOTUNIQ */ +#ifdef ENXIO + puts("ENXIO macro available"); +#else + puts("ENXIO macro missing"); +#endif /* ENXIO */ +#ifdef EOPNOTSUPP + puts("EOPNOTSUPP macro available"); +#else + puts("EOPNOTSUPP macro missing"); +#endif /* EOPNOTSUPP */ +#ifdef EOVERFLOW + puts("EOVERFLOW macro available"); +#else + puts("EOVERFLOW macro missing"); +#endif /* EOVERFLOW */ +#ifdef EOWNERDEAD + puts("EOWNERDEAD macro available"); +#else + puts("EOWNERDEAD macro missing"); +#endif /* EOWNERDEAD */ +#ifdef EPERM + puts("EPERM macro available"); +#else + puts("EPERM macro missing"); +#endif /* EPERM */ +#ifdef EPFNOSUPPORT + puts("EPFNOSUPPORT macro available"); +#else + puts("EPFNOSUPPORT macro missing"); +#endif /* EPFNOSUPPORT */ +#ifdef EPIPE + puts("EPIPE macro available"); +#else + puts("EPIPE macro missing"); +#endif /* EPIPE */ +#ifdef EPROTO + puts("EPROTO macro available"); +#else + puts("EPROTO macro missing"); +#endif /* EPROTO */ +#ifdef EPROTONOSUPPORT + puts("EPROTONOSUPPORT macro available"); +#else + puts("EPROTONOSUPPORT macro missing"); +#endif /* EPROTONOSUPPORT */ +#ifdef EPROTOTYPE + puts("EPROTOTYPE macro available"); +#else + puts("EPROTOTYPE macro missing"); +#endif /* EPROTOTYPE */ +#ifdef ERANGE + puts("ERANGE macro available"); +#else + puts("ERANGE macro missing"); +#endif /* ERANGE */ +#ifdef EREMCHG + puts("EREMCHG macro available"); +#else + puts("EREMCHG macro missing"); +#endif /* EREMCHG */ +#ifdef EREMOTE + puts("EREMOTE macro available"); +#else + puts("EREMOTE macro missing"); +#endif /* EREMOTE */ +#ifdef EREMOTEIO + puts("EREMOTEIO macro available"); +#else + puts("EREMOTEIO macro missing"); +#endif /* EREMOTEIO */ +#ifdef ERESTART + puts("ERESTART macro available"); +#else + puts("ERESTART macro missing"); +#endif /* ERESTART */ +#ifdef EROFS + puts("EROFS macro available"); +#else + puts("EROFS macro missing"); +#endif /* EROFS */ +#ifdef ESHUTDOWN + puts("ESHUTDOWN macro available"); +#else + puts("ESHUTDOWN macro missing"); +#endif /* ESHUTDOWN */ +#ifdef ESOCKTNOSUPPORT + puts("ESOCKTNOSUPPORT macro available"); +#else + puts("ESOCKTNOSUPPORT macro missing"); +#endif /* ESOCKTNOSUPPORT */ +#ifdef ESPIPE + puts("ESPIPE macro available"); +#else + puts("ESPIPE macro missing"); +#endif /* ESPIPE */ +#ifdef ESRCH + puts("ESRCH macro available"); +#else + puts("ESRCH macro missing"); +#endif /* ESRCH */ +#ifdef ESRMNT + puts("ESRMNT macro available"); +#else + puts("ESRMNT macro missing"); +#endif /* ESRMNT */ +#ifdef ESTALE + puts("ESTALE macro available"); +#else + puts("ESTALE macro missing"); +#endif /* ESTALE */ +#ifdef ESTRPIPE + puts("ESTRPIPE macro available"); +#else + puts("ESTRPIPE macro missing"); +#endif /* ESTRPIPE */ +#ifdef ETIME + puts("ETIME macro available"); +#else + puts("ETIME macro missing"); +#endif /* ETIME */ +#ifdef ETIMEDOUT + puts("ETIMEDOUT macro available"); +#else + puts("ETIMEDOUT macro missing"); +#endif /* ETIMEDOUT */ +#ifdef ETOOMANYREFS + puts("ETOOMANYREFS macro available"); +#else + puts("ETOOMANYREFS macro missing"); +#endif /* ETOOMANYREFS */ +#ifdef ETXTBSY + puts("ETXTBSY macro available"); +#else + puts("ETXTBSY macro missing"); +#endif /* ETXTBSY */ +#ifdef EUCLEAN + puts("EUCLEAN macro available"); +#else + puts("EUCLEAN macro missing"); +#endif /* EUCLEAN */ +#ifdef EUNATCH + puts("EUNATCH macro available"); +#else + puts("EUNATCH macro missing"); +#endif /* EUNATCH */ +#ifdef EUSERS + puts("EUSERS macro available"); +#else + puts("EUSERS macro missing"); +#endif /* EUSERS */ +#ifdef EWOULDBLOCK + puts("EWOULDBLOCK macro available"); +#else + puts("EWOULDBLOCK macro missing"); +#endif /* EWOULDBLOCK */ +#ifdef EXDEV + puts("EXDEV macro available"); +#else + puts("EXDEV macro missing"); +#endif /* EXDEV */ +#ifdef EXFULL + puts("EXFULL macro available"); +#else + puts("EXFULL macro missing"); +#endif /* EXFULL */ + + assert(argc > 0); + + puts(program_invocation_short_name); + + argv[0] = "changed to argv[0]"; + program_invocation_name = "changed to program_invocation_name"; + program_invocation_short_name = "changed to program_invocation_short_name"; + + puts(argv[0]); + puts(program_invocation_name); + puts(program_invocation_short_name); +} diff --git a/tests/error.c b/tests/error.c new file mode 100644 index 0000000000..61ba98f7f3 --- /dev/null +++ b/tests/error.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +// does not compile with glibc +#ifndef __GLIBC__ +int main(void) { + chdir("nonexistent"); + int err = errno; + + printf("errno: %d = %s\n", err, strerror(errno)); + perror("perror"); + + char buf1[256] = {0}; + int ret1 = strerror_r(err, buf1, 256); + printf("errno: %d = %s, return: %d\n", err, buf1, ret1); + + char buf2[3] = {0}; + int ret2 = strerror_r(err, buf2, 3); + printf("errno: %d = %s, return: %d\n", err, buf2, ret2); + + char buf3[256] = {0}; + int ret3 = strerror_r(err, buf3, 0); + printf("errno: %d = %s, return: %d\n", err, buf3, ret3); + + // Test that the statically allocated buffer doesn't overflow + for (int code = EPERM; code < ENOTRECOVERABLE; ++code) { + const char* message = strerror(code); + if (!message) { + errx(EXIT_FAILURE, + "Expected message for error code: %d", + code + ); + } + } + + const char* actual_error = strerror(INT_MAX); + char expected_error[256] = {0}; + sprintf(expected_error, "Unknown error %d", INT_MAX); + if (strncmp(expected_error, actual_error, 25) != 0) { + errx(EXIT_FAILURE, + "Expected: %s\nGot: %s", + expected_error, actual_error + ); + } + + return EXIT_SUCCESS; +} + +#else +int main(void) { + exit(0); +} +#endif diff --git a/tests/example_dir/1-never-gonna-give-you-up b/tests/example_dir/1-never-gonna-give-you-up new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/2-never-gonna-let-you-down b/tests/example_dir/2-never-gonna-let-you-down new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/3-never-gonna-run-around b/tests/example_dir/3-never-gonna-run-around new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/4-and-desert-you b/tests/example_dir/4-and-desert-you new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/5-never-gonna-make-you-cry b/tests/example_dir/5-never-gonna-make-you-cry new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/6-never-gonna-say-goodbye b/tests/example_dir/6-never-gonna-say-goodbye new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/7-never-gonna-tell-a-lie b/tests/example_dir/7-never-gonna-tell-a-lie new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/example_dir/8-and-hurt-you b/tests/example_dir/8-and-hurt-you new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/alloca.stderr b/tests/expected/bins_dynamic/alloca.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/alloca.stdout b/tests/expected/bins_dynamic/alloca.stdout new file mode 100644 index 0000000000..d0a7d827f2 --- /dev/null +++ b/tests/expected/bins_dynamic/alloca.stdout @@ -0,0 +1 @@ +AAAAAAAAAAAAAAAA diff --git a/tests/expected/bins_dynamic/args.stderr b/tests/expected/bins_dynamic/args.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/args.stdout b/tests/expected/bins_dynamic/args.stdout new file mode 100644 index 0000000000..deefb2f3da --- /dev/null +++ b/tests/expected/bins_dynamic/args.stdout @@ -0,0 +1 @@ +test args diff --git a/tests/expected/bins_dynamic/arpainet.stderr b/tests/expected/bins_dynamic/arpainet.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/arpainet.stdout b/tests/expected/bins_dynamic/arpainet.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/assert.stderr b/tests/expected/bins_dynamic/assert.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/assert.stdout b/tests/expected/bins_dynamic/assert.stdout new file mode 100644 index 0000000000..028678d987 --- /dev/null +++ b/tests/expected/bins_dynamic/assert.stdout @@ -0,0 +1,2 @@ +yay! +groovy! diff --git a/tests/expected/bins_dynamic/constructor.stderr b/tests/expected/bins_dynamic/constructor.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/constructor.stdout b/tests/expected/bins_dynamic/constructor.stdout new file mode 100644 index 0000000000..14e23d4cbb --- /dev/null +++ b/tests/expected/bins_dynamic/constructor.stdout @@ -0,0 +1,6 @@ +constructor (101) +constructor (102) +constructor (103) +constructor (104) +constructor (no priority) +main diff --git a/tests/expected/bins_dynamic/crypt/blowfish.stderr b/tests/expected/bins_dynamic/crypt/blowfish.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/blowfish.stdout b/tests/expected/bins_dynamic/crypt/blowfish.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/md5.stderr b/tests/expected/bins_dynamic/crypt/md5.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/md5.stdout b/tests/expected/bins_dynamic/crypt/md5.stdout new file mode 100644 index 0000000000..f985b46aff --- /dev/null +++ b/tests/expected/bins_dynamic/crypt/md5.stdout @@ -0,0 +1 @@ +Success! diff --git a/tests/expected/bins_dynamic/crypt/pbkdf2.stderr b/tests/expected/bins_dynamic/crypt/pbkdf2.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/pbkdf2.stdout b/tests/expected/bins_dynamic/crypt/pbkdf2.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/scrypt.stderr b/tests/expected/bins_dynamic/crypt/scrypt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/scrypt.stdout b/tests/expected/bins_dynamic/crypt/scrypt.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/sha256.stderr b/tests/expected/bins_dynamic/crypt/sha256.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/sha256.stdout b/tests/expected/bins_dynamic/crypt/sha256.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/sha512.stderr b/tests/expected/bins_dynamic/crypt/sha512.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/crypt/sha512.stdout b/tests/expected/bins_dynamic/crypt/sha512.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/ctype.stderr b/tests/expected/bins_dynamic/ctype.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/ctype.stdout b/tests/expected/bins_dynamic/ctype.stdout new file mode 100644 index 0000000000..e83b1e5f11 --- /dev/null +++ b/tests/expected/bins_dynamic/ctype.stdout @@ -0,0 +1 @@ +Success: 0 diff --git a/tests/expected/bins_dynamic/destructor.stderr b/tests/expected/bins_dynamic/destructor.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/destructor.stdout b/tests/expected/bins_dynamic/destructor.stdout new file mode 100644 index 0000000000..1da7c29706 --- /dev/null +++ b/tests/expected/bins_dynamic/destructor.stdout @@ -0,0 +1,6 @@ +main +destructor (no priority) +destructor (104) +destructor (103) +destructor (102) +destructor (101) diff --git a/tests/expected/bins_dynamic/dirent/fdopendir.stderr b/tests/expected/bins_dynamic/dirent/fdopendir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/dirent/fdopendir.stdout b/tests/expected/bins_dynamic/dirent/fdopendir.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/dirent/scandir.stderr b/tests/expected/bins_dynamic/dirent/scandir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/dirent/scandir.stdout b/tests/expected/bins_dynamic/dirent/scandir.stdout new file mode 100644 index 0000000000..4820bea846 --- /dev/null +++ b/tests/expected/bins_dynamic/dirent/scandir.stdout @@ -0,0 +1,7 @@ +1-never-gonna-give-you-up +2-never-gonna-let-you-down +4-and-desert-you +5-never-gonna-make-you-cry +6-never-gonna-say-goodbye +7-never-gonna-tell-a-lie +8-and-hurt-you diff --git a/tests/expected/bins_dynamic/dlfcn.stderr b/tests/expected/bins_dynamic/dlfcn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/dlfcn.stdout b/tests/expected/bins_dynamic/dlfcn.stdout new file mode 100644 index 0000000000..5b51826bea --- /dev/null +++ b/tests/expected/bins_dynamic/dlfcn.stdout @@ -0,0 +1,6 @@ +add(22, 33) = 55 +puts from dlopened libc +sharedlib: global_var == 42 +sharedlib: tls_var == 21 +main: global_var == 42 +main: tls_var == 21 diff --git a/tests/expected/bins_dynamic/dlopen_scopes.stderr b/tests/expected/bins_dynamic/dlopen_scopes.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/dlopen_scopes.stdout b/tests/expected/bins_dynamic/dlopen_scopes.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/err.stderr b/tests/expected/bins_dynamic/err.stderr new file mode 100644 index 0000000000..b79d9c196f --- /dev/null +++ b/tests/expected/bins_dynamic/err.stderr @@ -0,0 +1,6 @@ +err: Ran out of coffee: Owner died +err: Cat pulled out your ethernet cable +err: Eat 42 cookies: Permission denied +err: Potato, krumpli: Invalid exchange +err: I'm sorry, Dave. I'm afraid I can't do that.: Operation not permitted +err: Bye. It's crowded.: Too many users diff --git a/tests/expected/bins_dynamic/err.stdout b/tests/expected/bins_dynamic/err.stdout new file mode 100644 index 0000000000..8ef72cb5bc --- /dev/null +++ b/tests/expected/bins_dynamic/err.stdout @@ -0,0 +1,2 @@ +err: Dang it, Bobby.: No such file or directory +Exiting due to error code: 87 diff --git a/tests/expected/bins_dynamic/errno.stderr b/tests/expected/bins_dynamic/errno.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/errno.stdout b/tests/expected/bins_dynamic/errno.stdout new file mode 100644 index 0000000000..58419805ea --- /dev/null +++ b/tests/expected/bins_dynamic/errno.stdout @@ -0,0 +1,136 @@ +E2BIG macro available +EACCES macro available +EADDRINUSE macro available +EADDRNOTAVAIL macro available +EADV macro available +EAFNOSUPPORT macro available +EAGAIN macro available +EALREADY macro available +EBADE macro available +EBADF macro available +EBADFD macro available +EBADMSG macro available +EBADR macro available +EBADRQC macro available +EBADSLT macro available +EBFONT macro available +EBUSY macro available +ECANCELED macro available +ECHILD macro available +ECHRNG macro available +ECOMM macro available +ECONNABORTED macro available +ECONNREFUSED macro available +ECONNRESET macro available +EDEADLK macro available +EDEADLOCK macro available +EDESTADDRREQ macro available +EDOM macro available +EDOTDOT macro available +EDQUOT macro available +EEXIST macro available +EFAULT macro available +EFBIG macro available +EHOSTDOWN macro available +EHOSTUNREACH macro available +EIDRM macro available +EILSEQ macro available +EINPROGRESS macro available +EINTR macro available +EINVAL macro available +EIO macro available +EISCONN macro available +EISDIR macro available +EISNAM macro available +EKEYEXPIRED macro available +EKEYREJECTED macro available +EKEYREVOKED macro available +EL2HLT macro available +EL2NSYNC macro available +EL3HLT macro available +EL3RST macro available +ELIBACC macro available +ELIBBAD macro available +ELIBEXEC macro available +ELIBMAX macro available +ELIBSCN macro available +ELNRNG macro available +ELOOP macro available +EMEDIUMTYPE macro available +EMFILE macro available +EMLINK macro available +EMSGSIZE macro available +EMULTIHOP macro available +ENAMETOOLONG macro available +ENAVAIL macro available +ENETDOWN macro available +ENETRESET macro available +ENETUNREACH macro available +ENFILE macro available +ENOANO macro available +ENOBUFS macro available +ENOCSI macro available +ENODATA macro available +ENODEV macro available +ENOENT macro available +ENOEXEC macro available +ENOKEY macro available +ENOLCK macro available +ENOLINK macro available +ENOMEDIUM macro available +ENOMEM macro available +ENOMSG macro available +ENONET macro available +ENOPKG macro available +ENOPROTOOPT macro available +ENOSPC macro available +ENOSR macro available +ENOSTR macro available +ENOSYS macro available +ENOTBLK macro available +ENOTCONN macro available +ENOTDIR macro available +ENOTEMPTY macro available +ENOTNAM macro available +ENOTRECOVERABLE macro available +ENOTSOCK macro available +ENOTSUP macro available +ENOTTY macro available +ENOTUNIQ macro available +ENXIO macro available +EOPNOTSUPP macro available +EOVERFLOW macro available +EOWNERDEAD macro available +EPERM macro available +EPFNOSUPPORT macro available +EPIPE macro available +EPROTO macro available +EPROTONOSUPPORT macro available +EPROTOTYPE macro available +ERANGE macro available +EREMCHG macro available +EREMOTE macro available +EREMOTEIO macro available +ERESTART macro available +EROFS macro available +ESHUTDOWN macro available +ESOCKTNOSUPPORT macro available +ESPIPE macro available +ESRCH macro available +ESRMNT macro available +ESTALE macro available +ESTRPIPE macro available +ETIME macro available +ETIMEDOUT macro available +ETOOMANYREFS macro available +ETXTBSY macro available +EUCLEAN macro available +EUNATCH macro available +EUSERS macro available +EWOULDBLOCK macro available +EXDEV macro available +EXFULL macro available +errno +changed to argv[0] +changed to program_invocation_name +changed to program_invocation_short_name diff --git a/tests/expected/bins_dynamic/error.stderr b/tests/expected/bins_dynamic/error.stderr new file mode 100644 index 0000000000..4eb19d82f3 --- /dev/null +++ b/tests/expected/bins_dynamic/error.stderr @@ -0,0 +1 @@ +perror: No such file or directory diff --git a/tests/expected/bins_dynamic/error.stdout b/tests/expected/bins_dynamic/error.stdout new file mode 100644 index 0000000000..4e744d1bfb --- /dev/null +++ b/tests/expected/bins_dynamic/error.stdout @@ -0,0 +1,4 @@ +errno: 2 = No such file or directory +errno: 2 = No such file or directory, return: 0 +errno: 2 = No, return: 34 +errno: 2 = , return: 34 diff --git a/tests/expected/bins_dynamic/fcntl/create.stderr b/tests/expected/bins_dynamic/fcntl/create.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fcntl/create.stdout b/tests/expected/bins_dynamic/fcntl/create.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fcntl/fcntl.stderr b/tests/expected/bins_dynamic/fcntl/fcntl.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fcntl/fcntl.stdout b/tests/expected/bins_dynamic/fcntl/fcntl.stdout new file mode 100644 index 0000000000..4fb8e07602 --- /dev/null +++ b/tests/expected/bins_dynamic/fcntl/fcntl.stdout @@ -0,0 +1 @@ +duped fd is 1 greater than the original fd diff --git a/tests/expected/bins_dynamic/fcntl/open.stderr b/tests/expected/bins_dynamic/fcntl/open.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fcntl/open.stdout b/tests/expected/bins_dynamic/fcntl/open.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fcntl/posix_fallocate.stderr b/tests/expected/bins_dynamic/fcntl/posix_fallocate.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fcntl/posix_fallocate.stdout b/tests/expected/bins_dynamic/fcntl/posix_fallocate.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fnmatch.stderr b/tests/expected/bins_dynamic/fnmatch.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/fnmatch.stdout b/tests/expected/bins_dynamic/fnmatch.stdout new file mode 100644 index 0000000000..48ba8aef2e --- /dev/null +++ b/tests/expected/bins_dynamic/fnmatch.stdout @@ -0,0 +1,32 @@ +Should succeed: +"*World" matches "Hello World" +"*World" matches "World" +"Hello*" matches "Hello World" +"H[ae]llo?World" matches "Hallo+World" +"H[ae]llo?World" matches "Hello_World" +"[0-9][!a]" matches "1b" +"/a/*/d" matches "/a/b/c/d" +"/a/*/d" matches "/a/bc/d" +"*hello" matches ".hello" +"/*hello" matches "/.hello" +"[a!][a!]" matches "!a" +"[\]]" matches "]" +"[\\]" matches "\" +"hello[/+]world" matches "hello/world" +"hello world" matches "HELLO WORLD" + +Should fail: +"*World" doesn't match "Hello Potato" +"*World" doesn't match "Potato" +"H[ae]llo?World" doesn't match "Hillo+World" +"H[ae]llo?World" doesn't match "Hello__World" +"[0-9][!a]" doesn't match "ab" +"[0-9][!a]" doesn't match "2a" +"/a/*/d" doesn't match "/a/b/c/d" +"/a/*/d" doesn't match "/a/bc/d/" +"*hello" doesn't match ".hello" +"/*hello" doesn't match "/.hello" +"[a!][a!]" doesn't match "ab" +"hello[/+]world" doesn't match "hello/world" +"hello world" doesn't match "HELLO WORLD" +"********************a" doesn't match "xxxxxxxxxxxxxxxxxxxxb" diff --git a/tests/expected/bins_dynamic/futimens.stderr b/tests/expected/bins_dynamic/futimens.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/futimens.stdout b/tests/expected/bins_dynamic/futimens.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/iso646.stderr b/tests/expected/bins_dynamic/iso646.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/iso646.stdout b/tests/expected/bins_dynamic/iso646.stdout new file mode 100644 index 0000000000..5f8a399fe8 --- /dev/null +++ b/tests/expected/bins_dynamic/iso646.stdout @@ -0,0 +1,33 @@ +0 and 0: 0 +1 and 0: 0 +0 and 1: 0 +1 and 1: 1 +0 and_eq 0: 0 +1 and_eq 0: 0 +0 and_eq 1: 0 +1 and_eq 1: 1 +a bitand b: 8 +a bitor b: 14 +compl a: 245 +not 0: 1 +not 1: 0 +0 not_eq 0: 0 +1 not_eq 0: 1 +0 not_eq 1: 0 +1 not_eq 1: 1 +0 or 0: 0 +1 or 0: 1 +0 or 1: 1 +1 or 1: 1 +0 or_eq 0: 0 +1 or_eq 0: 1 +0 or_eq 1: 1 +1 or_eq 1: 1 +0 xor 0: 0 +1 xor 0: 1 +0 xor 1: 1 +1 xor 1: 0 +0 xor_eq 0: 0 +1 xor_eq 0: 1 +0 xor_eq 1: 1 +1 xor_eq 1: 0 diff --git a/tests/expected/bins_dynamic/libgen.stderr b/tests/expected/bins_dynamic/libgen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/libgen.stdout b/tests/expected/bins_dynamic/libgen.stdout new file mode 100644 index 0000000000..6e32d2008d --- /dev/null +++ b/tests/expected/bins_dynamic/libgen.stdout @@ -0,0 +1,21 @@ +dirname("") == "." +basename("") == "." +dirname(".") == "." +basename(".") == "." +dirname("..") == "." +basename("..") == ".." +dirname("/") == "/" +basename("/") == "/" +dirname("///") == "/" +basename("///") == "/" +dirname("//usr//lib//") == "//usr" +basename("//usr//lib//") == "lib" +dirname("/usr") == "/" +basename("/usr") == "usr" +dirname("/usr/") == "/" +basename("/usr/") == "usr" +dirname("/usr/lib") == "/usr" +basename("/usr/lib") == "lib" +dirname(NULL) == "." +basename(NULL) == "." +Success: 0 diff --git a/tests/expected/bins_dynamic/locale/duplocale.stderr b/tests/expected/bins_dynamic/locale/duplocale.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/locale/duplocale.stdout b/tests/expected/bins_dynamic/locale/duplocale.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/locale/newlocale.stderr b/tests/expected/bins_dynamic/locale/newlocale.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/locale/newlocale.stdout b/tests/expected/bins_dynamic/locale/newlocale.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/locale/setlocale.stderr b/tests/expected/bins_dynamic/locale/setlocale.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/locale/setlocale.stdout b/tests/expected/bins_dynamic/locale/setlocale.stdout new file mode 100644 index 0000000000..5d516f2e6d --- /dev/null +++ b/tests/expected/bins_dynamic/locale/setlocale.stdout @@ -0,0 +1 @@ +success! diff --git a/tests/expected/bins_dynamic/malloc/usable_size.stderr b/tests/expected/bins_dynamic/malloc/usable_size.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/malloc/usable_size.stdout b/tests/expected/bins_dynamic/malloc/usable_size.stdout new file mode 100644 index 0000000000..4188b67b66 --- /dev/null +++ b/tests/expected/bins_dynamic/malloc/usable_size.stdout @@ -0,0 +1,2 @@ +malloc: 27 bytes +malloc_usable_size: 48 bytes diff --git a/tests/expected/bins_dynamic/math.stderr b/tests/expected/bins_dynamic/math.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/math.stdout b/tests/expected/bins_dynamic/math.stdout new file mode 100644 index 0000000000..8654177967 --- /dev/null +++ b/tests/expected/bins_dynamic/math.stdout @@ -0,0 +1 @@ +cos(3.140000) = -0.999999 diff --git a/tests/expected/bins_dynamic/mkfifo.stderr b/tests/expected/bins_dynamic/mkfifo.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/mkfifo.stdout b/tests/expected/bins_dynamic/mkfifo.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/mknod.stderr b/tests/expected/bins_dynamic/mknod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/mknod.stdout b/tests/expected/bins_dynamic/mknod.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/mknodat.stderr b/tests/expected/bins_dynamic/mknodat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/mknodat.stdout b/tests/expected/bins_dynamic/mknodat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/netdb/getaddrinfo.stderr b/tests/expected/bins_dynamic/netdb/getaddrinfo.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/netdb/getaddrinfo.stdout b/tests/expected/bins_dynamic/netdb/getaddrinfo.stdout new file mode 100644 index 0000000000..6bc12bb59b --- /dev/null +++ b/tests/expected/bins_dynamic/netdb/getaddrinfo.stdout @@ -0,0 +1 @@ +IPv4 address: 23.21.162.66 (www.redox-os.org) diff --git a/tests/expected/bins_dynamic/ptrace.stderr b/tests/expected/bins_dynamic/ptrace.stderr new file mode 100644 index 0000000000..289c82a876 --- /dev/null +++ b/tests/expected/bins_dynamic/ptrace.stderr @@ -0,0 +1,4 @@ +This is printed to STDOUT. +Or, at least, that's what I thought. +But all write(...) syscalls are actually redirected to STDERR by the tracer. +Big surprise, right! diff --git a/tests/expected/bins_dynamic/ptrace.stdout b/tests/expected/bins_dynamic/ptrace.stdout new file mode 100644 index 0000000000..824e89accd --- /dev/null +++ b/tests/expected/bins_dynamic/ptrace.stdout @@ -0,0 +1,5 @@ +Set regs +Set regs +Set regs +Set regs +Child exited with status 0 diff --git a/tests/expected/bins_dynamic/pty/forkpty.stderr b/tests/expected/bins_dynamic/pty/forkpty.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/pty/forkpty.stdout b/tests/expected/bins_dynamic/pty/forkpty.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/regex.stderr b/tests/expected/bins_dynamic/regex.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/regex.stdout b/tests/expected/bins_dynamic/regex.stdout new file mode 100644 index 0000000000..07caf6fc01 --- /dev/null +++ b/tests/expected/bins_dynamic/regex.stdout @@ -0,0 +1,3 @@ +Matching group: 25 - 36 +Matching group: 31 - 36 +Matching group: -1 - -1 diff --git a/tests/expected/bins_dynamic/select.stderr b/tests/expected/bins_dynamic/select.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/select.stdout b/tests/expected/bins_dynamic/select.stdout new file mode 100644 index 0000000000..a4a5b784a2 --- /dev/null +++ b/tests/expected/bins_dynamic/select.stdout @@ -0,0 +1,8 @@ +Testing select on file +Is set before? 1 +Amount of things ready: 1 +Is set after? 1 +Testing select on pipe +Is set before? 1 +Amount of things ready: 1 +Is set after? 1 diff --git a/tests/expected/bins_dynamic/setjmp.stderr b/tests/expected/bins_dynamic/setjmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/setjmp.stdout b/tests/expected/bins_dynamic/setjmp.stdout new file mode 100644 index 0000000000..c7e6bcddd8 --- /dev/null +++ b/tests/expected/bins_dynamic/setjmp.stdout @@ -0,0 +1,2 @@ +jumping... +hi from jump diff --git a/tests/expected/bins_dynamic/sigaction.stderr b/tests/expected/bins_dynamic/sigaction.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sigaction.stdout b/tests/expected/bins_dynamic/sigaction.stdout new file mode 100644 index 0000000000..8a6d2448ef --- /dev/null +++ b/tests/expected/bins_dynamic/sigaction.stdout @@ -0,0 +1,5 @@ +Raising... +Signal handler1 called! +Raising... +Signal handler2 called! +Raised. diff --git a/tests/expected/bins_dynamic/sigaltstack.stderr b/tests/expected/bins_dynamic/sigaltstack.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sigaltstack.stdout b/tests/expected/bins_dynamic/sigaltstack.stdout new file mode 100644 index 0000000000..be1dfac342 --- /dev/null +++ b/tests/expected/bins_dynamic/sigaltstack.stdout @@ -0,0 +1,101 @@ +SIGUSR2 handler00 +SIGUSR2 handler01 +SIGUSR2 handler02 +SIGUSR2 handler03 +SIGUSR2 handler04 +SIGUSR2 handler05 +SIGUSR2 handler06 +SIGUSR2 handler07 +SIGUSR2 handler08 +SIGUSR2 handler09 +SIGUSR2 handler10 +SIGUSR2 handler11 +SIGUSR2 handler12 +SIGUSR2 handler13 +SIGUSR2 handler14 +SIGUSR2 handler15 +SIGUSR2 handler16 +SIGUSR2 handler17 +SIGUSR2 handler18 +SIGUSR2 handler19 +SIGUSR2 handler20 +SIGUSR2 handler21 +SIGUSR2 handler22 +SIGUSR2 handler23 +SIGUSR2 handler24 +SIGUSR2 handler25 +SIGUSR2 handler26 +SIGUSR2 handler27 +SIGUSR2 handler28 +SIGUSR2 handler29 +SIGUSR2 handler30 +SIGUSR2 handler31 +SIGUSR2 handler32 +SIGUSR2 handler33 +SIGUSR2 handler34 +SIGUSR2 handler35 +SIGUSR2 handler36 +SIGUSR2 handler37 +SIGUSR2 handler38 +SIGUSR2 handler39 +SIGUSR2 handler40 +SIGUSR2 handler41 +SIGUSR2 handler42 +SIGUSR2 handler43 +SIGUSR2 handler44 +SIGUSR2 handler45 +SIGUSR2 handler46 +SIGUSR2 handler47 +SIGUSR2 handler48 +SIGUSR2 handler49 +SIGUSR2 handler50 +SIGUSR2 handler51 +SIGUSR2 handler52 +SIGUSR2 handler53 +SIGUSR2 handler54 +SIGUSR2 handler55 +SIGUSR2 handler56 +SIGUSR2 handler57 +SIGUSR2 handler58 +SIGUSR2 handler59 +SIGUSR2 handler60 +SIGUSR2 handler61 +SIGUSR2 handler62 +SIGUSR2 handler63 +SIGUSR2 handler64 +SIGUSR2 handler65 +SIGUSR2 handler66 +SIGUSR2 handler67 +SIGUSR2 handler68 +SIGUSR2 handler69 +SIGUSR2 handler70 +SIGUSR2 handler71 +SIGUSR2 handler72 +SIGUSR2 handler73 +SIGUSR2 handler74 +SIGUSR2 handler75 +SIGUSR2 handler76 +SIGUSR2 handler77 +SIGUSR2 handler78 +SIGUSR2 handler79 +SIGUSR2 handler80 +SIGUSR2 handler81 +SIGUSR2 handler82 +SIGUSR2 handler83 +SIGUSR2 handler84 +SIGUSR2 handler85 +SIGUSR2 handler86 +SIGUSR2 handler87 +SIGUSR2 handler88 +SIGUSR2 handler89 +SIGUSR2 handler90 +SIGUSR2 handler91 +SIGUSR2 handler92 +SIGUSR2 handler93 +SIGUSR2 handler94 +SIGUSR2 handler95 +SIGUSR2 handler96 +SIGUSR2 handler97 +SIGUSR2 handler98 +SIGUSR2 handler99 +SIGUSR2 handler00 diff --git a/tests/expected/bins_dynamic/signal.stderr b/tests/expected/bins_dynamic/signal.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/signal.stdout b/tests/expected/bins_dynamic/signal.stdout new file mode 100644 index 0000000000..7d184e34c8 --- /dev/null +++ b/tests/expected/bins_dynamic/signal.stdout @@ -0,0 +1,3 @@ +Raising... +Signal handler called! +Raised. diff --git a/tests/expected/bins_dynamic/sigsetjmp.stderr b/tests/expected/bins_dynamic/sigsetjmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sigsetjmp.stdout b/tests/expected/bins_dynamic/sigsetjmp.stdout new file mode 100644 index 0000000000..eb99a60c8e --- /dev/null +++ b/tests/expected/bins_dynamic/sigsetjmp.stdout @@ -0,0 +1,2 @@ +Starting jump... +Jump done. diff --git a/tests/expected/bins_dynamic/stdio/all.stderr b/tests/expected/bins_dynamic/stdio/all.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/all.stdout b/tests/expected/bins_dynamic/stdio/all.stdout new file mode 100644 index 0000000000..92881132c2 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/all.stdout @@ -0,0 +1,4 @@ +H +Jello World! + +Hello diff --git a/tests/expected/bins_dynamic/stdio/buffer.stderr b/tests/expected/bins_dynamic/stdio/buffer.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/buffer.stdout b/tests/expected/bins_dynamic/stdio/buffer.stdout new file mode 100644 index 0000000000..4b124bc7c0 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/buffer.stdout @@ -0,0 +1,5 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaTest +Hello +World +It works +No buffering issues here diff --git a/tests/expected/bins_dynamic/stdio/dprintf.stderr b/tests/expected/bins_dynamic/stdio/dprintf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/dprintf.stdout b/tests/expected/bins_dynamic/stdio/dprintf.stdout new file mode 100644 index 0000000000..e12bc872a0 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/dprintf.stdout @@ -0,0 +1,2 @@ +Hello, world +a diff --git a/tests/expected/bins_dynamic/stdio/fgets.stderr b/tests/expected/bins_dynamic/stdio/fgets.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fgets.stdout b/tests/expected/bins_dynamic/stdio/fgets.stdout new file mode 100644 index 0000000000..a856e2de52 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/fgets.stdout @@ -0,0 +1,31 @@ +Hello World! + +Line 2 + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg +hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj +kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll +mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm +nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp +qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr +sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss +ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +EOF diff --git a/tests/expected/bins_dynamic/stdio/fputs.stderr b/tests/expected/bins_dynamic/stdio/fputs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fputs.stdout b/tests/expected/bins_dynamic/stdio/fputs.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fread.stderr b/tests/expected/bins_dynamic/stdio/fread.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fread.stdout b/tests/expected/bins_dynamic/stdio/fread.stdout new file mode 100644 index 0000000000..f3fbf213be --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/fread.stdout @@ -0,0 +1,32 @@ +1 +22 +333 +4444 +55555 +666666 +7777777 +88888888 +999999999 +AAAAAAAAAA +BBBBBBBBBBB +CCCCCCCCCCCC +DDDDDDDDDDDDD +EEEEEEEEEEEEEE +FFFFFFFFFFFFFFF +0000000000000000 +11111111111111111 +222222222222222222 +3333333333333333333 +44444444444444444444 +555555555555555555555 +6666666666666666666666 +77777777777777777777777 +888888888888888888888888 +9999999999999999999999999 +AAAAAAAAAAAAAAAAAAAAAAAAAA +BBBBBBBBBBBBBBBBBBBBBBBBBBB +CCCCCCCCCCCCCCCCCCCCCCCCCCCC +DDDDDDDDDDDDDDDDDDDDDDDDDDDDD +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +00000000000000000000000000000000 diff --git a/tests/expected/bins_dynamic/stdio/freopen.stderr b/tests/expected/bins_dynamic/stdio/freopen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/freopen.stdout b/tests/expected/bins_dynamic/stdio/freopen.stdout new file mode 100644 index 0000000000..1dd338fd1a --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/freopen.stdout @@ -0,0 +1,3 @@ +Hello +0 +0 diff --git a/tests/expected/bins_dynamic/stdio/fscanf.stderr b/tests/expected/bins_dynamic/stdio/fscanf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fscanf.stdout b/tests/expected/bins_dynamic/stdio/fscanf.stdout new file mode 100644 index 0000000000..73f11e68cf --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/fscanf.stdout @@ -0,0 +1,14 @@ +1 2 3 9 +9 +16 +35 +48 +92 +109 +152 +201 +241 +276 +293 +299 +301 diff --git a/tests/expected/bins_dynamic/stdio/fscanf_offby1.stderr b/tests/expected/bins_dynamic/stdio/fscanf_offby1.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fscanf_offby1.stdout b/tests/expected/bins_dynamic/stdio/fscanf_offby1.stdout new file mode 100644 index 0000000000..daf724a9e5 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/fscanf_offby1.stdout @@ -0,0 +1 @@ +1234, 7, 32 diff --git a/tests/expected/bins_dynamic/stdio/fseek.stderr b/tests/expected/bins_dynamic/stdio/fseek.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fseek.stdout b/tests/expected/bins_dynamic/stdio/fseek.stdout new file mode 100644 index 0000000000..b6163b2663 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/fseek.stdout @@ -0,0 +1,2 @@ +Line 2 +ftello: 21 diff --git a/tests/expected/bins_dynamic/stdio/fwrite.stderr b/tests/expected/bins_dynamic/stdio/fwrite.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/fwrite.stdout b/tests/expected/bins_dynamic/stdio/fwrite.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/getc_unget.stderr b/tests/expected/bins_dynamic/stdio/getc_unget.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/getc_unget.stdout b/tests/expected/bins_dynamic/stdio/getc_unget.stdout new file mode 100644 index 0000000000..f01a3f968d --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/getc_unget.stdout @@ -0,0 +1 @@ +Worked! diff --git a/tests/expected/bins_dynamic/stdio/getline.stderr b/tests/expected/bins_dynamic/stdio/getline.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/getline.stdout b/tests/expected/bins_dynamic/stdio/getline.stdout new file mode 100644 index 0000000000..ba753ef6b2 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/getline.stdout @@ -0,0 +1,41 @@ + 1: getline(NULL, NULL, stream) + => 22 (Invalid argument) - EINVAL + 2: getline(NULL, &n, stream) + => 22 (Invalid argument) - EINVAL + 3: getline(&lineptr, NULL, stream) + => 22 (Invalid argument) - EINVAL + 4: getline(NULL, NULL, NULL) + => 22 (Invalid argument) - EINVAL + 5: getline(&lineptr, NULL, NULL) + => 22 (Invalid argument) - EINVAL + 6: getline(NULL, &n, NULL) + => 22 (Invalid argument) - EINVAL + 7: getline(&lineptr, &n, NULL) + => 22 (Invalid argument) - EINVAL + 8: getdelim(&lineptr, &n, 25600, stream) + => 22 (Invalid argument) - EINVAL + + 1: status = 27, strlen = 27, feof = 0, ferror = 0 + |>Space: the final frontier. + 2: status = 1, strlen = 1, feof = 0, ferror = 0 + |> + 3: status = 50, strlen = 50, feof = 0, ferror = 0 + |>These are the voyages of the starship Enterprise. + 4: status = 24, strlen = 24, feof = 0, ferror = 0 + |>Its continuing mission: + 5: status = 33, strlen = 33, feof = 0, ferror = 0 + |>- to explore strange new worlds; + 6: status = 46, strlen = 46, feof = 0, ferror = 0 + |>- to seek out new life and new civilizations; + 7: status = 45, strlen = 45, feof = 0, ferror = 0 + |>- to boldly go where no one has gone before! + +overread 1, status = -1, feof = 1, ferror = 0 +|~- to boldly go where no one has gone before! + +overread 2, status = -1, feof = 1, ferror = 0 +|~- to boldly go where no one has gone before! + +overread 3, status = -1, feof = 1, ferror = 0 +|~- to boldly go where no one has gone before! + diff --git a/tests/expected/bins_dynamic/stdio/mutex.stderr b/tests/expected/bins_dynamic/stdio/mutex.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/mutex.stdout b/tests/expected/bins_dynamic/stdio/mutex.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/popen.stderr b/tests/expected/bins_dynamic/stdio/popen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/popen.stdout b/tests/expected/bins_dynamic/stdio/popen.stdout new file mode 100644 index 0000000000..05f7216cbe --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/popen.stdout @@ -0,0 +1,8 @@ +Line 1: 1-never-gonna-give-you-up +Line 2: 2-never-gonna-let-you-down +Line 3: 3-never-gonna-run-around +Line 4: 4-and-desert-you +Line 5: 5-never-gonna-make-you-cry +Line 6: 6-never-gonna-say-goodbye +Line 7: 7-never-gonna-tell-a-lie +Line 8: 8-and-hurt-you diff --git a/tests/expected/bins_dynamic/stdio/printf.stderr b/tests/expected/bins_dynamic/stdio/printf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/printf.stdout b/tests/expected/bins_dynamic/stdio/printf.stdout new file mode 100644 index 0000000000..a13543aeb2 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/printf.stdout @@ -0,0 +1,78 @@ +percent: % +string: String +char: c +char: +int: -16 +uint: 32 +hex: beef +HEX: C0FFEE +string: end +%n returned 51, total len of write: 94 + +Padding madness: + 001 +2 + c + 0x00000ff + 001 +0 0x1 + 1 +(00123) ( 123) +(-0123) ( -123) +( ) +0xabcdef +(nil) + +Positional madness: +4 3 2 +00002 +|Fiz |Buz | Fiz| Tot| +int: 5 double: 0.100000 0.200000 0.300000 0.400000 +-1717986918 0.100000 +-1717986918 0.200000 + +Float madness: + 1.234568e+02 + 1.000000E-05 + 123.456789 + 0.000010 + -1.234568e+02 +-00000001.234568e+02 +%.5g: -123.46 +%.5f: -123.45679 +%.5e: -1.23457e+02 +%.*g: -1.2e+02 +%.*f: -123.46 +%.*e: -1.23e+02 +%.*2$g: -123.46 +%.*2$f: -123.45679 +%.*2$e: -1.23457e+02 +100000 +1e+06 +1.000000e+06 +0.0001 +1E-05 +1.000000E-05 +001234.56 +001234.56 + +Non-finite float madness: +%e: inf -inf nan -nan +%E: INF -INF NAN -NAN +%f: inf -inf nan -nan +%F: INF -INF NAN -NAN +%g: inf -inf nan -nan +%G: INF -INF NAN -NAN +Things that have been buggy ++05 +Testing asprintf... +printed: test string, value: 11 +printed: test string 2, value: 13 +printed: test string 2, value: 13 + +%m: Owner died + +C23: +Binary %b: 100 +Binary %b alternate: 0b100 +Binary %B: 100 +Binary %B alternate: 0B100 diff --git a/tests/expected/bins_dynamic/stdio/printf_neg_pad.stderr b/tests/expected/bins_dynamic/stdio/printf_neg_pad.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/printf_neg_pad.stdout b/tests/expected/bins_dynamic/stdio/printf_neg_pad.stdout new file mode 100644 index 0000000000..f573a2b6d2 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/printf_neg_pad.stdout @@ -0,0 +1,2 @@ +A BCC/ +AB CC/ diff --git a/tests/expected/bins_dynamic/stdio/printf_space_pad.stderr b/tests/expected/bins_dynamic/stdio/printf_space_pad.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/printf_space_pad.stdout b/tests/expected/bins_dynamic/stdio/printf_space_pad.stdout new file mode 100644 index 0000000000..8e9107f351 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/printf_space_pad.stdout @@ -0,0 +1 @@ +A B diff --git a/tests/expected/bins_dynamic/stdio/putc_unlocked.stderr b/tests/expected/bins_dynamic/stdio/putc_unlocked.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/putc_unlocked.stdout b/tests/expected/bins_dynamic/stdio/putc_unlocked.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/rename.stderr b/tests/expected/bins_dynamic/stdio/rename.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/rename.stdout b/tests/expected/bins_dynamic/stdio/rename.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/renameat.stderr b/tests/expected/bins_dynamic/stdio/renameat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/renameat.stdout b/tests/expected/bins_dynamic/stdio/renameat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/scanf.stderr b/tests/expected/bins_dynamic/stdio/scanf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/scanf.stdout b/tests/expected/bins_dynamic/stdio/scanf.stdout new file mode 100644 index 0000000000..dca295ccb5 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/scanf.stdout @@ -0,0 +1,16 @@ +2, { sa: 12, ia: 345, ib: 0, ic: 0, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string1: , string2: , string3: , string4: } +3, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string1: , string2: , string3: , string4: } +2, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: (nil), char: a, string1: , string2: , string3: , string4: } +1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string1: , string2: , string3: , string4: } +1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string1: Hello, string2: , string3: , string4: } +1, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string1: Hello, string2: , string3: , string4: } +2, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: elllo, string2: , string3: , string4: } +1, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: elllo, string2: , string3: , string4: } +0, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: elllo, string2: , string3: , string4: } +2, { sa: 12, ia: 42, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: world, string2: , string3: , string4: } +2, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: world, string2: planet, string3: , string4: } +4, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: e, string2: o, string3: l, string4: d } +4, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: a, string2: e, string3: f, string4: dddddd } +4, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: a, string2: e, string3: f, string4: dddddd } +1, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: testbbbb, string2: e, string3: f, string4: dddddd } +3 "https" "//" "redox-os.org" "" diff --git a/tests/expected/bins_dynamic/stdio/setvbuf.stderr b/tests/expected/bins_dynamic/stdio/setvbuf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/setvbuf.stdout b/tests/expected/bins_dynamic/stdio/setvbuf.stdout new file mode 100644 index 0000000000..ebda98d01a --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/setvbuf.stdout @@ -0,0 +1,4 @@ +H +Hello World! + +Hello diff --git a/tests/expected/bins_dynamic/stdio/sprintf.stderr b/tests/expected/bins_dynamic/stdio/sprintf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/sprintf.stdout b/tests/expected/bins_dynamic/stdio/sprintf.stdout new file mode 100644 index 0000000000..4c76b1a825 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/sprintf.stdout @@ -0,0 +1 @@ +This stri diff --git a/tests/expected/bins_dynamic/stdio/ungetc_ftell.stderr b/tests/expected/bins_dynamic/stdio/ungetc_ftell.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/ungetc_ftell.stdout b/tests/expected/bins_dynamic/stdio/ungetc_ftell.stdout new file mode 100644 index 0000000000..e36f413519 --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/ungetc_ftell.stdout @@ -0,0 +1,28 @@ +#, 0 +i, 1 +n, 2 +h, -9 +e, -8 +l, -7 +l, -6 +o, -5 + , -4 +w, -3 +o, -2 +r, -1 +l, 0 +d, 1 + +, 2 +c, 3 +l, 4 +u, 5 +d, 6 +e, 7 + , 8 +<, 9 +s, 10 +t, 11 +d, 12 +i, 13 +o, 14 diff --git a/tests/expected/bins_dynamic/stdio/ungetc_multiple.stderr b/tests/expected/bins_dynamic/stdio/ungetc_multiple.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdio/ungetc_multiple.stdout b/tests/expected/bins_dynamic/stdio/ungetc_multiple.stdout new file mode 100644 index 0000000000..3b18e512db --- /dev/null +++ b/tests/expected/bins_dynamic/stdio/ungetc_multiple.stdout @@ -0,0 +1 @@ +hello world diff --git a/tests/expected/bins_dynamic/stdlib/a64l.stderr b/tests/expected/bins_dynamic/stdlib/a64l.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/a64l.stdout b/tests/expected/bins_dynamic/stdlib/a64l.stdout new file mode 100644 index 0000000000..d22f10d973 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/a64l.stdout @@ -0,0 +1,42 @@ +Correct a64l: azAZ9. = 194301926 +Correct a64l: azA = 53222 +l64a(0): +l64a(1): / +l64a(2): 0 +l64a(11): 9 +l64a(12): A +l64a(37): Z +l64a(38): a +l64a(63): z +l64a(64): ./ +l64a(65): // +l64a(4095): zz +l64a(4096): ../ +l64a(262143): zzz +l64a(262144): .../ +l64a(16777215): zzzz +l64a(16777216): ..../ +l64a(1073741823): zzzzz +l64a(1073741824): ...../ +l64a(2147483647): zzzzz/ +a64l(l64a(0)): 0 +a64l(l64a(1)): 1 +a64l(l64a(2)): 2 +a64l(l64a(11)): 11 +a64l(l64a(12)): 12 +a64l(l64a(37)): 37 +a64l(l64a(38)): 38 +a64l(l64a(63)): 63 +a64l(l64a(64)): 64 +a64l(l64a(65)): 65 +a64l(l64a(4095)): 4095 +a64l(l64a(4096)): 4096 +a64l(l64a(262143)): 262143 +a64l(l64a(262144)): 262144 +a64l(l64a(16777215)): 16777215 +a64l(l64a(16777216)): 16777216 +a64l(l64a(1073741823)): 1073741823 +a64l(l64a(1073741824)): 1073741824 +a64l(l64a(2147483647)): 2147483647 +l64a(x) (lower 32 bits of x are 1985229328): E61Jq/ +a64l(l64a(x)) (lower 32 bits of x are 1985229328): 1985229328 diff --git a/tests/expected/bins_dynamic/stdlib/alloc.stderr b/tests/expected/bins_dynamic/stdlib/alloc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/alloc.stdout b/tests/expected/bins_dynamic/stdlib/alloc.stdout new file mode 100644 index 0000000000..d5bc92dc00 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/alloc.stdout @@ -0,0 +1,30 @@ +malloc (size 0): (OK) +malloc: pointer: (not NULL), error value: 0 = Success +malloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +calloc (size 0): (OK) +calloc: pointer: (not NULL), error value: 0 = Success +calloc (overflowing): pointer: (nil), error value: 12 = Out of memory +realloc (size 0): (OK) +realloc: pointer: (not NULL), error value: 0 = Success +realloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +reallocarray: pointer: (not NULL), error value: 0 = Success +reallocarray (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +memalign (size 0): (OK) +memalign: pointer: (alignment OK), error value: 0 = Success +memalign (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +memalign (alignment 0): pointer: (nil), error value: 22 = Invalid argument +memalign (alignment 3): pointer: (nil), error value: 22 = Invalid argument +aligned_alloc (size % alignment == 0): pointer: (alignment OK), error value: 0 = Success +aligned_alloc (size % alignment != 0): pointer: (nil), error value: 22 = Invalid argument +valloc (size 0): (OK) +valloc: pointer: (alignment OK), error value: 0 = Success +valloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +posix_memalign: pointer: (alignment OK), error value: 0 = Success +posix_memalign (alignment 0): pointer: (nil), error value: 22 = Invalid argument +posix_memalign (non-power-of-two multiple of sizeof(void *)): pointer: (nil), error value: 22 = Invalid argument +posix_memalign (size 0): (OK) +posix_memalign (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +pvalloc (size 0): (OK) +pvalloc: pointer: (alignment OK), error value: 0 = Success +pvalloc (2 pages): pointer: (alignment OK), error value: 0 = Success +pvalloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory diff --git a/tests/expected/bins_dynamic/stdlib/atof.stderr b/tests/expected/bins_dynamic/stdlib/atof.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/atof.stdout b/tests/expected/bins_dynamic/stdlib/atof.stdout new file mode 100644 index 0000000000..3227d85385 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/atof.stdout @@ -0,0 +1,2 @@ +-3.140000 +inf diff --git a/tests/expected/bins_dynamic/stdlib/atoi.stderr b/tests/expected/bins_dynamic/stdlib/atoi.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/atoi.stdout b/tests/expected/bins_dynamic/stdlib/atoi.stdout new file mode 100644 index 0000000000..d90e37ccea --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/atoi.stdout @@ -0,0 +1,6 @@ +-42 +555 +1234567890 +-42 +555 +1234567890 diff --git a/tests/expected/bins_dynamic/stdlib/div.stderr b/tests/expected/bins_dynamic/stdlib/div.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/div.stdout b/tests/expected/bins_dynamic/stdlib/div.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/env.stderr b/tests/expected/bins_dynamic/stdlib/env.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/env.stdout b/tests/expected/bins_dynamic/stdlib/env.stdout new file mode 100644 index 0000000000..a0596ab55e --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/env.stdout @@ -0,0 +1,8 @@ +It's working!! +Updates accordingly. +in place +TEST=in place +Value overwritten and not in place because it's really long +TEST=in place +Value overwritten and not in place because it's really long +Value deleted successfully! diff --git a/tests/expected/bins_dynamic/stdlib/getsubopt.stderr b/tests/expected/bins_dynamic/stdlib/getsubopt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/getsubopt.stdout b/tests/expected/bins_dynamic/stdlib/getsubopt.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/mkostemps.stderr b/tests/expected/bins_dynamic/stdlib/mkostemps.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/mkostemps.stdout b/tests/expected/bins_dynamic/stdlib/mkostemps.stdout new file mode 100644 index 0000000000..f3562f6879 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/mkostemps.stdout @@ -0,0 +1,3 @@ +Start unchanged: 0 +End unchanged: 0 +Read & Write Successful diff --git a/tests/expected/bins_dynamic/stdlib/ptsname.stderr b/tests/expected/bins_dynamic/stdlib/ptsname.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/ptsname.stdout b/tests/expected/bins_dynamic/stdlib/ptsname.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/qsort.stderr b/tests/expected/bins_dynamic/stdlib/qsort.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/qsort.stdout b/tests/expected/bins_dynamic/stdlib/qsort.stdout new file mode 100644 index 0000000000..49755a611e --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/qsort.stdout @@ -0,0 +1,2 @@ +Before: 23 16 8 4 42 15 +After: 4 8 15 16 23 42 diff --git a/tests/expected/bins_dynamic/stdlib/rand.stderr b/tests/expected/bins_dynamic/stdlib/rand.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/rand.stdout b/tests/expected/bins_dynamic/stdlib/rand.stdout new file mode 100644 index 0000000000..2a50461db2 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/rand.stdout @@ -0,0 +1,8 @@ +67141780 +201425341 +201425341 +67141780 +264204 +271585844 +264204 +271585844 diff --git a/tests/expected/bins_dynamic/stdlib/rand48.stderr b/tests/expected/bins_dynamic/stdlib/rand48.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/rand48.stdout b/tests/expected/bins_dynamic/stdlib/rand48.stdout new file mode 100644 index 0000000000..fa871030ba --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/rand48.stdout @@ -0,0 +1,14 @@ +lrand48 (uninitialized): 0 2116118 89401895 379337186 782977366 196130996 198207689 1046291021 1131187612 975888346 +drand48 (seeded with srand48): 0.750266 0.607593 0.567593 0.799563 0.984984 0.205670 0.625922 0.794426 0.369416 0.854100 +lrand48 (seeded with srand48): 1611183183 1304796356 1218897288 1717049088 2115236938 441672110 1344158015 1706017430 793314380 1834165927 +mrand48 (seeded with srand48): -1072600929 -1685374584 -1857172720 -860869119 -64493420 883344220 -1606651266 -882932436 1586628760 -626635441 +erand48: 0.210555 0.014158 0.111353 0.658369 0.103767 0.180385 0.945033 0.745768 0.290272 0.111716 +nrand48: 514983590 590935818 1480794144 1496813112 2133865028 1816766485 2095074020 126058208 909762120 14734916 +jrand48: -1066398599 903693914 -1922375113 -2090140830 1218074962 1662411059 -722435322 764426686 -874142666 -1454656015 +seed48_return: [40c0, 4d4f, daa6] +lrand48 (seeded with seed48): 386486647 2049879217 706208537 1265096744 1586830881 1884641178 1566935266 1256810805 875501172 1670187935 +xsubi restored froom seed48 return value: [40c0, 4d4f, daa6] +nrand48 (from xsubi value returned by seed48): 1654466549 509433115 2007628085 1022767111 1935848687 967444157 1967758021 686622820 989863078 1688030308 +lrand48 (with parameters from lcong48): 2015972364 2009368981 971134301 317520085 149004773 538917235 242519436 1066970146 991527304 1588277058 +lrand48 (seeded with srand48 after lcong48 call): 1611183183 1304796356 1218897288 1717049088 2115236938 441672110 1344158015 1706017430 793314380 1834165927 +lrand48 (seeded with seed48 after lcong48 call): 386486647 2049879217 706208537 1265096744 1586830881 1884641178 1566935266 1256810805 875501172 1670187935 diff --git a/tests/expected/bins_dynamic/stdlib/random.stderr b/tests/expected/bins_dynamic/stdlib/random.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/random.stdout b/tests/expected/bins_dynamic/stdlib/random.stdout new file mode 100644 index 0000000000..9a754a9eb6 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/random.stdout @@ -0,0 +1,1011 @@ +Uninitialized: +262836907 +2022765545 +1985587709 +1559253607 +547725525 +1277054513 +317849018 +1695317205 +643446864 +1262735440 +964614733 +622582094 +280266812 +1939173898 +732650777 +1270232897 +336669216 +1669264236 +554325996 +1080138202 +1193538846 +2016671310 +1114491314 +2115358120 +2122955106 +982493572 +450262557 +439534734 +1723376538 +2066693252 +901041 +1986213445 +1941975150 +1986488750 +1397983404 +342217027 +1116059615 +1715832423 +2037534232 +1759506479 +831084215 +854665318 +234604926 +1111351027 +646355568 +967255703 +234100276 +983024785 +489036291 +788426273 +2063162987 +1682575137 +657613935 +1030170653 +1650449610 +633085394 +2012664225 +2100712167 +1072620128 +1588557116 +2019921771 +1073521169 +1427286913 +1814413273 +912526271 +677786670 +9146653 +2028585886 +246135445 +2046680885 + +Seed 1: +262836907 +2022765545 +1985587709 +1559253607 +547725525 +1277054513 +317849018 +1695317205 +643446864 +1262735440 +964614733 +622582094 +280266812 +1939173898 +732650777 +1270232897 +336669216 +1669264236 +554325996 +1080138202 +1193538846 +2016671310 +1114491314 +2115358120 +2122955106 +982493572 +450262557 +439534734 +1723376538 +2066693252 +901041 +1986213445 +1941975150 +1986488750 +1397983404 +342217027 +1116059615 +1715832423 +2037534232 +1759506479 +831084215 +854665318 +234604926 +1111351027 +646355568 +967255703 +234100276 +983024785 +489036291 +788426273 +2063162987 +1682575137 +657613935 +1030170653 +1650449610 +633085394 +2012664225 +2100712167 +1072620128 +1588557116 +2019921771 +1073521169 +1427286913 +1814413273 +912526271 +677786670 +9146653 +2028585886 +246135445 +2046680885 + +Seed 1337: +1124688561 +1792831582 +1708122057 +1060061055 +2060905921 +703006049 +867046930 +1599327113 +1946461956 +1851068511 +2107308161 +1080994526 +361067458 +2035914970 +1577981791 +72922558 +783590643 +1752267570 +1350489214 +1338091804 +225460930 +62532035 +540341638 +1391100769 +1535109757 +2039272559 +206716062 +1139547941 +462353593 +1176324546 +1766476968 +1587042155 +821672480 +1327115377 +499619562 +735094753 +2030121427 +1366666492 +186938219 +1829099735 +1070251355 +146762732 +762610613 +1431318813 +35194054 +193108756 +1504241371 +818784698 +1945376326 +707246937 +9392854 +23353608 +769778973 +549734493 +1414454377 +157405082 +441523404 +1621170440 +1296953024 +903876997 +650011338 +915946344 +343435504 +1471683818 +95578073 +843055066 +59294924 +2125699500 +62237911 +246233143 + +Seed 42, size 8: +1250496027 +1116302264 +1000676753 +1668674806 +908095735 +71666532 +896336333 +1736731266 +1314989459 +1535244752 +391441865 +1108520142 +1206814703 +534045436 +1974836613 +238077914 +1413854219 +705377000 +397905153 +1440974758 +1972995559 +282367380 +881784893 +1823504434 +879663491 +70219520 +1215814457 +1726604670 +318196447 +1939145516 +1030877685 +968547210 +1513076219 +1793402392 +1673312369 +1341335126 +481933015 +1376947140 +1980983981 +529748450 +192473459 +2072944688 +631833769 +202943022 +2012129743 +762679132 +1776566885 +562641722 +542512107 +293225800 +266051553 +1950339334 +562037703 +932343284 +1167153437 +829945746 +711923043 +392328544 +485452313 +748288734 +1303618751 +1013548940 +1115918037 +1967580906 +210226651 +149786744 +1677411153 +1109690294 +2007723191 +1000815652 + +Seed 42, size 31: +1250496027 +1116302264 +1000676753 +1668674806 +908095735 +71666532 +896336333 +1736731266 +1314989459 +1535244752 +391441865 +1108520142 +1206814703 +534045436 +1974836613 +238077914 +1413854219 +705377000 +397905153 +1440974758 +1972995559 +282367380 +881784893 +1823504434 +879663491 +70219520 +1215814457 +1726604670 +318196447 +1939145516 +1030877685 +968547210 +1513076219 +1793402392 +1673312369 +1341335126 +481933015 +1376947140 +1980983981 +529748450 +192473459 +2072944688 +631833769 +202943022 +2012129743 +762679132 +1776566885 +562641722 +542512107 +293225800 +266051553 +1950339334 +562037703 +932343284 +1167153437 +829945746 +711923043 +392328544 +485452313 +748288734 +1303618751 +1013548940 +1115918037 +1967580906 +210226651 +149786744 +1677411153 +1109690294 +2007723191 +1000815652 + +Seed 42, size 32: +1105844101 +1165395678 +461296413 +1259424640 +70225557 +1467961981 +315013290 +1176069659 +485874011 +776309703 +288010651 +556099569 +96788036 +603023941 +1732169228 +582662048 +1379333644 +2020179879 +1138761617 +1476121681 +475720173 +723447197 +2058783729 +1855053817 +596143428 +1050061698 +1183691850 +1071863601 +1773508895 +1094991931 +779433771 +222168675 +2145053629 +1963125621 +1294032277 +1771078876 +910633905 +2073466048 +1993247552 +908203886 +1889108021 +1139796181 +531799115 +652258278 +1065778581 +377563019 +1560462165 +807402954 +1517359200 +2092261280 +1459661233 +435654133 +322340651 +872639750 +1243057087 +1839699851 +817417382 +555234672 +127870336 +1139758033 +1427874422 +1370927423 +831974236 +97808156 +1926162096 +959844572 +1237566189 +1206552870 +183288347 +2069540425 + +Seed 42, size 63: +1105844101 +1165395678 +461296413 +1259424640 +70225557 +1467961981 +315013290 +1176069659 +485874011 +776309703 +288010651 +556099569 +96788036 +603023941 +1732169228 +582662048 +1379333644 +2020179879 +1138761617 +1476121681 +475720173 +723447197 +2058783729 +1855053817 +596143428 +1050061698 +1183691850 +1071863601 +1773508895 +1094991931 +779433771 +222168675 +2145053629 +1963125621 +1294032277 +1771078876 +910633905 +2073466048 +1993247552 +908203886 +1889108021 +1139796181 +531799115 +652258278 +1065778581 +377563019 +1560462165 +807402954 +1517359200 +2092261280 +1459661233 +435654133 +322340651 +872639750 +1243057087 +1839699851 +817417382 +555234672 +127870336 +1139758033 +1427874422 +1370927423 +831974236 +97808156 +1926162096 +959844572 +1237566189 +1206552870 +183288347 +2069540425 + +Seed 42, size 64: +2058979096 +1114567745 +1168098319 +1326828429 +585052544 +738633083 +1597984519 +103976400 +637164838 +949355699 +301019638 +1539653489 +829283131 +1994476715 +899306595 +810802043 +1925369788 +945984460 +125329241 +710381785 +1449014869 +899515740 +1003492141 +1640656979 +442529030 +743548669 +135718510 +965001641 +811994708 +1711301303 +374619698 +152505839 +1098490299 +1223819540 +1934201325 +1235732546 +2135248287 +991256780 +484430111 +926959142 +1670507811 +1806226321 +623744314 +1435739022 +999556678 +1374176376 +1526682215 +477688866 +1701508406 +1488226084 +576474982 +564239621 +1555496401 +2039926513 +819402007 +342426170 +1168843 +624913157 +2060652179 +912725209 +139417938 +1666100153 +2143789020 +1697813778 +1038556214 +1615031197 +31787170 +1587283572 +1479726437 +151644796 + +Seed 42, size 127: +2058979096 +1114567745 +1168098319 +1326828429 +585052544 +738633083 +1597984519 +103976400 +637164838 +949355699 +301019638 +1539653489 +829283131 +1994476715 +899306595 +810802043 +1925369788 +945984460 +125329241 +710381785 +1449014869 +899515740 +1003492141 +1640656979 +442529030 +743548669 +135718510 +965001641 +811994708 +1711301303 +374619698 +152505839 +1098490299 +1223819540 +1934201325 +1235732546 +2135248287 +991256780 +484430111 +926959142 +1670507811 +1806226321 +623744314 +1435739022 +999556678 +1374176376 +1526682215 +477688866 +1701508406 +1488226084 +576474982 +564239621 +1555496401 +2039926513 +819402007 +342426170 +1168843 +624913157 +2060652179 +912725209 +139417938 +1666100153 +2143789020 +1697813778 +1038556214 +1615031197 +31787170 +1587283572 +1479726437 +151644796 + +Seed 42, size 128: +1105844101 +1165395678 +461296413 +1259424640 +2024747114 +1114771942 +1792613078 +189454326 +466435881 +883763281 +1626567616 +1631629466 +1277725798 +1234887654 +657326752 +1836263319 +715263951 +471085233 +2086498417 +1163602145 +1547997976 +1405363974 +632333445 +201224285 +286947872 +1468210882 +103627486 +2147177614 +373040761 +1110293054 +1202766264 +1478884863 +128205084 +1664062677 +590825855 +5468550 +631350971 +235955286 +194922877 +1097786852 +1119718567 +1821490493 +581932670 +249960717 +908894499 +1239259422 +2086224037 +1624158450 +1710344655 +2025238806 +640276948 +1110858983 +1283119132 +1272610393 +1312083268 +1570067004 +593337627 +1415710754 +1569760970 +966378389 +378520161 +625043586 +297779604 +506725245 +141622615 +888605459 +512193796 +772973586 +1124560745 +707116673 + +Seed 42, size 255: +1105844101 +1165395678 +461296413 +1259424640 +2024747114 +1114771942 +1792613078 +189454326 +466435881 +883763281 +1626567616 +1631629466 +1277725798 +1234887654 +657326752 +1836263319 +715263951 +471085233 +2086498417 +1163602145 +1547997976 +1405363974 +632333445 +201224285 +286947872 +1468210882 +103627486 +2147177614 +373040761 +1110293054 +1202766264 +1478884863 +128205084 +1664062677 +590825855 +5468550 +631350971 +235955286 +194922877 +1097786852 +1119718567 +1821490493 +581932670 +249960717 +908894499 +1239259422 +2086224037 +1624158450 +1710344655 +2025238806 +640276948 +1110858983 +1283119132 +1272610393 +1312083268 +1570067004 +593337627 +1415710754 +1569760970 +966378389 +378520161 +625043586 +297779604 +506725245 +141622615 +888605459 +512193796 +772973586 +1124560745 +707116673 + +Seed 42, size 256: +2058979096 +1114567745 +1168098319 +1326828429 +585052544 +738633083 +1597984519 +103976400 +637164838 +949355699 +301019638 +1539653489 +829283131 +1994476715 +240955585 +1996759270 +1022456556 +1580994077 +1061370374 +875128855 +1125363953 +1573702147 +503131242 +1969480447 +1438211747 +91438056 +1120505602 +1956383039 +1858786240 +1571532334 +1295962411 +342691299 +190922750 +587812343 +1882440834 +1251108553 +1546539316 +237235092 +229958515 +1788514159 +1180131104 +1278005195 +1236529116 +1834489224 +2008240486 +153071308 +1122241977 +1443493489 +827253675 +1844422894 +1615942404 +1444719301 +1322997186 +2068652336 +1108669174 +1117641112 +329168079 +1033958612 +870068653 +1235189147 +1628422544 +186507460 +1238820988 +1150316436 +117400533 +1285498853 +464843634 +1049896178 +1788529262 +1239030133 + +Seed 42, other state array: +1105844101 +1165395678 +461296413 +1259424640 +70225557 +1467961981 +315013290 +1176069659 +485874011 +776309703 +288010651 +556099569 +96788036 +603023941 +1732169228 +582662048 +1379333644 +2020179879 +1138761617 +1476121681 +475720173 +723447197 +2058783729 +1855053817 +596143428 +1050061698 +1183691850 +1071863601 +1773508895 +1094991931 +779433771 +222168675 +2145053629 +1963125621 +1294032277 +1771078876 +910633905 +2073466048 +1993247552 +908203886 +1889108021 +1139796181 +531799115 +652258278 +1065778581 +377563019 +1560462165 +807402954 +1517359200 +2092261280 +1459661233 +435654133 +322340651 +872639750 +1243057087 +1839699851 +817417382 +555234672 +127870336 +1139758033 +1427874422 +1370927423 +831974236 +97808156 +1926162096 +959844572 +1237566189 +1206552870 +183288347 +2069540425 + +State data pointer restored correctly by setstate(). + +Seed 42, back to first state array: +1343006534 +1980171372 +782043423 +1083063062 +475232903 +1304516034 +1151509101 +1392464686 +1241740309 +116713217 +1697707295 +611594021 +1486722877 +464603182 +2038305329 +393952924 +215949723 +1654161471 +1745599527 +718621482 +527520873 +238823465 +1810355799 +958834563 +1301525862 +1492448612 +2080260955 +1815218141 +918843046 +317898715 +555133807 +785092322 +426122834 +1606253938 +736775485 +1973304601 +1660310177 +1521067015 +1674138324 +648896653 +2092390142 +772160169 +469099415 +2085041819 +1382277472 +557791010 +478959698 +1587628873 +557786337 +886954417 +1920913029 +643498035 +1878687182 +1359626079 +1546133539 +637470879 +1787787315 +1905187849 +1043203054 +1508046688 +410459218 +51504832 +1290534966 +486057852 +318745576 +1100789000 +36368414 +511601317 +1816117351 +820142804 + +Pointer returned by initstate with size < 8: (nil) diff --git a/tests/expected/bins_dynamic/stdlib/strtod.stderr b/tests/expected/bins_dynamic/stdlib/strtod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/strtod.stdout b/tests/expected/bins_dynamic/stdlib/strtod.stdout new file mode 100644 index 0000000000..a92def04f3 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/strtod.stdout @@ -0,0 +1,196 @@ +d: 0.000000 Endptr: "a 1 hello" +d: 1.000000 Endptr: " hello" +d: 1.000000 Endptr: " hello 2" +d: 10.123000 Endptr: "" +d: 10.123000 Endptr: "" +d: -5.300000 Endptr: "" +d: 16.071045 Endptr: "" +d: 1.136719 Endptr: "" +d: 3.128906 Endptr: "" +d: 100000.000000 Endptr: "" +d: 100000.000000 Endptr: "" +d: 0.000010 Endptr: "" +d: 100000.000000 Endptr: " " +d: 100000.000000 Endptr: " " +d: 0.000010 Endptr: " " +d: 10000000000.000000 Endptr: "" +d: 1.000000 Endptr: "eXXXX" +d: 1.000000 Endptr: "e" +d: 1.000000 Endptr: "e " +d: 10000000000.000000 Endptr: "" +d: 1.000000 Endptr: "e+XXXX" +d: 1.000000 Endptr: "e+" +d: 1.000000 Endptr: "e+ " +d: 0.000000 Endptr: "" +d: 1.000000 Endptr: "e-XXXX" +d: 1.000000 Endptr: "e-" +d: 1.000000 Endptr: "e- " +d: -100000.000000 Endptr: "" +d: -100000.000000 Endptr: "" +d: -0.000010 Endptr: "" +d: -100000.000000 Endptr: " " +d: -100000.000000 Endptr: " " +d: -0.000010 Endptr: " " +d: -10000000000.000000 Endptr: "" +d: -1.000000 Endptr: "eXXXX" +d: -1.000000 Endptr: "e" +d: -1.000000 Endptr: "e " +d: -10000000000.000000 Endptr: "" +d: -1.000000 Endptr: "e+XXXX" +d: -1.000000 Endptr: "e+" +d: -1.000000 Endptr: "e+ " +d: -0.000000 Endptr: "" +d: -1.000000 Endptr: "e-XXXX" +d: -1.000000 Endptr: "e-" +d: -1.000000 Endptr: "e- " +d: 1234000.000000 Endptr: "" +d: 1234000.000000 Endptr: "" +d: 0.000123 Endptr: "" +d: 1234000.000000 Endptr: " " +d: 1234000.000000 Endptr: " " +d: 0.000123 Endptr: " " +d: 123400000000.000000 Endptr: "" +d: 12.340000 Endptr: "eXXXX" +d: 12.340000 Endptr: "e" +d: 12.340000 Endptr: "e " +d: 123400000000.000000 Endptr: "" +d: 12.340000 Endptr: "e+XXXX" +d: 12.340000 Endptr: "e+" +d: 12.340000 Endptr: "e+ " +d: 0.000000 Endptr: "" +d: 12.340000 Endptr: "e-XXXX" +d: 12.340000 Endptr: "e-" +d: 12.340000 Endptr: "e- " +d: -1234000.000000 Endptr: "" +d: -1234000.000000 Endptr: "" +d: -0.000123 Endptr: "" +d: -1234000.000000 Endptr: " " +d: -1234000.000000 Endptr: " " +d: -0.000123 Endptr: " " +d: -123400000000.000000 Endptr: "" +d: -12.340000 Endptr: "eXXXX" +d: -12.340000 Endptr: "e" +d: -12.340000 Endptr: "e " +d: -123400000000.000000 Endptr: "" +d: -12.340000 Endptr: "e+XXXX" +d: -12.340000 Endptr: "e+" +d: -12.340000 Endptr: "e+ " +d: -0.000000 Endptr: "" +d: -12.340000 Endptr: "e-XXXX" +d: -12.340000 Endptr: "e-" +d: -12.340000 Endptr: "e- " +d: 192.000000 Endptr: "" +d: -192.000000 Endptr: "" +d: 0.005859 Endptr: "" +d: -0.005859 Endptr: "" +d: 10.000000 Endptr: "" +d: 0.156250 Endptr: "" +d: -10.000000 Endptr: "" +d: -0.156250 Endptr: "" +d: 16.062500 Endptr: "" +d: 16.062500 Endptr: "" +d: -16.062500 Endptr: "" +d: -16.062500 Endptr: "" +d: 0.500000 Endptr: "" +d: 5.000000 Endptr: "" +d: 50.000000 Endptr: "" +d: 500.000000 Endptr: "" +d: 5000.000000 Endptr: "" +d: 50000.000000 Endptr: "" +d: 500000.000000 Endptr: "" +d: 5000000.000000 Endptr: "" +d: 50000000.000000 Endptr: "" +d: 500000000.000000 Endptr: "" +d: 5000000000.000000 Endptr: "" +d: 50000000000.000000 Endptr: "" +d: 500000000000.000000 Endptr: "" +d: 5000000000000.000000 Endptr: "" +d: 50000000000000.000000 Endptr: "" +d: 500000000000000.000000 Endptr: "" +d: 5000000000000000.000000 Endptr: "" +d: 50000000000000000.000000 Endptr: "" +d: 500000000000000000.000000 Endptr: "" +d: 5000000000000000000.000000 Endptr: "" +d: 50000000000000000000.000000 Endptr: "" +d: 500000000000000000000.000000 Endptr: "" +d: 5000000000000000000000.000000 Endptr: "" +d: 49999999999999995805696.000000 Endptr: "" +d: 499999999999999991611392.000000 Endptr: "" +d: 5000000000000000452984832.000000 Endptr: "" +d: 50000000000000002382364672.000000 Endptr: "" +d: 500000000000000006643777536.000000 Endptr: "" +d: 4999999999999999791559868416.000000 Endptr: "" +d: 49999999999999995716575428608.000000 Endptr: "" +d: 500000000000000009942312419328.000000 Endptr: "" +d: 4999999999999999817948147482624.000000 Endptr: "" +d: 50000000000000002683081102196736.000000 Endptr: "" +d: 499999999999999972787615493521408.000000 Endptr: "" +d: 4999999999999999727876154935214080.000000 Endptr: "" +d: 49999999999999998431683053958987776.000000 Endptr: "" +d: 500000000000000021210318687008980992.000000 Endptr: "" +d: 4999999999999999769381329101060571136.000000 Endptr: "" +d: 49999999999999998874404911728017014784.000000 Endptr: "" +d: -0.500000 Endptr: "" +d: -5.000000 Endptr: "" +d: -50.000000 Endptr: "" +d: -500.000000 Endptr: "" +d: -5000.000000 Endptr: "" +d: -50000.000000 Endptr: "" +d: -500000.000000 Endptr: "" +d: -5000000.000000 Endptr: "" +d: -50000000.000000 Endptr: "" +d: -500000000.000000 Endptr: "" +d: -5000000000.000000 Endptr: "" +d: -50000000000.000000 Endptr: "" +d: -500000000000.000000 Endptr: "" +d: -5000000000000.000000 Endptr: "" +d: -50000000000000.000000 Endptr: "" +d: -500000000000000.000000 Endptr: "" +d: -5000000000000000.000000 Endptr: "" +d: -50000000000000000.000000 Endptr: "" +d: -500000000000000000.000000 Endptr: "" +d: -5000000000000000000.000000 Endptr: "" +d: -50000000000000000000.000000 Endptr: "" +d: -500000000000000000000.000000 Endptr: "" +d: -5000000000000000000000.000000 Endptr: "" +d: -49999999999999995805696.000000 Endptr: "" +d: -499999999999999991611392.000000 Endptr: "" +d: -5000000000000000452984832.000000 Endptr: "" +d: -50000000000000002382364672.000000 Endptr: "" +d: -500000000000000006643777536.000000 Endptr: "" +d: -4999999999999999791559868416.000000 Endptr: "" +d: -49999999999999995716575428608.000000 Endptr: "" +d: -500000000000000009942312419328.000000 Endptr: "" +d: -4999999999999999817948147482624.000000 Endptr: "" +d: -50000000000000002683081102196736.000000 Endptr: "" +d: -499999999999999972787615493521408.000000 Endptr: "" +d: -4999999999999999727876154935214080.000000 Endptr: "" +d: -49999999999999998431683053958987776.000000 Endptr: "" +d: -500000000000000021210318687008980992.000000 Endptr: "" +d: -4999999999999999769381329101060571136.000000 Endptr: "" +d: -49999999999999998874404911728017014784.000000 Endptr: "" +d: -0.000000 Endptr: "" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: " foobarbaz" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: " foobarbaz" +d: -inf Endptr: "" +d: -inf Endptr: "" +d: -inf Endptr: "" +d: -inf Endptr: " foobarbaz" +d: nan Endptr: "0.1e5" +d: nan Endptr: "-37" +d: nan Endptr: "1.05" +d: nan Endptr: " foo bar baz" +d: nan Endptr: "0.1e5" +d: nan Endptr: "-37" +d: nan Endptr: "1.05" +d: nan Endptr: " foo bar baz" +d: nan Endptr: "0.1e5" +d: nan Endptr: "-37" +d: nan Endptr: "1.05" +d: nan Endptr: " foo bar baz" diff --git a/tests/expected/bins_dynamic/stdlib/strtol.stderr b/tests/expected/bins_dynamic/stdlib/strtol.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/strtol.stdout b/tests/expected/bins_dynamic/stdlib/strtol.stdout new file mode 100644 index 0000000000..4c4177ba7b --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/strtol.stdout @@ -0,0 +1,26 @@ +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +38acf +endptr "g" +abcdef12 +endptr "" +cafebeef +endptr "" +731 +endptr "89" +731 +endptr "89" +0 +endptr "b" +0 +endptr "b" diff --git a/tests/expected/bins_dynamic/stdlib/strtoul.stderr b/tests/expected/bins_dynamic/stdlib/strtoul.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/strtoul.stdout b/tests/expected/bins_dynamic/stdlib/strtoul.stdout new file mode 100644 index 0000000000..398260ab44 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/strtoul.stdout @@ -0,0 +1,26 @@ +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +38acf +endptr "g" +abcdef12 +endptr "" +21000004 +endptr "" +731 +endptr "89" +731 +endptr "89" +0 +endptr "b" +0 +endptr "b" diff --git a/tests/expected/bins_dynamic/stdlib/system.stderr b/tests/expected/bins_dynamic/stdlib/system.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/system.stdout b/tests/expected/bins_dynamic/stdlib/system.stdout new file mode 100644 index 0000000000..90f99da2e1 --- /dev/null +++ b/tests/expected/bins_dynamic/stdlib/system.stdout @@ -0,0 +1,2 @@ +shell found: 1 +test of system diff --git a/tests/expected/bins_dynamic/string/mem.stderr b/tests/expected/bins_dynamic/string/mem.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/mem.stdout b/tests/expected/bins_dynamic/string/mem.stdout new file mode 100644 index 0000000000..a6c001081f --- /dev/null +++ b/tests/expected/bins_dynamic/string/mem.stdout @@ -0,0 +1,5 @@ +# mem # +Correct memchr +Correct memrchr +Correct memccpy +Correct memcmp diff --git a/tests/expected/bins_dynamic/string/memcpy.stderr b/tests/expected/bins_dynamic/string/memcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/memcpy.stdout b/tests/expected/bins_dynamic/string/memcpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/memmem.stderr b/tests/expected/bins_dynamic/string/memmem.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/memmem.stdout b/tests/expected/bins_dynamic/string/memmem.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/stpcpy.stderr b/tests/expected/bins_dynamic/string/stpcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/stpcpy.stdout b/tests/expected/bins_dynamic/string/stpcpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/stpncpy.stderr b/tests/expected/bins_dynamic/string/stpncpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/stpncpy.stdout b/tests/expected/bins_dynamic/string/stpncpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strcat.stderr b/tests/expected/bins_dynamic/string/strcat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strcat.stdout b/tests/expected/bins_dynamic/string/strcat.stdout new file mode 100644 index 0000000000..10bda51843 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strcat.stdout @@ -0,0 +1,2 @@ +hello world +hello world diff --git a/tests/expected/bins_dynamic/string/strchr.stderr b/tests/expected/bins_dynamic/string/strchr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strchr.stdout b/tests/expected/bins_dynamic/string/strchr.stdout new file mode 100644 index 0000000000..43f3dcfdf0 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strchr.stdout @@ -0,0 +1,4 @@ +ello +ld + +(nil) diff --git a/tests/expected/bins_dynamic/string/strchrnul.stderr b/tests/expected/bins_dynamic/string/strchrnul.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strchrnul.stdout b/tests/expected/bins_dynamic/string/strchrnul.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strcpy.stderr b/tests/expected/bins_dynamic/string/strcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strcpy.stdout b/tests/expected/bins_dynamic/string/strcpy.stdout new file mode 100644 index 0000000000..f08e831ac1 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strcpy.stdout @@ -0,0 +1,7 @@ +strcpy works! +strncpy works! +strncpy shaaaaaaaaa +strlcpy works! +copied 14 +strlcpy works! and strlcat! +copied 27 diff --git a/tests/expected/bins_dynamic/string/strcspn.stderr b/tests/expected/bins_dynamic/string/strcspn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strcspn.stdout b/tests/expected/bins_dynamic/string/strcspn.stdout new file mode 100644 index 0000000000..2b24e31277 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strcspn.stdout @@ -0,0 +1,2 @@ +2 +6 diff --git a/tests/expected/bins_dynamic/string/strlen.stderr b/tests/expected/bins_dynamic/string/strlen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strlen.stdout b/tests/expected/bins_dynamic/string/strlen.stdout new file mode 100644 index 0000000000..9ce90c8a65 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strlen.stdout @@ -0,0 +1,9 @@ +12 +0 +12 +12 +0 +12 +6 +12 +0 diff --git a/tests/expected/bins_dynamic/string/strncmp.stderr b/tests/expected/bins_dynamic/string/strncmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strncmp.stdout b/tests/expected/bins_dynamic/string/strncmp.stdout new file mode 100644 index 0000000000..e385d8a988 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strncmp.stdout @@ -0,0 +1,6 @@ +-97 +-195 +1 +-255 +-2 +0 diff --git a/tests/expected/bins_dynamic/string/strpbrk.stderr b/tests/expected/bins_dynamic/string/strpbrk.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strpbrk.stdout b/tests/expected/bins_dynamic/string/strpbrk.stdout new file mode 100644 index 0000000000..7a6bc8b089 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strpbrk.stdout @@ -0,0 +1,3 @@ +The quick drawn fix jumps over the lazy bug +lazy bug +NULL diff --git a/tests/expected/bins_dynamic/string/strrchr.stderr b/tests/expected/bins_dynamic/string/strrchr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strrchr.stdout b/tests/expected/bins_dynamic/string/strrchr.stdout new file mode 100644 index 0000000000..836469707d --- /dev/null +++ b/tests/expected/bins_dynamic/string/strrchr.stdout @@ -0,0 +1 @@ +strrch PASS diff --git a/tests/expected/bins_dynamic/string/strsep.stderr b/tests/expected/bins_dynamic/string/strsep.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strsep.stdout b/tests/expected/bins_dynamic/string/strsep.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strsignal.stderr b/tests/expected/bins_dynamic/string/strsignal.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strsignal.stdout b/tests/expected/bins_dynamic/string/strsignal.stdout new file mode 100644 index 0000000000..833dc63089 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strsignal.stdout @@ -0,0 +1 @@ +# strsignal # diff --git a/tests/expected/bins_dynamic/string/strspn.stderr b/tests/expected/bins_dynamic/string/strspn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strspn.stdout b/tests/expected/bins_dynamic/string/strspn.stdout new file mode 100644 index 0000000000..54d9c92604 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strspn.stdout @@ -0,0 +1,3 @@ +5 +1 +0 diff --git a/tests/expected/bins_dynamic/string/strstr.stderr b/tests/expected/bins_dynamic/string/strstr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strstr.stdout b/tests/expected/bins_dynamic/string/strstr.stdout new file mode 100644 index 0000000000..56fe13b7b1 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strstr.stdout @@ -0,0 +1,5 @@ +rust +libc we trust +(null) +(null) +RUST diff --git a/tests/expected/bins_dynamic/string/strtok.stderr b/tests/expected/bins_dynamic/string/strtok.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strtok.stdout b/tests/expected/bins_dynamic/string/strtok.stdout new file mode 100644 index 0000000000..4428c25c54 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strtok.stdout @@ -0,0 +1 @@ +I'd_just_like_to_interject_for_a_moment._What_you're_referring_to_as_Linux,_is_in_fact,_GNU/Linux,_or_as_I've_recently_taken_to_calling_it,_GNU_plus_Linux. diff --git a/tests/expected/bins_dynamic/string/strtok_r.stderr b/tests/expected/bins_dynamic/string/strtok_r.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/string/strtok_r.stdout b/tests/expected/bins_dynamic/string/strtok_r.stdout new file mode 100644 index 0000000000..4428c25c54 --- /dev/null +++ b/tests/expected/bins_dynamic/string/strtok_r.stdout @@ -0,0 +1 @@ +I'd_just_like_to_interject_for_a_moment._What_you're_referring_to_as_Linux,_is_in_fact,_GNU/Linux,_or_as_I've_recently_taken_to_calling_it,_GNU_plus_Linux. diff --git a/tests/expected/bins_dynamic/strings.stderr b/tests/expected/bins_dynamic/strings.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/strings.stdout b/tests/expected/bins_dynamic/strings.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_epoll/epollet.stdout b/tests/expected/bins_dynamic/sys_epoll/epollet.stdout new file mode 100644 index 0000000000..cc912d90cf --- /dev/null +++ b/tests/expected/bins_dynamic/sys_epoll/epollet.stdout @@ -0,0 +1,5 @@ +ctl ADD: 0 +wait for HUP, edge-triggered: 1 +wait for HUP again: 0 +ctl MOD: 0 +wait for HUP: 1 diff --git a/tests/expected/bins_dynamic/sys_mman.stderr b/tests/expected/bins_dynamic/sys_mman.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_mman.stdout b/tests/expected/bins_dynamic/sys_mman.stdout new file mode 100644 index 0000000000..108b1819b0 --- /dev/null +++ b/tests/expected/bins_dynamic/sys_mman.stdout @@ -0,0 +1,9 @@ +Page size: 4096 +Mapping 1 page of memory... +Mapping 3 pages of memory... +Randomizing mapping #1 +Randomizing mapping #2 +Unmapping page 2 of map2 +Randomizing page 1 of mapping #2 +Randomizing page 3 of mapping #2 +Unmapping it all at once! diff --git a/tests/expected/bins_dynamic/sys_socket/recv.stdout b/tests/expected/bins_dynamic/sys_socket/recv.stdout new file mode 100644 index 0000000000..cb2239126a --- /dev/null +++ b/tests/expected/bins_dynamic/sys_socket/recv.stdout @@ -0,0 +1,2 @@ +send foo +recv foo diff --git a/tests/expected/bins_dynamic/sys_socket/recvfrom.stdout b/tests/expected/bins_dynamic/sys_socket/recvfrom.stdout new file mode 100644 index 0000000000..e9f413f935 --- /dev/null +++ b/tests/expected/bins_dynamic/sys_socket/recvfrom.stdout @@ -0,0 +1,2 @@ +sendto bar +recvfrom bar diff --git a/tests/expected/bins_dynamic/sys_socket/unixpeername.stdout b/tests/expected/bins_dynamic/sys_socket/unixpeername.stdout new file mode 100644 index 0000000000..f9686e7295 --- /dev/null +++ b/tests/expected/bins_dynamic/sys_socket/unixpeername.stdout @@ -0,0 +1,3 @@ +listen sockname [AF_UNIX]: unixpeername.sock +client peername [AF_UNIX]: unixpeername.sock +client sockname [AF_UNIX]: (unnamed) diff --git a/tests/expected/bins_dynamic/sys_socket/unixrecv.stdout b/tests/expected/bins_dynamic/sys_socket/unixrecv.stdout new file mode 100644 index 0000000000..e9636a2916 --- /dev/null +++ b/tests/expected/bins_dynamic/sys_socket/unixrecv.stdout @@ -0,0 +1,2 @@ +send ipsum +recv ipsum diff --git a/tests/expected/bins_dynamic/sys_socket/unixrecvfrom.stdout b/tests/expected/bins_dynamic/sys_socket/unixrecvfrom.stdout new file mode 100644 index 0000000000..ad8e531910 --- /dev/null +++ b/tests/expected/bins_dynamic/sys_socket/unixrecvfrom.stdout @@ -0,0 +1,2 @@ +send lorem +recvfrom lorem diff --git a/tests/expected/bins_dynamic/sys_socket/unixsocketpair.stdout b/tests/expected/bins_dynamic/sys_socket/unixsocketpair.stdout new file mode 100644 index 0000000000..bb7fc428fa --- /dev/null +++ b/tests/expected/bins_dynamic/sys_socket/unixsocketpair.stdout @@ -0,0 +1,6 @@ +SOCK_STREAM +child: ping +parent: pong +SOCK_DGRAM +child: ping +parent: pong diff --git a/tests/expected/bins_dynamic/sys_stat/chmod.stderr b/tests/expected/bins_dynamic/sys_stat/chmod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_stat/chmod.stdout b/tests/expected/bins_dynamic/sys_stat/chmod.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_stat/fstatat.stderr b/tests/expected/bins_dynamic/sys_stat/fstatat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_stat/fstatat.stdout b/tests/expected/bins_dynamic/sys_stat/fstatat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_stat/lstat.stderr b/tests/expected/bins_dynamic/sys_stat/lstat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_stat/lstat.stdout b/tests/expected/bins_dynamic/sys_stat/lstat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/sys_syslog/syslog.stderr b/tests/expected/bins_dynamic/sys_syslog/syslog.stderr new file mode 100644 index 0000000000..faddade2a6 --- /dev/null +++ b/tests/expected/bins_dynamic/sys_syslog/syslog.stderr @@ -0,0 +1,13 @@ +relibc_test: This is a test message with formatting: 5 +relibc_test: Hank Hill +relibc_test: Foo has been bar'd +relibc_test: And now, Eeveelutions +relibc_test: Espeon +relibc_test: Umbreon +relibc_test: Sylveon +relibc_test: Glaceon +relibc_test: Leafeon +relibc_test: Vaporeon +relibc_test: Flareon +relibc_test: Jolteon +relibc_test: Bye from relibc's syslog tests! diff --git a/tests/expected/bins_dynamic/sys_syslog/syslog.stdout b/tests/expected/bins_dynamic/sys_syslog/syslog.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/termios.stderr b/tests/expected/bins_dynamic/termios.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/termios.stdout b/tests/expected/bins_dynamic/termios.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/asctime.stderr b/tests/expected/bins_dynamic/time/asctime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/asctime.stdout b/tests/expected/bins_dynamic/time/asctime.stdout new file mode 100644 index 0000000000..f21cdff5f4 --- /dev/null +++ b/tests/expected/bins_dynamic/time/asctime.stdout @@ -0,0 +1,5 @@ +Thu Jan 1 00:00:00 1970 +Sun Jan 1 00:00:00 1000 +Sat Dec 31 23:59:60 9999 +Sun Jan-99 00:00:00 -999 +Sat Dec999 99:99:99 9999 diff --git a/tests/expected/bins_dynamic/time/constants.stderr b/tests/expected/bins_dynamic/time/constants.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/constants.stdout b/tests/expected/bins_dynamic/time/constants.stdout new file mode 100644 index 0000000000..cf14858d8c --- /dev/null +++ b/tests/expected/bins_dynamic/time/constants.stdout @@ -0,0 +1,2 @@ +(nil) +1000000 diff --git a/tests/expected/bins_dynamic/time/gmtime.stderr b/tests/expected/bins_dynamic/time/gmtime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/gmtime.stdout b/tests/expected/bins_dynamic/time/gmtime.stdout new file mode 100644 index 0000000000..a5da960f12 --- /dev/null +++ b/tests/expected/bins_dynamic/time/gmtime.stdout @@ -0,0 +1,168 @@ +Unix epoch: + tm_sec = 0 + tm_min = 0 + tm_hour = 0 + tm_mday = 1 + tm_mon = 0 + tm_year = 70 + tm_wday = 4 + tm_yday = 0 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1970-03-01 00:00:00: + tm_sec = 0 + tm_min = 0 + tm_hour = 0 + tm_mday = 1 + tm_mon = 2 + tm_year = 70 + tm_wday = 0 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1970-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 70 + tm_wday = 4 + tm_yday = 364 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1972-02-29 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 29 + tm_mon = 1 + tm_year = 72 + tm_wday = 2 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1972-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 72 + tm_wday = 0 + tm_yday = 365 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2000-02-29 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 29 + tm_mon = 1 + tm_year = 100 + tm_wday = 2 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2000-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 100 + tm_wday = 0 + tm_yday = 365 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2100-03-01 00:00:00: + tm_sec = 0 + tm_min = 0 + tm_hour = 0 + tm_mday = 1 + tm_mon = 2 + tm_year = 200 + tm_wday = 1 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2100-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 200 + tm_wday = 5 + tm_yday = 364 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2038, pre-i32 overflow: + tm_sec = 7 + tm_min = 14 + tm_hour = 3 + tm_mday = 19 + tm_mon = 0 + tm_year = 138 + tm_wday = 2 + tm_yday = 18 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2038, post-i32 overflow: + tm_sec = 8 + tm_min = 14 + tm_hour = 3 + tm_mday = 19 + tm_mon = 0 + tm_year = 138 + tm_wday = 2 + tm_yday = 18 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2106, pre-u32 overflow: + tm_sec = 15 + tm_min = 28 + tm_hour = 6 + tm_mday = 7 + tm_mon = 1 + tm_year = 206 + tm_wday = 0 + tm_yday = 37 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2106, post-u32 overflow: + tm_sec = 16 + tm_min = 28 + tm_hour = 6 + tm_mday = 7 + tm_mon = 1 + tm_year = 206 + tm_wday = 0 + tm_yday = 37 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC diff --git a/tests/expected/bins_dynamic/time/localtime.stderr b/tests/expected/bins_dynamic/time/localtime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/localtime.stdout b/tests/expected/bins_dynamic/time/localtime.stdout new file mode 100644 index 0000000000..eb828a2b0d --- /dev/null +++ b/tests/expected/bins_dynamic/time/localtime.stdout @@ -0,0 +1,9 @@ +Year 69, Day of year: 332, Month 10, Day of month: 29, Day of week: 6, 0:0:0 +Year 69, Day of year: 364, Month 11, Day of month: 31, Day of week: 3, 0:0:0 +Year 69, Day of year: 364, Month 11, Day of month: 31, Day of week: 3, 23:59:59 +Year 69, Day of year: 364, Month 11, Day of month: 31, Day of week: 3, 23:51:40 +Year 70, Day of year: 0, Month 0, Day of month: 1, Day of week: 4, 0:0:0 +Year 70, Day of year: 0, Month 0, Day of month: 1, Day of week: 4, 0:0:1 +Year 118, Day of year: 193, Month 6, Day of month: 13, Day of week: 5, 4:9:10 +Fri Jul 13 06:03:43 2018 +Fri Jul 13 06:03:43 2018 diff --git a/tests/expected/bins_dynamic/time/localtime_r.stderr b/tests/expected/bins_dynamic/time/localtime_r.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/localtime_r.stdout b/tests/expected/bins_dynamic/time/localtime_r.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/macros.stderr b/tests/expected/bins_dynamic/time/macros.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/macros.stdout b/tests/expected/bins_dynamic/time/macros.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/mktime.stderr b/tests/expected/bins_dynamic/time/mktime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/mktime.stdout b/tests/expected/bins_dynamic/time/mktime.stdout new file mode 100644 index 0000000000..c9b309e63c --- /dev/null +++ b/tests/expected/bins_dynamic/time/mktime.stdout @@ -0,0 +1,6 @@ +31536000 +-2851200 = -2851200 +-86400 = -86400 +-500 = -500 +0 = 0 +1531454950 = 1531454950 diff --git a/tests/expected/bins_dynamic/time/strftime.stderr b/tests/expected/bins_dynamic/time/strftime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/strftime.stdout b/tests/expected/bins_dynamic/time/strftime.stdout new file mode 100644 index 0000000000..7d12e13954 --- /dev/null +++ b/tests/expected/bins_dynamic/time/strftime.stdout @@ -0,0 +1,14 @@ +20: Tue Tuesday Jul July +16: The 21st century +11: 06:25:42 AM +11: 03:00:00 PM +5: 15:00 +15: 15 1531839600 2 +6: 198 28 +28: Tue Jul 17 15:00:00 UTC 2018 +0: Tue Aug 07 19:17:11 UTC 2018Tue Aug 07 19:17:11 U +53 +52 +52 +1 +53 diff --git a/tests/expected/bins_dynamic/time/strptime.stderr b/tests/expected/bins_dynamic/time/strptime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/strptime.stdout b/tests/expected/bins_dynamic/time/strptime.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/time.stderr b/tests/expected/bins_dynamic/time/time.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/time.stdout b/tests/expected/bins_dynamic/time/time.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/timegm.stderr b/tests/expected/bins_dynamic/time/timegm.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/timegm.stdout b/tests/expected/bins_dynamic/time/timegm.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/tzset.stderr b/tests/expected/bins_dynamic/time/tzset.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/time/tzset.stdout b/tests/expected/bins_dynamic/time/tzset.stdout new file mode 100644 index 0000000000..9962d8acc0 --- /dev/null +++ b/tests/expected/bins_dynamic/time/tzset.stdout @@ -0,0 +1 @@ +tzname[0] UTC, tzname[1] UTC, daylight 0, timezone 0 diff --git a/tests/expected/bins_dynamic/tls.stderr b/tests/expected/bins_dynamic/tls.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/tls.stdout b/tests/expected/bins_dynamic/tls.stdout new file mode 100644 index 0000000000..d0b7b3c2e6 --- /dev/null +++ b/tests/expected/bins_dynamic/tls.stdout @@ -0,0 +1,2 @@ +0 == 0 +1 == 1 diff --git a/tests/expected/bins_dynamic/unistd/access.stderr b/tests/expected/bins_dynamic/unistd/access.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/access.stdout b/tests/expected/bins_dynamic/unistd/access.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/alarm.stderr b/tests/expected/bins_dynamic/unistd/alarm.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/alarm.stdout b/tests/expected/bins_dynamic/unistd/alarm.stdout new file mode 100644 index 0000000000..4ed7f83631 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/alarm.stdout @@ -0,0 +1,4 @@ +alarm(0) baseline: ok +alarm(1) fires: ok +alarm(0) cancel: ok +alarm re-arm returns remaining: ok diff --git a/tests/expected/bins_dynamic/unistd/brk.stderr b/tests/expected/bins_dynamic/unistd/brk.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/brk.stdout b/tests/expected/bins_dynamic/unistd/brk.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/confstr.stderr b/tests/expected/bins_dynamic/unistd/confstr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/confstr.stdout b/tests/expected/bins_dynamic/unistd/confstr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/constants.stderr b/tests/expected/bins_dynamic/unistd/constants.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/constants.stdout b/tests/expected/bins_dynamic/unistd/constants.stdout new file mode 100644 index 0000000000..90c907f1ff --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/constants.stdout @@ -0,0 +1,16 @@ +_POSIX_VERSION: 200809 +NULL: (nil) +R_OK: 4 +W_OK: 2 +X_OK: 1 +F_OK: 0 +SEEK_SET: 0 +SEEK_CUR: 1 +SEEK_END: 2 +F_LOCK: 1 +F_ULOCK: 0 +F_TEST: 3 +F_TLOCK: 2 +STDIN_FILENO: 0 +STDOUT_FILENO: 1 +STDERR_FILENO: 2 diff --git a/tests/expected/bins_dynamic/unistd/dup.stderr b/tests/expected/bins_dynamic/unistd/dup.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/dup.stdout b/tests/expected/bins_dynamic/unistd/dup.stdout new file mode 100644 index 0000000000..4fb8e07602 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/dup.stdout @@ -0,0 +1 @@ +duped fd is 1 greater than the original fd diff --git a/tests/expected/bins_dynamic/unistd/exec.stderr b/tests/expected/bins_dynamic/unistd/exec.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/exec.stdout b/tests/expected/bins_dynamic/unistd/exec.stdout new file mode 100644 index 0000000000..0f56119c73 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/exec.stdout @@ -0,0 +1 @@ +exec works :D diff --git a/tests/expected/bins_dynamic/unistd/fchdir.stderr b/tests/expected/bins_dynamic/unistd/fchdir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/fchdir.stdout b/tests/expected/bins_dynamic/unistd/fchdir.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/fork.stderr b/tests/expected/bins_dynamic/unistd/fork.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/fork.stdout b/tests/expected/bins_dynamic/unistd/fork.stdout new file mode 100644 index 0000000000..98f331c9de --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/fork.stdout @@ -0,0 +1,3 @@ +Hello from prepare +Hello from child +Hello from parent diff --git a/tests/expected/bins_dynamic/unistd/fsync.stderr b/tests/expected/bins_dynamic/unistd/fsync.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/fsync.stdout b/tests/expected/bins_dynamic/unistd/fsync.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/ftruncate.stderr b/tests/expected/bins_dynamic/unistd/ftruncate.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/ftruncate.stdout b/tests/expected/bins_dynamic/unistd/ftruncate.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/getopt.stderr b/tests/expected/bins_dynamic/unistd/getopt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/getopt.stdout b/tests/expected/bins_dynamic/unistd/getopt.stdout new file mode 100644 index 0000000000..aba2d44d3f --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/getopt.stdout @@ -0,0 +1,42 @@ +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 0 +errflg: 0 +ifile: +ofile: +result: 0 diff --git a/tests/expected/bins_dynamic/unistd/getopt_long.stderr b/tests/expected/bins_dynamic/unistd/getopt_long.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/getopt_long.stdout b/tests/expected/bins_dynamic/unistd/getopt_long.stdout new file mode 100644 index 0000000000..d2f352b822 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/getopt_long.stdout @@ -0,0 +1,20 @@ +--- Running: test --test0 -a +getopt_long returned 1, argument test0=(null) +Option -a with value (null) +--- Running: test --test1 -a +getopt_long returned 0, set flag to 2, argument test1=(null) +Option -a with value (null) +--- Running: test --test2 -a +getopt_long returned 3, argument test2=(null) +Option -a with value (null) +--- Running: test --test2=arg -a +getopt_long returned 3, argument test2=arg +Option -a with value (null) +--- Running: test --test3 -a +getopt_long returned 4, argument test3=-a +--- Running: test --test3=arg -a +getopt_long returned 4, argument test3=arg +Option -a with value (null) +--- Running: test --test3 arg -a +getopt_long returned 4, argument test3=arg +Option -a with value (null) diff --git a/tests/expected/bins_dynamic/unistd/pipe.stderr b/tests/expected/bins_dynamic/unistd/pipe.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/pipe.stdout b/tests/expected/bins_dynamic/unistd/pipe.stdout new file mode 100644 index 0000000000..980a0d5f19 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/pipe.stdout @@ -0,0 +1 @@ +Hello World! diff --git a/tests/expected/bins_dynamic/unistd/readlinkat.stderr b/tests/expected/bins_dynamic/unistd/readlinkat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/readlinkat.stdout b/tests/expected/bins_dynamic/unistd/readlinkat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/rmdir.stderr b/tests/expected/bins_dynamic/unistd/rmdir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/rmdir.stdout b/tests/expected/bins_dynamic/unistd/rmdir.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/sleep.stderr b/tests/expected/bins_dynamic/unistd/sleep.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/sleep.stdout b/tests/expected/bins_dynamic/unistd/sleep.stdout new file mode 100644 index 0000000000..25867863a2 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/sleep.stdout @@ -0,0 +1 @@ +unslept: 0 diff --git a/tests/expected/bins_dynamic/unistd/swab.stderr b/tests/expected/bins_dynamic/unistd/swab.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/swab.stdout b/tests/expected/bins_dynamic/unistd/swab.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/write.stderr b/tests/expected/bins_dynamic/unistd/write.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/unistd/write.stdout b/tests/expected/bins_dynamic/unistd/write.stdout new file mode 100644 index 0000000000..980a0d5f19 --- /dev/null +++ b/tests/expected/bins_dynamic/unistd/write.stdout @@ -0,0 +1 @@ +Hello World! diff --git a/tests/expected/bins_dynamic/wchar/fgetwc.stderr b/tests/expected/bins_dynamic/wchar/fgetwc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/fgetwc.stdout b/tests/expected/bins_dynamic/wchar/fgetwc.stdout new file mode 100644 index 0000000000..e7cabf537e --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/fgetwc.stdout @@ -0,0 +1,3 @@ +êçã +こんにちは世界 +Привет мир \ No newline at end of file diff --git a/tests/expected/bins_dynamic/wchar/fwide.stderr b/tests/expected/bins_dynamic/wchar/fwide.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/fwide.stdout b/tests/expected/bins_dynamic/wchar/fwide.stdout new file mode 100644 index 0000000000..44e0be8e35 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/fwide.stdout @@ -0,0 +1,4 @@ +0 +0 +0 +0 diff --git a/tests/expected/bins_dynamic/wchar/mbrtowc.stderr b/tests/expected/bins_dynamic/wchar/mbrtowc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/mbrtowc.stdout b/tests/expected/bins_dynamic/wchar/mbrtowc.stdout new file mode 100644 index 0000000000..8ea12e1952 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/mbrtowc.stdout @@ -0,0 +1,2 @@ +Processing 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ] +into 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ] diff --git a/tests/expected/bins_dynamic/wchar/mbsrtowcs.stderr b/tests/expected/bins_dynamic/wchar/mbsrtowcs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/mbsrtowcs.stdout b/tests/expected/bins_dynamic/wchar/mbsrtowcs.stdout new file mode 100644 index 0000000000..e8f8258f2e --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/mbsrtowcs.stdout @@ -0,0 +1 @@ +The length, including '\0': 5 diff --git a/tests/expected/bins_dynamic/wchar/printf-on-wchars.stderr b/tests/expected/bins_dynamic/wchar/printf-on-wchars.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/printf-on-wchars.stdout b/tests/expected/bins_dynamic/wchar/printf-on-wchars.stdout new file mode 100644 index 0000000000..b14ed25516 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/printf-on-wchars.stdout @@ -0,0 +1,4 @@ +This is a few one-byte chars: 1 2 a b +Long one-byte string: Hello World +This is a few multi-byte chars: ❤ R 😠 C +Long multi-byte string: 👉😎👉 Zoop! diff --git a/tests/expected/bins_dynamic/wchar/putwchar.stderr b/tests/expected/bins_dynamic/wchar/putwchar.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/putwchar.stdout b/tests/expected/bins_dynamic/wchar/putwchar.stdout new file mode 100644 index 0000000000..42d65d1da0 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/putwchar.stdout @@ -0,0 +1 @@ +zß水🍌 \ No newline at end of file diff --git a/tests/expected/bins_dynamic/wchar/ungetwc.stderr b/tests/expected/bins_dynamic/wchar/ungetwc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/ungetwc.stdout b/tests/expected/bins_dynamic/wchar/ungetwc.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcpcpy.stderr b/tests/expected/bins_dynamic/wchar/wcpcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcpcpy.stdout b/tests/expected/bins_dynamic/wchar/wcpcpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcpncpy.stderr b/tests/expected/bins_dynamic/wchar/wcpncpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcpncpy.stdout b/tests/expected/bins_dynamic/wchar/wcpncpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcrtomb.stderr b/tests/expected/bins_dynamic/wchar/wcrtomb.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcrtomb.stdout b/tests/expected/bins_dynamic/wchar/wcrtomb.stdout new file mode 100644 index 0000000000..ed85da3696 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcrtomb.stdout @@ -0,0 +1,2 @@ +Processing 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ] +into 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ] diff --git a/tests/expected/bins_dynamic/wchar/wcscasecmp.stderr b/tests/expected/bins_dynamic/wchar/wcscasecmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcscasecmp.stdout b/tests/expected/bins_dynamic/wchar/wcscasecmp.stdout new file mode 100644 index 0000000000..fdd11f6552 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcscasecmp.stdout @@ -0,0 +1,4 @@ +wcscasecmp(s1, s1) = 0 +wcscasecmp(s1, s2) = -1 +wcscasecmp(s2, s1) = 1 +wcscasecmp(s2, s2) = 0 diff --git a/tests/expected/bins_dynamic/wchar/wcschr.stderr b/tests/expected/bins_dynamic/wchar/wcschr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcschr.stdout b/tests/expected/bins_dynamic/wchar/wcschr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcscspn.stderr b/tests/expected/bins_dynamic/wchar/wcscspn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcscspn.stdout b/tests/expected/bins_dynamic/wchar/wcscspn.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsdup.stderr b/tests/expected/bins_dynamic/wchar/wcsdup.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsdup.stdout b/tests/expected/bins_dynamic/wchar/wcsdup.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsncasecmp.stderr b/tests/expected/bins_dynamic/wchar/wcsncasecmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsncasecmp.stdout b/tests/expected/bins_dynamic/wchar/wcsncasecmp.stdout new file mode 100644 index 0000000000..6a4bd43fc4 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcsncasecmp.stdout @@ -0,0 +1,5 @@ +wcsncasecmp(s1, s1, 17) = 0 +wcsncasecmp(s1, s2, 17) = -1 +wcsncasecmp(s2, s1, 17) = 1 +wcsncasecmp(s2, s1, 15) = 0 +wcsncasecmp(s1, s2, 0) = 0 diff --git a/tests/expected/bins_dynamic/wchar/wcsnlen.stderr b/tests/expected/bins_dynamic/wchar/wcsnlen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsnlen.stdout b/tests/expected/bins_dynamic/wchar/wcsnlen.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsnrtombs.stderr b/tests/expected/bins_dynamic/wchar/wcsnrtombs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsnrtombs.stdout b/tests/expected/bins_dynamic/wchar/wcsnrtombs.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsrchr.stderr b/tests/expected/bins_dynamic/wchar/wcsrchr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsrchr.stdout b/tests/expected/bins_dynamic/wchar/wcsrchr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsrtombs.stderr b/tests/expected/bins_dynamic/wchar/wcsrtombs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsrtombs.stdout b/tests/expected/bins_dynamic/wchar/wcsrtombs.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsstr.stderr b/tests/expected/bins_dynamic/wchar/wcsstr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcsstr.stdout b/tests/expected/bins_dynamic/wchar/wcsstr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcstod.stderr b/tests/expected/bins_dynamic/wchar/wcstod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcstod.stdout b/tests/expected/bins_dynamic/wchar/wcstod.stdout new file mode 100644 index 0000000000..0292990d95 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcstod.stdout @@ -0,0 +1,6 @@ +strtod(1.2345wowzah) = (1.234500, wowzah) +strtod(53) = (53.000000, ) +strtod(-254352.5...) = (-254352.500000, ...) +strtod( 19.2 wat) = (19.200000, wat) +strtod(365.24 29.53) = (365.240000, 29.53) +strtod( 29.53) = (29.530000, ) diff --git a/tests/expected/bins_dynamic/wchar/wcstoimax.stderr b/tests/expected/bins_dynamic/wchar/wcstoimax.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcstoimax.stdout b/tests/expected/bins_dynamic/wchar/wcstoimax.stdout new file mode 100644 index 0000000000..b9a14be845 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcstoimax.stdout @@ -0,0 +1,6 @@ +-123 +255 +44027 +8 +10 +16 diff --git a/tests/expected/bins_dynamic/wchar/wcstok.stderr b/tests/expected/bins_dynamic/wchar/wcstok.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcstok.stdout b/tests/expected/bins_dynamic/wchar/wcstok.stdout new file mode 100644 index 0000000000..bb7ba090dd --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcstok.stdout @@ -0,0 +1,12 @@ +Hello +from +the +otter +slide. +Must +have +gone +down +a +thousand +tiiiiiimeeeees... diff --git a/tests/expected/bins_dynamic/wchar/wcstol.stderr b/tests/expected/bins_dynamic/wchar/wcstol.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcstol.stdout b/tests/expected/bins_dynamic/wchar/wcstol.stdout new file mode 100644 index 0000000000..55aa50779a --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcstol.stdout @@ -0,0 +1 @@ +The decimal equivalents are: 2001, 6340800, -3624224 and 7340031. diff --git a/tests/expected/bins_dynamic/wchar/wcstoumax.stderr b/tests/expected/bins_dynamic/wchar/wcstoumax.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcstoumax.stdout b/tests/expected/bins_dynamic/wchar/wcstoumax.stdout new file mode 100644 index 0000000000..8e372f7589 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcstoumax.stdout @@ -0,0 +1,3 @@ +nptr = `10110134932` +wcstoumax = 10110134932 +Stopped scan at `` diff --git a/tests/expected/bins_dynamic/wchar/wcswidth.stderr b/tests/expected/bins_dynamic/wchar/wcswidth.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wcswidth.stdout b/tests/expected/bins_dynamic/wchar/wcswidth.stdout new file mode 100644 index 0000000000..7da79364d8 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wcswidth.stdout @@ -0,0 +1 @@ +wcswidth(L"relibc", 6) = 6 diff --git a/tests/expected/bins_dynamic/wchar/wprintf.stderr b/tests/expected/bins_dynamic/wchar/wprintf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wprintf.stdout b/tests/expected/bins_dynamic/wchar/wprintf.stdout new file mode 100644 index 0000000000..724e64b5b5 --- /dev/null +++ b/tests/expected/bins_dynamic/wchar/wprintf.stdout @@ -0,0 +1,67 @@ +percent: % +wstring: String +char: c +int: -16 +uint: 32 +hex: beef +HEX: C0FFEE +string: end +%n returned 44, total len of write: 87 + +Padding madness: + 001 +2 + c + 0x00000ff + 001 +0 0x1 + 1 +(00123) ( 123) +(-0123) ( -123) +( ) +0xabcdef +(nil) + +Positional madness: +4 3 2 +00002 +|Fiz |Buz | Fiz| Tot| +int: 5 double: 0.100000 0.200000 0.300000 0.400000 +-1717986918 0.100000 +-1717986918 0.200000 + +Float madness: + 1.234568e+02 + 1.000000E-05 + 123.456789 + 0.000010 + -1.234568e+02 +-00000001.234568e+02 +%.5g: -123.46 +%.5f: -123.45679 +%.5e: -1.23457e+02 +%.*g: -1.2e+02 +%.*f: -123.46 +%.*e: -1.23e+02 +%.*2$g: -123.46 +%.*2$f: -123.45679 +%.*2$e: -1.23457e+02 +100000 +1e+06 +1.000000e+06 +0.0001 +1E-05 +1.000000E-05 + +Non-finite float madness: +%e: inf -inf nan -nan +%E: INF -INF NAN -NAN +%f: inf -inf nan -nan +%F: INF -INF NAN -NAN +%g: inf -inf nan -nan +%G: INF -INF NAN -NAN +Things that have been buggy ++05 +Testing asprintf... +printed: test string, value: 11 +printed: test string 2, value: 13 +printed: test string 2, value: 13 diff --git a/tests/expected/bins_dynamic/wchar/wscanf.stderr b/tests/expected/bins_dynamic/wchar/wscanf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wchar/wscanf.stdout b/tests/expected/bins_dynamic/wchar/wscanf.stdout new file mode 100644 index 0000000000000000000000000000000000000000..90abc9a7f6599960cae1452846e7df0076fc6949 GIT binary patch literal 4414 zcmd^@%}&BV5XU|96dO*OROlB^1M_Mjpd4&J=i@(eY4 z^%4B{Yq4xlq83Wr13RcYLU0W5z984t-4Slu_;bXX==*8+4;0NvV+}`9c+(m z>?g-LY4-d=IXr$@+6@8VXBhz3o)lm{fRIo?jJ#DVAp8jcf1p%rsNeeWSFIl6lVZjG_8?1l^(C7@tdlFgvZE}H6nDxVOt9#Ew({~W+z}D7>AGWg;EpCIR8!oK`{<4*C~z#Wi=U(sH>s@)h8%hPvm+|G>#VK7`Ag2f-zU2I*FPC;wslva~)46j7&OPM;p)+(w z&I4V^3L#PeO*K9-;wkjMO*j7l@sP>|0iXKR!`-~z4ylW5{N6u1_5WNT`G#$`tvtyW Ti}*1$)i~HTtb$?I;pTH69Stt> literal 0 HcmV?d00001 diff --git a/tests/expected/bins_dynamic/wctype/towlower.stderr b/tests/expected/bins_dynamic/wctype/towlower.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wctype/towlower.stdout b/tests/expected/bins_dynamic/wctype/towlower.stdout new file mode 100644 index 0000000000..fdbf17836f --- /dev/null +++ b/tests/expected/bins_dynamic/wctype/towlower.stdout @@ -0,0 +1,2 @@ +HaLf WiDe ChAr StRiNg! +half wide char string! diff --git a/tests/expected/bins_dynamic/wctype/towupper.stderr b/tests/expected/bins_dynamic/wctype/towupper.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/wctype/towupper.stdout b/tests/expected/bins_dynamic/wctype/towupper.stdout new file mode 100644 index 0000000000..a09771e7bc --- /dev/null +++ b/tests/expected/bins_dynamic/wctype/towupper.stdout @@ -0,0 +1,2 @@ +HaLf WiDe ChAr StRiNg! +HALF WIDE CHAR STRING! diff --git a/tests/expected/bins_static/alloca.stderr b/tests/expected/bins_static/alloca.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/alloca.stdout b/tests/expected/bins_static/alloca.stdout new file mode 100644 index 0000000000..d0a7d827f2 --- /dev/null +++ b/tests/expected/bins_static/alloca.stdout @@ -0,0 +1 @@ +AAAAAAAAAAAAAAAA diff --git a/tests/expected/bins_static/args.stderr b/tests/expected/bins_static/args.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/args.stdout b/tests/expected/bins_static/args.stdout new file mode 100644 index 0000000000..deefb2f3da --- /dev/null +++ b/tests/expected/bins_static/args.stdout @@ -0,0 +1 @@ +test args diff --git a/tests/expected/bins_static/arpainet.stderr b/tests/expected/bins_static/arpainet.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/arpainet.stdout b/tests/expected/bins_static/arpainet.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/assert.stderr b/tests/expected/bins_static/assert.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/assert.stdout b/tests/expected/bins_static/assert.stdout new file mode 100644 index 0000000000..028678d987 --- /dev/null +++ b/tests/expected/bins_static/assert.stdout @@ -0,0 +1,2 @@ +yay! +groovy! diff --git a/tests/expected/bins_static/constructor.stderr b/tests/expected/bins_static/constructor.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/constructor.stdout b/tests/expected/bins_static/constructor.stdout new file mode 100644 index 0000000000..14e23d4cbb --- /dev/null +++ b/tests/expected/bins_static/constructor.stdout @@ -0,0 +1,6 @@ +constructor (101) +constructor (102) +constructor (103) +constructor (104) +constructor (no priority) +main diff --git a/tests/expected/bins_static/crypt/blowfish.stderr b/tests/expected/bins_static/crypt/blowfish.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/blowfish.stdout b/tests/expected/bins_static/crypt/blowfish.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/md5.stderr b/tests/expected/bins_static/crypt/md5.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/md5.stdout b/tests/expected/bins_static/crypt/md5.stdout new file mode 100644 index 0000000000..f985b46aff --- /dev/null +++ b/tests/expected/bins_static/crypt/md5.stdout @@ -0,0 +1 @@ +Success! diff --git a/tests/expected/bins_static/crypt/pbkdf2.stderr b/tests/expected/bins_static/crypt/pbkdf2.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/pbkdf2.stdout b/tests/expected/bins_static/crypt/pbkdf2.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/scrypt.stderr b/tests/expected/bins_static/crypt/scrypt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/scrypt.stdout b/tests/expected/bins_static/crypt/scrypt.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/sha256.stderr b/tests/expected/bins_static/crypt/sha256.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/sha256.stdout b/tests/expected/bins_static/crypt/sha256.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/sha512.stderr b/tests/expected/bins_static/crypt/sha512.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/crypt/sha512.stdout b/tests/expected/bins_static/crypt/sha512.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/ctype.stderr b/tests/expected/bins_static/ctype.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/ctype.stdout b/tests/expected/bins_static/ctype.stdout new file mode 100644 index 0000000000..e83b1e5f11 --- /dev/null +++ b/tests/expected/bins_static/ctype.stdout @@ -0,0 +1 @@ +Success: 0 diff --git a/tests/expected/bins_static/destructor.stderr b/tests/expected/bins_static/destructor.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/destructor.stdout b/tests/expected/bins_static/destructor.stdout new file mode 100644 index 0000000000..1da7c29706 --- /dev/null +++ b/tests/expected/bins_static/destructor.stdout @@ -0,0 +1,6 @@ +main +destructor (no priority) +destructor (104) +destructor (103) +destructor (102) +destructor (101) diff --git a/tests/expected/bins_static/dirent/fdopendir.stderr b/tests/expected/bins_static/dirent/fdopendir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/dirent/fdopendir.stdout b/tests/expected/bins_static/dirent/fdopendir.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/dirent/scandir.stderr b/tests/expected/bins_static/dirent/scandir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/dirent/scandir.stdout b/tests/expected/bins_static/dirent/scandir.stdout new file mode 100644 index 0000000000..4820bea846 --- /dev/null +++ b/tests/expected/bins_static/dirent/scandir.stdout @@ -0,0 +1,7 @@ +1-never-gonna-give-you-up +2-never-gonna-let-you-down +4-and-desert-you +5-never-gonna-make-you-cry +6-never-gonna-say-goodbye +7-never-gonna-tell-a-lie +8-and-hurt-you diff --git a/tests/expected/bins_static/err.stderr b/tests/expected/bins_static/err.stderr new file mode 100644 index 0000000000..b79d9c196f --- /dev/null +++ b/tests/expected/bins_static/err.stderr @@ -0,0 +1,6 @@ +err: Ran out of coffee: Owner died +err: Cat pulled out your ethernet cable +err: Eat 42 cookies: Permission denied +err: Potato, krumpli: Invalid exchange +err: I'm sorry, Dave. I'm afraid I can't do that.: Operation not permitted +err: Bye. It's crowded.: Too many users diff --git a/tests/expected/bins_static/err.stdout b/tests/expected/bins_static/err.stdout new file mode 100644 index 0000000000..8ef72cb5bc --- /dev/null +++ b/tests/expected/bins_static/err.stdout @@ -0,0 +1,2 @@ +err: Dang it, Bobby.: No such file or directory +Exiting due to error code: 87 diff --git a/tests/expected/bins_static/errno.stderr b/tests/expected/bins_static/errno.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/errno.stdout b/tests/expected/bins_static/errno.stdout new file mode 100644 index 0000000000..58419805ea --- /dev/null +++ b/tests/expected/bins_static/errno.stdout @@ -0,0 +1,136 @@ +E2BIG macro available +EACCES macro available +EADDRINUSE macro available +EADDRNOTAVAIL macro available +EADV macro available +EAFNOSUPPORT macro available +EAGAIN macro available +EALREADY macro available +EBADE macro available +EBADF macro available +EBADFD macro available +EBADMSG macro available +EBADR macro available +EBADRQC macro available +EBADSLT macro available +EBFONT macro available +EBUSY macro available +ECANCELED macro available +ECHILD macro available +ECHRNG macro available +ECOMM macro available +ECONNABORTED macro available +ECONNREFUSED macro available +ECONNRESET macro available +EDEADLK macro available +EDEADLOCK macro available +EDESTADDRREQ macro available +EDOM macro available +EDOTDOT macro available +EDQUOT macro available +EEXIST macro available +EFAULT macro available +EFBIG macro available +EHOSTDOWN macro available +EHOSTUNREACH macro available +EIDRM macro available +EILSEQ macro available +EINPROGRESS macro available +EINTR macro available +EINVAL macro available +EIO macro available +EISCONN macro available +EISDIR macro available +EISNAM macro available +EKEYEXPIRED macro available +EKEYREJECTED macro available +EKEYREVOKED macro available +EL2HLT macro available +EL2NSYNC macro available +EL3HLT macro available +EL3RST macro available +ELIBACC macro available +ELIBBAD macro available +ELIBEXEC macro available +ELIBMAX macro available +ELIBSCN macro available +ELNRNG macro available +ELOOP macro available +EMEDIUMTYPE macro available +EMFILE macro available +EMLINK macro available +EMSGSIZE macro available +EMULTIHOP macro available +ENAMETOOLONG macro available +ENAVAIL macro available +ENETDOWN macro available +ENETRESET macro available +ENETUNREACH macro available +ENFILE macro available +ENOANO macro available +ENOBUFS macro available +ENOCSI macro available +ENODATA macro available +ENODEV macro available +ENOENT macro available +ENOEXEC macro available +ENOKEY macro available +ENOLCK macro available +ENOLINK macro available +ENOMEDIUM macro available +ENOMEM macro available +ENOMSG macro available +ENONET macro available +ENOPKG macro available +ENOPROTOOPT macro available +ENOSPC macro available +ENOSR macro available +ENOSTR macro available +ENOSYS macro available +ENOTBLK macro available +ENOTCONN macro available +ENOTDIR macro available +ENOTEMPTY macro available +ENOTNAM macro available +ENOTRECOVERABLE macro available +ENOTSOCK macro available +ENOTSUP macro available +ENOTTY macro available +ENOTUNIQ macro available +ENXIO macro available +EOPNOTSUPP macro available +EOVERFLOW macro available +EOWNERDEAD macro available +EPERM macro available +EPFNOSUPPORT macro available +EPIPE macro available +EPROTO macro available +EPROTONOSUPPORT macro available +EPROTOTYPE macro available +ERANGE macro available +EREMCHG macro available +EREMOTE macro available +EREMOTEIO macro available +ERESTART macro available +EROFS macro available +ESHUTDOWN macro available +ESOCKTNOSUPPORT macro available +ESPIPE macro available +ESRCH macro available +ESRMNT macro available +ESTALE macro available +ESTRPIPE macro available +ETIME macro available +ETIMEDOUT macro available +ETOOMANYREFS macro available +ETXTBSY macro available +EUCLEAN macro available +EUNATCH macro available +EUSERS macro available +EWOULDBLOCK macro available +EXDEV macro available +EXFULL macro available +errno +changed to argv[0] +changed to program_invocation_name +changed to program_invocation_short_name diff --git a/tests/expected/bins_static/error.stderr b/tests/expected/bins_static/error.stderr new file mode 100644 index 0000000000..4eb19d82f3 --- /dev/null +++ b/tests/expected/bins_static/error.stderr @@ -0,0 +1 @@ +perror: No such file or directory diff --git a/tests/expected/bins_static/error.stdout b/tests/expected/bins_static/error.stdout new file mode 100644 index 0000000000..4e744d1bfb --- /dev/null +++ b/tests/expected/bins_static/error.stdout @@ -0,0 +1,4 @@ +errno: 2 = No such file or directory +errno: 2 = No such file or directory, return: 0 +errno: 2 = No, return: 34 +errno: 2 = , return: 34 diff --git a/tests/expected/bins_static/fcntl/create.stderr b/tests/expected/bins_static/fcntl/create.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fcntl/create.stdout b/tests/expected/bins_static/fcntl/create.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fcntl/fcntl.stderr b/tests/expected/bins_static/fcntl/fcntl.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fcntl/fcntl.stdout b/tests/expected/bins_static/fcntl/fcntl.stdout new file mode 100644 index 0000000000..4fb8e07602 --- /dev/null +++ b/tests/expected/bins_static/fcntl/fcntl.stdout @@ -0,0 +1 @@ +duped fd is 1 greater than the original fd diff --git a/tests/expected/bins_static/fcntl/open.stderr b/tests/expected/bins_static/fcntl/open.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fcntl/open.stdout b/tests/expected/bins_static/fcntl/open.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fcntl/posix_fallocate.stderr b/tests/expected/bins_static/fcntl/posix_fallocate.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fcntl/posix_fallocate.stdout b/tests/expected/bins_static/fcntl/posix_fallocate.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fnmatch.stderr b/tests/expected/bins_static/fnmatch.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/fnmatch.stdout b/tests/expected/bins_static/fnmatch.stdout new file mode 100644 index 0000000000..48ba8aef2e --- /dev/null +++ b/tests/expected/bins_static/fnmatch.stdout @@ -0,0 +1,32 @@ +Should succeed: +"*World" matches "Hello World" +"*World" matches "World" +"Hello*" matches "Hello World" +"H[ae]llo?World" matches "Hallo+World" +"H[ae]llo?World" matches "Hello_World" +"[0-9][!a]" matches "1b" +"/a/*/d" matches "/a/b/c/d" +"/a/*/d" matches "/a/bc/d" +"*hello" matches ".hello" +"/*hello" matches "/.hello" +"[a!][a!]" matches "!a" +"[\]]" matches "]" +"[\\]" matches "\" +"hello[/+]world" matches "hello/world" +"hello world" matches "HELLO WORLD" + +Should fail: +"*World" doesn't match "Hello Potato" +"*World" doesn't match "Potato" +"H[ae]llo?World" doesn't match "Hillo+World" +"H[ae]llo?World" doesn't match "Hello__World" +"[0-9][!a]" doesn't match "ab" +"[0-9][!a]" doesn't match "2a" +"/a/*/d" doesn't match "/a/b/c/d" +"/a/*/d" doesn't match "/a/bc/d/" +"*hello" doesn't match ".hello" +"/*hello" doesn't match "/.hello" +"[a!][a!]" doesn't match "ab" +"hello[/+]world" doesn't match "hello/world" +"hello world" doesn't match "HELLO WORLD" +"********************a" doesn't match "xxxxxxxxxxxxxxxxxxxxb" diff --git a/tests/expected/bins_static/futimens.stderr b/tests/expected/bins_static/futimens.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/futimens.stdout b/tests/expected/bins_static/futimens.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/iso646.stderr b/tests/expected/bins_static/iso646.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/iso646.stdout b/tests/expected/bins_static/iso646.stdout new file mode 100644 index 0000000000..5f8a399fe8 --- /dev/null +++ b/tests/expected/bins_static/iso646.stdout @@ -0,0 +1,33 @@ +0 and 0: 0 +1 and 0: 0 +0 and 1: 0 +1 and 1: 1 +0 and_eq 0: 0 +1 and_eq 0: 0 +0 and_eq 1: 0 +1 and_eq 1: 1 +a bitand b: 8 +a bitor b: 14 +compl a: 245 +not 0: 1 +not 1: 0 +0 not_eq 0: 0 +1 not_eq 0: 1 +0 not_eq 1: 0 +1 not_eq 1: 1 +0 or 0: 0 +1 or 0: 1 +0 or 1: 1 +1 or 1: 1 +0 or_eq 0: 0 +1 or_eq 0: 1 +0 or_eq 1: 1 +1 or_eq 1: 1 +0 xor 0: 0 +1 xor 0: 1 +0 xor 1: 1 +1 xor 1: 0 +0 xor_eq 0: 0 +1 xor_eq 0: 1 +0 xor_eq 1: 1 +1 xor_eq 1: 0 diff --git a/tests/expected/bins_static/libgen.stderr b/tests/expected/bins_static/libgen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/libgen.stdout b/tests/expected/bins_static/libgen.stdout new file mode 100644 index 0000000000..6e32d2008d --- /dev/null +++ b/tests/expected/bins_static/libgen.stdout @@ -0,0 +1,21 @@ +dirname("") == "." +basename("") == "." +dirname(".") == "." +basename(".") == "." +dirname("..") == "." +basename("..") == ".." +dirname("/") == "/" +basename("/") == "/" +dirname("///") == "/" +basename("///") == "/" +dirname("//usr//lib//") == "//usr" +basename("//usr//lib//") == "lib" +dirname("/usr") == "/" +basename("/usr") == "usr" +dirname("/usr/") == "/" +basename("/usr/") == "usr" +dirname("/usr/lib") == "/usr" +basename("/usr/lib") == "lib" +dirname(NULL) == "." +basename(NULL) == "." +Success: 0 diff --git a/tests/expected/bins_static/locale/duplocale.stderr b/tests/expected/bins_static/locale/duplocale.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/locale/duplocale.stdout b/tests/expected/bins_static/locale/duplocale.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/locale/newlocale.stderr b/tests/expected/bins_static/locale/newlocale.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/locale/newlocale.stdout b/tests/expected/bins_static/locale/newlocale.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/locale/setlocale.stderr b/tests/expected/bins_static/locale/setlocale.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/locale/setlocale.stdout b/tests/expected/bins_static/locale/setlocale.stdout new file mode 100644 index 0000000000..5d516f2e6d --- /dev/null +++ b/tests/expected/bins_static/locale/setlocale.stdout @@ -0,0 +1 @@ +success! diff --git a/tests/expected/bins_static/malloc/usable_size.stderr b/tests/expected/bins_static/malloc/usable_size.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/malloc/usable_size.stdout b/tests/expected/bins_static/malloc/usable_size.stdout new file mode 100644 index 0000000000..4188b67b66 --- /dev/null +++ b/tests/expected/bins_static/malloc/usable_size.stdout @@ -0,0 +1,2 @@ +malloc: 27 bytes +malloc_usable_size: 48 bytes diff --git a/tests/expected/bins_static/math.stderr b/tests/expected/bins_static/math.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/math.stdout b/tests/expected/bins_static/math.stdout new file mode 100644 index 0000000000..8654177967 --- /dev/null +++ b/tests/expected/bins_static/math.stdout @@ -0,0 +1 @@ +cos(3.140000) = -0.999999 diff --git a/tests/expected/bins_static/mkfifo.stderr b/tests/expected/bins_static/mkfifo.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mkfifo.stdout b/tests/expected/bins_static/mkfifo.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknod.stderr b/tests/expected/bins_static/mknod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknod.stdout b/tests/expected/bins_static/mknod.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknodat.stderr b/tests/expected/bins_static/mknodat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/mknodat.stdout b/tests/expected/bins_static/mknodat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/ptrace.stderr b/tests/expected/bins_static/ptrace.stderr new file mode 100644 index 0000000000..289c82a876 --- /dev/null +++ b/tests/expected/bins_static/ptrace.stderr @@ -0,0 +1,4 @@ +This is printed to STDOUT. +Or, at least, that's what I thought. +But all write(...) syscalls are actually redirected to STDERR by the tracer. +Big surprise, right! diff --git a/tests/expected/bins_static/ptrace.stdout b/tests/expected/bins_static/ptrace.stdout new file mode 100644 index 0000000000..824e89accd --- /dev/null +++ b/tests/expected/bins_static/ptrace.stdout @@ -0,0 +1,5 @@ +Set regs +Set regs +Set regs +Set regs +Child exited with status 0 diff --git a/tests/expected/bins_static/pty/forkpty.stderr b/tests/expected/bins_static/pty/forkpty.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/pty/forkpty.stdout b/tests/expected/bins_static/pty/forkpty.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/regex.stderr b/tests/expected/bins_static/regex.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/regex.stdout b/tests/expected/bins_static/regex.stdout new file mode 100644 index 0000000000..07caf6fc01 --- /dev/null +++ b/tests/expected/bins_static/regex.stdout @@ -0,0 +1,3 @@ +Matching group: 25 - 36 +Matching group: 31 - 36 +Matching group: -1 - -1 diff --git a/tests/expected/bins_static/select.stderr b/tests/expected/bins_static/select.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/select.stdout b/tests/expected/bins_static/select.stdout new file mode 100644 index 0000000000..a4a5b784a2 --- /dev/null +++ b/tests/expected/bins_static/select.stdout @@ -0,0 +1,8 @@ +Testing select on file +Is set before? 1 +Amount of things ready: 1 +Is set after? 1 +Testing select on pipe +Is set before? 1 +Amount of things ready: 1 +Is set after? 1 diff --git a/tests/expected/bins_static/setjmp.stderr b/tests/expected/bins_static/setjmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/setjmp.stdout b/tests/expected/bins_static/setjmp.stdout new file mode 100644 index 0000000000..c7e6bcddd8 --- /dev/null +++ b/tests/expected/bins_static/setjmp.stdout @@ -0,0 +1,2 @@ +jumping... +hi from jump diff --git a/tests/expected/bins_static/sigaction.stderr b/tests/expected/bins_static/sigaction.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sigaction.stdout b/tests/expected/bins_static/sigaction.stdout new file mode 100644 index 0000000000..8a6d2448ef --- /dev/null +++ b/tests/expected/bins_static/sigaction.stdout @@ -0,0 +1,5 @@ +Raising... +Signal handler1 called! +Raising... +Signal handler2 called! +Raised. diff --git a/tests/expected/bins_static/sigaltstack.stderr b/tests/expected/bins_static/sigaltstack.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sigaltstack.stdout b/tests/expected/bins_static/sigaltstack.stdout new file mode 100644 index 0000000000..be1dfac342 --- /dev/null +++ b/tests/expected/bins_static/sigaltstack.stdout @@ -0,0 +1,101 @@ +SIGUSR2 handler00 +SIGUSR2 handler01 +SIGUSR2 handler02 +SIGUSR2 handler03 +SIGUSR2 handler04 +SIGUSR2 handler05 +SIGUSR2 handler06 +SIGUSR2 handler07 +SIGUSR2 handler08 +SIGUSR2 handler09 +SIGUSR2 handler10 +SIGUSR2 handler11 +SIGUSR2 handler12 +SIGUSR2 handler13 +SIGUSR2 handler14 +SIGUSR2 handler15 +SIGUSR2 handler16 +SIGUSR2 handler17 +SIGUSR2 handler18 +SIGUSR2 handler19 +SIGUSR2 handler20 +SIGUSR2 handler21 +SIGUSR2 handler22 +SIGUSR2 handler23 +SIGUSR2 handler24 +SIGUSR2 handler25 +SIGUSR2 handler26 +SIGUSR2 handler27 +SIGUSR2 handler28 +SIGUSR2 handler29 +SIGUSR2 handler30 +SIGUSR2 handler31 +SIGUSR2 handler32 +SIGUSR2 handler33 +SIGUSR2 handler34 +SIGUSR2 handler35 +SIGUSR2 handler36 +SIGUSR2 handler37 +SIGUSR2 handler38 +SIGUSR2 handler39 +SIGUSR2 handler40 +SIGUSR2 handler41 +SIGUSR2 handler42 +SIGUSR2 handler43 +SIGUSR2 handler44 +SIGUSR2 handler45 +SIGUSR2 handler46 +SIGUSR2 handler47 +SIGUSR2 handler48 +SIGUSR2 handler49 +SIGUSR2 handler50 +SIGUSR2 handler51 +SIGUSR2 handler52 +SIGUSR2 handler53 +SIGUSR2 handler54 +SIGUSR2 handler55 +SIGUSR2 handler56 +SIGUSR2 handler57 +SIGUSR2 handler58 +SIGUSR2 handler59 +SIGUSR2 handler60 +SIGUSR2 handler61 +SIGUSR2 handler62 +SIGUSR2 handler63 +SIGUSR2 handler64 +SIGUSR2 handler65 +SIGUSR2 handler66 +SIGUSR2 handler67 +SIGUSR2 handler68 +SIGUSR2 handler69 +SIGUSR2 handler70 +SIGUSR2 handler71 +SIGUSR2 handler72 +SIGUSR2 handler73 +SIGUSR2 handler74 +SIGUSR2 handler75 +SIGUSR2 handler76 +SIGUSR2 handler77 +SIGUSR2 handler78 +SIGUSR2 handler79 +SIGUSR2 handler80 +SIGUSR2 handler81 +SIGUSR2 handler82 +SIGUSR2 handler83 +SIGUSR2 handler84 +SIGUSR2 handler85 +SIGUSR2 handler86 +SIGUSR2 handler87 +SIGUSR2 handler88 +SIGUSR2 handler89 +SIGUSR2 handler90 +SIGUSR2 handler91 +SIGUSR2 handler92 +SIGUSR2 handler93 +SIGUSR2 handler94 +SIGUSR2 handler95 +SIGUSR2 handler96 +SIGUSR2 handler97 +SIGUSR2 handler98 +SIGUSR2 handler99 +SIGUSR2 handler00 diff --git a/tests/expected/bins_static/signal.stderr b/tests/expected/bins_static/signal.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/signal.stdout b/tests/expected/bins_static/signal.stdout new file mode 100644 index 0000000000..7d184e34c8 --- /dev/null +++ b/tests/expected/bins_static/signal.stdout @@ -0,0 +1,3 @@ +Raising... +Signal handler called! +Raised. diff --git a/tests/expected/bins_static/sigsetjmp.stderr b/tests/expected/bins_static/sigsetjmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sigsetjmp.stdout b/tests/expected/bins_static/sigsetjmp.stdout new file mode 100644 index 0000000000..eb99a60c8e --- /dev/null +++ b/tests/expected/bins_static/sigsetjmp.stdout @@ -0,0 +1,2 @@ +Starting jump... +Jump done. diff --git a/tests/expected/bins_static/stdio/all.stderr b/tests/expected/bins_static/stdio/all.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/all.stdout b/tests/expected/bins_static/stdio/all.stdout new file mode 100644 index 0000000000..92881132c2 --- /dev/null +++ b/tests/expected/bins_static/stdio/all.stdout @@ -0,0 +1,4 @@ +H +Jello World! + +Hello diff --git a/tests/expected/bins_static/stdio/buffer.stderr b/tests/expected/bins_static/stdio/buffer.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/buffer.stdout b/tests/expected/bins_static/stdio/buffer.stdout new file mode 100644 index 0000000000..4b124bc7c0 --- /dev/null +++ b/tests/expected/bins_static/stdio/buffer.stdout @@ -0,0 +1,5 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaTest +Hello +World +It works +No buffering issues here diff --git a/tests/expected/bins_static/stdio/dprintf.stderr b/tests/expected/bins_static/stdio/dprintf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/dprintf.stdout b/tests/expected/bins_static/stdio/dprintf.stdout new file mode 100644 index 0000000000..e12bc872a0 --- /dev/null +++ b/tests/expected/bins_static/stdio/dprintf.stdout @@ -0,0 +1,2 @@ +Hello, world +a diff --git a/tests/expected/bins_static/stdio/fgets.stderr b/tests/expected/bins_static/stdio/fgets.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fgets.stdout b/tests/expected/bins_static/stdio/fgets.stdout new file mode 100644 index 0000000000..a856e2de52 --- /dev/null +++ b/tests/expected/bins_static/stdio/fgets.stdout @@ -0,0 +1,31 @@ +Hello World! + +Line 2 + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg +hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj +kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll +mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm +nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp +qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr +sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss +ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +EOF diff --git a/tests/expected/bins_static/stdio/fputs.stderr b/tests/expected/bins_static/stdio/fputs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fputs.stdout b/tests/expected/bins_static/stdio/fputs.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fread.stderr b/tests/expected/bins_static/stdio/fread.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fread.stdout b/tests/expected/bins_static/stdio/fread.stdout new file mode 100644 index 0000000000..f3fbf213be --- /dev/null +++ b/tests/expected/bins_static/stdio/fread.stdout @@ -0,0 +1,32 @@ +1 +22 +333 +4444 +55555 +666666 +7777777 +88888888 +999999999 +AAAAAAAAAA +BBBBBBBBBBB +CCCCCCCCCCCC +DDDDDDDDDDDDD +EEEEEEEEEEEEEE +FFFFFFFFFFFFFFF +0000000000000000 +11111111111111111 +222222222222222222 +3333333333333333333 +44444444444444444444 +555555555555555555555 +6666666666666666666666 +77777777777777777777777 +888888888888888888888888 +9999999999999999999999999 +AAAAAAAAAAAAAAAAAAAAAAAAAA +BBBBBBBBBBBBBBBBBBBBBBBBBBB +CCCCCCCCCCCCCCCCCCCCCCCCCCCC +DDDDDDDDDDDDDDDDDDDDDDDDDDDDD +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEE +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +00000000000000000000000000000000 diff --git a/tests/expected/bins_static/stdio/freopen.stderr b/tests/expected/bins_static/stdio/freopen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/freopen.stdout b/tests/expected/bins_static/stdio/freopen.stdout new file mode 100644 index 0000000000..1dd338fd1a --- /dev/null +++ b/tests/expected/bins_static/stdio/freopen.stdout @@ -0,0 +1,3 @@ +Hello +0 +0 diff --git a/tests/expected/bins_static/stdio/fscanf.stderr b/tests/expected/bins_static/stdio/fscanf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fscanf.stdout b/tests/expected/bins_static/stdio/fscanf.stdout new file mode 100644 index 0000000000..73f11e68cf --- /dev/null +++ b/tests/expected/bins_static/stdio/fscanf.stdout @@ -0,0 +1,14 @@ +1 2 3 9 +9 +16 +35 +48 +92 +109 +152 +201 +241 +276 +293 +299 +301 diff --git a/tests/expected/bins_static/stdio/fscanf_offby1.stderr b/tests/expected/bins_static/stdio/fscanf_offby1.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fscanf_offby1.stdout b/tests/expected/bins_static/stdio/fscanf_offby1.stdout new file mode 100644 index 0000000000..daf724a9e5 --- /dev/null +++ b/tests/expected/bins_static/stdio/fscanf_offby1.stdout @@ -0,0 +1 @@ +1234, 7, 32 diff --git a/tests/expected/bins_static/stdio/fseek.stderr b/tests/expected/bins_static/stdio/fseek.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fseek.stdout b/tests/expected/bins_static/stdio/fseek.stdout new file mode 100644 index 0000000000..b6163b2663 --- /dev/null +++ b/tests/expected/bins_static/stdio/fseek.stdout @@ -0,0 +1,2 @@ +Line 2 +ftello: 21 diff --git a/tests/expected/bins_static/stdio/fwrite.stderr b/tests/expected/bins_static/stdio/fwrite.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/fwrite.stdout b/tests/expected/bins_static/stdio/fwrite.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/getc_unget.stderr b/tests/expected/bins_static/stdio/getc_unget.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/getc_unget.stdout b/tests/expected/bins_static/stdio/getc_unget.stdout new file mode 100644 index 0000000000..f01a3f968d --- /dev/null +++ b/tests/expected/bins_static/stdio/getc_unget.stdout @@ -0,0 +1 @@ +Worked! diff --git a/tests/expected/bins_static/stdio/getline.stderr b/tests/expected/bins_static/stdio/getline.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/getline.stdout b/tests/expected/bins_static/stdio/getline.stdout new file mode 100644 index 0000000000..ba753ef6b2 --- /dev/null +++ b/tests/expected/bins_static/stdio/getline.stdout @@ -0,0 +1,41 @@ + 1: getline(NULL, NULL, stream) + => 22 (Invalid argument) - EINVAL + 2: getline(NULL, &n, stream) + => 22 (Invalid argument) - EINVAL + 3: getline(&lineptr, NULL, stream) + => 22 (Invalid argument) - EINVAL + 4: getline(NULL, NULL, NULL) + => 22 (Invalid argument) - EINVAL + 5: getline(&lineptr, NULL, NULL) + => 22 (Invalid argument) - EINVAL + 6: getline(NULL, &n, NULL) + => 22 (Invalid argument) - EINVAL + 7: getline(&lineptr, &n, NULL) + => 22 (Invalid argument) - EINVAL + 8: getdelim(&lineptr, &n, 25600, stream) + => 22 (Invalid argument) - EINVAL + + 1: status = 27, strlen = 27, feof = 0, ferror = 0 + |>Space: the final frontier. + 2: status = 1, strlen = 1, feof = 0, ferror = 0 + |> + 3: status = 50, strlen = 50, feof = 0, ferror = 0 + |>These are the voyages of the starship Enterprise. + 4: status = 24, strlen = 24, feof = 0, ferror = 0 + |>Its continuing mission: + 5: status = 33, strlen = 33, feof = 0, ferror = 0 + |>- to explore strange new worlds; + 6: status = 46, strlen = 46, feof = 0, ferror = 0 + |>- to seek out new life and new civilizations; + 7: status = 45, strlen = 45, feof = 0, ferror = 0 + |>- to boldly go where no one has gone before! + +overread 1, status = -1, feof = 1, ferror = 0 +|~- to boldly go where no one has gone before! + +overread 2, status = -1, feof = 1, ferror = 0 +|~- to boldly go where no one has gone before! + +overread 3, status = -1, feof = 1, ferror = 0 +|~- to boldly go where no one has gone before! + diff --git a/tests/expected/bins_static/stdio/mutex.stderr b/tests/expected/bins_static/stdio/mutex.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/mutex.stdout b/tests/expected/bins_static/stdio/mutex.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/popen.stderr b/tests/expected/bins_static/stdio/popen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/popen.stdout b/tests/expected/bins_static/stdio/popen.stdout new file mode 100644 index 0000000000..05f7216cbe --- /dev/null +++ b/tests/expected/bins_static/stdio/popen.stdout @@ -0,0 +1,8 @@ +Line 1: 1-never-gonna-give-you-up +Line 2: 2-never-gonna-let-you-down +Line 3: 3-never-gonna-run-around +Line 4: 4-and-desert-you +Line 5: 5-never-gonna-make-you-cry +Line 6: 6-never-gonna-say-goodbye +Line 7: 7-never-gonna-tell-a-lie +Line 8: 8-and-hurt-you diff --git a/tests/expected/bins_static/stdio/printf.stderr b/tests/expected/bins_static/stdio/printf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/printf.stdout b/tests/expected/bins_static/stdio/printf.stdout new file mode 100644 index 0000000000..a13543aeb2 --- /dev/null +++ b/tests/expected/bins_static/stdio/printf.stdout @@ -0,0 +1,78 @@ +percent: % +string: String +char: c +char: +int: -16 +uint: 32 +hex: beef +HEX: C0FFEE +string: end +%n returned 51, total len of write: 94 + +Padding madness: + 001 +2 + c + 0x00000ff + 001 +0 0x1 + 1 +(00123) ( 123) +(-0123) ( -123) +( ) +0xabcdef +(nil) + +Positional madness: +4 3 2 +00002 +|Fiz |Buz | Fiz| Tot| +int: 5 double: 0.100000 0.200000 0.300000 0.400000 +-1717986918 0.100000 +-1717986918 0.200000 + +Float madness: + 1.234568e+02 + 1.000000E-05 + 123.456789 + 0.000010 + -1.234568e+02 +-00000001.234568e+02 +%.5g: -123.46 +%.5f: -123.45679 +%.5e: -1.23457e+02 +%.*g: -1.2e+02 +%.*f: -123.46 +%.*e: -1.23e+02 +%.*2$g: -123.46 +%.*2$f: -123.45679 +%.*2$e: -1.23457e+02 +100000 +1e+06 +1.000000e+06 +0.0001 +1E-05 +1.000000E-05 +001234.56 +001234.56 + +Non-finite float madness: +%e: inf -inf nan -nan +%E: INF -INF NAN -NAN +%f: inf -inf nan -nan +%F: INF -INF NAN -NAN +%g: inf -inf nan -nan +%G: INF -INF NAN -NAN +Things that have been buggy ++05 +Testing asprintf... +printed: test string, value: 11 +printed: test string 2, value: 13 +printed: test string 2, value: 13 + +%m: Owner died + +C23: +Binary %b: 100 +Binary %b alternate: 0b100 +Binary %B: 100 +Binary %B alternate: 0B100 diff --git a/tests/expected/bins_static/stdio/printf_neg_pad.stderr b/tests/expected/bins_static/stdio/printf_neg_pad.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/printf_neg_pad.stdout b/tests/expected/bins_static/stdio/printf_neg_pad.stdout new file mode 100644 index 0000000000..f573a2b6d2 --- /dev/null +++ b/tests/expected/bins_static/stdio/printf_neg_pad.stdout @@ -0,0 +1,2 @@ +A BCC/ +AB CC/ diff --git a/tests/expected/bins_static/stdio/printf_space_pad.stderr b/tests/expected/bins_static/stdio/printf_space_pad.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/printf_space_pad.stdout b/tests/expected/bins_static/stdio/printf_space_pad.stdout new file mode 100644 index 0000000000..8e9107f351 --- /dev/null +++ b/tests/expected/bins_static/stdio/printf_space_pad.stdout @@ -0,0 +1 @@ +A B diff --git a/tests/expected/bins_static/stdio/putc_unlocked.stderr b/tests/expected/bins_static/stdio/putc_unlocked.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/putc_unlocked.stdout b/tests/expected/bins_static/stdio/putc_unlocked.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/rename.stderr b/tests/expected/bins_static/stdio/rename.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/rename.stdout b/tests/expected/bins_static/stdio/rename.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/renameat.stderr b/tests/expected/bins_static/stdio/renameat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/renameat.stdout b/tests/expected/bins_static/stdio/renameat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/scanf.stderr b/tests/expected/bins_static/stdio/scanf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/scanf.stdout b/tests/expected/bins_static/stdio/scanf.stdout new file mode 100644 index 0000000000..dca295ccb5 --- /dev/null +++ b/tests/expected/bins_static/stdio/scanf.stdout @@ -0,0 +1,16 @@ +2, { sa: 12, ia: 345, ib: 0, ic: 0, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string1: , string2: , string3: , string4: } +3, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.000000, da: 0.000000, ptr: (nil), char: a, string1: , string2: , string3: , string4: } +2, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: (nil), char: a, string1: , string2: , string3: , string4: } +1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string1: , string2: , string3: , string4: } +1, { sa: 12, ia: 18, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string1: Hello, string2: , string3: , string4: } +1, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: a, string1: Hello, string2: , string3: , string4: } +2, { sa: 12, ia: 15, ib: 837, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: elllo, string2: , string3: , string4: } +1, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: elllo, string2: , string3: , string4: } +0, { sa: 12, ia: 0, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: elllo, string2: , string3: , string4: } +2, { sa: 12, ia: 42, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: world, string2: , string3: , string4: } +2, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: world, string2: planet, string3: , string4: } +4, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: e, string2: o, string3: l, string4: d } +4, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: a, string2: e, string3: f, string4: dddddd } +4, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: a, string2: e, string3: f, string4: dddddd } +1, { sa: 12, ia: 84, ib: 8, ic: 8, fa: 0.100000, da: 0.200000, ptr: 0xabcdef, char: h, string1: testbbbb, string2: e, string3: f, string4: dddddd } +3 "https" "//" "redox-os.org" "" diff --git a/tests/expected/bins_static/stdio/setvbuf.stderr b/tests/expected/bins_static/stdio/setvbuf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/setvbuf.stdout b/tests/expected/bins_static/stdio/setvbuf.stdout new file mode 100644 index 0000000000..ebda98d01a --- /dev/null +++ b/tests/expected/bins_static/stdio/setvbuf.stdout @@ -0,0 +1,4 @@ +H +Hello World! + +Hello diff --git a/tests/expected/bins_static/stdio/sprintf.stderr b/tests/expected/bins_static/stdio/sprintf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/sprintf.stdout b/tests/expected/bins_static/stdio/sprintf.stdout new file mode 100644 index 0000000000..4c76b1a825 --- /dev/null +++ b/tests/expected/bins_static/stdio/sprintf.stdout @@ -0,0 +1 @@ +This stri diff --git a/tests/expected/bins_static/stdio/ungetc_ftell.stderr b/tests/expected/bins_static/stdio/ungetc_ftell.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/ungetc_ftell.stdout b/tests/expected/bins_static/stdio/ungetc_ftell.stdout new file mode 100644 index 0000000000..e36f413519 --- /dev/null +++ b/tests/expected/bins_static/stdio/ungetc_ftell.stdout @@ -0,0 +1,28 @@ +#, 0 +i, 1 +n, 2 +h, -9 +e, -8 +l, -7 +l, -6 +o, -5 + , -4 +w, -3 +o, -2 +r, -1 +l, 0 +d, 1 + +, 2 +c, 3 +l, 4 +u, 5 +d, 6 +e, 7 + , 8 +<, 9 +s, 10 +t, 11 +d, 12 +i, 13 +o, 14 diff --git a/tests/expected/bins_static/stdio/ungetc_multiple.stderr b/tests/expected/bins_static/stdio/ungetc_multiple.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdio/ungetc_multiple.stdout b/tests/expected/bins_static/stdio/ungetc_multiple.stdout new file mode 100644 index 0000000000..3b18e512db --- /dev/null +++ b/tests/expected/bins_static/stdio/ungetc_multiple.stdout @@ -0,0 +1 @@ +hello world diff --git a/tests/expected/bins_static/stdlib/a64l.stderr b/tests/expected/bins_static/stdlib/a64l.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/a64l.stdout b/tests/expected/bins_static/stdlib/a64l.stdout new file mode 100644 index 0000000000..d22f10d973 --- /dev/null +++ b/tests/expected/bins_static/stdlib/a64l.stdout @@ -0,0 +1,42 @@ +Correct a64l: azAZ9. = 194301926 +Correct a64l: azA = 53222 +l64a(0): +l64a(1): / +l64a(2): 0 +l64a(11): 9 +l64a(12): A +l64a(37): Z +l64a(38): a +l64a(63): z +l64a(64): ./ +l64a(65): // +l64a(4095): zz +l64a(4096): ../ +l64a(262143): zzz +l64a(262144): .../ +l64a(16777215): zzzz +l64a(16777216): ..../ +l64a(1073741823): zzzzz +l64a(1073741824): ...../ +l64a(2147483647): zzzzz/ +a64l(l64a(0)): 0 +a64l(l64a(1)): 1 +a64l(l64a(2)): 2 +a64l(l64a(11)): 11 +a64l(l64a(12)): 12 +a64l(l64a(37)): 37 +a64l(l64a(38)): 38 +a64l(l64a(63)): 63 +a64l(l64a(64)): 64 +a64l(l64a(65)): 65 +a64l(l64a(4095)): 4095 +a64l(l64a(4096)): 4096 +a64l(l64a(262143)): 262143 +a64l(l64a(262144)): 262144 +a64l(l64a(16777215)): 16777215 +a64l(l64a(16777216)): 16777216 +a64l(l64a(1073741823)): 1073741823 +a64l(l64a(1073741824)): 1073741824 +a64l(l64a(2147483647)): 2147483647 +l64a(x) (lower 32 bits of x are 1985229328): E61Jq/ +a64l(l64a(x)) (lower 32 bits of x are 1985229328): 1985229328 diff --git a/tests/expected/bins_static/stdlib/alloc.stderr b/tests/expected/bins_static/stdlib/alloc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/alloc.stdout b/tests/expected/bins_static/stdlib/alloc.stdout new file mode 100644 index 0000000000..d5bc92dc00 --- /dev/null +++ b/tests/expected/bins_static/stdlib/alloc.stdout @@ -0,0 +1,30 @@ +malloc (size 0): (OK) +malloc: pointer: (not NULL), error value: 0 = Success +malloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +calloc (size 0): (OK) +calloc: pointer: (not NULL), error value: 0 = Success +calloc (overflowing): pointer: (nil), error value: 12 = Out of memory +realloc (size 0): (OK) +realloc: pointer: (not NULL), error value: 0 = Success +realloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +reallocarray: pointer: (not NULL), error value: 0 = Success +reallocarray (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +memalign (size 0): (OK) +memalign: pointer: (alignment OK), error value: 0 = Success +memalign (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +memalign (alignment 0): pointer: (nil), error value: 22 = Invalid argument +memalign (alignment 3): pointer: (nil), error value: 22 = Invalid argument +aligned_alloc (size % alignment == 0): pointer: (alignment OK), error value: 0 = Success +aligned_alloc (size % alignment != 0): pointer: (nil), error value: 22 = Invalid argument +valloc (size 0): (OK) +valloc: pointer: (alignment OK), error value: 0 = Success +valloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +posix_memalign: pointer: (alignment OK), error value: 0 = Success +posix_memalign (alignment 0): pointer: (nil), error value: 22 = Invalid argument +posix_memalign (non-power-of-two multiple of sizeof(void *)): pointer: (nil), error value: 22 = Invalid argument +posix_memalign (size 0): (OK) +posix_memalign (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory +pvalloc (size 0): (OK) +pvalloc: pointer: (alignment OK), error value: 0 = Success +pvalloc (2 pages): pointer: (alignment OK), error value: 0 = Success +pvalloc (SIZE_MAX): pointer: (nil), error value: 12 = Out of memory diff --git a/tests/expected/bins_static/stdlib/atof.stderr b/tests/expected/bins_static/stdlib/atof.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/atof.stdout b/tests/expected/bins_static/stdlib/atof.stdout new file mode 100644 index 0000000000..3227d85385 --- /dev/null +++ b/tests/expected/bins_static/stdlib/atof.stdout @@ -0,0 +1,2 @@ +-3.140000 +inf diff --git a/tests/expected/bins_static/stdlib/atoi.stderr b/tests/expected/bins_static/stdlib/atoi.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/atoi.stdout b/tests/expected/bins_static/stdlib/atoi.stdout new file mode 100644 index 0000000000..d90e37ccea --- /dev/null +++ b/tests/expected/bins_static/stdlib/atoi.stdout @@ -0,0 +1,6 @@ +-42 +555 +1234567890 +-42 +555 +1234567890 diff --git a/tests/expected/bins_static/stdlib/div.stderr b/tests/expected/bins_static/stdlib/div.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/div.stdout b/tests/expected/bins_static/stdlib/div.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/env.stderr b/tests/expected/bins_static/stdlib/env.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/env.stdout b/tests/expected/bins_static/stdlib/env.stdout new file mode 100644 index 0000000000..a0596ab55e --- /dev/null +++ b/tests/expected/bins_static/stdlib/env.stdout @@ -0,0 +1,8 @@ +It's working!! +Updates accordingly. +in place +TEST=in place +Value overwritten and not in place because it's really long +TEST=in place +Value overwritten and not in place because it's really long +Value deleted successfully! diff --git a/tests/expected/bins_static/stdlib/getsubopt.stderr b/tests/expected/bins_static/stdlib/getsubopt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/getsubopt.stdout b/tests/expected/bins_static/stdlib/getsubopt.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/mkostemps.stderr b/tests/expected/bins_static/stdlib/mkostemps.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/mkostemps.stdout b/tests/expected/bins_static/stdlib/mkostemps.stdout new file mode 100644 index 0000000000..f3562f6879 --- /dev/null +++ b/tests/expected/bins_static/stdlib/mkostemps.stdout @@ -0,0 +1,3 @@ +Start unchanged: 0 +End unchanged: 0 +Read & Write Successful diff --git a/tests/expected/bins_static/stdlib/ptsname.stderr b/tests/expected/bins_static/stdlib/ptsname.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/ptsname.stdout b/tests/expected/bins_static/stdlib/ptsname.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/qsort.stderr b/tests/expected/bins_static/stdlib/qsort.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/qsort.stdout b/tests/expected/bins_static/stdlib/qsort.stdout new file mode 100644 index 0000000000..49755a611e --- /dev/null +++ b/tests/expected/bins_static/stdlib/qsort.stdout @@ -0,0 +1,2 @@ +Before: 23 16 8 4 42 15 +After: 4 8 15 16 23 42 diff --git a/tests/expected/bins_static/stdlib/rand.stderr b/tests/expected/bins_static/stdlib/rand.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/rand.stdout b/tests/expected/bins_static/stdlib/rand.stdout new file mode 100644 index 0000000000..2a50461db2 --- /dev/null +++ b/tests/expected/bins_static/stdlib/rand.stdout @@ -0,0 +1,8 @@ +67141780 +201425341 +201425341 +67141780 +264204 +271585844 +264204 +271585844 diff --git a/tests/expected/bins_static/stdlib/rand48.stderr b/tests/expected/bins_static/stdlib/rand48.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/rand48.stdout b/tests/expected/bins_static/stdlib/rand48.stdout new file mode 100644 index 0000000000..fa871030ba --- /dev/null +++ b/tests/expected/bins_static/stdlib/rand48.stdout @@ -0,0 +1,14 @@ +lrand48 (uninitialized): 0 2116118 89401895 379337186 782977366 196130996 198207689 1046291021 1131187612 975888346 +drand48 (seeded with srand48): 0.750266 0.607593 0.567593 0.799563 0.984984 0.205670 0.625922 0.794426 0.369416 0.854100 +lrand48 (seeded with srand48): 1611183183 1304796356 1218897288 1717049088 2115236938 441672110 1344158015 1706017430 793314380 1834165927 +mrand48 (seeded with srand48): -1072600929 -1685374584 -1857172720 -860869119 -64493420 883344220 -1606651266 -882932436 1586628760 -626635441 +erand48: 0.210555 0.014158 0.111353 0.658369 0.103767 0.180385 0.945033 0.745768 0.290272 0.111716 +nrand48: 514983590 590935818 1480794144 1496813112 2133865028 1816766485 2095074020 126058208 909762120 14734916 +jrand48: -1066398599 903693914 -1922375113 -2090140830 1218074962 1662411059 -722435322 764426686 -874142666 -1454656015 +seed48_return: [40c0, 4d4f, daa6] +lrand48 (seeded with seed48): 386486647 2049879217 706208537 1265096744 1586830881 1884641178 1566935266 1256810805 875501172 1670187935 +xsubi restored froom seed48 return value: [40c0, 4d4f, daa6] +nrand48 (from xsubi value returned by seed48): 1654466549 509433115 2007628085 1022767111 1935848687 967444157 1967758021 686622820 989863078 1688030308 +lrand48 (with parameters from lcong48): 2015972364 2009368981 971134301 317520085 149004773 538917235 242519436 1066970146 991527304 1588277058 +lrand48 (seeded with srand48 after lcong48 call): 1611183183 1304796356 1218897288 1717049088 2115236938 441672110 1344158015 1706017430 793314380 1834165927 +lrand48 (seeded with seed48 after lcong48 call): 386486647 2049879217 706208537 1265096744 1586830881 1884641178 1566935266 1256810805 875501172 1670187935 diff --git a/tests/expected/bins_static/stdlib/random.stderr b/tests/expected/bins_static/stdlib/random.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/random.stdout b/tests/expected/bins_static/stdlib/random.stdout new file mode 100644 index 0000000000..9a754a9eb6 --- /dev/null +++ b/tests/expected/bins_static/stdlib/random.stdout @@ -0,0 +1,1011 @@ +Uninitialized: +262836907 +2022765545 +1985587709 +1559253607 +547725525 +1277054513 +317849018 +1695317205 +643446864 +1262735440 +964614733 +622582094 +280266812 +1939173898 +732650777 +1270232897 +336669216 +1669264236 +554325996 +1080138202 +1193538846 +2016671310 +1114491314 +2115358120 +2122955106 +982493572 +450262557 +439534734 +1723376538 +2066693252 +901041 +1986213445 +1941975150 +1986488750 +1397983404 +342217027 +1116059615 +1715832423 +2037534232 +1759506479 +831084215 +854665318 +234604926 +1111351027 +646355568 +967255703 +234100276 +983024785 +489036291 +788426273 +2063162987 +1682575137 +657613935 +1030170653 +1650449610 +633085394 +2012664225 +2100712167 +1072620128 +1588557116 +2019921771 +1073521169 +1427286913 +1814413273 +912526271 +677786670 +9146653 +2028585886 +246135445 +2046680885 + +Seed 1: +262836907 +2022765545 +1985587709 +1559253607 +547725525 +1277054513 +317849018 +1695317205 +643446864 +1262735440 +964614733 +622582094 +280266812 +1939173898 +732650777 +1270232897 +336669216 +1669264236 +554325996 +1080138202 +1193538846 +2016671310 +1114491314 +2115358120 +2122955106 +982493572 +450262557 +439534734 +1723376538 +2066693252 +901041 +1986213445 +1941975150 +1986488750 +1397983404 +342217027 +1116059615 +1715832423 +2037534232 +1759506479 +831084215 +854665318 +234604926 +1111351027 +646355568 +967255703 +234100276 +983024785 +489036291 +788426273 +2063162987 +1682575137 +657613935 +1030170653 +1650449610 +633085394 +2012664225 +2100712167 +1072620128 +1588557116 +2019921771 +1073521169 +1427286913 +1814413273 +912526271 +677786670 +9146653 +2028585886 +246135445 +2046680885 + +Seed 1337: +1124688561 +1792831582 +1708122057 +1060061055 +2060905921 +703006049 +867046930 +1599327113 +1946461956 +1851068511 +2107308161 +1080994526 +361067458 +2035914970 +1577981791 +72922558 +783590643 +1752267570 +1350489214 +1338091804 +225460930 +62532035 +540341638 +1391100769 +1535109757 +2039272559 +206716062 +1139547941 +462353593 +1176324546 +1766476968 +1587042155 +821672480 +1327115377 +499619562 +735094753 +2030121427 +1366666492 +186938219 +1829099735 +1070251355 +146762732 +762610613 +1431318813 +35194054 +193108756 +1504241371 +818784698 +1945376326 +707246937 +9392854 +23353608 +769778973 +549734493 +1414454377 +157405082 +441523404 +1621170440 +1296953024 +903876997 +650011338 +915946344 +343435504 +1471683818 +95578073 +843055066 +59294924 +2125699500 +62237911 +246233143 + +Seed 42, size 8: +1250496027 +1116302264 +1000676753 +1668674806 +908095735 +71666532 +896336333 +1736731266 +1314989459 +1535244752 +391441865 +1108520142 +1206814703 +534045436 +1974836613 +238077914 +1413854219 +705377000 +397905153 +1440974758 +1972995559 +282367380 +881784893 +1823504434 +879663491 +70219520 +1215814457 +1726604670 +318196447 +1939145516 +1030877685 +968547210 +1513076219 +1793402392 +1673312369 +1341335126 +481933015 +1376947140 +1980983981 +529748450 +192473459 +2072944688 +631833769 +202943022 +2012129743 +762679132 +1776566885 +562641722 +542512107 +293225800 +266051553 +1950339334 +562037703 +932343284 +1167153437 +829945746 +711923043 +392328544 +485452313 +748288734 +1303618751 +1013548940 +1115918037 +1967580906 +210226651 +149786744 +1677411153 +1109690294 +2007723191 +1000815652 + +Seed 42, size 31: +1250496027 +1116302264 +1000676753 +1668674806 +908095735 +71666532 +896336333 +1736731266 +1314989459 +1535244752 +391441865 +1108520142 +1206814703 +534045436 +1974836613 +238077914 +1413854219 +705377000 +397905153 +1440974758 +1972995559 +282367380 +881784893 +1823504434 +879663491 +70219520 +1215814457 +1726604670 +318196447 +1939145516 +1030877685 +968547210 +1513076219 +1793402392 +1673312369 +1341335126 +481933015 +1376947140 +1980983981 +529748450 +192473459 +2072944688 +631833769 +202943022 +2012129743 +762679132 +1776566885 +562641722 +542512107 +293225800 +266051553 +1950339334 +562037703 +932343284 +1167153437 +829945746 +711923043 +392328544 +485452313 +748288734 +1303618751 +1013548940 +1115918037 +1967580906 +210226651 +149786744 +1677411153 +1109690294 +2007723191 +1000815652 + +Seed 42, size 32: +1105844101 +1165395678 +461296413 +1259424640 +70225557 +1467961981 +315013290 +1176069659 +485874011 +776309703 +288010651 +556099569 +96788036 +603023941 +1732169228 +582662048 +1379333644 +2020179879 +1138761617 +1476121681 +475720173 +723447197 +2058783729 +1855053817 +596143428 +1050061698 +1183691850 +1071863601 +1773508895 +1094991931 +779433771 +222168675 +2145053629 +1963125621 +1294032277 +1771078876 +910633905 +2073466048 +1993247552 +908203886 +1889108021 +1139796181 +531799115 +652258278 +1065778581 +377563019 +1560462165 +807402954 +1517359200 +2092261280 +1459661233 +435654133 +322340651 +872639750 +1243057087 +1839699851 +817417382 +555234672 +127870336 +1139758033 +1427874422 +1370927423 +831974236 +97808156 +1926162096 +959844572 +1237566189 +1206552870 +183288347 +2069540425 + +Seed 42, size 63: +1105844101 +1165395678 +461296413 +1259424640 +70225557 +1467961981 +315013290 +1176069659 +485874011 +776309703 +288010651 +556099569 +96788036 +603023941 +1732169228 +582662048 +1379333644 +2020179879 +1138761617 +1476121681 +475720173 +723447197 +2058783729 +1855053817 +596143428 +1050061698 +1183691850 +1071863601 +1773508895 +1094991931 +779433771 +222168675 +2145053629 +1963125621 +1294032277 +1771078876 +910633905 +2073466048 +1993247552 +908203886 +1889108021 +1139796181 +531799115 +652258278 +1065778581 +377563019 +1560462165 +807402954 +1517359200 +2092261280 +1459661233 +435654133 +322340651 +872639750 +1243057087 +1839699851 +817417382 +555234672 +127870336 +1139758033 +1427874422 +1370927423 +831974236 +97808156 +1926162096 +959844572 +1237566189 +1206552870 +183288347 +2069540425 + +Seed 42, size 64: +2058979096 +1114567745 +1168098319 +1326828429 +585052544 +738633083 +1597984519 +103976400 +637164838 +949355699 +301019638 +1539653489 +829283131 +1994476715 +899306595 +810802043 +1925369788 +945984460 +125329241 +710381785 +1449014869 +899515740 +1003492141 +1640656979 +442529030 +743548669 +135718510 +965001641 +811994708 +1711301303 +374619698 +152505839 +1098490299 +1223819540 +1934201325 +1235732546 +2135248287 +991256780 +484430111 +926959142 +1670507811 +1806226321 +623744314 +1435739022 +999556678 +1374176376 +1526682215 +477688866 +1701508406 +1488226084 +576474982 +564239621 +1555496401 +2039926513 +819402007 +342426170 +1168843 +624913157 +2060652179 +912725209 +139417938 +1666100153 +2143789020 +1697813778 +1038556214 +1615031197 +31787170 +1587283572 +1479726437 +151644796 + +Seed 42, size 127: +2058979096 +1114567745 +1168098319 +1326828429 +585052544 +738633083 +1597984519 +103976400 +637164838 +949355699 +301019638 +1539653489 +829283131 +1994476715 +899306595 +810802043 +1925369788 +945984460 +125329241 +710381785 +1449014869 +899515740 +1003492141 +1640656979 +442529030 +743548669 +135718510 +965001641 +811994708 +1711301303 +374619698 +152505839 +1098490299 +1223819540 +1934201325 +1235732546 +2135248287 +991256780 +484430111 +926959142 +1670507811 +1806226321 +623744314 +1435739022 +999556678 +1374176376 +1526682215 +477688866 +1701508406 +1488226084 +576474982 +564239621 +1555496401 +2039926513 +819402007 +342426170 +1168843 +624913157 +2060652179 +912725209 +139417938 +1666100153 +2143789020 +1697813778 +1038556214 +1615031197 +31787170 +1587283572 +1479726437 +151644796 + +Seed 42, size 128: +1105844101 +1165395678 +461296413 +1259424640 +2024747114 +1114771942 +1792613078 +189454326 +466435881 +883763281 +1626567616 +1631629466 +1277725798 +1234887654 +657326752 +1836263319 +715263951 +471085233 +2086498417 +1163602145 +1547997976 +1405363974 +632333445 +201224285 +286947872 +1468210882 +103627486 +2147177614 +373040761 +1110293054 +1202766264 +1478884863 +128205084 +1664062677 +590825855 +5468550 +631350971 +235955286 +194922877 +1097786852 +1119718567 +1821490493 +581932670 +249960717 +908894499 +1239259422 +2086224037 +1624158450 +1710344655 +2025238806 +640276948 +1110858983 +1283119132 +1272610393 +1312083268 +1570067004 +593337627 +1415710754 +1569760970 +966378389 +378520161 +625043586 +297779604 +506725245 +141622615 +888605459 +512193796 +772973586 +1124560745 +707116673 + +Seed 42, size 255: +1105844101 +1165395678 +461296413 +1259424640 +2024747114 +1114771942 +1792613078 +189454326 +466435881 +883763281 +1626567616 +1631629466 +1277725798 +1234887654 +657326752 +1836263319 +715263951 +471085233 +2086498417 +1163602145 +1547997976 +1405363974 +632333445 +201224285 +286947872 +1468210882 +103627486 +2147177614 +373040761 +1110293054 +1202766264 +1478884863 +128205084 +1664062677 +590825855 +5468550 +631350971 +235955286 +194922877 +1097786852 +1119718567 +1821490493 +581932670 +249960717 +908894499 +1239259422 +2086224037 +1624158450 +1710344655 +2025238806 +640276948 +1110858983 +1283119132 +1272610393 +1312083268 +1570067004 +593337627 +1415710754 +1569760970 +966378389 +378520161 +625043586 +297779604 +506725245 +141622615 +888605459 +512193796 +772973586 +1124560745 +707116673 + +Seed 42, size 256: +2058979096 +1114567745 +1168098319 +1326828429 +585052544 +738633083 +1597984519 +103976400 +637164838 +949355699 +301019638 +1539653489 +829283131 +1994476715 +240955585 +1996759270 +1022456556 +1580994077 +1061370374 +875128855 +1125363953 +1573702147 +503131242 +1969480447 +1438211747 +91438056 +1120505602 +1956383039 +1858786240 +1571532334 +1295962411 +342691299 +190922750 +587812343 +1882440834 +1251108553 +1546539316 +237235092 +229958515 +1788514159 +1180131104 +1278005195 +1236529116 +1834489224 +2008240486 +153071308 +1122241977 +1443493489 +827253675 +1844422894 +1615942404 +1444719301 +1322997186 +2068652336 +1108669174 +1117641112 +329168079 +1033958612 +870068653 +1235189147 +1628422544 +186507460 +1238820988 +1150316436 +117400533 +1285498853 +464843634 +1049896178 +1788529262 +1239030133 + +Seed 42, other state array: +1105844101 +1165395678 +461296413 +1259424640 +70225557 +1467961981 +315013290 +1176069659 +485874011 +776309703 +288010651 +556099569 +96788036 +603023941 +1732169228 +582662048 +1379333644 +2020179879 +1138761617 +1476121681 +475720173 +723447197 +2058783729 +1855053817 +596143428 +1050061698 +1183691850 +1071863601 +1773508895 +1094991931 +779433771 +222168675 +2145053629 +1963125621 +1294032277 +1771078876 +910633905 +2073466048 +1993247552 +908203886 +1889108021 +1139796181 +531799115 +652258278 +1065778581 +377563019 +1560462165 +807402954 +1517359200 +2092261280 +1459661233 +435654133 +322340651 +872639750 +1243057087 +1839699851 +817417382 +555234672 +127870336 +1139758033 +1427874422 +1370927423 +831974236 +97808156 +1926162096 +959844572 +1237566189 +1206552870 +183288347 +2069540425 + +State data pointer restored correctly by setstate(). + +Seed 42, back to first state array: +1343006534 +1980171372 +782043423 +1083063062 +475232903 +1304516034 +1151509101 +1392464686 +1241740309 +116713217 +1697707295 +611594021 +1486722877 +464603182 +2038305329 +393952924 +215949723 +1654161471 +1745599527 +718621482 +527520873 +238823465 +1810355799 +958834563 +1301525862 +1492448612 +2080260955 +1815218141 +918843046 +317898715 +555133807 +785092322 +426122834 +1606253938 +736775485 +1973304601 +1660310177 +1521067015 +1674138324 +648896653 +2092390142 +772160169 +469099415 +2085041819 +1382277472 +557791010 +478959698 +1587628873 +557786337 +886954417 +1920913029 +643498035 +1878687182 +1359626079 +1546133539 +637470879 +1787787315 +1905187849 +1043203054 +1508046688 +410459218 +51504832 +1290534966 +486057852 +318745576 +1100789000 +36368414 +511601317 +1816117351 +820142804 + +Pointer returned by initstate with size < 8: (nil) diff --git a/tests/expected/bins_static/stdlib/strtod.stderr b/tests/expected/bins_static/stdlib/strtod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/strtod.stdout b/tests/expected/bins_static/stdlib/strtod.stdout new file mode 100644 index 0000000000..a92def04f3 --- /dev/null +++ b/tests/expected/bins_static/stdlib/strtod.stdout @@ -0,0 +1,196 @@ +d: 0.000000 Endptr: "a 1 hello" +d: 1.000000 Endptr: " hello" +d: 1.000000 Endptr: " hello 2" +d: 10.123000 Endptr: "" +d: 10.123000 Endptr: "" +d: -5.300000 Endptr: "" +d: 16.071045 Endptr: "" +d: 1.136719 Endptr: "" +d: 3.128906 Endptr: "" +d: 100000.000000 Endptr: "" +d: 100000.000000 Endptr: "" +d: 0.000010 Endptr: "" +d: 100000.000000 Endptr: " " +d: 100000.000000 Endptr: " " +d: 0.000010 Endptr: " " +d: 10000000000.000000 Endptr: "" +d: 1.000000 Endptr: "eXXXX" +d: 1.000000 Endptr: "e" +d: 1.000000 Endptr: "e " +d: 10000000000.000000 Endptr: "" +d: 1.000000 Endptr: "e+XXXX" +d: 1.000000 Endptr: "e+" +d: 1.000000 Endptr: "e+ " +d: 0.000000 Endptr: "" +d: 1.000000 Endptr: "e-XXXX" +d: 1.000000 Endptr: "e-" +d: 1.000000 Endptr: "e- " +d: -100000.000000 Endptr: "" +d: -100000.000000 Endptr: "" +d: -0.000010 Endptr: "" +d: -100000.000000 Endptr: " " +d: -100000.000000 Endptr: " " +d: -0.000010 Endptr: " " +d: -10000000000.000000 Endptr: "" +d: -1.000000 Endptr: "eXXXX" +d: -1.000000 Endptr: "e" +d: -1.000000 Endptr: "e " +d: -10000000000.000000 Endptr: "" +d: -1.000000 Endptr: "e+XXXX" +d: -1.000000 Endptr: "e+" +d: -1.000000 Endptr: "e+ " +d: -0.000000 Endptr: "" +d: -1.000000 Endptr: "e-XXXX" +d: -1.000000 Endptr: "e-" +d: -1.000000 Endptr: "e- " +d: 1234000.000000 Endptr: "" +d: 1234000.000000 Endptr: "" +d: 0.000123 Endptr: "" +d: 1234000.000000 Endptr: " " +d: 1234000.000000 Endptr: " " +d: 0.000123 Endptr: " " +d: 123400000000.000000 Endptr: "" +d: 12.340000 Endptr: "eXXXX" +d: 12.340000 Endptr: "e" +d: 12.340000 Endptr: "e " +d: 123400000000.000000 Endptr: "" +d: 12.340000 Endptr: "e+XXXX" +d: 12.340000 Endptr: "e+" +d: 12.340000 Endptr: "e+ " +d: 0.000000 Endptr: "" +d: 12.340000 Endptr: "e-XXXX" +d: 12.340000 Endptr: "e-" +d: 12.340000 Endptr: "e- " +d: -1234000.000000 Endptr: "" +d: -1234000.000000 Endptr: "" +d: -0.000123 Endptr: "" +d: -1234000.000000 Endptr: " " +d: -1234000.000000 Endptr: " " +d: -0.000123 Endptr: " " +d: -123400000000.000000 Endptr: "" +d: -12.340000 Endptr: "eXXXX" +d: -12.340000 Endptr: "e" +d: -12.340000 Endptr: "e " +d: -123400000000.000000 Endptr: "" +d: -12.340000 Endptr: "e+XXXX" +d: -12.340000 Endptr: "e+" +d: -12.340000 Endptr: "e+ " +d: -0.000000 Endptr: "" +d: -12.340000 Endptr: "e-XXXX" +d: -12.340000 Endptr: "e-" +d: -12.340000 Endptr: "e- " +d: 192.000000 Endptr: "" +d: -192.000000 Endptr: "" +d: 0.005859 Endptr: "" +d: -0.005859 Endptr: "" +d: 10.000000 Endptr: "" +d: 0.156250 Endptr: "" +d: -10.000000 Endptr: "" +d: -0.156250 Endptr: "" +d: 16.062500 Endptr: "" +d: 16.062500 Endptr: "" +d: -16.062500 Endptr: "" +d: -16.062500 Endptr: "" +d: 0.500000 Endptr: "" +d: 5.000000 Endptr: "" +d: 50.000000 Endptr: "" +d: 500.000000 Endptr: "" +d: 5000.000000 Endptr: "" +d: 50000.000000 Endptr: "" +d: 500000.000000 Endptr: "" +d: 5000000.000000 Endptr: "" +d: 50000000.000000 Endptr: "" +d: 500000000.000000 Endptr: "" +d: 5000000000.000000 Endptr: "" +d: 50000000000.000000 Endptr: "" +d: 500000000000.000000 Endptr: "" +d: 5000000000000.000000 Endptr: "" +d: 50000000000000.000000 Endptr: "" +d: 500000000000000.000000 Endptr: "" +d: 5000000000000000.000000 Endptr: "" +d: 50000000000000000.000000 Endptr: "" +d: 500000000000000000.000000 Endptr: "" +d: 5000000000000000000.000000 Endptr: "" +d: 50000000000000000000.000000 Endptr: "" +d: 500000000000000000000.000000 Endptr: "" +d: 5000000000000000000000.000000 Endptr: "" +d: 49999999999999995805696.000000 Endptr: "" +d: 499999999999999991611392.000000 Endptr: "" +d: 5000000000000000452984832.000000 Endptr: "" +d: 50000000000000002382364672.000000 Endptr: "" +d: 500000000000000006643777536.000000 Endptr: "" +d: 4999999999999999791559868416.000000 Endptr: "" +d: 49999999999999995716575428608.000000 Endptr: "" +d: 500000000000000009942312419328.000000 Endptr: "" +d: 4999999999999999817948147482624.000000 Endptr: "" +d: 50000000000000002683081102196736.000000 Endptr: "" +d: 499999999999999972787615493521408.000000 Endptr: "" +d: 4999999999999999727876154935214080.000000 Endptr: "" +d: 49999999999999998431683053958987776.000000 Endptr: "" +d: 500000000000000021210318687008980992.000000 Endptr: "" +d: 4999999999999999769381329101060571136.000000 Endptr: "" +d: 49999999999999998874404911728017014784.000000 Endptr: "" +d: -0.500000 Endptr: "" +d: -5.000000 Endptr: "" +d: -50.000000 Endptr: "" +d: -500.000000 Endptr: "" +d: -5000.000000 Endptr: "" +d: -50000.000000 Endptr: "" +d: -500000.000000 Endptr: "" +d: -5000000.000000 Endptr: "" +d: -50000000.000000 Endptr: "" +d: -500000000.000000 Endptr: "" +d: -5000000000.000000 Endptr: "" +d: -50000000000.000000 Endptr: "" +d: -500000000000.000000 Endptr: "" +d: -5000000000000.000000 Endptr: "" +d: -50000000000000.000000 Endptr: "" +d: -500000000000000.000000 Endptr: "" +d: -5000000000000000.000000 Endptr: "" +d: -50000000000000000.000000 Endptr: "" +d: -500000000000000000.000000 Endptr: "" +d: -5000000000000000000.000000 Endptr: "" +d: -50000000000000000000.000000 Endptr: "" +d: -500000000000000000000.000000 Endptr: "" +d: -5000000000000000000000.000000 Endptr: "" +d: -49999999999999995805696.000000 Endptr: "" +d: -499999999999999991611392.000000 Endptr: "" +d: -5000000000000000452984832.000000 Endptr: "" +d: -50000000000000002382364672.000000 Endptr: "" +d: -500000000000000006643777536.000000 Endptr: "" +d: -4999999999999999791559868416.000000 Endptr: "" +d: -49999999999999995716575428608.000000 Endptr: "" +d: -500000000000000009942312419328.000000 Endptr: "" +d: -4999999999999999817948147482624.000000 Endptr: "" +d: -50000000000000002683081102196736.000000 Endptr: "" +d: -499999999999999972787615493521408.000000 Endptr: "" +d: -4999999999999999727876154935214080.000000 Endptr: "" +d: -49999999999999998431683053958987776.000000 Endptr: "" +d: -500000000000000021210318687008980992.000000 Endptr: "" +d: -4999999999999999769381329101060571136.000000 Endptr: "" +d: -49999999999999998874404911728017014784.000000 Endptr: "" +d: -0.000000 Endptr: "" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: " foobarbaz" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: "" +d: inf Endptr: " foobarbaz" +d: -inf Endptr: "" +d: -inf Endptr: "" +d: -inf Endptr: "" +d: -inf Endptr: " foobarbaz" +d: nan Endptr: "0.1e5" +d: nan Endptr: "-37" +d: nan Endptr: "1.05" +d: nan Endptr: " foo bar baz" +d: nan Endptr: "0.1e5" +d: nan Endptr: "-37" +d: nan Endptr: "1.05" +d: nan Endptr: " foo bar baz" +d: nan Endptr: "0.1e5" +d: nan Endptr: "-37" +d: nan Endptr: "1.05" +d: nan Endptr: " foo bar baz" diff --git a/tests/expected/bins_static/stdlib/strtol.stderr b/tests/expected/bins_static/stdlib/strtol.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/strtol.stdout b/tests/expected/bins_static/stdlib/strtol.stdout new file mode 100644 index 0000000000..4c4177ba7b --- /dev/null +++ b/tests/expected/bins_static/stdlib/strtol.stdout @@ -0,0 +1,26 @@ +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +38acf +endptr "g" +abcdef12 +endptr "" +cafebeef +endptr "" +731 +endptr "89" +731 +endptr "89" +0 +endptr "b" +0 +endptr "b" diff --git a/tests/expected/bins_static/stdlib/strtoul.stderr b/tests/expected/bins_static/stdlib/strtoul.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/strtoul.stdout b/tests/expected/bins_static/stdlib/strtoul.stdout new file mode 100644 index 0000000000..398260ab44 --- /dev/null +++ b/tests/expected/bins_static/stdlib/strtoul.stdout @@ -0,0 +1,26 @@ +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +-42 +endptr "" +555 +endptr "" +1234567890 +endptr " " +38acf +endptr "g" +abcdef12 +endptr "" +21000004 +endptr "" +731 +endptr "89" +731 +endptr "89" +0 +endptr "b" +0 +endptr "b" diff --git a/tests/expected/bins_static/stdlib/system.stderr b/tests/expected/bins_static/stdlib/system.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/system.stdout b/tests/expected/bins_static/stdlib/system.stdout new file mode 100644 index 0000000000..90f99da2e1 --- /dev/null +++ b/tests/expected/bins_static/stdlib/system.stdout @@ -0,0 +1,2 @@ +shell found: 1 +test of system diff --git a/tests/expected/bins_static/string/mem.stderr b/tests/expected/bins_static/string/mem.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/mem.stdout b/tests/expected/bins_static/string/mem.stdout new file mode 100644 index 0000000000..a6c001081f --- /dev/null +++ b/tests/expected/bins_static/string/mem.stdout @@ -0,0 +1,5 @@ +# mem # +Correct memchr +Correct memrchr +Correct memccpy +Correct memcmp diff --git a/tests/expected/bins_static/string/memcpy.stderr b/tests/expected/bins_static/string/memcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/memcpy.stdout b/tests/expected/bins_static/string/memcpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/memmem.stderr b/tests/expected/bins_static/string/memmem.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/memmem.stdout b/tests/expected/bins_static/string/memmem.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/stpcpy.stderr b/tests/expected/bins_static/string/stpcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/stpcpy.stdout b/tests/expected/bins_static/string/stpcpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/stpncpy.stderr b/tests/expected/bins_static/string/stpncpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/stpncpy.stdout b/tests/expected/bins_static/string/stpncpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strcat.stderr b/tests/expected/bins_static/string/strcat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strcat.stdout b/tests/expected/bins_static/string/strcat.stdout new file mode 100644 index 0000000000..10bda51843 --- /dev/null +++ b/tests/expected/bins_static/string/strcat.stdout @@ -0,0 +1,2 @@ +hello world +hello world diff --git a/tests/expected/bins_static/string/strchr.stderr b/tests/expected/bins_static/string/strchr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strchr.stdout b/tests/expected/bins_static/string/strchr.stdout new file mode 100644 index 0000000000..43f3dcfdf0 --- /dev/null +++ b/tests/expected/bins_static/string/strchr.stdout @@ -0,0 +1,4 @@ +ello +ld + +(nil) diff --git a/tests/expected/bins_static/string/strchrnul.stderr b/tests/expected/bins_static/string/strchrnul.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strchrnul.stdout b/tests/expected/bins_static/string/strchrnul.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strcpy.stderr b/tests/expected/bins_static/string/strcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strcpy.stdout b/tests/expected/bins_static/string/strcpy.stdout new file mode 100644 index 0000000000..f08e831ac1 --- /dev/null +++ b/tests/expected/bins_static/string/strcpy.stdout @@ -0,0 +1,7 @@ +strcpy works! +strncpy works! +strncpy shaaaaaaaaa +strlcpy works! +copied 14 +strlcpy works! and strlcat! +copied 27 diff --git a/tests/expected/bins_static/string/strcspn.stderr b/tests/expected/bins_static/string/strcspn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strcspn.stdout b/tests/expected/bins_static/string/strcspn.stdout new file mode 100644 index 0000000000..2b24e31277 --- /dev/null +++ b/tests/expected/bins_static/string/strcspn.stdout @@ -0,0 +1,2 @@ +2 +6 diff --git a/tests/expected/bins_static/string/strlen.stderr b/tests/expected/bins_static/string/strlen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strlen.stdout b/tests/expected/bins_static/string/strlen.stdout new file mode 100644 index 0000000000..9ce90c8a65 --- /dev/null +++ b/tests/expected/bins_static/string/strlen.stdout @@ -0,0 +1,9 @@ +12 +0 +12 +12 +0 +12 +6 +12 +0 diff --git a/tests/expected/bins_static/string/strncmp.stderr b/tests/expected/bins_static/string/strncmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strncmp.stdout b/tests/expected/bins_static/string/strncmp.stdout new file mode 100644 index 0000000000..e385d8a988 --- /dev/null +++ b/tests/expected/bins_static/string/strncmp.stdout @@ -0,0 +1,6 @@ +-97 +-195 +1 +-255 +-2 +0 diff --git a/tests/expected/bins_static/string/strpbrk.stderr b/tests/expected/bins_static/string/strpbrk.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strpbrk.stdout b/tests/expected/bins_static/string/strpbrk.stdout new file mode 100644 index 0000000000..7a6bc8b089 --- /dev/null +++ b/tests/expected/bins_static/string/strpbrk.stdout @@ -0,0 +1,3 @@ +The quick drawn fix jumps over the lazy bug +lazy bug +NULL diff --git a/tests/expected/bins_static/string/strrchr.stderr b/tests/expected/bins_static/string/strrchr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strrchr.stdout b/tests/expected/bins_static/string/strrchr.stdout new file mode 100644 index 0000000000..836469707d --- /dev/null +++ b/tests/expected/bins_static/string/strrchr.stdout @@ -0,0 +1 @@ +strrch PASS diff --git a/tests/expected/bins_static/string/strsep.stderr b/tests/expected/bins_static/string/strsep.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strsep.stdout b/tests/expected/bins_static/string/strsep.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strsignal.stderr b/tests/expected/bins_static/string/strsignal.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strsignal.stdout b/tests/expected/bins_static/string/strsignal.stdout new file mode 100644 index 0000000000..833dc63089 --- /dev/null +++ b/tests/expected/bins_static/string/strsignal.stdout @@ -0,0 +1 @@ +# strsignal # diff --git a/tests/expected/bins_static/string/strspn.stderr b/tests/expected/bins_static/string/strspn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strspn.stdout b/tests/expected/bins_static/string/strspn.stdout new file mode 100644 index 0000000000..54d9c92604 --- /dev/null +++ b/tests/expected/bins_static/string/strspn.stdout @@ -0,0 +1,3 @@ +5 +1 +0 diff --git a/tests/expected/bins_static/string/strstr.stderr b/tests/expected/bins_static/string/strstr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strstr.stdout b/tests/expected/bins_static/string/strstr.stdout new file mode 100644 index 0000000000..56fe13b7b1 --- /dev/null +++ b/tests/expected/bins_static/string/strstr.stdout @@ -0,0 +1,5 @@ +rust +libc we trust +(null) +(null) +RUST diff --git a/tests/expected/bins_static/string/strtok.stderr b/tests/expected/bins_static/string/strtok.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strtok.stdout b/tests/expected/bins_static/string/strtok.stdout new file mode 100644 index 0000000000..4428c25c54 --- /dev/null +++ b/tests/expected/bins_static/string/strtok.stdout @@ -0,0 +1 @@ +I'd_just_like_to_interject_for_a_moment._What_you're_referring_to_as_Linux,_is_in_fact,_GNU/Linux,_or_as_I've_recently_taken_to_calling_it,_GNU_plus_Linux. diff --git a/tests/expected/bins_static/string/strtok_r.stderr b/tests/expected/bins_static/string/strtok_r.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/string/strtok_r.stdout b/tests/expected/bins_static/string/strtok_r.stdout new file mode 100644 index 0000000000..4428c25c54 --- /dev/null +++ b/tests/expected/bins_static/string/strtok_r.stdout @@ -0,0 +1 @@ +I'd_just_like_to_interject_for_a_moment._What_you're_referring_to_as_Linux,_is_in_fact,_GNU/Linux,_or_as_I've_recently_taken_to_calling_it,_GNU_plus_Linux. diff --git a/tests/expected/bins_static/strings.stderr b/tests/expected/bins_static/strings.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/strings.stdout b/tests/expected/bins_static/strings.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_epoll/epollet.stdout b/tests/expected/bins_static/sys_epoll/epollet.stdout new file mode 100644 index 0000000000..cc912d90cf --- /dev/null +++ b/tests/expected/bins_static/sys_epoll/epollet.stdout @@ -0,0 +1,5 @@ +ctl ADD: 0 +wait for HUP, edge-triggered: 1 +wait for HUP again: 0 +ctl MOD: 0 +wait for HUP: 1 diff --git a/tests/expected/bins_static/sys_mman.stderr b/tests/expected/bins_static/sys_mman.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_mman.stdout b/tests/expected/bins_static/sys_mman.stdout new file mode 100644 index 0000000000..108b1819b0 --- /dev/null +++ b/tests/expected/bins_static/sys_mman.stdout @@ -0,0 +1,9 @@ +Page size: 4096 +Mapping 1 page of memory... +Mapping 3 pages of memory... +Randomizing mapping #1 +Randomizing mapping #2 +Unmapping page 2 of map2 +Randomizing page 1 of mapping #2 +Randomizing page 3 of mapping #2 +Unmapping it all at once! diff --git a/tests/expected/bins_static/sys_socket/recv.stdout b/tests/expected/bins_static/sys_socket/recv.stdout new file mode 100644 index 0000000000..cb2239126a --- /dev/null +++ b/tests/expected/bins_static/sys_socket/recv.stdout @@ -0,0 +1,2 @@ +send foo +recv foo diff --git a/tests/expected/bins_static/sys_socket/recvfrom.stdout b/tests/expected/bins_static/sys_socket/recvfrom.stdout new file mode 100644 index 0000000000..e9f413f935 --- /dev/null +++ b/tests/expected/bins_static/sys_socket/recvfrom.stdout @@ -0,0 +1,2 @@ +sendto bar +recvfrom bar diff --git a/tests/expected/bins_static/sys_socket/unixpeername.stdout b/tests/expected/bins_static/sys_socket/unixpeername.stdout new file mode 100644 index 0000000000..f9686e7295 --- /dev/null +++ b/tests/expected/bins_static/sys_socket/unixpeername.stdout @@ -0,0 +1,3 @@ +listen sockname [AF_UNIX]: unixpeername.sock +client peername [AF_UNIX]: unixpeername.sock +client sockname [AF_UNIX]: (unnamed) diff --git a/tests/expected/bins_static/sys_socket/unixrecv.stdout b/tests/expected/bins_static/sys_socket/unixrecv.stdout new file mode 100644 index 0000000000..e9636a2916 --- /dev/null +++ b/tests/expected/bins_static/sys_socket/unixrecv.stdout @@ -0,0 +1,2 @@ +send ipsum +recv ipsum diff --git a/tests/expected/bins_static/sys_socket/unixrecvfrom.stdout b/tests/expected/bins_static/sys_socket/unixrecvfrom.stdout new file mode 100644 index 0000000000..ad8e531910 --- /dev/null +++ b/tests/expected/bins_static/sys_socket/unixrecvfrom.stdout @@ -0,0 +1,2 @@ +send lorem +recvfrom lorem diff --git a/tests/expected/bins_static/sys_socket/unixsocketpair.stdout b/tests/expected/bins_static/sys_socket/unixsocketpair.stdout new file mode 100644 index 0000000000..bb7fc428fa --- /dev/null +++ b/tests/expected/bins_static/sys_socket/unixsocketpair.stdout @@ -0,0 +1,6 @@ +SOCK_STREAM +child: ping +parent: pong +SOCK_DGRAM +child: ping +parent: pong diff --git a/tests/expected/bins_static/sys_stat/chmod.stderr b/tests/expected/bins_static/sys_stat/chmod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_stat/chmod.stdout b/tests/expected/bins_static/sys_stat/chmod.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_stat/fstatat.stderr b/tests/expected/bins_static/sys_stat/fstatat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_stat/fstatat.stdout b/tests/expected/bins_static/sys_stat/fstatat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_stat/lstat.stderr b/tests/expected/bins_static/sys_stat/lstat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_stat/lstat.stdout b/tests/expected/bins_static/sys_stat/lstat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/sys_syslog/syslog.stderr b/tests/expected/bins_static/sys_syslog/syslog.stderr new file mode 100644 index 0000000000..faddade2a6 --- /dev/null +++ b/tests/expected/bins_static/sys_syslog/syslog.stderr @@ -0,0 +1,13 @@ +relibc_test: This is a test message with formatting: 5 +relibc_test: Hank Hill +relibc_test: Foo has been bar'd +relibc_test: And now, Eeveelutions +relibc_test: Espeon +relibc_test: Umbreon +relibc_test: Sylveon +relibc_test: Glaceon +relibc_test: Leafeon +relibc_test: Vaporeon +relibc_test: Flareon +relibc_test: Jolteon +relibc_test: Bye from relibc's syslog tests! diff --git a/tests/expected/bins_static/sys_syslog/syslog.stdout b/tests/expected/bins_static/sys_syslog/syslog.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/termios.stderr b/tests/expected/bins_static/termios.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/termios.stdout b/tests/expected/bins_static/termios.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/asctime.stderr b/tests/expected/bins_static/time/asctime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/asctime.stdout b/tests/expected/bins_static/time/asctime.stdout new file mode 100644 index 0000000000..f21cdff5f4 --- /dev/null +++ b/tests/expected/bins_static/time/asctime.stdout @@ -0,0 +1,5 @@ +Thu Jan 1 00:00:00 1970 +Sun Jan 1 00:00:00 1000 +Sat Dec 31 23:59:60 9999 +Sun Jan-99 00:00:00 -999 +Sat Dec999 99:99:99 9999 diff --git a/tests/expected/bins_static/time/constants.stderr b/tests/expected/bins_static/time/constants.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/constants.stdout b/tests/expected/bins_static/time/constants.stdout new file mode 100644 index 0000000000..cf14858d8c --- /dev/null +++ b/tests/expected/bins_static/time/constants.stdout @@ -0,0 +1,2 @@ +(nil) +1000000 diff --git a/tests/expected/bins_static/time/gmtime.stderr b/tests/expected/bins_static/time/gmtime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/gmtime.stdout b/tests/expected/bins_static/time/gmtime.stdout new file mode 100644 index 0000000000..a5da960f12 --- /dev/null +++ b/tests/expected/bins_static/time/gmtime.stdout @@ -0,0 +1,168 @@ +Unix epoch: + tm_sec = 0 + tm_min = 0 + tm_hour = 0 + tm_mday = 1 + tm_mon = 0 + tm_year = 70 + tm_wday = 4 + tm_yday = 0 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1970-03-01 00:00:00: + tm_sec = 0 + tm_min = 0 + tm_hour = 0 + tm_mday = 1 + tm_mon = 2 + tm_year = 70 + tm_wday = 0 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1970-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 70 + tm_wday = 4 + tm_yday = 364 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1972-02-29 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 29 + tm_mon = 1 + tm_year = 72 + tm_wday = 2 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +1972-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 72 + tm_wday = 0 + tm_yday = 365 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2000-02-29 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 29 + tm_mon = 1 + tm_year = 100 + tm_wday = 2 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2000-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 100 + tm_wday = 0 + tm_yday = 365 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2100-03-01 00:00:00: + tm_sec = 0 + tm_min = 0 + tm_hour = 0 + tm_mday = 1 + tm_mon = 2 + tm_year = 200 + tm_wday = 1 + tm_yday = 59 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +2100-12-31 23:59:59: + tm_sec = 59 + tm_min = 59 + tm_hour = 23 + tm_mday = 31 + tm_mon = 11 + tm_year = 200 + tm_wday = 5 + tm_yday = 364 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2038, pre-i32 overflow: + tm_sec = 7 + tm_min = 14 + tm_hour = 3 + tm_mday = 19 + tm_mon = 0 + tm_year = 138 + tm_wday = 2 + tm_yday = 18 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2038, post-i32 overflow: + tm_sec = 8 + tm_min = 14 + tm_hour = 3 + tm_mday = 19 + tm_mon = 0 + tm_year = 138 + tm_wday = 2 + tm_yday = 18 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2106, pre-u32 overflow: + tm_sec = 15 + tm_min = 28 + tm_hour = 6 + tm_mday = 7 + tm_mon = 1 + tm_year = 206 + tm_wday = 0 + tm_yday = 37 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC + +Year 2106, post-u32 overflow: + tm_sec = 16 + tm_min = 28 + tm_hour = 6 + tm_mday = 7 + tm_mon = 1 + tm_year = 206 + tm_wday = 0 + tm_yday = 37 + tm_isdst = 0 + tm_gmtoff = 0 + tm_zone = UTC diff --git a/tests/expected/bins_static/time/localtime.stderr b/tests/expected/bins_static/time/localtime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/localtime.stdout b/tests/expected/bins_static/time/localtime.stdout new file mode 100644 index 0000000000..eb828a2b0d --- /dev/null +++ b/tests/expected/bins_static/time/localtime.stdout @@ -0,0 +1,9 @@ +Year 69, Day of year: 332, Month 10, Day of month: 29, Day of week: 6, 0:0:0 +Year 69, Day of year: 364, Month 11, Day of month: 31, Day of week: 3, 0:0:0 +Year 69, Day of year: 364, Month 11, Day of month: 31, Day of week: 3, 23:59:59 +Year 69, Day of year: 364, Month 11, Day of month: 31, Day of week: 3, 23:51:40 +Year 70, Day of year: 0, Month 0, Day of month: 1, Day of week: 4, 0:0:0 +Year 70, Day of year: 0, Month 0, Day of month: 1, Day of week: 4, 0:0:1 +Year 118, Day of year: 193, Month 6, Day of month: 13, Day of week: 5, 4:9:10 +Fri Jul 13 06:03:43 2018 +Fri Jul 13 06:03:43 2018 diff --git a/tests/expected/bins_static/time/localtime_r.stderr b/tests/expected/bins_static/time/localtime_r.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/localtime_r.stdout b/tests/expected/bins_static/time/localtime_r.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/macros.stderr b/tests/expected/bins_static/time/macros.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/macros.stdout b/tests/expected/bins_static/time/macros.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/mktime.stderr b/tests/expected/bins_static/time/mktime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/mktime.stdout b/tests/expected/bins_static/time/mktime.stdout new file mode 100644 index 0000000000..c9b309e63c --- /dev/null +++ b/tests/expected/bins_static/time/mktime.stdout @@ -0,0 +1,6 @@ +31536000 +-2851200 = -2851200 +-86400 = -86400 +-500 = -500 +0 = 0 +1531454950 = 1531454950 diff --git a/tests/expected/bins_static/time/strftime.stderr b/tests/expected/bins_static/time/strftime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/strftime.stdout b/tests/expected/bins_static/time/strftime.stdout new file mode 100644 index 0000000000..7d12e13954 --- /dev/null +++ b/tests/expected/bins_static/time/strftime.stdout @@ -0,0 +1,14 @@ +20: Tue Tuesday Jul July +16: The 21st century +11: 06:25:42 AM +11: 03:00:00 PM +5: 15:00 +15: 15 1531839600 2 +6: 198 28 +28: Tue Jul 17 15:00:00 UTC 2018 +0: Tue Aug 07 19:17:11 UTC 2018Tue Aug 07 19:17:11 U +53 +52 +52 +1 +53 diff --git a/tests/expected/bins_static/time/strptime.stderr b/tests/expected/bins_static/time/strptime.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/strptime.stdout b/tests/expected/bins_static/time/strptime.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/time.stderr b/tests/expected/bins_static/time/time.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/time.stdout b/tests/expected/bins_static/time/time.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/timegm.stderr b/tests/expected/bins_static/time/timegm.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/timegm.stdout b/tests/expected/bins_static/time/timegm.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/tzset.stderr b/tests/expected/bins_static/time/tzset.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/time/tzset.stdout b/tests/expected/bins_static/time/tzset.stdout new file mode 100644 index 0000000000..9962d8acc0 --- /dev/null +++ b/tests/expected/bins_static/time/tzset.stdout @@ -0,0 +1 @@ +tzname[0] UTC, tzname[1] UTC, daylight 0, timezone 0 diff --git a/tests/expected/bins_static/tls.stderr b/tests/expected/bins_static/tls.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/tls.stdout b/tests/expected/bins_static/tls.stdout new file mode 100644 index 0000000000..d0b7b3c2e6 --- /dev/null +++ b/tests/expected/bins_static/tls.stdout @@ -0,0 +1,2 @@ +0 == 0 +1 == 1 diff --git a/tests/expected/bins_static/unistd/access.stderr b/tests/expected/bins_static/unistd/access.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/access.stdout b/tests/expected/bins_static/unistd/access.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/alarm.stderr b/tests/expected/bins_static/unistd/alarm.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/alarm.stdout b/tests/expected/bins_static/unistd/alarm.stdout new file mode 100644 index 0000000000..4ed7f83631 --- /dev/null +++ b/tests/expected/bins_static/unistd/alarm.stdout @@ -0,0 +1,4 @@ +alarm(0) baseline: ok +alarm(1) fires: ok +alarm(0) cancel: ok +alarm re-arm returns remaining: ok diff --git a/tests/expected/bins_static/unistd/brk.stderr b/tests/expected/bins_static/unistd/brk.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/brk.stdout b/tests/expected/bins_static/unistd/brk.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/confstr.stderr b/tests/expected/bins_static/unistd/confstr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/confstr.stdout b/tests/expected/bins_static/unistd/confstr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/constants.stderr b/tests/expected/bins_static/unistd/constants.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/constants.stdout b/tests/expected/bins_static/unistd/constants.stdout new file mode 100644 index 0000000000..90c907f1ff --- /dev/null +++ b/tests/expected/bins_static/unistd/constants.stdout @@ -0,0 +1,16 @@ +_POSIX_VERSION: 200809 +NULL: (nil) +R_OK: 4 +W_OK: 2 +X_OK: 1 +F_OK: 0 +SEEK_SET: 0 +SEEK_CUR: 1 +SEEK_END: 2 +F_LOCK: 1 +F_ULOCK: 0 +F_TEST: 3 +F_TLOCK: 2 +STDIN_FILENO: 0 +STDOUT_FILENO: 1 +STDERR_FILENO: 2 diff --git a/tests/expected/bins_static/unistd/dup.stderr b/tests/expected/bins_static/unistd/dup.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/dup.stdout b/tests/expected/bins_static/unistd/dup.stdout new file mode 100644 index 0000000000..4fb8e07602 --- /dev/null +++ b/tests/expected/bins_static/unistd/dup.stdout @@ -0,0 +1 @@ +duped fd is 1 greater than the original fd diff --git a/tests/expected/bins_static/unistd/exec.stderr b/tests/expected/bins_static/unistd/exec.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/exec.stdout b/tests/expected/bins_static/unistd/exec.stdout new file mode 100644 index 0000000000..0f56119c73 --- /dev/null +++ b/tests/expected/bins_static/unistd/exec.stdout @@ -0,0 +1 @@ +exec works :D diff --git a/tests/expected/bins_static/unistd/fchdir.stderr b/tests/expected/bins_static/unistd/fchdir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/fchdir.stdout b/tests/expected/bins_static/unistd/fchdir.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/fork.stderr b/tests/expected/bins_static/unistd/fork.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/fork.stdout b/tests/expected/bins_static/unistd/fork.stdout new file mode 100644 index 0000000000..98f331c9de --- /dev/null +++ b/tests/expected/bins_static/unistd/fork.stdout @@ -0,0 +1,3 @@ +Hello from prepare +Hello from child +Hello from parent diff --git a/tests/expected/bins_static/unistd/fsync.stderr b/tests/expected/bins_static/unistd/fsync.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/fsync.stdout b/tests/expected/bins_static/unistd/fsync.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/ftruncate.stderr b/tests/expected/bins_static/unistd/ftruncate.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/ftruncate.stdout b/tests/expected/bins_static/unistd/ftruncate.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/getopt.stderr b/tests/expected/bins_static/unistd/getopt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/getopt.stdout b/tests/expected/bins_static/unistd/getopt.stdout new file mode 100644 index 0000000000..aba2d44d3f --- /dev/null +++ b/tests/expected/bins_static/unistd/getopt.stdout @@ -0,0 +1,42 @@ +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 1 +errflg: 0 +ifile: +ofile: arg +result: 0 +bflg: 0 +aflg: 0 +errflg: 0 +ifile: +ofile: +result: 0 diff --git a/tests/expected/bins_static/unistd/getopt_long.stderr b/tests/expected/bins_static/unistd/getopt_long.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/getopt_long.stdout b/tests/expected/bins_static/unistd/getopt_long.stdout new file mode 100644 index 0000000000..d2f352b822 --- /dev/null +++ b/tests/expected/bins_static/unistd/getopt_long.stdout @@ -0,0 +1,20 @@ +--- Running: test --test0 -a +getopt_long returned 1, argument test0=(null) +Option -a with value (null) +--- Running: test --test1 -a +getopt_long returned 0, set flag to 2, argument test1=(null) +Option -a with value (null) +--- Running: test --test2 -a +getopt_long returned 3, argument test2=(null) +Option -a with value (null) +--- Running: test --test2=arg -a +getopt_long returned 3, argument test2=arg +Option -a with value (null) +--- Running: test --test3 -a +getopt_long returned 4, argument test3=-a +--- Running: test --test3=arg -a +getopt_long returned 4, argument test3=arg +Option -a with value (null) +--- Running: test --test3 arg -a +getopt_long returned 4, argument test3=arg +Option -a with value (null) diff --git a/tests/expected/bins_static/unistd/pipe.stderr b/tests/expected/bins_static/unistd/pipe.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/pipe.stdout b/tests/expected/bins_static/unistd/pipe.stdout new file mode 100644 index 0000000000..980a0d5f19 --- /dev/null +++ b/tests/expected/bins_static/unistd/pipe.stdout @@ -0,0 +1 @@ +Hello World! diff --git a/tests/expected/bins_static/unistd/readlinkat.stderr b/tests/expected/bins_static/unistd/readlinkat.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/readlinkat.stdout b/tests/expected/bins_static/unistd/readlinkat.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/rmdir.stderr b/tests/expected/bins_static/unistd/rmdir.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/rmdir.stdout b/tests/expected/bins_static/unistd/rmdir.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/sleep.stderr b/tests/expected/bins_static/unistd/sleep.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/sleep.stdout b/tests/expected/bins_static/unistd/sleep.stdout new file mode 100644 index 0000000000..25867863a2 --- /dev/null +++ b/tests/expected/bins_static/unistd/sleep.stdout @@ -0,0 +1 @@ +unslept: 0 diff --git a/tests/expected/bins_static/unistd/swab.stderr b/tests/expected/bins_static/unistd/swab.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/swab.stdout b/tests/expected/bins_static/unistd/swab.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/write.stderr b/tests/expected/bins_static/unistd/write.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/unistd/write.stdout b/tests/expected/bins_static/unistd/write.stdout new file mode 100644 index 0000000000..980a0d5f19 --- /dev/null +++ b/tests/expected/bins_static/unistd/write.stdout @@ -0,0 +1 @@ +Hello World! diff --git a/tests/expected/bins_static/wchar/fgetwc.stderr b/tests/expected/bins_static/wchar/fgetwc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/fgetwc.stdout b/tests/expected/bins_static/wchar/fgetwc.stdout new file mode 100644 index 0000000000..e7cabf537e --- /dev/null +++ b/tests/expected/bins_static/wchar/fgetwc.stdout @@ -0,0 +1,3 @@ +êçã +こんにちは世界 +Привет мир \ No newline at end of file diff --git a/tests/expected/bins_static/wchar/fwide.stderr b/tests/expected/bins_static/wchar/fwide.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/fwide.stdout b/tests/expected/bins_static/wchar/fwide.stdout new file mode 100644 index 0000000000..44e0be8e35 --- /dev/null +++ b/tests/expected/bins_static/wchar/fwide.stdout @@ -0,0 +1,4 @@ +0 +0 +0 +0 diff --git a/tests/expected/bins_static/wchar/mbrtowc.stderr b/tests/expected/bins_static/wchar/mbrtowc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/mbrtowc.stdout b/tests/expected/bins_static/wchar/mbrtowc.stdout new file mode 100644 index 0000000000..8ea12e1952 --- /dev/null +++ b/tests/expected/bins_static/wchar/mbrtowc.stdout @@ -0,0 +1,2 @@ +Processing 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ] +into 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ] diff --git a/tests/expected/bins_static/wchar/mbsrtowcs.stderr b/tests/expected/bins_static/wchar/mbsrtowcs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/mbsrtowcs.stdout b/tests/expected/bins_static/wchar/mbsrtowcs.stdout new file mode 100644 index 0000000000..e8f8258f2e --- /dev/null +++ b/tests/expected/bins_static/wchar/mbsrtowcs.stdout @@ -0,0 +1 @@ +The length, including '\0': 5 diff --git a/tests/expected/bins_static/wchar/printf-on-wchars.stderr b/tests/expected/bins_static/wchar/printf-on-wchars.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/printf-on-wchars.stdout b/tests/expected/bins_static/wchar/printf-on-wchars.stdout new file mode 100644 index 0000000000..b14ed25516 --- /dev/null +++ b/tests/expected/bins_static/wchar/printf-on-wchars.stdout @@ -0,0 +1,4 @@ +This is a few one-byte chars: 1 2 a b +Long one-byte string: Hello World +This is a few multi-byte chars: ❤ R 😠 C +Long multi-byte string: 👉😎👉 Zoop! diff --git a/tests/expected/bins_static/wchar/putwchar.stderr b/tests/expected/bins_static/wchar/putwchar.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/putwchar.stdout b/tests/expected/bins_static/wchar/putwchar.stdout new file mode 100644 index 0000000000..42d65d1da0 --- /dev/null +++ b/tests/expected/bins_static/wchar/putwchar.stdout @@ -0,0 +1 @@ +zß水🍌 \ No newline at end of file diff --git a/tests/expected/bins_static/wchar/ungetwc.stderr b/tests/expected/bins_static/wchar/ungetwc.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/ungetwc.stdout b/tests/expected/bins_static/wchar/ungetwc.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcpcpy.stderr b/tests/expected/bins_static/wchar/wcpcpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcpcpy.stdout b/tests/expected/bins_static/wchar/wcpcpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcpncpy.stderr b/tests/expected/bins_static/wchar/wcpncpy.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcpncpy.stdout b/tests/expected/bins_static/wchar/wcpncpy.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcrtomb.stderr b/tests/expected/bins_static/wchar/wcrtomb.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcrtomb.stdout b/tests/expected/bins_static/wchar/wcrtomb.stdout new file mode 100644 index 0000000000..ed85da3696 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcrtomb.stdout @@ -0,0 +1,2 @@ +Processing 5 wchar_t units: [ 0x7a 0xdf 0x6c34 0x1f34c 0 ] +into 11 UTF-8 code units: [ 0x7a 0xc3 0x9f 0xe6 0xb0 0xb4 0xf0 0x9f 0x8d 0x8c 0 ] diff --git a/tests/expected/bins_static/wchar/wcscasecmp.stderr b/tests/expected/bins_static/wchar/wcscasecmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcscasecmp.stdout b/tests/expected/bins_static/wchar/wcscasecmp.stdout new file mode 100644 index 0000000000..fdd11f6552 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcscasecmp.stdout @@ -0,0 +1,4 @@ +wcscasecmp(s1, s1) = 0 +wcscasecmp(s1, s2) = -1 +wcscasecmp(s2, s1) = 1 +wcscasecmp(s2, s2) = 0 diff --git a/tests/expected/bins_static/wchar/wcschr.stderr b/tests/expected/bins_static/wchar/wcschr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcschr.stdout b/tests/expected/bins_static/wchar/wcschr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcscspn.stderr b/tests/expected/bins_static/wchar/wcscspn.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcscspn.stdout b/tests/expected/bins_static/wchar/wcscspn.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsdup.stderr b/tests/expected/bins_static/wchar/wcsdup.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsdup.stdout b/tests/expected/bins_static/wchar/wcsdup.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsncasecmp.stderr b/tests/expected/bins_static/wchar/wcsncasecmp.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsncasecmp.stdout b/tests/expected/bins_static/wchar/wcsncasecmp.stdout new file mode 100644 index 0000000000..6a4bd43fc4 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcsncasecmp.stdout @@ -0,0 +1,5 @@ +wcsncasecmp(s1, s1, 17) = 0 +wcsncasecmp(s1, s2, 17) = -1 +wcsncasecmp(s2, s1, 17) = 1 +wcsncasecmp(s2, s1, 15) = 0 +wcsncasecmp(s1, s2, 0) = 0 diff --git a/tests/expected/bins_static/wchar/wcsnlen.stderr b/tests/expected/bins_static/wchar/wcsnlen.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsnlen.stdout b/tests/expected/bins_static/wchar/wcsnlen.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsnrtombs.stderr b/tests/expected/bins_static/wchar/wcsnrtombs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsnrtombs.stdout b/tests/expected/bins_static/wchar/wcsnrtombs.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsrchr.stderr b/tests/expected/bins_static/wchar/wcsrchr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsrchr.stdout b/tests/expected/bins_static/wchar/wcsrchr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsrtombs.stderr b/tests/expected/bins_static/wchar/wcsrtombs.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsrtombs.stdout b/tests/expected/bins_static/wchar/wcsrtombs.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsstr.stderr b/tests/expected/bins_static/wchar/wcsstr.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcsstr.stdout b/tests/expected/bins_static/wchar/wcsstr.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcstod.stderr b/tests/expected/bins_static/wchar/wcstod.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcstod.stdout b/tests/expected/bins_static/wchar/wcstod.stdout new file mode 100644 index 0000000000..0292990d95 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcstod.stdout @@ -0,0 +1,6 @@ +strtod(1.2345wowzah) = (1.234500, wowzah) +strtod(53) = (53.000000, ) +strtod(-254352.5...) = (-254352.500000, ...) +strtod( 19.2 wat) = (19.200000, wat) +strtod(365.24 29.53) = (365.240000, 29.53) +strtod( 29.53) = (29.530000, ) diff --git a/tests/expected/bins_static/wchar/wcstoimax.stderr b/tests/expected/bins_static/wchar/wcstoimax.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcstoimax.stdout b/tests/expected/bins_static/wchar/wcstoimax.stdout new file mode 100644 index 0000000000..b9a14be845 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcstoimax.stdout @@ -0,0 +1,6 @@ +-123 +255 +44027 +8 +10 +16 diff --git a/tests/expected/bins_static/wchar/wcstok.stderr b/tests/expected/bins_static/wchar/wcstok.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcstok.stdout b/tests/expected/bins_static/wchar/wcstok.stdout new file mode 100644 index 0000000000..bb7ba090dd --- /dev/null +++ b/tests/expected/bins_static/wchar/wcstok.stdout @@ -0,0 +1,12 @@ +Hello +from +the +otter +slide. +Must +have +gone +down +a +thousand +tiiiiiimeeeees... diff --git a/tests/expected/bins_static/wchar/wcstol.stderr b/tests/expected/bins_static/wchar/wcstol.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcstol.stdout b/tests/expected/bins_static/wchar/wcstol.stdout new file mode 100644 index 0000000000..55aa50779a --- /dev/null +++ b/tests/expected/bins_static/wchar/wcstol.stdout @@ -0,0 +1 @@ +The decimal equivalents are: 2001, 6340800, -3624224 and 7340031. diff --git a/tests/expected/bins_static/wchar/wcstoumax.stderr b/tests/expected/bins_static/wchar/wcstoumax.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcstoumax.stdout b/tests/expected/bins_static/wchar/wcstoumax.stdout new file mode 100644 index 0000000000..8e372f7589 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcstoumax.stdout @@ -0,0 +1,3 @@ +nptr = `10110134932` +wcstoumax = 10110134932 +Stopped scan at `` diff --git a/tests/expected/bins_static/wchar/wcswidth.stderr b/tests/expected/bins_static/wchar/wcswidth.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wcswidth.stdout b/tests/expected/bins_static/wchar/wcswidth.stdout new file mode 100644 index 0000000000..7da79364d8 --- /dev/null +++ b/tests/expected/bins_static/wchar/wcswidth.stdout @@ -0,0 +1 @@ +wcswidth(L"relibc", 6) = 6 diff --git a/tests/expected/bins_static/wchar/wprintf.stderr b/tests/expected/bins_static/wchar/wprintf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wprintf.stdout b/tests/expected/bins_static/wchar/wprintf.stdout new file mode 100644 index 0000000000..724e64b5b5 --- /dev/null +++ b/tests/expected/bins_static/wchar/wprintf.stdout @@ -0,0 +1,67 @@ +percent: % +wstring: String +char: c +int: -16 +uint: 32 +hex: beef +HEX: C0FFEE +string: end +%n returned 44, total len of write: 87 + +Padding madness: + 001 +2 + c + 0x00000ff + 001 +0 0x1 + 1 +(00123) ( 123) +(-0123) ( -123) +( ) +0xabcdef +(nil) + +Positional madness: +4 3 2 +00002 +|Fiz |Buz | Fiz| Tot| +int: 5 double: 0.100000 0.200000 0.300000 0.400000 +-1717986918 0.100000 +-1717986918 0.200000 + +Float madness: + 1.234568e+02 + 1.000000E-05 + 123.456789 + 0.000010 + -1.234568e+02 +-00000001.234568e+02 +%.5g: -123.46 +%.5f: -123.45679 +%.5e: -1.23457e+02 +%.*g: -1.2e+02 +%.*f: -123.46 +%.*e: -1.23e+02 +%.*2$g: -123.46 +%.*2$f: -123.45679 +%.*2$e: -1.23457e+02 +100000 +1e+06 +1.000000e+06 +0.0001 +1E-05 +1.000000E-05 + +Non-finite float madness: +%e: inf -inf nan -nan +%E: INF -INF NAN -NAN +%f: inf -inf nan -nan +%F: INF -INF NAN -NAN +%g: inf -inf nan -nan +%G: INF -INF NAN -NAN +Things that have been buggy ++05 +Testing asprintf... +printed: test string, value: 11 +printed: test string 2, value: 13 +printed: test string 2, value: 13 diff --git a/tests/expected/bins_static/wchar/wscanf.stderr b/tests/expected/bins_static/wchar/wscanf.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wchar/wscanf.stdout b/tests/expected/bins_static/wchar/wscanf.stdout new file mode 100644 index 0000000000000000000000000000000000000000..90abc9a7f6599960cae1452846e7df0076fc6949 GIT binary patch literal 4414 zcmd^@%}&BV5XU|96dO*OROlB^1M_Mjpd4&J=i@(eY4 z^%4B{Yq4xlq83Wr13RcYLU0W5z984t-4Slu_;bXX==*8+4;0NvV+}`9c+(m z>?g-LY4-d=IXr$@+6@8VXBhz3o)lm{fRIo?jJ#DVAp8jcf1p%rsNeeWSFIl6lVZjG_8?1l^(C7@tdlFgvZE}H6nDxVOt9#Ew({~W+z}D7>AGWg;EpCIR8!oK`{<4*C~z#Wi=U(sH>s@)h8%hPvm+|G>#VK7`Ag2f-zU2I*FPC;wslva~)46j7&OPM;p)+(w z&I4V^3L#PeO*K9-;wkjMO*j7l@sP>|0iXKR!`-~z4ylW5{N6u1_5WNT`G#$`tvtyW Ti}*1$)i~HTtb$?I;pTH69Stt> literal 0 HcmV?d00001 diff --git a/tests/expected/bins_static/wctype/towlower.stderr b/tests/expected/bins_static/wctype/towlower.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wctype/towlower.stdout b/tests/expected/bins_static/wctype/towlower.stdout new file mode 100644 index 0000000000..fdbf17836f --- /dev/null +++ b/tests/expected/bins_static/wctype/towlower.stdout @@ -0,0 +1,2 @@ +HaLf WiDe ChAr StRiNg! +half wide char string! diff --git a/tests/expected/bins_static/wctype/towupper.stderr b/tests/expected/bins_static/wctype/towupper.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/wctype/towupper.stdout b/tests/expected/bins_static/wctype/towupper.stdout new file mode 100644 index 0000000000..a09771e7bc --- /dev/null +++ b/tests/expected/bins_static/wctype/towupper.stdout @@ -0,0 +1,2 @@ +HaLf WiDe ChAr StRiNg! +HALF WIDE CHAR STRING! diff --git a/tests/expected/endian.stderr b/tests/expected/endian.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/endian.stdout b/tests/expected/endian.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/features.stderr b/tests/expected/features.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/features.stdout b/tests/expected/features.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/glob.stderr b/tests/expected/glob.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/glob.stdout b/tests/expected/glob.stdout new file mode 100644 index 0000000000..ef1351361b --- /dev/null +++ b/tests/expected/glob.stdout @@ -0,0 +1,70 @@ +Pattern = eample_dir/* +(NOMATCH) + +Pattern = eample_dir/* +glob_err("eample_dir", "No such file or directory") -> 0 +(ABORTED) + +Pattern = ./example_dir/* +(Matched 8) +0 - ./example_dir/1-never-gonna-give-you-up +1 - ./example_dir/2-never-gonna-let-you-down +2 - ./example_dir/3-never-gonna-run-around +3 - ./example_dir/4-and-desert-you +4 - ./example_dir/5-never-gonna-make-you-cry +5 - ./example_dir/6-never-gonna-say-goodbye +6 - ./example_dir/7-never-gonna-tell-a-lie +7 - ./example_dir/8-and-hurt-you + +Pattern = example_dir/*never* +(Matched 6) +0 - example_dir/1-never-gonna-give-you-up +1 - example_dir/2-never-gonna-let-you-down +2 - example_dir/3-never-gonna-run-around +3 - example_dir/5-never-gonna-make-you-cry +4 - example_dir/6-never-gonna-say-goodbye +5 - example_dir/7-never-gonna-tell-a-lie + +Pattern = example_dir/?-and* +(Matched 8) +0 - example_dir/1-never-gonna-give-you-up +1 - example_dir/2-never-gonna-let-you-down +2 - example_dir/3-never-gonna-run-around +3 - example_dir/5-never-gonna-make-you-cry +4 - example_dir/6-never-gonna-say-goodbye +5 - example_dir/7-never-gonna-tell-a-lie +6 - example_dir/4-and-desert-you +7 - example_dir/8-and-hurt-you + +Pattern = example_dir/*never* +(Matched 6) (with 4 gl_offs) +0 - NULL +1 - NULL +2 - NULL +3 - NULL +4 - example_dir/1-never-gonna-give-you-up +5 - example_dir/2-never-gonna-let-you-down +6 - example_dir/3-never-gonna-run-around +7 - example_dir/5-never-gonna-make-you-cry +8 - example_dir/6-never-gonna-say-goodbye +9 - example_dir/7-never-gonna-tell-a-lie + +Pattern = example_dir/?-and* +(Matched 8) (with 4 gl_offs) +0 - +1 - NULL +2 - NULL +3 - NULL +4 - example_dir/1-never-gonna-give-you-up +5 - example_dir/2-never-gonna-let-you-down +6 - example_dir/3-never-gonna-run-around +7 - example_dir/5-never-gonna-make-you-cry +8 - example_dir/6-never-gonna-say-goodbye +9 - example_dir/7-never-gonna-tell-a-lie +10 - example_dir/4-and-desert-you +11 - example_dir/8-and-hurt-you + +Pattern = example_dir +(Matched 1) +0 - example_dir/ + diff --git a/tests/fcntl/create.c b/tests/fcntl/create.c new file mode 100644 index 0000000000..c365bbcbd7 --- /dev/null +++ b/tests/fcntl/create.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int fd = creat("create.out", 0755); + ERROR_IF(creat, fd, == -1); + UNEXP_IF(creat, fd, < 0); + + int written = write(fd, "Hello World!\n", 13); + ERROR_IF(write, written, == -1); + + int c = close(fd); + ERROR_IF(close, c, == -1); + UNEXP_IF(close, c, != 0); +} diff --git a/tests/fcntl/fcntl.c b/tests/fcntl/fcntl.c new file mode 100644 index 0000000000..66c6bd3bd7 --- /dev/null +++ b/tests/fcntl/fcntl.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + // Lose our fd and pull it again + { + int fd = creat("fcntl.out", 0777); + ERROR_IF(creat, fd, == -1); + UNEXP_IF(creat, fd, < 0); + } + + int newfd = open("fcntl.out", 0); + ERROR_IF(open, newfd, == -1); + UNEXP_IF(open, newfd, < 0); + + int newfd2 = fcntl(newfd, F_DUPFD, 0); + // TODO: The standard doesn't define errors for F_DUPFD + + printf("duped fd is %d greater than the original fd\n", newfd2 - newfd); + + int c1 = close(newfd); + ERROR_IF(close, c1, == -1); + UNEXP_IF(close, c1, != 0); + + int c2 = close(newfd2); + ERROR_IF(close, c2, == -1); + UNEXP_IF(close, c2, != 0); +} diff --git a/tests/fcntl/open.c b/tests/fcntl/open.c new file mode 100644 index 0000000000..ed8a999f3a --- /dev/null +++ b/tests/fcntl/open.c @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +__attribute__((nonnull)) +int cloexec_test(char path[], int argc, char* argv[]) { + assert(argc >= 1); + + int fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + perror("open (O_CLOEXEC)"); + return -1; + } + + pid_t child = fork(); + if (child == -1) { + perror("fork"); + close(fd); + return -1; + } else if (child == 0) { + // In child process where fd should be closed after execv. + char fd_str[8] = {0}; + sprintf(fd_str, "%d", fd); + + char* new_argv[] = { + argv[0], + fd_str, + NULL + }; + + if (execv(argv[0], new_argv) == -1) { + perror("execv"); + } + + exit(EXIT_FAILURE); + } else { + // In parent where fd should remain open. + int status = -1; + if (waitpid(child, &status, 0) == -1) { + perror("waitpid"); + return -1; + } + + if (fcntl(fd, F_GETFD) < 0) { + fputs( + "File descriptor closed in parent after exec with O_CLOEXEC\n", + stderr + ); + kill(child, SIGKILL); + waitpid(child, NULL, 0); + + return -1; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) { + return 0; + } else { + fputs( + "Child process to test O_CLOEXEC failed\n", + stderr + ); + return -1; + } + } + + // Unreachable + return -1; +} + +int cloexec_validate(int argc, char* argv[]) { + assert(argc == 2); + + char* end = NULL; + int fd = (int) strtol(argv[1], &end, 0); + + if (*end == '\0') { + if (fcntl(fd, F_GETFD) >= 0) { + fputs( + "File descriptor still open after exec with O_CLOEXEC\n", + stderr + ); + + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } else { + fputs( + "Invalid file descriptor number passed to cloxec_validate\n", + stderr + ); + return EXIT_FAILURE; + } +} + +int main(int argc, char* argv[]) { + // The test runner inserts two fixed arg, so argc == 2 is fine + if (argc == 2) { + return cloexec_validate(argc, argv); + } else if (argc == 0) { + fprintf( + stderr, + "Invalid number of arguments: %d\n", + argc + ); + return EXIT_FAILURE; + } + + int result = EXIT_FAILURE; + + char dir_template[] = "open_tests.XXXXXX"; + size_t dlen = sizeof(dir_template) - 1; + if (!mkdtemp(dir_template)) { + perror("mkdtemp"); + goto bye; + } + + char file_path[PATH_MAX] = {0}; + const char file_name[] = "peanut"; + memcpy(file_path, dir_template, dlen); + file_path[dlen] = '/'; + memcpy(&file_path[dlen + 1], file_name, sizeof(file_name)); + + int fd = open(file_path, O_CREAT, 0644); + if (fd == -1) { + perror("open (creating file in temp dir)"); + goto clean_opened_dir; + } + if (close(fd) == -1) { + perror("close (file)"); + goto clean_opened_dir; + } + + // Check that close actually closed fd + if (fcntl(fd, F_GETFD) >= 0) { + fputs( + "File descriptor was not closed by close\n", + stderr + ); + goto clean_opened_file; + } + + // Opening a file with O_DIRECTORY should fail + fd = open(file_path, O_DIRECTORY); + if (fd != -1) { + fputs( + "File successfully opened with O_DIRECTORY\n", + stderr + ); + close(fd); + goto clean_opened_file; + } + + // O_CREAT | O_EXCL should fail on existing file + fd = open(file_path, O_CREAT | O_EXCL, 0644); + if (fd != -1) { + fputs( + "Existing file created with O_CREAT | O_EXCL\n", + stderr + ); + close(fd); + goto clean_opened_file; + } + + // O_PATH + fd = open(file_path, O_PATH | O_WRONLY); + // O_PATH should ignore O_WRONLY but NOT fail. + if (fd == -1) { + perror("open (O_PATH)"); + goto clean_opened_file; + } + +// TODO: There is no special handling to O_PATH at kernel level +#ifndef __redox__ + // Writing this buf should fail. + const char buf[] = "Valencia peanuts"; + if (write(fd, buf, sizeof(buf)) != -1) { + fputs( + "Writing to an O_PATH fd succeeded\n", + stderr + ); + close(fd); + goto clean_opened_file; + } +#endif + + // But fstat should succeed with O_PATH + struct stat stat = {0}; + if (fstat(fd, &stat) == -1) { + perror("fstat (O_PATH)"); + close(fd); + goto clean_opened_file; + } + + // O_NOATIME (disabled since Redox doesn't have it) + // struct timespec atime_expected = stat.st_atim; + + close(fd); + /* fd = open(file_path, O_NOATIME); */ + /* if (fd == -1) { */ + /* perror("open (O_NOATIME)"); */ + /* close(fd); */ + /* goto clean_opened_file; */ + /* } */ + /**/ + /* if (fstat(fd, &stat) == -1) { */ + /* perror("fstat (O_PATH)"); */ + /* close(fd); */ + /* goto clean_opened_file; */ + /* } */ + /**/ + /* struct timespec atime_actual = stat.st_atim; */ + /* if ( */ + /* (atime_expected.tv_nsec != atime_actual.tv_nsec) */ + /* || (atime_expected.tv_sec != atime_actual.tv_sec) */ + /* ) { */ + /* fputs( */ + /* "Access time changed with O_NOATIME\n", */ + /* stderr */ + /* ); */ + /* close(fd); */ + /* goto clean_opened_file; */ + /* } */ + /* close(fd); */ + + // O_NOFOLLOW + char sym_path[PATH_MAX] = {0}; + const char sym_name[] = "cashew"; + memcpy(sym_path, dir_template, dlen); + sym_path[dlen] = '/'; + memcpy(&sym_path[dlen + 1], sym_name, sizeof(sym_name)); + + if (symlink(file_path, sym_path) == -1) { + perror("symlink"); + goto clean_opened_file; + } + fd = open(sym_path, O_NOFOLLOW); + if (fd != -1) { + fputs( + "Symlink opened with O_NOFOLLOW\n", + stderr + ); + close(fd); + goto clean_symlink; + } + + // O_CLOEXEC + cloexec_test(file_path, argc, argv); + + // Opening existing directory + int dirfd = open(dir_template, O_RDONLY); + if (dirfd == -1) { + perror("open (existing dir)"); + goto clean_symlink; + } + close(dirfd); + + // Opening existing directory (read/write) + dirfd = open(dir_template, O_RDWR); + if (dirfd != -1) { + fputs( + "Opening a directory with O_RDWR incorrectly succeeded\n", + stderr + ); + goto clean_symlink; + } + close(dirfd); + + // Opening existing directory (write only; should fail) + dirfd = -1; + dirfd = open(dir_template, O_WRONLY); + if (dirfd != -1) { + fputs( + "Opening a directory with O_WRONLY incorrectly succeeded\n", + stderr + ); + goto clean_symlink; + } + + result = EXIT_SUCCESS; +clean_symlink: + unlink(sym_path); +clean_opened_file: + unlink(file_path); +clean_opened_dir: + rmdir(dir_template); +bye: + return result; +} diff --git a/tests/fcntl/posix_fallocate.c b/tests/fcntl/posix_fallocate.c new file mode 100644 index 0000000000..bb5fdded95 --- /dev/null +++ b/tests/fcntl/posix_fallocate.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +__attribute__((nonnull)) +int run_test( + int filefd, + const char error_msg[], + off_t offset, + off_t length, + int dirfd, + const char file_name[], + off_t expected_size +) { + // posix_fallocate does not set errno. + int result = posix_fallocate(filefd, offset, length); + if (result != 0) { + const char* error = strerror(result); + fprintf(stderr, "%s: %s\n", error_msg, error); + return -1; + } + + struct stat stat = {0}; + if (fstatat(dirfd, file_name, &stat, 0) == -1) { + perror("fstatat"); + return -1; + } + + if (stat.st_size != expected_size) { + fprintf( + stderr, + "Expected: %zd\nActual: %zd\n", + expected_size, + stat.st_size + ); + return -1; + } + + return 0; +} + +int main(void) { + int result = EXIT_FAILURE; + + char dir_template[] = "/tmp/pfalloctest.XXXXXX"; + size_t dlen = sizeof(dir_template) - 1; + if (!mkdtemp(dir_template)) { + perror("mkdtemp"); + goto bye; + } + int dirfd = open(dir_template, O_DIRECTORY); + if (dirfd == -1) { + perror("open"); + goto rmtemp; + } + + // Failure conditions. + // These are mostly a gut check for whatever backing syscall is used + // in relibc. If it ever changes, we have to make sure the correct + // errors are returned. If they're not, we have to add checks to our + // relibc implementation. Linux's backing syscall (SYS_fallocate) + // already does these checks. + // + // Directory: + if (posix_fallocate(dirfd, 0, 1000) == 0) { + fputs("posix_fallocate succeeded on a directory\n", stderr); + goto rmtemp; + } + + // Socket: + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + perror("socket"); + goto rmtemp; + } + if (posix_fallocate(sock, 0, 1000) == 0) { + fputs("posix_fallocate succeeded on a socket\n", stderr); + close(sock); + goto rmtemp; + } + close(sock); + + // Pipe: + int pipefd[2] = {0}; + if (pipe(pipefd) == -1) { + perror("pipe"); + goto rmtemp; + } + if (posix_fallocate(pipefd[0], 0, 1000) == 0) { + fputs("posix_fallocate succeeded on a pipe\n", stderr); + close(pipefd[0]); + close(pipefd[1]); + goto rmtemp; + } + close(pipefd[0]); + close(pipefd[1]); + + // Success conditions. + const char name[] = "commander_keen"; + char path[PATH_MAX] = {0}; + memcpy(path, dir_template, dlen); + path[dlen] = '/'; + memcpy(&path[dlen + 1], name, sizeof(name)); + + int file = open(path, O_CREAT | O_RDWR); + if (file == -1) { + perror("open"); + goto rmtemp; + } + + // Expand an empty file. + if ( + run_test( + file, + "posix_fallocate failed expanding an empty file", + 0, + 1000, + dirfd, + name, + 1000 + ) == -1 + ) { + fputs("FAILED: Expanding empty file test\n", stderr); + goto rmtempfile; + } + + // Don't shrink a file. + if ( + run_test( + file, + "posix_fallocate failed on already allocated file", + 0, + 1, + dirfd, + name, + 1000 + ) == -1 + ) { + fputs("FAILED: posix_fallocate should not shrink files\n", stderr); + goto rmtempfile; + } + + // Don't overwrite allocated byte ranges. + if (ftruncate(file, 0) == -1) { + perror("ftruncate"); + goto rmtempfile; + } + const char msg[] = "If you're reading this you must play Commander Keen"; + if (write(file, msg, sizeof(msg)) != sizeof(msg)) { + perror("write"); + goto rmtempfile; + } + + if ( + run_test( + file, + "posix_fallocate failed on an allocated range", + 0, + sizeof(msg), + dirfd, + name, + sizeof(msg) + ) == -1 + ) { + fputs( + "FAILED: posix_fallocate don't overwrite allocated ranges test\n", + stderr + ); + goto rmtempfile; + } + // And now check that the range wasn't overwritten. + char buf[sizeof(msg)] = {0}; + if (lseek(file, 0, SEEK_SET) == -1) { + perror("lseek"); + goto rmtempfile; + } + if (read(file, buf, sizeof(msg)) != sizeof(msg)) { + perror("read"); + goto rmtempfile; + } + if (strncmp(msg, buf, sizeof(msg)) != 0) { + fputs("FAILED: posix_fallocate overwrote/destroyed a file\n", stderr); + goto rmtempfile; + } + + // Offset + length that goes beyond file end expands file. + if ( + run_test( + file, + "posix_fallocate failed on file expansion test", + sizeof(msg), + 1000, + dirfd, + name, + sizeof(msg) + 1000 + ) == -1 + ) { + fputs("FAILED: posix_fallocate file expansion test\n", stderr); + goto rmtempfile; + } + + result = EXIT_SUCCESS; +rmtempfile: + close(file); + unlink(path); +rmtemp: + rmdir(dir_template); +bye: + return result; +} + diff --git a/tests/features.c b/tests/features.c new file mode 100644 index 0000000000..d33bda8429 --- /dev/null +++ b/tests/features.c @@ -0,0 +1,50 @@ +// These tests are primarily to ensure the macros compile without +// causing any funny business. +// TODO: does not compile with glibc + +#include +#include +#include +#include + +#ifndef __GLIBC__ +__deprecated +#endif +static void legacy(void) {} + +#ifndef __GLIBC__ +__deprecatedNote("Sometimes deletes user's home (oops); use foobar") +#endif +static void legacy_notes(void) {} + +#ifndef __GLIBC__ +__nodiscard +#endif +static uint8_t the_answer(void) { + return 42; +} + +// GCC bug +/* #pragma GCC diagnostic push */ +/* #pragma GCC diagnostic ignored "-Wattributes" */ +/* __noreturn */ +/* static void foobar(void) { */ + // The test suite isn't picking up noreturn in the headers for exit, abort + // Those functions (and this test) works fine in both Redox itself and Linux + // Using _Exit instead works for the tests in CI. Why? I dunno. +/* _Exit(0); */ +/* } */ +/* #pragma GCC diagnostic pop */ + +int main(void) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + legacy(); + legacy_notes(); + #pragma GCC diagnostic pop + const int answer = the_answer(); + char buf[40] = {0}; + sprintf(buf, "Hey, -Werror, I'm using answer: %d\n", answer); + /* foobar(); */ + return 0; +} diff --git a/tests/fnmatch.c b/tests/fnmatch.c new file mode 100644 index 0000000000..ab88cfd3ad --- /dev/null +++ b/tests/fnmatch.c @@ -0,0 +1,48 @@ +#include +#include + +#include "test_helpers.h" + +void test(char* pattern, char* input, int flags) { + if (!fnmatch(pattern, input, flags)) { + printf("\"%s\" matches \"%s\"\n", pattern, input); + } else { + printf("\"%s\" doesn't match \"%s\"\n", pattern, input); + } +} + +int main(void) { + puts("Should succeed:"); + test("*World", "Hello World", 0); + test("*World", "World", 0); + test("Hello*", "Hello World", 0); + test("H[ae]llo?World", "Hallo+World", 0); + test("H[ae]llo?World", "Hello_World", 0); + test("[0-9][!a]", "1b", 0); + test("/a/*/d", "/a/b/c/d", 0); + test("/a/*/d", "/a/bc/d", FNM_PATHNAME); + test("*hello", ".hello", 0); + test("/*hello", "/.hello", FNM_PERIOD); + test("[a!][a!]", "!a", 0); + test("[\\]]", "]", 0); + test("[\\\\]", "\\", 0); + test("hello[/+]world", "hello/world", 0); + test("hello world", "HELLO WORLD", FNM_CASEFOLD); + + puts(""); + puts("Should fail:"); + test("*World", "Hello Potato", 0); + test("*World", "Potato", 0); + test("H[ae]llo?World", "Hillo+World", 0); + test("H[ae]llo?World", "Hello__World", 0); + test("[0-9][!a]", "ab", 0); + test("[0-9][!a]", "2a", 0); + test("/a/*/d", "/a/b/c/d", FNM_PATHNAME); + test("/a/*/d", "/a/bc/d/", FNM_PATHNAME); + test("*hello", ".hello", FNM_PERIOD); + test("/*hello", "/.hello", FNM_PERIOD | FNM_PATHNAME); + test("[a!][a!]", "ab", 0); + test("hello[/+]world", "hello/world", FNM_PATHNAME); + test("hello world", "HELLO WORLD", 0); + test("********************a", "xxxxxxxxxxxxxxxxxxxxb", 0); +} diff --git a/tests/futimens.c b/tests/futimens.c new file mode 100644 index 0000000000..20b20ab15b --- /dev/null +++ b/tests/futimens.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int check_mtime(char *path, int expected_sec, int expected_nsec, int err_gap) { + // Checks whether the moditication time of the file located at *path* match the provided times. + // When err_gap is set, only checks for a match on sec with a margin of error of +/- err_gap. + struct stat sb; + if (stat(path, &sb) != 0) { + fprintf(stderr, "stat: %s\n", strerror(errno)); + return -1; + } + if (err_gap > 0) { + if (sb.st_mtim.tv_sec < expected_sec + err_gap && sb.st_mtim.tv_sec > expected_sec - err_gap) { + return 0; + } + } else { + if (sb.st_mtim.tv_sec == expected_sec && sb.st_mtim.tv_nsec == expected_nsec) { + return 0; + } + } + fprintf(stderr, "Wrong modified time: %d.%d\n", sb.st_mtim.tv_sec, sb.st_mtim.tv_nsec); + return -1; +} + + +int main(void) { + char temp[] = "/tmp/stattest-XXXXXX"; + const char file[] = "/mkfifo_fifo"; + int len = sizeof(temp) + sizeof(int); + char* path = calloc(len, sizeof(char)); + + if (path == NULL) { + fprintf(stderr, "Could not allocate: %s\n", strerror(errno)); + exit(1); + } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + strncat(path, mktemp(temp), sizeof(temp)); + #pragma GCC diagnostic pop + strncat(path, file, sizeof(file)); + if (mkdir(temp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "mkdir %s: %s\n", temp, strerror(errno)); + exit(1); + } + + int tmp = open(path, O_CREAT | O_CLOEXEC | O_RDONLY | S_IRWXU | S_IRWXG | S_IRWXO); + if (tmp == -1) { + fprintf(stderr, "touch %s: %s\n", path, strerror(errno)); + exit(1); + } + if (close(tmp) == -1) { + fprintf(stderr, "close %s: %s\n", path, strerror(errno)); + exit(1); + } + + int fd = open(path, 0, 0); + if (fd == -1) { + fprintf(stderr, "open %s: %s\n", path, strerror(errno)); + exit(1); + } + + const struct timespec times[] = { { .tv_sec = 10 }, { .tv_sec = 20 } }; + if (futimens(fd, times) == -1) { + fprintf(stderr, "futimens: %s\n", strerror(errno)); + exit(1); + } + if (check_mtime(path, 20, 0, 0) != 0) { + exit(1); + } + // Access times are not flushed to disk, so atime checks can't be (currently) performed + + const struct timespec omit_times[] = { { 25, UTIME_OMIT }, { 12, UTIME_OMIT } }; + if (futimens(fd, omit_times) == -1) { + fprintf(stderr, "futimens: %s\n", strerror(errno)); + exit(1); + } + if (check_mtime(path, 20, 0, 0) != 0) { + exit(1); + } + + const struct timespec now_times[] = { { 25, UTIME_NOW }, { 12, UTIME_NOW } }; + if (futimens(fd, now_times) == -1) { + fprintf(stderr, "futimens: %s\n", strerror(errno)); + exit(1); + } + int now_ts = time(NULL); + if (check_mtime(path, now_ts, 0, 1) != 0) { + exit(1); + } +} + diff --git a/tests/glob.c b/tests/glob.c new file mode 100644 index 0000000000..b7e545dd6e --- /dev/null +++ b/tests/glob.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include + + +typedef int (*err_func_t)(const char *epath, int eerrno); + + +static char *gl_offs_test_string = ""; + + +int glob_err(const char *epath, int eerrno) { + printf("glob_err(\"%s\", \"%s\") -> %d\n", epath, strerror(eerrno), 0); + return 0; +} + +void glob_test(const char *pattern, int flags, err_func_t errfunc, glob_t *pglob) { + printf("Pattern = %s\n", pattern); + + int retval = glob(pattern, flags, errfunc, pglob); + + if (retval != 0) { + if (retval == GLOB_ABORTED) { + puts("(ABORTED)\n"); + } + else if (retval == GLOB_NOMATCH) { + puts("(NOMATCH)\n"); + } + else if (retval == GLOB_NOSPACE) { + puts("(NOSPACE)\n"); + } + else { + printf(" (Unknown retval %d!)\n", retval); + } + return; + } + + size_t gl_offs = flags & GLOB_DOOFFS ? pglob->gl_offs : 0; + + printf("(Matched %lu)", pglob->gl_pathc); + + if (flags & GLOB_DOOFFS) { + printf(" (with %lu gl_offs)\n", gl_offs); + } + else { + puts(""); + } + + for(unsigned int i = 0; i < (gl_offs + pglob->gl_pathc); i++) { + if (!pglob->gl_pathv[i]) { + printf("%d - NULL\n", i); + } + else { + printf("%d - %s\n", i, pglob->gl_pathv[i]); + } + } + printf("%s", "\n"); +} + +int main(void) { + glob_t pglob = {0}; + + glob_test("eample_dir/*", 0, NULL, &pglob); + globfree(&pglob); + + glob_test("eample_dir/*", GLOB_ERR, glob_err, &pglob); + globfree(&pglob); + + glob_test("./example_dir/*", 0, glob_err, &pglob); + globfree(&pglob); + + glob_test("example_dir/*never*", 0, glob_err, &pglob); + glob_test("example_dir/?-and*", GLOB_APPEND, glob_err, &pglob); + globfree(&pglob); + + pglob.gl_offs = 4; + glob_test("example_dir/*never*", GLOB_DOOFFS, glob_err, &pglob); + pglob.gl_pathv[0] = gl_offs_test_string; + glob_test("example_dir/?-and*", GLOB_DOOFFS | GLOB_APPEND, glob_err, &pglob); + globfree(&pglob); + + glob_test("example_dir", GLOB_MARK, glob_err, &pglob); + globfree(&pglob); +} diff --git a/tests/grp/getgrgid_r.c b/tests/grp/getgrgid_r.c new file mode 100644 index 0000000000..ed2fea5dcc --- /dev/null +++ b/tests/grp/getgrgid_r.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include + +void test_getgrgid(gid_t gid) { + struct group *out = getgrgid(gid); + + if (out == NULL) { + printf("Did not find a group %d\n", gid); + return; + } + + printf("getgrgid\n"); + + printf(" %d = %s, GID: %d\n", gid, out->gr_name, out->gr_gid); +} + +void test_getgrgid_r(gid_t gid) { + char buf[100]; + + struct group grp; + struct group *tmp; + + int status = getgrgid_r(gid, &grp, buf, sizeof(buf), &tmp); + + if (tmp == NULL) { + const char *reason = status != 0 ? strerror(status) : "(not found)"; + printf("Did not find a group %d: %s\n", gid, reason); + return; + } + + printf("getgrgid_r\n"); + + printf(" %d = %s, GID: %d\n", gid, grp.gr_name, grp.gr_gid); +} + +int main(void) { + test_getgrgid(1050); + test_getgrgid_r(1050); +} diff --git a/tests/grp/getgrnam_r.c b/tests/grp/getgrnam_r.c new file mode 100644 index 0000000000..694d4fe1c9 --- /dev/null +++ b/tests/grp/getgrnam_r.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include + +void test_getgrnam(const char *gr_name) { + struct group* out = getgrnam(gr_name); + + if (out == NULL) { + printf("Did not find a group '%s'\n", gr_name); + return; + } + + printf("getgrnam\n"); + + printf(" '%s' = %d\n", gr_name, out->gr_gid); +} + +void test_getgrnam_r(const char *gr_name) { + char buf[100]; + + struct group grp; + struct group* out = &grp; + + int status = getgrnam_r(gr_name, &grp, buf, sizeof(buf), &out); + + if (out == NULL) { + const char *reason = (status != 0) ? strerror(status) : "(not found)"; + printf("Did not find a group %s: %s\n", gr_name, reason); + return; + } + + printf("getgrnam_r\n"); + + printf(" '%s' = %d\n", gr_name, out->gr_gid); +} + +int main(void) { + test_getgrnam("lcake"); + test_getgrnam_r("lcake"); +} diff --git a/tests/grp/getgrouplist.c b/tests/grp/getgrouplist.c new file mode 100644 index 0000000000..61e11d1a9a --- /dev/null +++ b/tests/grp/getgrouplist.c @@ -0,0 +1,15 @@ +#include +#include + +int main(void) { + gid_t groups[20]; + int ngroup = 20; + int num_groups = getgrouplist("user", 1000, groups, &ngroup); + + printf("Num Groups: %d\n", num_groups); + + for (int i = 0; i < num_groups; i++) + printf("i: %d\n", groups[i]); + + return 0; +} \ No newline at end of file diff --git a/tests/grp/getgroups.c b/tests/grp/getgroups.c new file mode 100644 index 0000000000..7b06f293e2 --- /dev/null +++ b/tests/grp/getgroups.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +int main() { + gid_t primary_gid = getegid(); + struct group *pg = getgrgid(primary_gid); + + printf("getegid: %u (%s)\n", primary_gid, pg ? pg->gr_name : "?"); + + int count = getgroups(0, NULL); + gid_t *list = malloc(sizeof(gid_t) * count); + getgroups(count, list); + + printf("getgroups: %d\n", count); + for (int i = 0; i < count; i++) { + struct group *sg = getgrgid(list[i]); + + printf(" - %u (%s)\n", list[i], sg ? sg->gr_name : "?"); + } + + free(list); + return 0; +} \ No newline at end of file diff --git a/tests/grp/gr_iter.c b/tests/grp/gr_iter.c new file mode 100644 index 0000000000..bf3e2e5090 --- /dev/null +++ b/tests/grp/gr_iter.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main(void) { + puts("getgrent\n"); + + for (struct group* grp = getgrent(); grp != NULL; grp = getgrent()) + printf(" %s = %d\n", grp->gr_name, grp->gr_gid); +} diff --git a/tests/includes.c b/tests/includes.c new file mode 100644 index 0000000000..014235d740 --- /dev/null +++ b/tests/includes.c @@ -0,0 +1,91 @@ +// This test file exist only to check dependency graph, using `redoxer cc includes.c -H` + +// generate: find src/header -mindepth 1 -maxdepth 1 -type d -not -name "_*" -printf "#include <%f.h>\n" | sed -e 's|_|/|g' | sort + +// TODO: No include guard in these headers, probably expected? +// #include +// #include +// #include + +#include +#include +#ifndef __GLIBC__ +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main() { +} diff --git a/tests/iso646.c b/tests/iso646.c new file mode 100644 index 0000000000..82f544807f --- /dev/null +++ b/tests/iso646.c @@ -0,0 +1,92 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main() { + uint8_t a_bits = 0xa; // 0b0000_1010 + uint8_t b_bits = 0xc; // 0b0000_1100 + + uint8_t c_bits; + int c_bool; + + printf("0 and 0: %d\n", 0 and 0); + printf("1 and 0: %d\n", 1 and 0); + printf("0 and 1: %d\n", 0 and 1); + printf("1 and 1: %d\n", 1 and 1); + + c_bool = 0; + c_bool and_eq 0; + printf("0 and_eq 0: %d\n", c_bool); + c_bool = 1; + c_bool and_eq 0; + printf("1 and_eq 0: %d\n", c_bool); + c_bool = 0; + c_bool and_eq 1; + printf("0 and_eq 1: %d\n", c_bool); + c_bool = 1; + c_bool and_eq 1; + printf("1 and_eq 1: %d\n", c_bool); + + c_bits = a_bits bitand b_bits; + printf("a bitand b: %d\n", c_bits); + + c_bits = a_bits bitor b_bits; + printf("a bitor b: %d\n", c_bits); + + c_bits = compl a_bits; + printf("compl a: %d\n", c_bits); + + printf("not 0: %d\n", not 0); + printf("not 1: %d\n", not 1); + + c_bool = 0; + c_bool not_eq 0; + printf("0 not_eq 0: %d\n", c_bool); + c_bool = 1; + c_bool not_eq 0; + printf("1 not_eq 0: %d\n", c_bool); + c_bool = 0; + c_bool not_eq 1; + printf("0 not_eq 1: %d\n", c_bool); + c_bool = 1; + c_bool not_eq 1; + printf("1 not_eq 1: %d\n", c_bool); + + printf("0 or 0: %d\n", 0 or 0); + printf("1 or 0: %d\n", 1 or 0); + printf("0 or 1: %d\n", 0 or 1); + printf("1 or 1: %d\n", 1 or 1); + + c_bool = 0; + c_bool or_eq 0; + printf("0 or_eq 0: %d\n", c_bool); + c_bool = 1; + c_bool or_eq 0; + printf("1 or_eq 0: %d\n", c_bool); + c_bool = 0; + c_bool or_eq 1; + printf("0 or_eq 1: %d\n", c_bool); + c_bool = 1; + c_bool or_eq 1; + printf("1 or_eq 1: %d\n", c_bool); + + printf("0 xor 0: %d\n", 0 xor 0); + printf("1 xor 0: %d\n", 1 xor 0); + printf("0 xor 1: %d\n", 0 xor 1); + printf("1 xor 1: %d\n", 1 xor 1); + + c_bool = 0; + c_bool xor_eq 0; + printf("0 xor_eq 0: %d\n", c_bool); + c_bool = 1; + c_bool xor_eq 0; + printf("1 xor_eq 0: %d\n", c_bool); + c_bool = 0; + c_bool xor_eq 1; + printf("0 xor_eq 1: %d\n", c_bool); + c_bool = 1; + c_bool xor_eq 1; + printf("1 xor_eq 1: %d\n", c_bool); +} diff --git a/tests/kill-waitpid.c b/tests/kill-waitpid.c new file mode 100644 index 0000000000..000029127c --- /dev/null +++ b/tests/kill-waitpid.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int err; + + int fds[2]; + err = pipe(fds); + ERROR_IF(pipe, err, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + if (child == 0) { + // block forever + char buf[1]; + read(fds[0], buf, 1); + } + + err = kill(child, SIGTERM); + ERROR_IF(kill, err, == -1); + + int status; + err = waitpid(child, &status, 0); + ERROR_IF(waitpid, err, == -1); + printf("STATUS %d\n", status); + assert(WIFSIGNALED(status)); + assert(WTERMSIG(status) == SIGTERM); +} diff --git a/tests/libfoo.c b/tests/libfoo.c new file mode 100644 index 0000000000..b2106a4ec5 --- /dev/null +++ b/tests/libfoo.c @@ -0,0 +1,3 @@ +char *FOO = "foo"; + +void a() {} diff --git a/tests/libfoobar.c b/tests/libfoobar.c new file mode 100644 index 0000000000..67cef36df6 --- /dev/null +++ b/tests/libfoobar.c @@ -0,0 +1,4 @@ +char *BAR = "bar"; + +void a(); +void b() { a(); } diff --git a/tests/libgen.c b/tests/libgen.c new file mode 100644 index 0000000000..8cec95a78b --- /dev/null +++ b/tests/libgen.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +#define TODO NULL + +// TODO: Tests for Redox schemes +struct test_case { + char *path; + char *dirname; + char *basename; +} test_cases[] = { + // Classic UNIX + // path dirname basename + { "", ".", "." }, + { ".", ".", "." }, + { "..", ".", ".." }, + { "/", "/", "/" }, + { "///", "/", "/" }, + { "//usr//lib//", "//usr", "lib" }, + { "/usr", "/", "usr" }, + { "/usr/", "/", "usr" }, + { "/usr/lib", "/usr", "lib" }, + { NULL, ".", "." } + // Root scheme + // path dirname basename + //{ ":", TODO, TODO }, + //{ ":/", TODO, TODO }, + //{ ":/scheme", TODO, TODO }, + // Regular scheme + // path dirname basename + //{ "file:", TODO, TODO }, + //{ "file:usr", TODO, TODO }, + //{ "file:usr/", TODO, TODO }, + //{ "file:usr/lib", TODO, TODO }, + //{ "file:/", TODO, TODO }, + //{ "file:/usr", TODO, TODO }, + //{ "file:/usr/", TODO, TODO }, + //{ "file:/usr/lib", TODO, TODO }, + //{ "file:///", TODO, TODO }, + //{ "file://usr", TODO, TODO }, + //{ "file://usr//", TODO, TODO }, + // Hierarchical scheme + // path dirname basename + //{ "disk/0:", TODO, TODO }, + //{ "disk/0:/", TODO, TODO }, + //{ "disk/0:///", TODO, TODO }, + //{ "disk/0:/usr", TODO, TODO }, + //{ "disk/0:/usr/", TODO, TODO }, + //{ "disk/0:/usr/lib", TODO, TODO }, + // Malformed + // path dirname basename + //{ "/file:/sys:/usr", TODO, TODO }, + //{ "/file:/usr", TODO, TODO }, + //{ "/file:sys:/usr", TODO, TODO }, + //{ "/file:usr", TODO, TODO }, + //{ ":file/usr", TODO, TODO }, + //{ "file:/sys:/usr", TODO, TODO }, + //{ "file::/", TODO, TODO }, + //{ "file::/usr/lib", TODO, TODO } +}; + +size_t num_test_cases = sizeof(test_cases) / sizeof(struct test_case); + +int safe_strcmp(char *s1, char *s2) { + if (s1 == NULL && s2 == NULL) { + return 0; + } else if (s1 == NULL && s2 != NULL) { + return 1; + } else if (s1 != NULL && s2 == NULL) { + return -1; + } else { + return strcmp(s1, s2); + } +} + +#define CHECK_TEST(tc, fn, retval) \ + do { \ + /* API for basename and dirname allow the passed in string to */ \ + /* be modified. This means we have to pass a modifiable copy. */ \ + char *path = NULL; \ + if (tc.path != NULL) \ + path = strdup(tc.path); \ + \ + char *output = fn(path); \ + \ + /* Printing NULLs with printf("%s") is undefined behaviour, */ \ + /* that's why they are handled here this way. */ \ + char display_path[64] = "NULL"; \ + char display_output[64] = "NULL"; \ + char display_expected[64] = "NULL"; \ + if (tc.path != NULL) sprintf(display_path, "\"%s\"", tc.path); \ + if (output != NULL) sprintf(display_output, "\"%s\"", output); \ + if (tc.fn != NULL) sprintf(display_expected, "\"%s\"", tc.fn); \ + \ + if (safe_strcmp(output, tc.fn) != 0) { \ + retval = EXIT_FAILURE; \ + printf("%s(%s) != %s, expected: %s\n", \ + #fn, display_path, display_output, display_expected); \ + } else { \ + printf("%s(%s) == %s\n", \ + #fn, display_path, display_output); \ + } \ + \ + free(path); \ + } while (0) + +int main(void) { + int retval = EXIT_SUCCESS; + + for(size_t i = 0; i < num_test_cases; ++i) { + struct test_case tc = test_cases[i]; + CHECK_TEST(tc, dirname, retval); + CHECK_TEST(tc, basename, retval); + } + + if (retval == EXIT_SUCCESS) { + printf("Success: %d\n", retval); + } else { + printf("Failure: %d\n", retval); + } + + return retval; +} diff --git a/tests/limits.c b/tests/limits.c new file mode 100644 index 0000000000..f59585ff7c --- /dev/null +++ b/tests/limits.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +int main() { + char c_max = CHAR_MAX; + char c_min = CHAR_MIN; + signed char sc_max = SCHAR_MAX; + signed char sc_min = SCHAR_MIN; + short s_max = SHRT_MAX; + short s_min = SHRT_MIN; + int i_max = INT_MAX; + int i_min = INT_MIN; + long l_max = LONG_MAX; + long l_min = LONG_MIN; + long long ll_max = LLONG_MAX; + long long ll_min = LLONG_MIN; + ssize_t ss_max = SSIZE_MAX; + unsigned char uc_max = UCHAR_MAX; + unsigned short us_max = USHRT_MAX; + unsigned int ui_max = UINT_MAX; + unsigned long ul_max = ULONG_MAX; + unsigned long long ull_max = ULLONG_MAX; +#ifndef __GLIBC__ + int long_bit = LONG_BIT; + int word_bit = WORD_BIT; +#endif + + printf("CHAR : [%d, %d]\n", c_min, c_max); + printf("SCHAR : [%d, %d]\n", sc_min, sc_max); + printf("SHRT : [%d, %d]\n", s_min, s_max); + printf("INT : [%d, %d]\n", i_min, i_max); + printf("LONG : [%ld, %ld]\n", l_min, l_max); + printf("LLONG : [%lld, %lld]\n", ll_min, ll_max); + printf("SSIZE_MAX : %zd\n\n", ss_max); + printf("UCHAR_MAX : %u\n", uc_max); + printf("USHRT_MAX : %u\n", us_max); + printf("UINT_MAX : %u\n", ui_max); + printf("ULONG_MAX : %lu\n", ul_max); + printf("ULLONG_MAX : %llu\n\n", ull_max); +#ifndef __GLIBC__ + printf("LONG_BIT : %d\n", long_bit); + printf("WORD_BIT : %d\n", word_bit); +#endif + + return 0; +} diff --git a/tests/locale/duplocale.c b/tests/locale/duplocale.c new file mode 100644 index 0000000000..735fbbe353 --- /dev/null +++ b/tests/locale/duplocale.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +int main() { + struct lconv *lconv0 = localeconv(); + + locale_t locale0 = uselocale(NULL); + assert(locale0 == LC_GLOBAL_LOCALE); + + locale_t locale1 = uselocale(LC_GLOBAL_LOCALE); + assert(locale1 == LC_GLOBAL_LOCALE); + + struct lconv *lconv1 = localeconv(); + assert(lconv0 == lconv1); + + locale_t locale2 = newlocale(LC_ALL_MASK, "C", (locale_t)0); + assert(locale2 != LC_GLOBAL_LOCALE); + + struct lconv *lconv2 = localeconv(); + assert(lconv2 == lconv1); + + locale_t locale3 = duplocale(locale2); + assert(locale3 != locale2); + assert(locale3 != LC_GLOBAL_LOCALE); + + locale_t locale4 = uselocale(locale3); + assert(locale4 == locale1); + + struct lconv *lconv3 = localeconv(); + assert(lconv3 != lconv2); + + // should not crash + freelocale(locale3); + freelocale(locale2); + freelocale(locale1); + return 0; +} \ No newline at end of file diff --git a/tests/locale/newlocale.c b/tests/locale/newlocale.c new file mode 100644 index 0000000000..c9d16b2cc6 --- /dev/null +++ b/tests/locale/newlocale.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +int main() { + locale_t locale0 = newlocale(LC_ALL_MASK, "C", (locale_t)0); + assert(locale0 != (locale_t)0); + + locale_t locale1 = newlocale(LC_ALL_MASK, "non-existent-locale", (locale_t)0); + assert(locale1 == (locale_t)0); + + // TODO: locale files inside redoxer (linux and redox)? + // locale_t locale2 = newlocale(LC_ALL_MASK, "en_US", (locale_t)0); + // assert(locale2 != (locale_t)0); + + return 0; +} diff --git a/tests/locale/setlocale.c b/tests/locale/setlocale.c new file mode 100644 index 0000000000..bb80cf7d0f --- /dev/null +++ b/tests/locale/setlocale.c @@ -0,0 +1,15 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + // TODO: Implement locale properly and test it here + char* val = setlocale(LC_ALL, NULL); + if (strcmp(val, "C") == 0) { + puts("success!"); + } else { + printf("setlocale returned the wrong value: %s", val); + } +} diff --git a/tests/malloc/usable_size.c b/tests/malloc/usable_size.c new file mode 100644 index 0000000000..933ebf03f2 --- /dev/null +++ b/tests/malloc/usable_size.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +int main(void) { + size_t request_size = 27; + char *ptr = (char *)malloc(request_size); + + if (!ptr) { + fprintf(stderr, "Allocation failed\n"); + return 1; + } + + size_t actual_size = malloc_usable_size(ptr); + + printf("malloc: %zu bytes\n", request_size); + printf("malloc_usable_size: %zu bytes\n", actual_size); + + assert(actual_size >= request_size); + memset(ptr, 'A', actual_size); + ptr[actual_size - 1] = '\0'; + + assert(ptr[0] == 'A'); + assert(ptr[actual_size - 2] == 'A'); + assert(ptr[actual_size - 1] == '\0'); + free(ptr); + + return 0; +} diff --git a/tests/math.c b/tests/math.c new file mode 100644 index 0000000000..8ff0a72911 --- /dev/null +++ b/tests/math.c @@ -0,0 +1,10 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + double pi = 3.14; + float c = cos(pi); + printf("cos(%f) = %f\n", pi, c); +} diff --git a/tests/mkfifo.c b/tests/mkfifo.c new file mode 100644 index 0000000000..318419a507 --- /dev/null +++ b/tests/mkfifo.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + char temp[] = "/tmp/stattest-XXXXXX"; + const char file[] = "/mkfifo_fifo"; + int len = sizeof(temp) + sizeof(file); + + char* path = malloc(len * sizeof(char)); + path[0] = '\0'; + + if (path == NULL) { + fprintf(stderr, "Could not allocate: %s\n", strerror(errno)); + exit(1); + } + + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + path = strncat(path, mktemp(temp), sizeof(temp)); + #pragma GCC diagnostic pop + path = strncat(path, file, sizeof(file)); + if (mkdir(temp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "mkdir %s: %s\n", temp, strerror(errno)); + exit(1); + } + if (mkfifo(path, S_IRUSR) == -1) { + fprintf(stderr, "mkfifo %s: %s\n", path, strerror(errno)); + exit(1); + } + struct stat sb; + if (stat(path, &sb) != 0) { + fprintf(stderr, "stat: %s\n", strerror(errno)); + exit(1); + } + if (!(sb.st_mode & S_IFIFO)) { + fprintf(stderr, "Not a FIFO: %d\n", sb.st_mode); + exit(1); + } +} diff --git a/tests/mknod.c b/tests/mknod.c new file mode 100644 index 0000000000..50d533a2a2 --- /dev/null +++ b/tests/mknod.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + char temp[] = "/tmp/stattest-XXXXXX"; + const char file[] = "/mknod"; + int len = sizeof(temp) + sizeof(file); + + char* path = malloc(len * sizeof(char)); + path[0] = '\0'; + + if (path == NULL) { + fprintf(stderr, "Could not allocate: %s\n", strerror(errno)); + exit(1); + } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + if(!mktemp(temp)) { + fprintf(stderr, "Unable to create a unique dir name %s: %s\n", temp, strerror(errno)); + exit(1); + } + #pragma GCC diagnostic pop + + path = strncat(path, temp, strlen(temp)); + path = strncat(path, file, strlen(file)); + if (mkdir(temp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "mkdir %s: %s\n", temp, strerror(errno)); + exit(1); + } + if (mknod(path, S_IFREG, S_IRUSR) == -1) { + fprintf(stderr, "mknod %s: %s\n", path, strerror(errno)); + exit(1); + } + struct stat sb; + if (stat(path, &sb) != 0) { + fprintf(stderr, "stat: %s\n", strerror(errno)); + exit(1); + } + if (!(sb.st_mode & S_IFREG)) { + fprintf(stderr, "Expected S_IFREG flag to be set, got mode: %d\n", sb.st_mode); + exit(1); + } +} diff --git a/tests/mknodat.c b/tests/mknodat.c new file mode 100644 index 0000000000..ad2798624f --- /dev/null +++ b/tests/mknodat.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + char temp[] = "/tmp/stattest-XXXXXX"; + const char separator[] = "/"; + const char file[] = "mknod"; // relative + int len = sizeof(temp) + sizeof(file) + sizeof(separator); + + char* path = malloc(len * sizeof(char)); + path[0] = '\0'; + + if (path == NULL) { + fprintf(stderr, "Could not allocate: %s\n", strerror(errno)); + exit(1); + } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + if(!mktemp(temp)) { + fprintf(stderr, "Unable to create a unique dir name %s: %s\n", temp, strerror(errno)); + exit(1); + } + #pragma GCC diagnostic pop + + if (mkdir(temp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "mkdir %s: %s\n", temp, strerror(errno)); + exit(1); + } + + int dir_fd = open(temp, O_RDONLY); + if(dir_fd == -1) { + fprintf(stderr, "unable to open temp directory: %s with error: %s\n", temp, strerror(errno)); + exit(1); + } + + if (mknodat(dir_fd, file, S_IFREG, S_IRUSR) == -1) { + fprintf(stderr, "mknod %s: %s\n", path, strerror(errno)); + exit(1); + } + + path = strncat(path, temp, strlen(temp)); + path = strncat(path, separator, strlen(temp)); + path = strncat(path, file, strlen(file)); + + struct stat sb; + if (stat(path, &sb) != 0) { + fprintf(stderr, "stat for %s: %s\n", path, strerror(errno)); + exit(1); + } + + if (!(sb.st_mode & S_IFREG)) { + fprintf(stderr, "Expected S_IFREG flag to be set, got mode: %d\n", sb.st_mode); + exit(1); + } +} diff --git a/tests/net/if.c b/tests/net/if.c new file mode 100644 index 0000000000..5cce1465f4 --- /dev/null +++ b/tests/net/if.c @@ -0,0 +1,43 @@ +#include +#include + +#include "../test_helpers.h" + +#define assert_eq(value, expected) \ + { \ + if (value != expected) { \ + fprintf(stderr, "%s:%d: failed\n", __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ + } + +int main(void) { + const struct if_nameindex *list = if_nameindex(); + + // Currently always returning a stub + const struct if_nameindex *first = &(list[0]); + assert_eq(first->if_index, 1); + assert_eq(strcmp(first->if_name, "stub"), 0); + + // Last item with 0 values determines the end of the list + const struct if_nameindex *second = &(list[1]); + assert_eq(second->if_index, 0); + assert_eq(second->if_name, 0); + + unsigned idx; + idx = if_nametoindex(0); + assert_eq(idx, 0); + idx = if_nametoindex("any"); + assert_eq(idx, 0); + idx = if_nametoindex("stub"); + assert_eq(idx, 1); + + const char *name; + name = if_indextoname(0, 0); + assert_eq(name, 0); + + name = if_indextoname(1, 0); + assert_eq(strcmp(name, "stub"), 0); + + printf("OK\n"); +} diff --git a/tests/netdb/getaddrinfo.c b/tests/netdb/getaddrinfo.c new file mode 100644 index 0000000000..e1bb13562f --- /dev/null +++ b/tests/netdb/getaddrinfo.c @@ -0,0 +1,49 @@ +// Adapted from https://gist.github.com/jirihnidek/bf7a2363e480491da72301b228b35d5d + +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + struct addrinfo hints, *res; + int errcode; + char addrstr[INET6_ADDRSTRLEN]; + void *ptr; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + errcode = getaddrinfo("www.redox-os.org", NULL, &hints, &res); + if (errcode != 0) { + perror("getaddrinfo"); + exit(EXIT_FAILURE); + } + + while (res) { + switch (res->ai_family) { + case AF_INET: + ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; + break; + } + inet_ntop(res->ai_family, ptr, addrstr, INET6_ADDRSTRLEN); + + printf( + "IPv%d address: %s (%s)\n", + res->ai_family == AF_INET6 ? 6 : 4, + addrstr, + res->ai_canonname + ); + + res = res->ai_next; + } +} diff --git a/tests/netdb/getaddrinfo_null.c b/tests/netdb/getaddrinfo_null.c new file mode 100644 index 0000000000..01b69cb860 --- /dev/null +++ b/tests/netdb/getaddrinfo_null.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int test_getaddrinfo(int ai_flags, size_t* ipv4_addr) { + struct addrinfo hints, *res; + int status; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = ai_flags; + + if ((status = getaddrinfo(NULL, "8080", &hints, &res)) != 0) { + return status; + } + + *ipv4_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr; + freeaddrinfo(res); + return 0; +} + +int main() { + size_t ipv4_addr; + char addrstr[INET_ADDRSTRLEN]; + + int status = test_getaddrinfo(0, &ipv4_addr); + ERROR_IF(getaddrinfo, status, != 0); + ERROR_IF(getaddrinfo_ai_addr, ipv4_addr, == 0); + inet_ntop(AF_INET, &ipv4_addr, addrstr, INET_ADDRSTRLEN); + printf("local IPv4 address 1: %s\n", addrstr); + + status = test_getaddrinfo(AI_PASSIVE, &ipv4_addr); + ERROR_IF(getaddrinfo, status, != 0); + ERROR_IF(getaddrinfo_ai_addr, ipv4_addr, != 0); + inet_ntop(AF_INET, &ipv4_addr, addrstr, INET_ADDRSTRLEN); + printf("local IPv4 address 2: %s\n", addrstr); +} diff --git a/tests/netdb/netdb.c b/tests/netdb/netdb.c new file mode 100644 index 0000000000..2ccf70ec00 --- /dev/null +++ b/tests/netdb/netdb.c @@ -0,0 +1,280 @@ +/* Copyright (C) 1998-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger , 1998. + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ +/* + Testing of some network related lookup functions. + The system databases looked up are: + - /etc/services + - /etc/hosts + - /etc/networks + - /etc/protocols + The tests try to be fairly generic and simple so that they work on + every possible setup (and might therefore not detect some possible + errors). +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define ADDR_SIZE sizeof(uint8_t) * 4 + +int error_count; +static void +output_servent (const char *call, struct servent *sptr) +{ + char **pptr; + if (sptr == NULL) + printf ("Call: %s returned NULL\n", call); + else + { + printf ("Call: %s, returned: s_name: %s, s_port: %d, s_proto: %s\n", + call, sptr->s_name, ntohs(sptr->s_port), sptr->s_proto); + for (pptr = sptr->s_aliases; *pptr != NULL; pptr++) + printf (" alias: %s\n", *pptr); + } +} +static void +test_services (void) +{ + struct servent *sptr; + sptr = getservbyname ("domain", "tcp"); + // output_servent ("getservbyname (\"domain\", \"tcp\")", sptr); + sptr = getservbyname ("domain", "udp"); + // output_servent ("getservbyname (\"domain\", \"udp\")", sptr); + sptr = getservbyname ("domain", NULL); + // output_servent ("getservbyname (\"domain\", NULL)", sptr); + sptr = getservbyname ("not-existant", NULL); + // output_servent ("getservbyname (\"not-existant\", NULL)", sptr); + /* This shouldn't return anything. */ + sptr = getservbyname ("", ""); + // output_servent ("getservbyname (\"\", \"\")", sptr); + sptr = getservbyname ("", "tcp"); + // output_servent ("getservbyname (\"\", \"tcp\")", sptr); + sptr = getservbyport (htons(53), "tcp"); + // output_servent ("getservbyport (htons(53), \"tcp\")", sptr); + sptr = getservbyport (htons(53), NULL); + // output_servent ("getservbyport (htons(53), NULL)", sptr); + sptr = getservbyport (htons(1), "udp"); /* shouldn't exist */ + // output_servent ("getservbyport (htons(1), \"udp\")", sptr); + setservent (0); + do + { + sptr = getservent (); + //output_servent ("getservent ()", sptr); + } + while (sptr != NULL); + endservent (); +} +static void +output_hostent (const char *call, struct hostent *hptr) +{ + char **pptr; + char buf[INET6_ADDRSTRLEN]; + if (hptr == NULL) + printf ("Call: %s returned NULL\n", call); + else + { + printf ("Call: %s returned: name: %s, addr_type: %d\n", + call, hptr->h_name, hptr->h_addrtype); + if (hptr->h_aliases) + for (pptr = hptr->h_aliases; *pptr != NULL; pptr++) + printf (" alias: %s\n", *pptr); + for (pptr = hptr->h_addr_list; *pptr != NULL; pptr++) + printf (" ip: %s\n", + inet_ntop (hptr->h_addrtype, *pptr, buf, sizeof (buf))); + } +} +static void +test_hosts (void) +{ + struct hostent *hptr1, *hptr2; + char *name = NULL; + size_t namelen = 0; + struct in_addr ip; + hptr1 = gethostbyname ("localhost"); + hptr2 = gethostbyname ("LocalHost"); + if (hptr1 != NULL || hptr2 != NULL) + { + if (hptr1 == NULL) + { + printf ("localhost not found - but LocalHost found:-(\n"); + ++error_count; + } + else if (hptr2 == NULL) + { + printf ("LocalHost not found - but localhost found:-(\n"); + ++error_count; + } + else if (strcmp (hptr1->h_name, hptr2->h_name) != 0) + { + printf ("localhost and LocalHost have different canoncial name\n"); + printf ("gethostbyname (\"localhost\")->%s\n", hptr1->h_name); + printf ("gethostbyname (\"LocalHost\")->%s\n", hptr2->h_name); + ++error_count; + } + //else + // output_hostent ("gethostbyname(\"localhost\")", hptr1); + } + hptr1 = gethostbyname ("127.0.0.1"); + //output_hostent ("gethostbyname (\"127.0.0.1\")", hptr1); + while (gethostname (name, namelen) < 0 && errno == ENAMETOOLONG) + { + namelen += 2; /* tiny increments to test a lot */ + name = realloc (name, namelen); + } + if (gethostname (name, namelen) == 0) + { + // printf ("Hostname: %s\n", name); + if (name != NULL) + { + hptr1 = gethostbyname (name); + // output_hostent ("gethostbyname (gethostname(...))", hptr1); + } + } + ip.s_addr = htonl (INADDR_LOOPBACK); + + hptr1 = gethostbyaddr ((char *) &ip, sizeof(ip), AF_INET); + if (hptr1 != NULL) + { + // printf ("official name of 127.0.0.1: %s\n", hptr1->h_name); + } + sethostent (0); + do + { + hptr1 = gethostent (); + //output_hostent ("gethostent ()", hptr1); + } + while (hptr1 != NULL); + endhostent (); + struct hostent* redox = gethostbyname("redox-os.org"); + if (redox == NULL) { + ++error_count; + } + //output_hostent("gethostbyname(\"redox-os.org\")", redox); + struct in_addr el_goog; + inet_aton("8.8.4.4", &el_goog); + struct hostent* google = gethostbyaddr(&el_goog, 4, AF_INET); + if (google == NULL) { + ++error_count; + } + //output_hostent("gethostbyaddr(\"8.8.4.4\")",google); +} + +static void +output_protoent (const char *call, struct protoent *prptr) +{ + char **pptr; + if (prptr == NULL) + printf ("Call: %s returned NULL\n", call); + else + { + printf ("Call: %s, returned: p_name: %s, p_proto: %d\n", + call, prptr->p_name, prptr->p_proto); + for (pptr = prptr->p_aliases; *pptr != NULL; pptr++) + printf (" alias: %s\n", *pptr); + } +} +static void +test_protocols (void) +{ + struct protoent *prptr; + prptr = getprotobyname ("ICMP"); + // output_protoent ("getprotobyname (\"ICMP\")", prptr); + prptr = getprotobynumber (1); + // output_protoent ("getprotobynumber (1)", prptr); + setprotoent (0); + do + { + prptr = getprotoent (); + // output_protoent ("getprotoent ()", prptr); + } + while (prptr != NULL); + endprotoent (); +} +static void +test_network (void) +{ + struct netent *nptr = getnetbyname ("loopback"); + if (nptr != NULL) { + printf("network name %d", nptr->n_net); + } else { + ++error_count; + } + do + { + nptr = getnetent(); + if (nptr != NULL) { + printf("network name %s", nptr->n_name); + } + } + while (nptr != NULL); + setnetent (0); +} +static void +test_h_errno (void) { + const uint8_t addr[] = {0, 0, 0, 0}; + struct hostent *host = gethostbyaddr(addr, ADDR_SIZE, AF_INET); + if (host) { + ++error_count; + } + + int err = h_errno; + herror("Prefix test"); + if (err != HOST_NOT_FOUND) { + ++error_count; + } + + host = gethostbyname(""); + if (host) { + ++error_count; + } + + err = h_errno; + herror(""); + if (err != HOST_NOT_FOUND) { + ++error_count; + } +} +static int +do_test (void) +{ + /* + setdb ("db"); + */ + test_h_errno (); + test_hosts (); + test_network (); + test_protocols (); + test_services (); + if (error_count) + printf ("\n %d errors occurred!\n", error_count); + else + printf ("No visible errors occurred!\n"); + return (error_count != 0); +} + +int main(void) { + do_test(); +} diff --git a/tests/psignal.c b/tests/psignal.c new file mode 100644 index 0000000000..375d1b0552 --- /dev/null +++ b/tests/psignal.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + puts("------psignal------"); + psignal(SIGUSR1, "a prefix"); + puts("------ end ------"); + puts("------psiginfo-----"); + siginfo_t info = { 0 }; + info.si_code = SI_USER; + info.si_pid = 42; + info.si_uid = 1337; + info.si_addr = (void *)0xdeadbeef; + info.si_value.sival_ptr = (void *)0xfedface; + psiginfo(&info, "another prefix"); + puts("------ -----"); +} diff --git a/tests/pthread/barrier.c b/tests/pthread/barrier.c new file mode 100644 index 0000000000..34be540015 --- /dev/null +++ b/tests/pthread/barrier.c @@ -0,0 +1,124 @@ +#include "../test_helpers.h" +#include "common.h" + +#include +#include +#include +#include + +// Same test logic as test_barrier in rustc/library/std/sync/barrier/tests.rs + +#define N 10 + +struct arg { + pthread_barrier_t *barrier; + volatile _Atomic(unsigned) *count; + bool is_leader; +}; + +void *routine(void *arg_raw) { + struct arg *arg = arg_raw; + + int status = pthread_barrier_wait(arg->barrier); + + arg->is_leader = status == PTHREAD_BARRIER_SERIAL_THREAD; + + if (!arg->is_leader) + ERROR_IF(pthread_barrier_wait, status, != 0); + + // We can now modify the counter. + atomic_fetch_add_explicit(arg->count, 1, memory_order_relaxed); + + return NULL; +} + +int main(void) { + int status; + + pthread_barrier_t barrier; + + pthread_barrierattr_t attr; + status = pthread_barrierattr_init(&attr); + ERROR_IF(pthread_barrierattr_init, status, != 0); + + int pshared; + + // + // BARRIER ATTR + // + + status = pthread_barrierattr_getpshared(&attr, &pshared); + + // PTHREAD_PROCESS_PRIVATE is default according to POSIX. + assert(pshared == PTHREAD_PROCESS_PRIVATE); + + ERROR_IF(pthread_barrierattr_getpshared, status, != 0); + + status = pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + ERROR_IF(pthread_barrierattr_setpshared, status, != 0); + + status = pthread_barrierattr_getpshared(&attr, &pshared); + assert(pshared == PTHREAD_PROCESS_SHARED); + ERROR_IF(pthread_barrierattr_getpshared, status, != 0); + + status = pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); + ERROR_IF(pthread_barrierattr_setpshared, status, != 0); + + status = pthread_barrierattr_getpshared(&attr, &pshared); + assert(pshared == PTHREAD_PROCESS_PRIVATE); + ERROR_IF(pthread_barrierattr_getpshared, status, != 0); + + // + // BARRIER + // + + status = pthread_barrier_init(&barrier, &attr, N); + ERROR_IF(pthread_barrier_init, status, != 0); + + status = pthread_barrierattr_destroy(&attr); + ERROR_IF(pthread_barrierattr_destroy, status, != 0); + + // + // CREATE THREAD + // + + pthread_t threads[N - 1]; + struct arg args[N - 1]; + _Atomic(unsigned) count = false; + + for (size_t i = 0; i < N - 1; i++) { + args[i] = (struct arg){ .count = &count, .barrier = &barrier, .is_leader = false }; + status = pthread_create(&threads[i], NULL, routine, &args[i]); + ERROR_IF(pthread_create, status, != 0); + } + + // Must not be set before having waited for the barrier. This is part of the + // test. Normally spawned threads run before the scheduler returns to the + // parent thread, so it should at least partially verify that barriers work. + unsigned value = atomic_load_explicit(&count, memory_order_relaxed); + + UNEXP_IF(count_before_barrier_wait, value, > 0); + + status = pthread_barrier_wait(&barrier); + + bool leader_found = status == PTHREAD_BARRIER_SERIAL_THREAD; + + if (!leader_found) { + ERROR_IF(pthread_barrier_wait, status, != 0); + } + + for (size_t i = 0; i < N - 1; i++) { + status = pthread_join(threads[i], NULL); + ERROR_IF(pthread_join, status, != 0); + + // SAFETY: pthread_create and pthread_join are Acquire-Release + leader_found |= args[i].is_leader; + } + + assert(leader_found); + + status = pthread_barrier_destroy(&barrier); + ERROR_IF(pthread_barrier_destroy, status, != 0); + + return 0; +} diff --git a/tests/pthread/cleanup.c b/tests/pthread/cleanup.c new file mode 100644 index 0000000000..015b56eda2 --- /dev/null +++ b/tests/pthread/cleanup.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include + +// TODO: glibc requires arg to be not const, but not relibc +#ifndef __GLIBC__ +const char *msg1 = "first"; +const char *msg2 = "second"; +const char *msg3 = "third"; +#else +char *msg1 = "first"; +char *msg2 = "second"; +char *msg3 = "third"; +#endif + +void cleanup1(void *arg) { + printf("Running %s cleanup callback\n", (const char *)arg); +} +void cleanup2(void *arg) { + fprintf(stderr, "Running %s cleanup callback, to stderr\n", (const char *)arg); +} +void cleanup3(void *arg) { + printf("Running final (%s) callback\n", (const char *)arg); +} + +void *routine(void *arg) { + assert(arg == NULL); + + puts("1"); + pthread_cleanup_push(cleanup1, msg1); + puts("2"); + pthread_cleanup_push(cleanup2, msg2); + puts("3"); + pthread_cleanup_push(cleanup3, msg3); + puts("4"); + pthread_cleanup_pop(true); + puts("5"); + //exit(EXIT_SUCCESS); + pthread_exit(NULL); + puts("6"); + pthread_cleanup_pop(true); + pthread_cleanup_pop(true); + return NULL; +} + +int main(void) { + int result; + + puts("Main thread started"); + pthread_t second_thread; + if ((result = pthread_create(&second_thread, NULL, routine, NULL)) != 0) { + fprintf(stderr, "thread creation failed: %s\n", strerror(result)); + return EXIT_FAILURE; + } + if ((result = pthread_join(second_thread, NULL)) != 0) { + fprintf(stderr, "failed to join thread: %s\n", strerror(result)); + return EXIT_FAILURE; + } + puts("Main thread about to exit"); + return EXIT_SUCCESS; +} diff --git a/tests/pthread/common.h b/tests/pthread/common.h new file mode 100644 index 0000000000..2c2dbc305a --- /dev/null +++ b/tests/pthread/common.h @@ -0,0 +1,24 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include +#include +#include + +int fail(int status, const char *reason) { + fprintf(stderr, "%s failed: %s\n", reason, strerror(status)); + + return EXIT_FAILURE; +} + +uintptr_t black_box_uintptr_t(uintptr_t arg) { + // Rust implements this by putting the value behind a volatile pointer and then simply loading it. + + uintptr_t arg_slot = arg; + volatile uintptr_t *ptr = &arg_slot; + + return *ptr; +} + +#endif // _COMMON_H diff --git a/tests/pthread/customstack.c b/tests/pthread/customstack.c new file mode 100644 index 0000000000..ebbc5eed10 --- /dev/null +++ b/tests/pthread/customstack.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define STACKSIZE 128 * 1024 + +struct retval { + uintptr_t some_stack_pointer; +}; +struct arg { + int status; +}; + +void *routine(void *arg_raw) { + struct arg *arg = arg_raw; + + struct retval *retval = malloc(sizeof(struct retval)); + + if (retval == NULL) { + arg->status = ENOMEM; + return NULL; + } + + char some_array[256] = { 0 }; + retval->some_stack_pointer = black_box_uintptr_t((uintptr_t)some_array); + + return retval; +} + +int main(void) { + int status; + static char stack[STACKSIZE]; + + pthread_attr_t attr; + if ((status = pthread_attr_init(&attr)) != 0) { + return fail(status, "attr init"); + } + if ((status = pthread_attr_setstack(&attr, stack, STACKSIZE)) != 0) { + return fail(status, "attr setstack"); + } + + void *stack_again; + size_t stacksize_again; + + if ((status = pthread_attr_getstack(&attr, &stack_again, &stacksize_again)) != 0) { + return fail(status, "attr getstack"); + } + + assert(stack_again == stack); + assert(stacksize_again == STACKSIZE); + + pthread_t thread; + + if ((status = pthread_create(&thread, &attr, routine, NULL)) != 0) { + return fail(status, "pthread create"); + } + + if ((status = pthread_attr_destroy(&attr)) != 0) { + return fail(status, "attr destroy"); + } + + void *retval_raw; + + if ((status = pthread_join(thread, &retval_raw)) != 0) { + return fail(status, "pthread join"); + } + + struct retval *retval = retval_raw; + + assert(retval->some_stack_pointer >= (uintptr_t)stack); + assert(retval->some_stack_pointer < ((uintptr_t)stack) + STACKSIZE); + + free(retval); + + return EXIT_SUCCESS; +} diff --git a/tests/pthread/exit.c b/tests/pthread/exit.c new file mode 100644 index 0000000000..5538a0f593 --- /dev/null +++ b/tests/pthread/exit.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#include + +#include "common.h" + +void *routine(void *arg) { + assert(arg == NULL); + + usleep(100000); + + puts("Thread succeeded"); + + return NULL; +} + +int main(void) { + int status; + pthread_t thread; + + if ((status = pthread_create(&thread, NULL, routine, NULL)) != 0) { + return fail(status, "failed to create thread"); + } + if ((status = pthread_detach(thread)) != 0) { + return fail(status, "failed to detach thread"); + } + + pthread_exit(NULL); +} diff --git a/tests/pthread/extjoin.c b/tests/pthread/extjoin.c new file mode 100644 index 0000000000..da19101075 --- /dev/null +++ b/tests/pthread/extjoin.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include + +#include "common.h" + +struct arg2 { + int status; + pthread_t t1; +}; + +void *routine1(void *arg) { + assert(arg == NULL); + puts("Thread 1 spawned, waiting 1s."); + usleep(100000); + puts("Thread 1 finished."); + return strdup("message from thread 1"); +} +void *routine2(void *arg_raw) { + struct arg2 *arg = arg_raw; + + puts("Thread 2 spawned, awaiting thread 1."); + + void *retval_raw; + int status; + + if ((status = pthread_join(arg->t1, &retval_raw)) != 0) { + arg->status = fail(status, "t1 join from thread 2"); + return NULL; + } + char *retval = retval_raw; + + assert(strcmp(retval, "message from thread 1") == 0); + + free(retval); + + return NULL; +} + +int main(void) { + pthread_t t1; + pthread_t t2; + + int status; + + puts("Main thread."); + + if ((status = pthread_create(&t1, NULL, routine1, NULL)) != 0) { + return fail(status, "t1 create"); + } + + puts("Created thread 1."); + + struct arg2 arg = { .status = 0, .t1 = t1 }; + + if ((status = pthread_create(&t2, NULL, routine2, &arg)) != 0) { + return fail(status, "t2 create"); + } + + if ((status = pthread_join(t2, NULL)) != 0) { + return fail(status, "t2 join"); + } + + return EXIT_SUCCESS; +} diff --git a/tests/pthread/main.c b/tests/pthread/main.c new file mode 100644 index 0000000000..186e306c2b --- /dev/null +++ b/tests/pthread/main.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +void *thread_main(void *arg) { + puts("Thread main"); + + assert(arg == NULL); + + return NULL; +} + +int main(void) { + int status; + + puts("Start, sleeping 1 second"); + usleep(100000); + pthread_t thread; + void *arg = NULL; + if ((status = pthread_create(&thread, NULL, thread_main, arg)) != 0) { + return fail(status, "create thread"); + } + puts("Started"); + void *retval; + if ((status = pthread_join(thread, &retval)) != 0) { + return fail(status, "join thread"); + } + assert(retval == NULL); + puts("Joined"); + + return EXIT_SUCCESS; +} diff --git a/tests/pthread/mutex_recursive.c b/tests/pthread/mutex_recursive.c new file mode 100644 index 0000000000..2e22a30f58 --- /dev/null +++ b/tests/pthread/mutex_recursive.c @@ -0,0 +1,108 @@ +#include +#include +#include + +#include "../test_helpers.h" + +#define N 10 +#define M 10000 + +struct arg { + pthread_mutex_t *mutex; + unsigned *protected; +}; + +void *routine(void *arg_raw) { + struct arg *arg = arg_raw; + int status; + + unsigned depth = 0; + unsigned i = 0; + + while (i < M) { + // Bad random distribution, but should work. + bool lock_again = (depth == 0) || (random_bool() && random_bool()); + + if (lock_again) { + status = pthread_mutex_lock(arg->mutex); + ERROR_IF(pthread_mutex_lock, status, != 0); + + depth += 1; + } else { + status = pthread_mutex_unlock(arg->mutex); + ERROR_IF(pthread_mutex_unlock, status, != 0); + + depth -= 1; + } + if (depth == 0) { + continue; + } + + unsigned value = *arg->protected; + *arg->protected = value + 1; + + i += 1; + } + while (depth > 0) { + status = pthread_mutex_unlock(arg->mutex); + ERROR_IF(pthread_mutex_unlock, status, != 0); + + depth--; + } + + return NULL; +} + +int main(void) { + int status; + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + + status = pthread_mutexattr_init(&attr); + ERROR_IF(pthread_mutexattr_init, status, != 0); + + status = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + ERROR_IF(pthread_mutexattr_settype, status, != 0); + + status = pthread_mutex_init(&mutex, &attr); + ERROR_IF(pthread_mutex_init, status, != 0); + + status = pthread_mutexattr_destroy(&attr); + ERROR_IF(pthread_mutexattr_destroy, status, != 0); + + status = pthread_mutex_lock(&mutex); + ERROR_IF(pthread_mutex_lock, status, != 0); + + status = pthread_mutex_trylock(&mutex); + ERROR_IF(pthread_mutex_trylock, status, != 0); + + status = pthread_mutex_unlock(&mutex); + ERROR_IF(pthread_mutex_unlock, status, != 0); + + // Still locked with count = 1. + + pthread_t threads[N]; + struct arg args[N]; + unsigned protected = 0; + + for (size_t i = 0; i < N; i++) { + args[i] = (struct arg){ .mutex = &mutex, .protected = &protected }; + status = pthread_create(&threads[i], NULL, routine, &args[i]); + ERROR_IF(pthread_create, status, != 0); + } + + protected = 1; + + status = pthread_mutex_unlock(&mutex); + ERROR_IF(pthread_mutex_unlock, status, != 0); + + for (size_t i = 0; i < N; i++) { + status = pthread_join(threads[i], NULL); + ERROR_IF(pthread_join, status, != 0); + } + + status = pthread_mutex_destroy(&mutex); + ERROR_IF(pthread_mutex_destroy, status, != 0); + + return 0; +} diff --git a/tests/pthread/once.c b/tests/pthread/once.c new file mode 100644 index 0000000000..40a9c5ce20 --- /dev/null +++ b/tests/pthread/once.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "../test_helpers.h" + +_Thread_local size_t this_thread_count = 0; + +#define COUNT 1024 +#define THREADS 4 + +static void constructor(void) { + this_thread_count++; +} + +struct arg { + pthread_barrier_t *barrier; + pthread_once_t *onces; + size_t count; + size_t index; +}; + +void *routine(void *arg_raw) { + int status; + struct arg *arg = arg_raw; + + if (arg->index == THREADS - 1) { + printf("main thread at %zu\n", arg->index); + } else { + printf("spawned %zu\n", arg->index); + } + + status = pthread_barrier_wait(arg->barrier); + + printf("waited %zu leader=%s\n", arg->index, (status == PTHREAD_BARRIER_SERIAL_THREAD) ? "true" : "false"); + + if (status != PTHREAD_BARRIER_SERIAL_THREAD) { + ERROR_IF(pthread_barrier_wait, status, != 0); + return NULL; + } + + for (size_t i = 0; i < COUNT; i++) { + status = pthread_once(&arg->onces[i], constructor); + ERROR_IF(pthread_once, status, != 0); + } + + arg->count = this_thread_count; + + return NULL; +} + +int main(void) { + // TODO: Better test to simulate contention? + + int status; + + pthread_once_t onces[COUNT]; + + for (size_t i = 0; i < COUNT; i++) { + onces[i] = PTHREAD_ONCE_INIT; + } + + pthread_barrier_t barrier; + + printf("Barrier at %p, onces at %p\n", &barrier, onces); + + status = pthread_barrier_init(&barrier, NULL, THREADS); + ERROR_IF(pthread_barrier_init, status, != 0); + + pthread_t threads[THREADS]; + struct arg args[THREADS]; + + for (size_t i = 0; i < THREADS; i++) { + args[i] = (struct arg){ .barrier = &barrier, .onces = onces, .count = 0, .index = i }; + printf("spawning %zu\n", i); + + status = pthread_create(&threads[i], NULL, routine, &args[i]); + ERROR_IF(pthread_create, status, != 0); + } + + size_t total_count = 0; + + for (size_t i = 0; i < THREADS; i++) { + status = pthread_join(threads[i], NULL); + ERROR_IF(pthread_join, status, != 0); + + total_count += args[i].count; + } + + status = pthread_barrier_destroy(&barrier); + ERROR_IF(pthread_barrier_destroy, status, != 0); + + assert(total_count == COUNT); + + return EXIT_SUCCESS; +} diff --git a/tests/pthread/rwlock_randtest.c b/tests/pthread/rwlock_randtest.c new file mode 100644 index 0000000000..e263efd472 --- /dev/null +++ b/tests/pthread/rwlock_randtest.c @@ -0,0 +1,64 @@ +#include "../test_helpers.h" +#include "common.h" + +#include +#include +#include + +// Same test logic as frob in rustc/library/std/sync/rwlock/tests.rs + +#define N 10 +//#define M 1000 +#define M 100000 + +struct arg { + pthread_rwlock_t *rwlock; +}; + +void *routine(void *arg_raw) { + struct arg *arg = arg_raw; + int status; + + for (uint64_t i = 0; i < M; i++) { + if (random_bool()) { + status = pthread_rwlock_wrlock(arg->rwlock); + ERROR_IF(pthread_rwlock_wrlock, status, != 0); + } else { + status = pthread_rwlock_rdlock(arg->rwlock); + ERROR_IF(pthread_rwlock_rdlock, status, != 0); + } + status = pthread_rwlock_unlock(arg->rwlock); + ERROR_IF(pthread_rwlock_unlock, status, != 0); + } + + return NULL; +} + +int main(void) { + int status; + + pthread_rwlock_t rwlock; + + status = pthread_rwlock_init(&rwlock, NULL); + ERROR_IF(pthread_rwlock_init, status, != 0); + + pthread_t threads[N]; + struct arg args[N]; + + for (size_t i = 0; i < N; i++) { + args[i] = (struct arg){ .rwlock = &rwlock }; + + status = pthread_create(&threads[i], NULL, routine, &args[i]); + ERROR_IF(pthread_create, status, != 0); + } + + for (size_t i = 0; i < N; i++) { + status = pthread_join(threads[i], NULL); + ERROR_IF(pthread_join, status, != 0); + } + + status = pthread_rwlock_destroy(&rwlock); + ERROR_IF(pthread_rwlock_destroy, status, != 0); + + return 0; +} diff --git a/tests/pthread/rwlock_trylock.c b/tests/pthread/rwlock_trylock.c new file mode 100644 index 0000000000..8a8274e53f --- /dev/null +++ b/tests/pthread/rwlock_trylock.c @@ -0,0 +1,58 @@ +#include "../test_helpers.h" +#include "common.h" + +#include +#include +#include +#include + +// Adapted from test_rwlock_try_write in rustc/library/std/sync/rwlock/tests.rs + +int main(void) { + int status; + pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + + pthread_rwlockattr_t attr; + status = pthread_rwlockattr_init(&attr); + ERROR_IF(pthread_rwlockattr_init, status, != 0); + + // Call setpshared twice to check both constants work. + status = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + ERROR_IF(pthread_rwlockattr_setpshared, status, != 0); + status = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); + ERROR_IF(pthread_rwlockattr_setpshared, status, != 0); + + status = pthread_rwlock_init(&rwlock, &attr); + ERROR_IF(pthread_rwlock_init, status, != 0); + + status = pthread_rwlockattr_destroy(&attr); + ERROR_IF(pthread_rwlockattr_destroy, status, != 0); + + status = pthread_rwlock_rdlock(&rwlock); + ERROR_IF(pthread_rwlock_rdlock, status, != 0); + + status = pthread_rwlock_trywrlock(&rwlock); + UNEXP_IF(pthread_rwlock_trywrlock, status, != EBUSY); + + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 200 * 1000000; + status = pthread_rwlock_timedwrlock(&rwlock, &ts); + UNEXP_IF(pthread_rwlock_timedwrlock, status, != ETIMEDOUT); + + status = pthread_rwlock_unlock(&rwlock); + ERROR_IF(pthread_rwlock_unlock, status, != 0); + + status = pthread_rwlock_trywrlock(&rwlock); + ERROR_IF(pthread_rwlock_rdlock, status, != 0); + + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 200 * 1000000; + status = pthread_rwlock_timedrdlock(&rwlock, &ts); + UNEXP_IF(pthread_rwlock_timedrdlock, status, != ETIMEDOUT); + + status = pthread_rwlock_destroy(&rwlock); + ERROR_IF(pthread_rwlock_destroy, status, != 0); + + return 0; +} diff --git a/tests/pthread/thread_fork.c b/tests/pthread/thread_fork.c new file mode 100644 index 0000000000..e4b0ee9c2e --- /dev/null +++ b/tests/pthread/thread_fork.c @@ -0,0 +1,51 @@ +/// test fork inside thread + +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define NUM_THREADS 4 +#define ITERATIONS 3 + + +void* thread_func(void* arg) { + long tid = (long)arg; + + for (int i = 0; i < ITERATIONS; i++) { + printf("thread %ld loop %i\n", tid, i); + + pid_t pid = fork(); + ERROR_IF(fork, pid, < 0); + + if (pid == 0) { + _exit(0); + } else { + waitpid(pid, NULL, 0); + } + + usleep(1000); + } + + return NULL; +} + +int main() { + pthread_t threads[NUM_THREADS]; + int status; + + for (long i = 0; i < NUM_THREADS; i++) { + status = pthread_create(&threads[i], NULL, thread_func, (void*)i); + ERROR_IF(pthread_create, status, != 0); + } + + printf("joining threads\n"); + for (int i = 0; i < NUM_THREADS; i++) { + pthread_join(threads[i], NULL); + } + + return 0; +} diff --git a/tests/pthread/timedwait.c b/tests/pthread/timedwait.c new file mode 100644 index 0000000000..d0d909b67e --- /dev/null +++ b/tests/pthread/timedwait.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +pthread_mutex_t lock; +pthread_cond_t cond; +struct timespec start_time; +int ready = 0; + +struct timespec get_timeout(long msec) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += msec * 1000000; + return ts; +} + +void print_timed(char* msg) { + struct timespec end_time; + clock_gettime(CLOCK_MONOTONIC, &end_time); + printf("[%.3f]: ", (end_time.tv_nsec - start_time.tv_nsec) / 1000000000.0 + + (end_time.tv_sec - start_time.tv_sec)); + printf("%s\n", msg); + fflush(NULL); +} + +void* signaler_thread(void* arg) { + (void)arg; + + usleep(150000); // 150ms + ready = 1; + print_timed("signaler_thread"); + pthread_cond_signal(&cond); + return NULL; +} + +void test_timeout_case() { + pthread_mutex_lock(&lock); + struct timespec ts = get_timeout(500); + print_timed("test_timeout_case start"); + int rc = pthread_cond_timedwait(&cond, &lock, &ts); + UNEXP_IF(pthread_cond_timedwait, rc, != ETIMEDOUT); + print_timed("test_timeout_case end"); + pthread_mutex_unlock(&lock); +} + +void test_success_case() { + ready = 0; + pthread_t thread; + pthread_create(&thread, NULL, signaler_thread, NULL); + pthread_mutex_lock(&lock); + struct timespec ts = get_timeout(50000); + print_timed("test_success_case start"); + while (!ready) { + print_timed("test_success_case waiting"); + int rc = pthread_cond_timedwait(&cond, &lock, &ts); + UNEXP_IF(pthread_cond_timedwait, rc, != 0); + } + print_timed("test_success_case end"); + pthread_mutex_unlock(&lock); + pthread_join(thread, NULL); +} + +int main() { + clock_gettime(CLOCK_MONOTONIC, &start_time); + if (pthread_mutex_init(&lock, NULL) != 0) { + perror("mutex init failed"); + return 1; + } + if (pthread_cond_init(&cond, NULL) != 0) { + perror("cond init failed"); + return 1; + } + test_timeout_case(); + // TODO: "rlct_clone not implemented for aarch64 yet" +#if defined(__linux__) && defined(__aarch64__) + printf("test_success_case skipped"); +#else + test_success_case(); +#endif + return 0; +} diff --git a/tests/pthread/timeout.c b/tests/pthread/timeout.c new file mode 100644 index 0000000000..eea85e1380 --- /dev/null +++ b/tests/pthread/timeout.c @@ -0,0 +1,91 @@ +#include "../test_helpers.h" +#include "common.h" + +#include +#include +#include +#include +#include + +#include +#include + +struct arg { + int status; + bool completed; + pthread_barrier_t barrier; + pthread_mutex_t mutex; +}; + +void *routine(void *arg_raw) { + struct arg *arg = (struct arg *)arg_raw; + + int barrier_status = pthread_barrier_wait(&arg->barrier); + + if (barrier_status != 0 && barrier_status != PTHREAD_BARRIER_SERIAL_THREAD) { + arg->status = barrier_status; + fputs("failed to wait for barrier\n", stderr); + return NULL; + } + puts("thread waited"); + + struct timespec abstime; + if ((arg->status = clock_gettime(CLOCK_MONOTONIC, &abstime)) != 0) { + fputs("failed to get current time\n", stderr); + return NULL; + } + abstime.tv_sec += 1; + + if ((arg->status = pthread_mutex_timedlock(&arg->mutex, &abstime)) != ETIMEDOUT) { + fputs("failed to fail at locking mutex\n", stderr); + return NULL; + } + arg->status = 0; + + return NULL; +} + +int main(void) { + int status; + + struct arg arg; + arg.completed = false; + + status = pthread_barrier_init(&arg.barrier, NULL, 2); + ERROR_IF2(pthread_barrier_init, status, != 0); + + status = pthread_mutex_init(&arg.mutex, NULL); + ERROR_IF2(pthread_mutex_init, status, != 0); + + status = pthread_mutex_trylock(&arg.mutex); + ERROR_IF2(pthread_mutex_trylock, status, != 0); + + pthread_t thread; + status = pthread_create(&thread, NULL, routine, &arg); + ERROR_IF2(pthread_create, status, != 0); + + status = pthread_barrier_wait(&arg.barrier); + if (status != PTHREAD_BARRIER_SERIAL_THREAD) { + ERROR_IF2(pthread_barrier_wait, status, != 0); + } + puts("main waited"); + + status = pthread_join(thread, NULL); + ERROR_IF2(pthread_join, status, != 0); + + status = pthread_mutex_unlock(&arg.mutex); + ERROR_IF2(pthread_mutex_unlock, status, != 0); + + if (arg.status != 0) { + fprintf(stderr, "thread failed: %s\n", strerror(arg.status)); + return EXIT_FAILURE; + } + + status = pthread_mutex_destroy(&arg.mutex); + ERROR_IF2(pthread_mutex_destroy, status, != 0); + + status = pthread_barrier_destroy(&arg.barrier); + ERROR_IF2(pthread_barrier_destroy, status, != 0); + + return EXIT_SUCCESS; +} diff --git a/tests/pthread/tls.c b/tests/pthread/tls.c new file mode 100644 index 0000000000..21a4a614c8 --- /dev/null +++ b/tests/pthread/tls.c @@ -0,0 +1,99 @@ +#include +// PTHREAD_KEYS_MAX, PTHREAD_DESTRUCTOR_ITERATIONS +#include +#include +#include +#include +#include + +pthread_key_t key_a, key_b, key_c, key_d, key_e; +size_t total_destructor_runs = 0; + +void drop_key_a(void *val_a) { + assert(pthread_getspecific(key_a) == NULL && val_a == (void *)0xaaaa); + total_destructor_runs++; + + void *val_b = pthread_getspecific(key_b); + printf("drop_key_a(a=%p, b=%p)\n", val_a, val_b); + + if (total_destructor_runs <= 2) { + pthread_setspecific(key_a, val_a); + return; + } + + if (val_b != NULL) { + assert(val_b == (void *)0xbbbb); + } else { + pthread_setspecific(key_b, (void *)0xbbbb); + } +} + +void drop_key_b(void *val_b) { + assert(pthread_getspecific(key_b) == NULL && val_b == (void *)0xbbbb); + total_destructor_runs++; + + void *val_a = pthread_getspecific(key_a); + printf("drop_key_b(a=%p, b=%p)\n", val_a, val_b); + + if (total_destructor_runs <= 2) { + pthread_setspecific(key_b, val_b); + return; + } + + if (val_a != NULL) { + assert(val_a == (void *)0xaaaa); + } else { + pthread_setspecific(key_a, (void *)0xaaaa); + } +} + +void drop_unreachable(void *val_c) { + // Should never be called. + (void)val_c; + assert(false); +} + +void drop_key_d(void *val_d) { + assert(pthread_getspecific(key_d) == NULL && val_d == (void *)0xdddd); + // [`pthread_key_{create,setspecific,getspecific,delete}`] can still be used + // inside the destructor itself. + printf("drop_key_d(d=%p)\n", val_d); + pthread_key_t key_g; + assert(!pthread_key_create(&key_g, drop_unreachable)); + assert(!pthread_setspecific(key_g, &key_g)); + assert(pthread_getspecific(key_g) == &key_g); + assert(!pthread_key_delete(key_g)); +} + +void *test_tls_destructors(void *arg) { + (void)arg; + assert(!pthread_key_create(&key_a, drop_key_a)); + assert(!pthread_key_create(&key_b, drop_key_b)); + assert(!pthread_key_create(&key_c, drop_unreachable)); + assert(!pthread_key_create(&key_d, drop_key_d)); + assert(!pthread_key_create(&key_e, drop_unreachable)); + assert(!pthread_setspecific(key_a, (void *)0xaaaa)); + assert(!pthread_setspecific(key_b, (void *)0xbbbb)); + assert(!pthread_setspecific(key_c, (void *)0xcccc)); + assert(!pthread_setspecific(key_d, (void *)0xdddd)); + assert(!pthread_key_delete(key_c)); + return NULL; +} + +int main(void) { + pthread_key_t key_f; + assert(!pthread_key_create(&key_f, NULL)); + assert(!pthread_setspecific(key_f, &key_f)); + assert(pthread_getspecific(key_f) == &key_f); + assert(!pthread_key_delete(key_f)); + + pthread_t t1; + assert(!pthread_create(&t1, NULL, test_tls_destructors, NULL)); + assert(!pthread_join(t1, NULL)); + printf("total_destructor_iterations: %zu\n", total_destructor_runs); + // There are two destructors (i.e., `drop_key_{a, b}`), so the total number + // of iterations will be `2 * PTHREAD_DESTRUCTOR_ITERATIONS`. + assert((total_destructor_runs / 2) == PTHREAD_DESTRUCTOR_ITERATIONS); + + return EXIT_SUCCESS; +} diff --git a/tests/ptrace.c b/tests/ptrace.c new file mode 100644 index 0000000000..23d3c833f1 --- /dev/null +++ b/tests/ptrace.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#ifdef __linux__ + +#define SYS_write 1 + +#endif +#ifdef __redox__ + +#define SYS_write 0x21000004 + +#endif + +int main() { + int pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + // Test behavior on Redox when TRACEME hasn't been activated + // before waitpid is invoked! + usleep(100000); + + int result = ptrace(PTRACE_TRACEME, 0, NULL, NULL); + ERROR_IF(ptrace, result, == -1); + UNEXP_IF(ptrace, result, != 0); + + // Alert parent: I'm ready + result = raise(SIGSTOP); + ERROR_IF(raise, result, == -1); + UNEXP_IF(raise, result, != 0); + + puts("This is printed to STDOUT."); + puts("Or, at least, that's what I thought."); + puts("But all write(...) syscalls are actually redirected to STDERR by the tracer."); + puts("Big surprise, right!"); + } else { + // Wait for child process to be ready + int result = waitpid(pid, NULL, 0); + ERROR_IF(waitpid, result, == -1); + + int status; + while (true) { + // puts("----- Pre-syscall -----"); + result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL); + ERROR_IF(ptrace, result, == -1); + UNEXP_IF(ptrace, result, != 0); + // puts("Wait..."); + result = waitpid(pid, &status, 0); + ERROR_IF(waitpid, result, == -1); + if (WIFEXITED(status)) + break; + + struct user_regs_struct regs; + // puts("Get regs"); + result = ptrace(PTRACE_GETREGS, pid, NULL, ®s); + ERROR_IF(ptrace, result, == -1); + + if (regs.orig_rax == SYS_write || regs.orig_rax == SYS_write) { + regs.rdi = 2; + puts("Set regs"); + result = ptrace(PTRACE_SETREGS, pid, NULL, ®s); + ERROR_IF(ptrace, result, == -1); + } + + // puts("Post-syscall"); + result = ptrace(PTRACE_SYSCALL, pid, NULL, NULL); + ERROR_IF(ptrace, result, == -1); + UNEXP_IF(ptrace, result, != 0); + // puts("Wait..."); + result = waitpid(pid, &status, 0); + ERROR_IF(waitpid, result, == -1); + if (WIFEXITED(status)) + break; + } + printf("Child exited with status %d\n", WEXITSTATUS(status)); + } +} diff --git a/tests/pty/forkpty.c b/tests/pty/forkpty.c new file mode 100644 index 0000000000..740acd457a --- /dev/null +++ b/tests/pty/forkpty.c @@ -0,0 +1,38 @@ +/* Test of pty.h and forkpty function. + Copyright (C) 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Simon Josefsson , 2009. */ + +#include +#include +#include + +int main () { + int res = 0; + int amaster = 0; + + res = forkpty (&amaster, NULL, NULL, NULL); + if (res == 0) { + printf("This is child process\n"); + } else if (res > 0) { + printf("This is parent process\n"); + wait(NULL); + } else { + printf ("forkpty returned %d\n", res); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/tests/pwd.c b/tests/pwd.c new file mode 100644 index 0000000000..7d3942d930 --- /dev/null +++ b/tests/pwd.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +void print(struct passwd *pwd) { + printf("pw_name: %s\n", pwd->pw_name); + printf("pw_password: %s\n", pwd->pw_passwd); + printf("pw_uid: %u\n", pwd->pw_uid); + printf("pw_gid: %u\n", pwd->pw_gid); + printf("pw_gecos: %s\n", pwd->pw_gecos); + printf("pw_dir: %s\n", pwd->pw_dir); + printf("pw_shell: %s\n", pwd->pw_shell); +} + +int main(void) { + puts("--- Checking getpwuid ---"); + errno = 0; + struct passwd *pwd = getpwuid(0); + if (errno != 0) { + perror("getpwuid"); + exit(EXIT_FAILURE); + } + if (pwd != NULL) { + print(pwd); + } + + puts("--- Checking getpwnam ---"); + errno = 0; + pwd = getpwnam("nobody"); + if (errno != 0) { + perror("getpwnam"); + exit(EXIT_FAILURE); + } + if (pwd != NULL) { + print(pwd); + } + + puts("--- Checking getpwuid_r ---"); + struct passwd pwd2; + struct passwd* result; + char* buf = malloc(300); + if (getpwuid_r(0, &pwd2, buf, 100, &result) < 0) { + perror("getpwuid_r"); + free(buf); + exit(EXIT_FAILURE); + } + if (result != NULL) { + if (result != &pwd2) { + free(buf); + exit(EXIT_FAILURE); + } + print(&pwd2); + } + + puts("--- Checking getpwnam_r ---"); + if (getpwnam_r("nobody", &pwd2, buf, 300, &result) < 0) { + perror("getpwuid_r"); + free(buf); + exit(EXIT_FAILURE); + } + if (result != NULL) { + if (result != &pwd2) { + free(buf); + exit(EXIT_FAILURE); + } + print(&pwd2); + } + free(buf); + + puts("--- Checking getpwuid_r error handling ---"); + char buf2[1]; + if (getpwuid_r(0, &pwd2, buf2, 1, &result) == 0) { + puts("This shouldn't have succeeded, but it did!"); + exit(EXIT_FAILURE); + } + if (errno != ERANGE) { + perror("getpwuid_r"); + exit(EXIT_FAILURE); + } + puts("Returned ERANGE because the buffer was too small 👍"); + + errno = 0; + + struct passwd *entry = NULL; + for (int i = 1; (entry = getpwent()); ++i) { + int backup = errno; + printf("--- getpwent #%d ---\n", i); + if (backup != 0) { + errno = backup; + perror("getpwent"); + exit(EXIT_FAILURE); + } + print(entry); + } + puts("--- getpwent #1 (rewind) ---"); + setpwent(); + entry = getpwent(); + perror("getpwent"); + print(entry); + + endpwent(); +} diff --git a/tests/regex.c b/tests/regex.c new file mode 100644 index 0000000000..894781e447 --- /dev/null +++ b/tests/regex.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + regex_t regex; + char error_buf[256]; + + int error = regcomp(®ex, "h.llo \\(w.rld\\)", REG_ICASE); + if (error) { + regerror(error, ®ex, error_buf, 255); + error_buf[255] = 0; + printf("regcomp error: %d = %s\n", error, error_buf); + exit(EXIT_FAILURE); + } + + regmatch_t matches[3] = {{0}}; + + error = regexec(®ex, "Hey, how are you? Hello? Hallo Wurld??", 3, matches, 0); + + regfree(®ex); + + if (error) { + regerror(error, ®ex, error_buf, 255); + printf("regexec error: %d = %s\n", error, error_buf); + exit(EXIT_FAILURE); + } + + for (int group = 0; group < 3; group += 1) { + printf("Matching group: %d - %d\n", matches[group].rm_so, matches[group].rm_eo); + } +} diff --git a/tests/sa_restart.c b/tests/sa_restart.c new file mode 100644 index 0000000000..75b086813f --- /dev/null +++ b/tests/sa_restart.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +void action(int sig, siginfo_t *info, void *context) { + assert (sig == SIGUSR1); + (void)info; + (void)context; + char *msg = "Signal handler\n"; + write(1, msg, strlen(msg)); + _exit(0); +} + +int main(void) { + int status; + + struct sigaction act; + act.sa_sigaction = action; + act.sa_flags = SA_RESTART; + sigemptyset(&act.sa_mask); + + status = sigaction(SIGUSR1, &act, NULL); + ERROR_IF(sigaction, status, == -1); + + int fds[2]; + status = pipe(fds); + ERROR_IF(pipe, status, == -1); + + int parent = getpid(); + + status = fork(); + ERROR_IF(fork, status, == -1); + + if (status == 0) { + for (int i = 0; i < 1000; i++) { + status = kill(parent, SIGUSR1); + ERROR_IF(kill, status, == -1); + } + return 0; + } + + char buffer[1]; + status = read(fds[0], buffer, 1); + ERROR_IF(read, status, == -1); + + return 1; +} diff --git a/tests/select.c b/tests/select.c new file mode 100644 index 0000000000..dfe7161c2a --- /dev/null +++ b/tests/select.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int file_test(void) { + int fd = open("select.c", 0, 0); + if (fd < 0) { + perror("open"); + return -1; + } + + printf("Testing select on file\n"); + + fd_set read; + FD_ZERO(&read); + FD_SET(fd, &read); + + printf("Is set before? %d\n", FD_ISSET(fd, &read)); + + int nfds = select(fd + 1, &read, NULL, NULL, NULL); + if (nfds < 0) { + perror("select"); + return 1; + } + printf("Amount of things ready: %d\n", nfds); + + printf("Is set after? %d\n", FD_ISSET(fd, &read)); + + close(fd); + + return 0; +} + +int pipe_test(void) { + int pipefd[2]; + if (pipe2(pipefd, O_NONBLOCK) < 0) { + perror("pipe"); + return 1; + } + + char c = 'c'; + if (write(pipefd[1], &c, sizeof(c)) < 0) { + perror("write"); + return 1; + } + + printf("Testing select on pipe\n"); + + fd_set read; + FD_ZERO(&read); + FD_SET(pipefd[0], &read); + + printf("Is set before? %d\n", FD_ISSET(pipefd[0], &read)); + + int nfds = select(pipefd[0] + 1, &read, NULL, NULL, NULL); + if (nfds < 0) { + perror("select"); + return 1; + } + printf("Amount of things ready: %d\n", nfds); + + printf("Is set after? %d\n", FD_ISSET(pipefd[0], &read)); + + close(pipefd[0]); + close(pipefd[1]); + + return 0; +} + +int main(void) { + if (file_test()) { + return 1; + } + + if (pipe_test()) { + return 1; + } + + return 0; +} diff --git a/tests/setjmp.c b/tests/setjmp.c new file mode 100644 index 0000000000..3abc56f804 --- /dev/null +++ b/tests/setjmp.c @@ -0,0 +1,14 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + jmp_buf buf; + if (setjmp(buf)) { + puts("hi from jump"); + } else { + puts("jumping..."); + longjmp(buf, 0); + } +} diff --git a/tests/sharedlib.c b/tests/sharedlib.c new file mode 100644 index 0000000000..fe2dbf8256 --- /dev/null +++ b/tests/sharedlib.c @@ -0,0 +1,10 @@ +#include + +int global_var = 42; +_Thread_local int tls_var = 21; + +void print() +{ + fprintf(stdout, "sharedlib: global_var == %d\n", global_var); + fprintf(stdout, "sharedlib: tls_var == %d\n", tls_var); +} diff --git a/tests/sigaction.c b/tests/sigaction.c new file mode 100644 index 0000000000..b332f3902d --- /dev/null +++ b/tests/sigaction.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +void handler1(int sig) { + assert(sig == SIGUSR1); + char *str = "Signal handler1 called!\n"; + write(STDOUT_FILENO, str, strlen(str)); +} + +sigset_t the_set = { 0 }; + +void handler2(int sig, siginfo_t *info, void *context_raw) { + assert(sig == SIGUSR1); + char *str = "Signal handler2 called!\n"; + write(STDOUT_FILENO, str, strlen(str)); + + assert(info != NULL); + assert(info->si_signo == SIGUSR1); +#ifndef __linux + // TODO: SI_TKILL? + assert(info->si_code == SI_USER); + assert(info->si_pid == getpid()); + assert(info->si_uid == getuid()); +#endif + + ucontext_t *context = context_raw; + assert(context != NULL); +#ifndef __linux__ // TODO + assert(memcmp(&context->uc_sigmask, &the_set, sizeof(sigset_t))); + assert(context->uc_link == NULL); +#endif +} + +int main(void) { + struct sigaction sa1 = { .sa_handler = handler1 }; + struct sigaction sa2 = { .sa_sigaction = handler2, .sa_flags = SA_SIGINFO }; + struct sigaction saold = {0}; + + sigemptyset(&sa1.sa_mask); + sigemptyset(&sa2.sa_mask); + + int status = sigprocmask(SIG_SETMASK, NULL, &the_set); + ERROR_IF(sigprocmask, status, == -1); + + int rcode = sigaction(SIGUSR1, &sa1, NULL); + ERROR_IF(signal, rcode, != 0); + + puts("Raising..."); + + int raise_status = raise(SIGUSR1); + ERROR_IF(raise, raise_status, < 0); + + rcode = sigaction(SIGUSR1, &sa2, &saold); + ERROR_IF(signal, rcode, != 0); + ERROR_IF(signal, saold.sa_handler, != sa1.sa_handler); + + puts("Raising..."); + + raise_status = raise(SIGUSR1); + ERROR_IF(raise, raise_status, < 0); + + puts("Raised."); +} diff --git a/tests/sigaltstack.c b/tests/sigaltstack.c new file mode 100644 index 0000000000..c0dbe7a188 --- /dev/null +++ b/tests/sigaltstack.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +size_t stack_size; +void *stack_base; + +volatile sig_atomic_t counter = 0; + +void action(int sig, siginfo_t *info, void *context_raw) { + assert(sig == SIGUSR2); + + ucontext_t *context = context_raw; + assert(context->uc_stack.ss_sp == stack_base); + assert(context->uc_stack.ss_size == stack_size); + + // TODO: Technically an implementation detail, but safe to check here. + assert((size_t)info >= (size_t)stack_base); + assert((size_t)info <= ((size_t)stack_base + stack_size)); + + int c = counter++; + + char str[] = "SIGUSR2 handlerXX\n"; + size_t len = strlen(str); + str[len - 2] = '0' + (c % 10); + str[len - 3] = '0' + ((c / 10) % 10); + write(STDOUT_FILENO, str, len); + + if (c < 100) { + raise(SIGUSR2); + } +} + +int main(void) { + int status; + + stack_size = 1024 * 1024; // TODO? + // in glibc MINSIGSTKSZ is a sysconf(MINSIGSTKSZ) macro + assert(stack_size >= (size_t)MINSIGSTKSZ * 100); + stack_base = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ERROR_IF(mmap, stack_base, == MAP_FAILED); + + stack_t old_stack; + stack_t stack = (stack_t) { .ss_sp = stack_base, .ss_size = stack_size, .ss_flags = 0 }; + + status = sigaltstack(&stack, &old_stack); + ERROR_IF(sigaltstack, status, == -1); + + assert((old_stack.ss_flags & SS_ONSTACK) == 0); + + stack_t same; + + status = sigaltstack(&old_stack, &same); + ERROR_IF(sigaltstack, status, == -1); + assert(same.ss_sp == stack.ss_sp); + assert(same.ss_size == stack.ss_size); + assert((same.ss_flags & SS_ONSTACK) == 0); + + status = sigaltstack(&stack, NULL); + ERROR_IF(sigaltstack, status, == -1); + + status = sigaltstack(NULL, &same); + ERROR_IF(sigaltstack, status, == -1); + assert(same.ss_sp == stack.ss_sp); + assert(same.ss_size == stack.ss_size); + assert((same.ss_flags & SS_ONSTACK) == 0); + + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; + sa.sa_sigaction = action; + + status = sigaction(SIGUSR2, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + raise(SIGUSR2); + + return 0; +} diff --git a/tests/sigchld.c b/tests/sigchld.c new file mode 100644 index 0000000000..6822bc01de --- /dev/null +++ b/tests/sigchld.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +volatile sig_atomic_t atomic = 0; +volatile sig_atomic_t atomic2 = 0; + +void action(int sig, siginfo_t *info, void *context) { + (void)context; + + assert(sig == SIGCHLD); + assert(info != NULL); + atomic += 1; +} +void handler(int sig) { + assert(sig == SIGPIPE); + atomic2 += 1; +} + +int main(void) { + int fds[2]; + int status = pipe(fds); + ERROR_IF(pipe, status, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + sigset_t set; + sigemptyset(&set); + status = sigprocmask(SIG_SETMASK, &set, NULL); + ERROR_IF(sigprocmask, status, == -1); + + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_sigaction = action; + status = sigaction(SIGCHLD, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + sa.sa_handler = handler; + status = sigaction(SIGPIPE, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + if (child == 0) { + status = close(fds[1]); + ERROR_IF(close, status, == -1); + + char buf[1]; + status = read(fds[0], buf, 1); + ERROR_IF(read, status, == -1); + puts("Child exiting."); + return EXIT_SUCCESS; + } else { + int waitpid_stat; + + close(fds[0]); + ERROR_IF(close, status, == -1); + + puts("Sending SIGSTOP..."); + status = kill(child, SIGSTOP); + ERROR_IF(kill, status, == -1); + + while (atomic == 0) { + status = sched_yield(); + ERROR_IF(sched_yield, status, == -1); + } + puts("First handler ran, checking status."); + + status = waitpid(child, &waitpid_stat, WUNTRACED); + ERROR_IF(waitpid, status, == -1); + assert(WIFSTOPPED(waitpid_stat)); + assert(WSTOPSIG(waitpid_stat) == SIGSTOP); + puts("Correct, sending SIGCONT..."); + + status = write(fds[1], "C", 1); + ERROR_IF(write, status, == -1); + + status = kill(child, SIGCONT); + ERROR_IF(kill, status, == -1); + + while (atomic == 1) { + status = sched_yield(); + ERROR_IF(sched_yield, status, == -1); + } + puts("Second handler ran, checking status."); + + status = waitpid(child, &waitpid_stat, 0); + ERROR_IF(waitpid, status, == -1); + assert(WIFEXITED(waitpid_stat)); + assert(WEXITSTATUS(waitpid_stat) == 0); + puts("Child exited."); + + puts("Writing to (broken) pipe."); + status = write(fds[1], "B", 1); + assert(status == -1); + assert(errno == EPIPE); + + /*while (atomic2 == 0) { + status = sched_yield(); + ERROR_IF(sched_yield, status, == -1); + }*/ + puts("SIGSTOP handler successfully executed."); + return EXIT_SUCCESS; + } +} diff --git a/tests/signal.c b/tests/signal.c new file mode 100644 index 0000000000..cdc3004821 --- /dev/null +++ b/tests/signal.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +void handler(int sig) { + UNEXP_IF(signal, sig, != SIGUSR1); + puts("Signal handler called!"); +} + +int main(void) { + void (*signal_status)(int) = signal(SIGUSR1, handler); + ERROR_IF(signal, signal_status, == SIG_ERR); + signal_status = signal(SIGUSR1, handler); + ERROR_IF(signal, signal_status, != handler); + + puts("Raising..."); + + int raise_status = raise(SIGUSR1); + ERROR_IF(raise, raise_status, < 0); + + puts("Raised."); +} diff --git a/tests/signals/kill-child.c b/tests/signals/kill-child.c new file mode 100644 index 0000000000..c2644bd670 --- /dev/null +++ b/tests/signals/kill-child.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +/* + * Test signal catching when being signalled from parent. + * Skip SIGKILL and SIGSTOP as these are not catchable. + */ + +volatile sig_atomic_t sig_handled = 0; + +void sig_handler(int signo) +{ + (void) signo; + sig_handled = 1; +} + +void child_proc(int signum) +{ + sigset_t sig_set; + int status; + + sig_handled = 0; + + status = sigemptyset(&sig_set); + ERROR_IF(sigemptyset, status, == -1); + + status = sigaddset(&sig_set, signum); + ERROR_IF(sigaddset, status, == -1); + + struct sigaction act; + act.sa_handler = sig_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(signum, &act, NULL); + + status = usleep(200000); + ERROR_IF(usleep, status, == 0); + + assert(sig_handled != 0); + + exit(EXIT_SUCCESS); +} + +void parent(int signum, pid_t pid) +{ + int status; + + usleep(100000); + status = kill(pid, signum); + ERROR_IF(kill, status, != 0); + + pid = wait(&status); + ERROR_IF(wait, pid, == (pid_t)-1); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) + { + printf("Child did not exit normally.\n"); + exit(EXIT_FAILURE); + } + else + { + return; + } +} + +void kill_child(int signum) +{ + int pid; + + if ((pid = fork()) == 0) + { + child_proc(signum); + } + else + { + parent(signum, pid); + } +} + +int main() +{ + for (long unsigned int i = 1; i < sizeof(signals_list)/sizeof(signals_list[0]); i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP) + { + continue; + } + printf("Testing for signal %s (%d)\n", strsignal(sig), sig); + kill_child(sig); + } + return EXIT_SUCCESS; +} diff --git a/tests/signals/kill-group.c b/tests/signals/kill-group.c new file mode 100644 index 0000000000..80059ef874 --- /dev/null +++ b/tests/signals/kill-group.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// this test is to make sure that when a negative pid is supplied to kill, all the processes in that group will be killed + +int handler_called = 0; +void sig_handler(int signo) +{ + (void) signo; + handler_called = 1; + return; +} + +int kill_group(int signum) +{ + int pgrp; + struct sigaction act; + int status; + + act.sa_handler=sig_handler; + act.sa_flags=0; + status = sigemptyset(&act.sa_mask); + ERROR_IF(sigemptyset, status, == -1); + + status = sigaction(signum, &act, 0); + ERROR_IF(sigaction, status, == -1); + + status = getpgrp(); + pgrp = status; + ERROR_IF(getpgrp, status, == -1); + + status = kill(-pgrp, signum); + ERROR_IF(kill, status, != 0); + + ERROR_IF(kill, handler_called, !=1); + handler_called = 0; + + return EXIT_SUCCESS; +} + +int main() +{ + // Ensure we don't kill what was already in the process group. + int status = setpgid(0, 0); + ERROR_IF(setpgid, status, == -1); + + for (unsigned int i = 0; i < sizeof(signals_list)/sizeof(signals_list[0]); i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP) + { + continue; + } + kill_group(sig); + } + return EXIT_SUCCESS; +} diff --git a/tests/signals/kill-invalid.c b/tests/signals/kill-invalid.c new file mode 100644 index 0000000000..8cf8e7d3e6 --- /dev/null +++ b/tests/signals/kill-invalid.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +//this test ensures that sending an invalid signal will set errno to esrch + +int main() +{ + + /* + * ESRCH + */ + int status; + status = kill(999999, 0); + ERROR_IF(kill, status, != -1); + ERROR_IF(kill, errno, != ESRCH); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/kill-permission.c b/tests/signals/kill-permission.c new file mode 100644 index 0000000000..eab0aef545 --- /dev/null +++ b/tests/signals/kill-permission.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +// makes sure that a process is not killed if the user doesn't have permission to kill the process + +int main(void) +{ + int status; + // This is added in case user is root. If user is normal user, then it has no effect on the tests + setuid(1000); + status = kill(1, 0); + ERROR_IF(kill, status, != -1); + ERROR_IF(kill, errno, != EPERM); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/kill-self.c b/tests/signals/kill-self.c new file mode 100644 index 0000000000..f05ec12a9f --- /dev/null +++ b/tests/signals/kill-self.c @@ -0,0 +1,56 @@ +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +/* + * Test signal catching when signalling self. + * Ensure all signals can be caught (other than SIGKILL and SIGSTOP). + */ + +volatile sig_atomic_t handler_called = 0; + +void sig_handler(int sig) +{ + (void) sig; + handler_called = 1; +} + +int kill_self(int sig) +{ + struct sigaction act; + int status; + + handler_called = 0; + + act.sa_handler = sig_handler; + act.sa_flags = 0; + + status = sigemptyset(&act.sa_mask); + ERROR_IF(sigemptyset, status, == -1); + + status = sigaction(sig, &act, NULL); + ERROR_IF(sigaction, status, == -1); + + status = kill(getpid(), sig); + ERROR_IF(kill, status, != 0); + + assert(handler_called == 1); + + return EXIT_SUCCESS; +} + +int main() +{ + + for (unsigned int i = 1; i < sizeof(signals_list)/sizeof(signals_list[0]); i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP) + { + continue; + } + kill_self(sig); + } + return EXIT_SUCCESS; +} diff --git a/tests/signals/kill0-self.c b/tests/signals/kill0-self.c new file mode 100644 index 0000000000..ad76980233 --- /dev/null +++ b/tests/signals/kill0-self.c @@ -0,0 +1,16 @@ +#include +#include "../test_helpers.h" + +/* + * Send signal 0 to self. This just reports whether a signal can be sent. + */ + +int main() +{ + int status = 0; + + status = kill(getpid(), 0); + ERROR_IF(kill, status, != 0); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/killpg-child.c b/tests/signals/killpg-child.c new file mode 100644 index 0000000000..d1ef669ca1 --- /dev/null +++ b/tests/signals/killpg-child.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// test killing a child process + +void sig_handler (int signo) { + (void) signo; + exit(EXIT_SUCCESS); +} + +int killpg_test2(int signum) +{ + int child_pid, child_pgid; + printf("we are on signal %d\n ", signum); + + if ((child_pid = fork()) == 0) { + /* child here */ + struct sigaction act; + act.sa_handler=sig_handler; + act.sa_flags=0; + sigemptyset(&act.sa_mask); + sigaction(signum, &act, 0); + + /* change child's process group id */ + setpgrp(); + + sigpause(SIGABRT); + + return EXIT_FAILURE; + } else { + /* parent here */ + int i; + sigignore(signum); + + usleep(100000); + + child_pgid = getpgid(child_pid); + ERROR_IF(getpgid, child_pgid, == -1); + + int status; + status = killpg(child_pgid, signum); + ERROR_IF(killpg, status, != 0); + + status = wait(&i); + ERROR_IF(wait, status, == -1); + + if (WEXITSTATUS(i) == EXIT_SUCCESS) { + printf("Child exited normally\n"); + printf("Test PASSED\n"); + return EXIT_SUCCESS; + } else { + printf("Child did not exit normally.\n"); + printf("Test FAILED\n"); + exit(EXIT_FAILURE); + } + } + + return EXIT_FAILURE; +} + +int main(){ + int x; + for (unsigned int i = 1; i < sizeof(signals_list)/sizeof(signals_list[0]); i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCHLD || sig == SIGINT || sig == SIGQUIT) + { + continue; + } + x = killpg_test2(sig); + if (x == EXIT_FAILURE){ + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + diff --git a/tests/signals/killpg-esrch.c b/tests/signals/killpg-esrch.c new file mode 100644 index 0000000000..ab1e62cd96 --- /dev/null +++ b/tests/signals/killpg-esrch.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +// if a user tries to kill a process that does not exist the esrch error will be returned +int main() +{ + + int status; + status = killpg(999999, 0); + ERROR_IF(killpg, status, !=-1); + + ERROR_IF(killpg, errno, != ESRCH); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/killpg-invalid.c b/tests/signals/killpg-invalid.c new file mode 100644 index 0000000000..b1f89dc023 --- /dev/null +++ b/tests/signals/killpg-invalid.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +// the test makes sure that if an invalid signal is passed it will return the EINVAL error +int main() +{ + int pgrp; + + pgrp = getpgrp(); + ERROR_IF(getpgrp, pgrp, == -1); + + int status; + status = killpg(pgrp, -1); + ERROR_IF(killpg, status, != -1); + + ERROR_IF(killpg, errno, != EINVAL); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/killpg-self.c b/tests/signals/killpg-self.c new file mode 100644 index 0000000000..afd4865112 --- /dev/null +++ b/tests/signals/killpg-self.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// Test that the killpg() function shall send signal sig to the process +// group specified by prgp. + +void sig_handler(int signo) +{ + // printf("Caught signal %d being tested!\n", signo); + // printf("Test PASSED\n"); + (void) signo; + return; +} + +int killpg_test1(int signum) +{ + int pgrp; + struct sigaction act; + + act.sa_handler=sig_handler; + act.sa_flags=0; + int status = sigemptyset(&act.sa_mask); + ERROR_IF(sigemptyset, status, == -1); + + status = sigaction(signum, &act, 0); + ERROR_IF(sigaction, status, == -1); + + pgrp = getpgrp(); + ERROR_IF(getpgrp, pgrp, == -1); + + status = killpg(pgrp, signum); + ERROR_IF(killpg, status, != 0); + + return EXIT_SUCCESS; +} + +int main(){ + // UB if pg == 1, so set it here first + int status = setpgid(0, 0); + ERROR_IF(setpgid, status, == -1); + + for (unsigned int i = 0; i < sizeof(signals_list)/sizeof(signals_list[0]); i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP) + { + continue; + } + killpg_test1(sig); + } + return EXIT_SUCCESS; +} + diff --git a/tests/signals/killpg0-self.c b/tests/signals/killpg0-self.c new file mode 100644 index 0000000000..e319660eca --- /dev/null +++ b/tests/signals/killpg0-self.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include "../test_helpers.h" + +// test sending signal 0 to self will killpg + +int main() +{ + // UB if pg == 1, so set it here first + int err = setpgid(0, 0); + ERROR_IF(setpgid, err, == -1); + + int pgrp; + + pgrp = getpgrp(); + ERROR_IF(getpgrp, pgrp, == -1); + + int status; + status = killpg(pgrp, 0); + ERROR_IF(killpg, status, != 0); + + return EXIT_SUCCESS; +} diff --git a/tests/signals/pthread_kill-child.c b/tests/signals/pthread_kill-child.c new file mode 100644 index 0000000000..04eb8a68aa --- /dev/null +++ b/tests/signals/pthread_kill-child.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +//test with pthread_kill to kill a child process + +void * thread_function(void *arg) +{ + /* Does nothing */ + (void) arg; + pthread_exit((void*)0); + + /* To please some compilers */ + return NULL; +} + +int main() +{ + pthread_t child_thread; + pthread_t invalid_tid; + + int rc; + + rc = pthread_create(&child_thread, NULL, + thread_function, NULL); + ERROR_IF(pthread_create, rc, != 0); + + rc = pthread_join(child_thread, NULL); + ERROR_IF(pthread_join, rc, != 0); + + // Now the child_thread exited, it is an invalid tid + memcpy(&invalid_tid, &child_thread, + sizeof(pthread_t)); + usleep(300); + + int status; + status = pthread_kill(invalid_tid, 0); + ERROR_IF(pthread_kill, status, != ESRCH); + + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/tests/signals/pthread_kill-invalid.c b/tests/signals/pthread_kill-invalid.c new file mode 100644 index 0000000000..053f817557 --- /dev/null +++ b/tests/signals/pthread_kill-invalid.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +//test with pthread_kill making sure sending an invalid signal returns einval + +int main() +{ + pthread_t main_thread; + + main_thread = pthread_self(); + + int status; + status = pthread_kill(main_thread, -1); + ERROR_IF(pthread_kill, status, != EINVAL); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/pthread_kill-self.c b/tests/signals/pthread_kill-self.c new file mode 100644 index 0000000000..dcc42b85c2 --- /dev/null +++ b/tests/signals/pthread_kill-self.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +//test pthread_kill on self + +# define INTHREAD 0 +# define INMAIN 1 +# define SIGTOTEST SIGABRT + +int sem1; /* Manual semaphore */ +volatile sig_atomic_t handler_called = 0; +int count = 1; + +struct signal { + int signum; +}; + +void handler() { + handler_called = 1; + return; +} + +void *a_thread_func( void *arg) +{ + + struct sigaction act; + act.sa_flags = 0; + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + sigaction(((struct signal *)arg)->signum, &act, 0); + + sem1=INMAIN; + + while(sem1==INMAIN) + usleep(100000); + + // sleep(50); + + handler_called=-1; + pthread_exit(0); + return NULL; +} + +int pthread_kill_test1(int signum) +{ + pthread_t new_th; + + sem1=INTHREAD; + + struct signal arg; + arg.signum = signum; + + int status; + status = pthread_create(&new_th, NULL, a_thread_func, &arg); + ERROR_IF(pthread_create, status, != 0); + + while(sem1==INTHREAD) + usleep(100000); + + status = pthread_kill(new_th, signum); + ERROR_IF(pthread_kill, status, != 0); + + usleep(100000); + sem1=INTHREAD; + + while(handler_called==0) + usleep(100000); + + ERROR_IF(pthread_kill, handler_called, == -1); + ERROR_IF(pthread_kill, handler_called, == 0); + + handler_called = 0; + return EXIT_SUCCESS; +} + +int main(){ + for (unsigned int i = 0; i < sizeof(signals_list)/sizeof(signals_list[0]); i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP) + { + continue; + } + pthread_kill_test1(sig); + } + return EXIT_SUCCESS; +} + diff --git a/tests/signals/pthread_kill0-self.c b/tests/signals/pthread_kill0-self.c new file mode 100644 index 0000000000..a4723a9e94 --- /dev/null +++ b/tests/signals/pthread_kill0-self.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +// test sending 0 with pthread_kill to self + +int main() +{ + pthread_t main_thread; + + main_thread = pthread_self(); + + int status; + status = pthread_kill(main_thread, 0); + ERROR_IF(pthread_kill, status, != 0); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/raise-compliance.c b/tests/signals/raise-compliance.c new file mode 100644 index 0000000000..6722729398 --- /dev/null +++ b/tests/signals/raise-compliance.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// The raise() function shall send the signal sig to the executing [CX] [Option Start] thread or process. [Option End] If a signal handler is called, the raise() function shall not return until after the signal handler does. + +// [CX] [Option Start] The effect of the raise() function shall be equivalent to calling: pthread_kill(pthread_self(), sig); + +void sig_hand(int i) + +{ + if (i < 1 || i > 32){ + printf("an invalid signal was given: %d", i); + } + static int count = 1; + + count++; + printf("%d \n", count); + if (count == 32) { + printf("reached 32nd signal\n"); + return; + } + else{ + printf("count is %d\n", count); + } +} + +void raise_test(int sig){ + signal(sig, sig_hand); + raise(sig); +} + +int main(void) +{ + for (int i = 0; i < N_SIGNALS; i++) + { + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP) + { + continue; + } + raise_test(sig); + } + return EXIT_SUCCESS; +} + diff --git a/tests/signals/sigaddset-add.c b/tests/signals/sigaddset-add.c new file mode 100644 index 0000000000..bcbad4aba7 --- /dev/null +++ b/tests/signals/sigaddset-add.c @@ -0,0 +1,39 @@ + +#define _OPEN_SYS +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// The sigaddset() function adds the individual signal specified by the signo to the signal set pointed to by set. + +// Applications shall call either sigemptyset() or sigfillset() at least once for each object of type sigset_t prior to any other use of that object. If such an object is not initialized in this way, but is nonetheless supplied as an argument to any of pthread_sigmask(), sigaction(), sigaddset(), sigdelset(), sigismember(), sigpending(), sigprocmask(), sigsuspend(), sigtimedwait(), sigwait(), or sigwaitinfo(), the results are undefined. + +void addset_test(sigset_t *sigset, int signal) +{ + int status; + + status = sigismember(sigset, signal); + ERROR_IF(sigismember, status, != 0); + + status = sigaddset(sigset, signal); + ERROR_IF(sigaddset, status, != 0); + + status = sigismember(sigset, signal); + ERROR_IF(sigismember, status, != 1); +} + +int main() +{ + sigset_t sigset; + + for (int i = 1; i < N_SIGNALS; i++) + { + sigemptyset(&sigset); + + int sig = signals_list[i - 1].signal; + + addset_test(&sigset, sig); + } +} diff --git a/tests/signals/sigdelset-delete.c b/tests/signals/sigdelset-delete.c new file mode 100644 index 0000000000..bfab99121a --- /dev/null +++ b/tests/signals/sigdelset-delete.c @@ -0,0 +1,36 @@ +#define _OPEN_SYS +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// The sigdelset() function deletes the individual signal specified by signo from the signal set pointed to by set. + +// Applications should call either sigemptyset() or sigfillset() at least once for each object of type sigset_t prior to any other use of that object. If such an object is not initialized in this way, but is nonetheless supplied as an argument to any of pthread_sigmask(), sigaction(), sigaddset(), sigdelset(), sigismember(), sigpending(), sigprocmask(), sigsuspend(), sigtimedwait(), sigwait(), or sigwaitinfo(), the results are undefined. + +void delset_test(sigset_t *sigset, int signal) +{ + int status; + status = sigismember(sigset, signal); + ERROR_IF(sigismember, status, != 1); + + status = sigdelset(sigset, signal); + ERROR_IF(sigdelset, status, != 0); + + status = sigismember(sigset, signal); + ERROR_IF(sigismember, status, != 0); +} + +int main() +{ + sigset_t sigset; + + for (int i = 1; i < N_SIGNALS; i++) + { + sigfillset(&sigset); + + int sig = signals_list[i - 1].signal; + delset_test(&sigset, sig); + } +} diff --git a/tests/signals/sigismember-invalid.c b/tests/signals/sigismember-invalid.c new file mode 100644 index 0000000000..80c45fa236 --- /dev/null +++ b/tests/signals/sigismember-invalid.c @@ -0,0 +1,21 @@ +#define _OPEN_SYS +#include +#include +#include +#include +#include "../test_helpers.h" + +// test to make sure that if you pass an invalid signal to sigismember it will return EINVAL + +int main() { + sigset_t sigset; + int status; + + sigfillset(&sigset); + status = sigismember(&sigset, -1); + ERROR_IF(sigismember, status, != -1); + ERROR_IF(sigismember, errno, != EINVAL); + + return EXIT_SUCCESS; + +} \ No newline at end of file diff --git a/tests/signals/sigismember-valid.c b/tests/signals/sigismember-valid.c new file mode 100644 index 0000000000..e6c54f5894 --- /dev/null +++ b/tests/signals/sigismember-valid.c @@ -0,0 +1,42 @@ +#define _OPEN_SYS +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// The sigismember() function shall test whether the signal specified by signo is a member of the set pointed to by set. + +// Applications should call either sigemptyset() or sigfillset() at least once for each object of type sigset_t prior to any other use of that object. If such an object is not initialized in this way, but is nonetheless supplied as an argument to any of pthread_sigmask(), sigaction(), sigaddset(), sigdelset(), sigismember(), sigpending(), sigprocmask(), sigsuspend(), sigtimedwait(), sigwait(), or sigwaitinfo(), the results are undefined. + +void check_full(sigset_t set, int signum) { + if (!sigismember(&set, signum)) { + exit(EXIT_FAILURE); + } + +} + +void check_empty(sigset_t set, int signum) { + if (sigismember(&set, signum)) { + exit(EXIT_FAILURE); + } + +} + + + +int main() { + sigset_t sigset; + + sigfillset(&sigset); + for (int i=1; i + +/* + * This is a test to ensure all required items for signal.h are defined. + * The definitions follow the order described in + * + */ + +void handler(int sig_num) +{ + (void)sig_num; +} + +void action(int sig_num, siginfo_t *info, void *context) +{ + (void)sig_num; + (void)info; + (void)context; +} + +int main() +{ + int (*sh)(int) __attribute__((unused)) = sighold; + int (*sigig)(int) __attribute__((unused)) = sigignore; + int (*sigintr)(int, int) __attribute__((unused)) = siginterrupt; + int (*paws)(int) __attribute__((unused)) = sigpause; + int (*srls)(int) __attribute__((unused)) = sigrelse; + void (*(*sset)(int, void (*)(int)))(int) __attribute__((unused)) = sigset; + + return EXIT_SUCCESS; +} diff --git a/tests/signals/signal-h.c b/tests/signals/signal-h.c new file mode 100644 index 0000000000..8ec09b374d --- /dev/null +++ b/tests/signals/signal-h.c @@ -0,0 +1,232 @@ +#include "../test_helpers.h" +#include + +/* + * This is a test to ensure all required items for signal.h are defined. + * The definitions follow the order described in + * + */ + +void handler(int sig_num) +{ + (void)sig_num; +} + +void action(int sig_num, siginfo_t *info, void *context) +{ + (void)sig_num; + (void)info; + (void)context; +} + +int main() +{ + void (*sig_dfl)(int) __attribute__((unused)) = SIG_DFL; + void (*sig_err)(int) __attribute__((unused)) = SIG_ERR; + void (*sig_ign)(int) __attribute__((unused)) = SIG_IGN; + + pthread_t pthread_num __attribute__((unused)) = 0; + size_t size __attribute__((unused)) = 0; + uid_t uid __attribute__((unused)) = 0; + + sig_atomic_t atomic __attribute__((unused)) = 0; + sigset_t sig_set __attribute__((unused)) ; + pid_t pid __attribute__((unused)) = 0; + + pthread_attr_t *attr __attribute__((unused)) = NULL; + + // struct sigevent sev; + union sigval sv; + + sv.sival_int = (int)0; + sv.sival_ptr = (void *)0; + + // sev.sigev_notify = (int)0; + // sev.sigev_notify = SIGEV_NONE; + // sev.sigev_notify = SIGEV_SIGNAL; + // sev.sigev_notify = SIGEV_THREAD; + + // sev.sigev_signo = (int)0; + // sev.sigev_value = sv; + // sev.sigev_value.sival_int = (int)0; + // sev.sigev_value.sival_ptr = (void *)0; + // sev.sigev_notify_function = (void (*)(union sigval))0; + // sev.sigev_notify_attributes = (pthread_attr_t *)0; + + int rt_sig_num __attribute__((unused)) = SIGRTMIN; + rt_sig_num = SIGRTMAX; + + // rt_sig_num = SIG2STR_MAX; + + // sev.sigev_signo = SIGABRT; + // sev.sigev_signo = SIGALRM; + // sev.sigev_signo = SIGBUS; + // sev.sigev_signo = SIGCHLD; + // sev.sigev_signo = SIGCONT; + // sev.sigev_signo = SIGFPE; + // sev.sigev_signo = SIGHUP; + // sev.sigev_signo = SIGILL; + // sev.sigev_signo = SIGINT; + // sev.sigev_signo = SIGKILL; + // sev.sigev_signo = SIGPIPE; + // sev.sigev_signo = SIGQUIT; + // sev.sigev_signo = SIGSEGV; + // sev.sigev_signo = SIGSTOP; + // sev.sigev_signo = SIGTERM; + // sev.sigev_signo = SIGTSTP; + // sev.sigev_signo = SIGTTIN; + // sev.sigev_signo = SIGTTOU; + // sev.sigev_signo = SIGUSR1; + // sev.sigev_signo = SIGUSR2; + // sev.sigev_signo = SIGWINCH; + // sev.sigev_signo = SIGPOLL; + // sev.sigev_signo = SIGPROF; + // sev.sigev_signo = SIGSYS; + // sev.sigev_signo = SIGTRAP; + // sev.sigev_signo = SIGURG; + // sev.sigev_signo = SIGVTALRM; + // sev.sigev_signo = SIGXCPU; + // sev.sigev_signo = SIGXFSZ; + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_handler = SIG_DFL; + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = (int)0; + sa.sa_sigaction = action; + +#ifndef SA_NOCLDSTOP +#error "Required constant not defined SA_NOCLDSTOP" +#endif +#ifndef SIG_BLOCK +#error "Required constant not defined SIG_BLOCK" +#endif +#ifndef SIG_UNBLOCK +#error "Required constant not defined SIG_UNBLOCK" +#endif +#ifndef SIG_SETMASK +#error "Required constant not defined SIG_SETMASK" +#endif +#ifndef SA_ONSTACK +#error "Required constant not defined SA_ONSTACK" +#endif +#ifndef SA_RESETHAND +#error "Required constant not defined SA_RESETHAND" +#endif +#ifndef SA_RESTART +#error "Required constant not defined SA_RESTART" +#endif +#ifndef SA_SIGINFO +#error "Required constant not defined SA_SIGINFO" +#endif +#ifndef SA_NOCLDWAIT +#error "Required constant not defined SA_NOCLDWAIT" +#endif +#ifndef SA_NODEFER +#error "Required constant not defined SA_NODEFER" +#endif +#ifndef SS_ONSTACK +#error "Required constant not defined SS_ONSTACK" +#endif +#ifndef SS_DISABLE +#error "Required constant not defined SS_DISABLE" +#endif +#ifndef MINSIGSTKSZ +#error "Required constant not defined MINSIGSTKSZ" +#endif +#ifndef SIGSTKSZ +#error "Required constant not defined SIGSTKSZ" +#endif + + // struct ucontext_t uc; + mcontext_t mc __attribute__((unused)); + + // add parts to ucontext_t + // uc.uc_link = NULL; + // uc.uc_sigmask = 0; + // uc.uc_stack = NULL; + // uc.uc_mcontext = NULL; + + + stack_t st __attribute__((unused)); + st.ss_sp = NULL; + st.ss_size = (size_t)0; + st.ss_flags = (int)0; + + siginfo_t si __attribute__((unused)); + si.si_signo = SIGHUP; + si.si_code = (int)0; + si.si_errno = (int)0; + si.si_pid = (pid_t)0; + si.si_uid = (uid_t)0; + si.si_addr = NULL; + si.si_status = (int)0; + si.si_value = sv; + + // si.si_code = ILL_ILLOPC; + // si.si_code = ILL_ILLOPN; + // si.si_code = ILL_ILLADR; + // si.si_code = ILL_ILLTRP; + // si.si_code = ILL_PRVOPC; + // si.si_code = ILL_PRVREG; + // si.si_code = ILL_COPROC; + // si.si_code = ILL_BADSTK; + // si.si_code = FPE_INTDIV; + // si.si_code = FPE_INTOVF; + // si.si_code = FPE_FLTDIV; + // si.si_code = FPE_FLTOVF; + // si.si_code = FPE_FLTUND; + // si.si_code = FPE_FLTRES; + // si.si_code = FPE_FLTINV; + // si.si_code = FPE_FLTSUB; + // si.si_code = SEGV_MAPERR; + // si.si_code = SEGV_ACCERR; + // si.si_code = BUS_ADRALN; + // si.si_code = BUS_ADRERR; + // si.si_code = BUS_OBJERR; + // si.si_code = TRAP_BRKPT; + // si.si_code = TRAP_TRACE; + // si.si_code = CLD_EXITED; + // si.si_code = CLD_KILLED; + // si.si_code = CLD_DUMPED; + // si.si_code = CLD_TRAPPED; + // si.si_code = CLD_STOPPED; + // si.si_code = CLD_CONTINUED; + // si.si_code = SI_USER; + // si.si_code = SI_QUEUE; + // si.si_code = SI_TIMER; + // si.si_code = SI_ASYNCIO; + // si.si_code = SI_MESGQ; + + // void (*(*bs)(int, void (*)(int)))(int) = bsd_signal; + int (*k)(pid_t, int) __attribute__((unused))= kill; + int (*kpg)(pid_t, int) __attribute__((unused))= killpg; + void (*psig)(const siginfo_t *, const char *) __attribute__((unused))= psiginfo; + void (*ps)(int, const char *) __attribute__((unused))= psignal; + int (*ptk)(pthread_t, int) __attribute__((unused))= pthread_kill; + int (*ptsm)(int, const sigset_t *, sigset_t *) __attribute__((unused))= pthread_sigmask; + int (*r)(int) __attribute__((unused))= raise; + // int (*s2s)(int, char*) = sig2str; + int (*sact)(int, const struct sigaction *restrict, + struct sigaction *restrict) __attribute__((unused)) = sigaction; + int (*sas)(sigset_t *, int) __attribute__((unused))= sigaddset; + int (*sastk)(const stack_t *restrict, stack_t *restrict) __attribute__((unused))= sigaltstack; + int (*sds)(sigset_t *, int) __attribute__((unused))= sigdelset; + int (*ses)(sigset_t *) __attribute__((unused))= sigemptyset; + int (*sfs)(sigset_t *) __attribute__((unused))= sigfillset; + int (*ismem)(const sigset_t *, int) __attribute__((unused))= sigismember; + void ( *(*sgnl)(int, void (*)(int)))(int) __attribute__((unused))= signal; + int (*pend)(sigset_t *) __attribute__((unused))= sigpending; + int (*spmsk)(int, const sigset_t *restrict, sigset_t *restrict) __attribute__((unused))= sigprocmask; + int (*sigq)(pid_t, int, const union sigval) __attribute__((unused))= sigqueue; + int (*susp)(const sigset_t *) __attribute__((unused))= sigsuspend; + int (*stmwt)(const sigset_t *restrict, siginfo_t *restrict, + const struct timespec *restrict) __attribute__((unused))= sigtimedwait; + int (*swt)(const sigset_t *restrict, int *restrict) __attribute__((unused))= sigwait; + int (*swtinfo)(const sigset_t *restrict, siginfo_t *restrict) __attribute__((unused))= sigwaitinfo; + // int (*str2s)(const char *restrict, int *restrict) = str2sig; + + return EXIT_SUCCESS; +} diff --git a/tests/signals/signal-handle_return.c b/tests/signals/signal-handle_return.c new file mode 100644 index 0000000000..933f992078 --- /dev/null +++ b/tests/signals/signal-handle_return.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include "../test_helpers.h" + +void SIGUSR1_handler(int signo) +{ + (void) signo; + printf("do nothing useful\n"); +} + +void SIGUSR2_handler(int signo) +{ + (void) signo; + printf("do nothing useful\n"); +} + +int main() +{ + void (*status) (int); + status = signal(SIGUSR1, SIGUSR1_handler); + ERROR_IF(signal, status, == SIG_ERR); + + status = signal(SIGUSR2, SIGUSR2_handler); + ERROR_IF(signal, status, == SIG_ERR); + + status = signal(SIGUSR1,SIG_IGN); + // printf("status is %d\n", status); + // printf("SIGUSR1_handler is %d\n", SIGUSR1_handler); + ERROR_IF(signal, status, != SIGUSR1_handler); + + //this seems to be a weird comparison error + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/signal-handler.c b/tests/signals/signal-handler.c new file mode 100644 index 0000000000..f6eba8e168 --- /dev/null +++ b/tests/signals/signal-handler.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include "../test_helpers.h" + +int handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + printf("SIGCHLD called. Inside handler\n"); + handler_called = 1; +} + +int main() +{ + void (*status) (int); + status = signal(SIGCHLD, sig_handler); + ERROR_IF(signal, status, == SIG_ERR); + + status = signal(SIGCHLD,SIG_DFL); + ERROR_IF(signal, status, != sig_handler); + + //same comparison error from handle_return + + raise(SIGCHLD); + + if (handler_called == 1) { + return EXIT_FAILURE; + } + handler_called = 0; + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/signal-handler2.c b/tests/signals/signal-handler2.c new file mode 100644 index 0000000000..7cc8394bdd --- /dev/null +++ b/tests/signals/signal-handler2.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +int handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + // printf("%d called. Inside handler\n", signo); + handler_called = 1; +} + +int signal_test3(int signum) +{ + void (*status) (int); + status = signal(signum, sig_handler); + ERROR_IF(signal, status, == SIG_ERR); + + raise(signum); + + ERROR_IF(raise, handler_called, != 1); + return EXIT_SUCCESS; +} + +int main(){ + for (unsigned int i = 0; i < sizeof(signals_list)/sizeof(signals_list[0]); i++){ + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP){ + continue; + } + signal_test3(sig); + } + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/signal-ignore.c b/tests/signals/signal-ignore.c new file mode 100644 index 0000000000..37a7c744bf --- /dev/null +++ b/tests/signals/signal-ignore.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +int handler_called = 0; + +void sig_handler(int signo) +{ + printf("%d called. Inside handler\n", signo); + handler_called = 1; +} + +int signal_test2(int signum) +{ + void (*status) (int); + status = signal(signum, sig_handler); + ERROR_IF(signal, status, == SIG_ERR); + + status = signal(signum, SIG_IGN); + ERROR_IF(signal, status, != sig_handler); + + // same issue as handle_return + + raise(signum); + + ERROR_IF(raise, handler_called, == 1); + handler_called = 0; + return EXIT_SUCCESS; +} + +int main(){ + for (unsigned int i = 0; i < sizeof(signals_list)/sizeof(signals_list[0]); i++){ + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP){ + continue; + } + signal_test2(sig); + } + return EXIT_SUCCESS; +} + + + diff --git a/tests/signals/signal-invalid.c b/tests/signals/signal-invalid.c new file mode 100644 index 0000000000..afad26247a --- /dev/null +++ b/tests/signals/signal-invalid.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include "../test_helpers.h" + +// test to make sure sending an invalid signal sets errno + +void sig_handler(int signo) +{ + (void) signo; + printf("handler does nothing useful.\n"); +} + +int main() +{ + errno = -1; + + void (*status) (int); + status = signal(-1, sig_handler); + ERROR_IF(signal, status, != SIG_ERR); + ERROR_IF(signal, errno, <= 0); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/signal-uncatchable.c b/tests/signals/signal-uncatchable.c new file mode 100644 index 0000000000..1bf85db82c --- /dev/null +++ b/tests/signals/signal-uncatchable.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include "../test_helpers.h" + +//make sure you can't catch uncatchable signals + +void sig_handler(int signo) +{ + (void) signo; + printf("handler does nothing useful.\n"); +} + +int main() +{ + errno = -1; + void (*status) (int); + status = signal(SIGKILL, sig_handler); + ERROR_IF(signal, status, != SIG_ERR); + + ERROR_IF(signal, errno, <= 0); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/signals_list.h b/tests/signals/signals_list.h new file mode 100644 index 0000000000..75bf3cd847 --- /dev/null +++ b/tests/signals/signals_list.h @@ -0,0 +1,79 @@ +#ifndef _SIGNALS_LIST +#define _SIGNALS_LIST 1 + +#include + +#ifdef SIGSTKFLT +#endif + +const int N_SIGNALS = (28 +#ifdef SIGSTKFLT + + 1 +#endif +#ifdef SIGWINCH + + 1 +#endif +#ifdef SIGIO + + 1 +#endif +#ifdef SIGPWR + + 1 +#endif +#ifdef SIGUNUSED + + 1 +#endif +); + +struct signalAction +{ + int signal; + char action; +}; + +const struct signalAction signals_list[] = { + {SIGABRT, 'A'}, + {SIGALRM, 'T'}, + {SIGBUS, 'A'}, + {SIGCHLD, 'I'}, + {SIGCONT, 'C'}, + {SIGFPE, 'A'}, + {SIGHUP, 'T'}, + {SIGILL, 'A'}, + {SIGINT, 'T'}, + {SIGKILL, 'T'}, + {SIGPIPE, 'T'}, + {SIGQUIT, 'A'}, + {SIGSEGV, 'A'}, + {SIGSTOP, 'S'}, + {SIGTERM, 'T'}, + {SIGTSTP, 'S'}, + {SIGTTIN, 'S'}, + {SIGTTOU, 'S'}, + {SIGUSR1, 'T'}, + {SIGUSR2, 'T'}, + // {SIGPOLL, 'T'}, + {SIGPROF, 'T'}, + {SIGSYS, 'A'}, + {SIGTRAP, 'A'}, + {SIGURG, 'I'}, + {SIGVTALRM, 'T'}, + {SIGXCPU, 'A'}, + {SIGXFSZ, 'A'}, +#ifdef SIGSTKFLT + {SIGSTKFLT, 'T'}, +#endif +#ifdef SIGWINCH + {SIGWINCH, 'I'}, +#endif +#ifdef SIGIO + {SIGIO, 'T'}, +#endif +#ifdef SIGPWR + {SIGPWR, 'T'}, +#endif +#ifdef SIGUNUSED + {SIGUNUSED, 'A'}, +#endif +}; + +#endif /* _SIGNALS_LIST */ diff --git a/tests/signals/sigpause-error.c b/tests/signals/sigpause-error.c new file mode 100644 index 0000000000..b7299bf092 --- /dev/null +++ b/tests/signals/sigpause-error.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// This program verifies that sigpause() returns -1 and sets errno to EINTR +// when it returns. + +#define INMAIN 0 +#define INTHREAD 1 + +int handler_called = 0; +int returned = 0; +int return_value = 2; +int result = 2; +int sem = INMAIN; + +void handler() { + // printf("signal was called\n"); + handler_called = 1; + return; +} + +void *d_thread_func(void *sig) +{ + + int signum = *(int *)sig; + printf("%d Pausing signal \n", signum); + int return_value = 0; + struct sigaction act; + act.sa_flags = 0; + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + sigaction(signum, &act, 0); + return_value = sigpause(signum); + ERROR_IF(sigpause, return_value, != -1); + ERROR_IF(sigpause, errno, != EINTR); + result = 0; + if (return_value == -1) { + if (errno == EINTR) { + printf ("Test PASSED: sigpause returned -1 and set errno to EINTR\n"); + result = 0; + } else { + printf ("Test FAILED: sigpause did not set errno to EINTR\n"); + result = 1; + } + } else { + if (errno == EINTR) { + printf ("Test FAILED: sigpause did not return -1\n"); + } + printf ("Test FAILED: sigpause did not set errno to EINTR\n"); + printf ("Test FAILED: sigpause did not return -1\n"); + result = 1; + + } + sem = INMAIN; + return NULL; +} + + +int sigpause_error(int signum){ + pthread_t new_th; + + int status; + status = pthread_create(&new_th, NULL, d_thread_func, (void *)&signum); + ERROR_IF(pthread_create, status, != 0); + + usleep(100000); + + status = pthread_kill(new_th, signum); + ERROR_IF(pthread_kill, status, != 0); + + sem = INTHREAD; + while (sem == INTHREAD) + usleep(100000); + + if(result == 2) { + exit(EXIT_FAILURE); + } + if(result == 1) { + exit(EXIT_FAILURE); + } + + printf("Test PASSED\n"); + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include +#include +#include +#include + +// This program verifies that sigpause() returns -1 and sets errno to EINVAL +// if passed an invalid signal number. + +#define INMAIN 0 +#define INTHREAD 1 + +volatile sig_atomic_t handler_called = 0; +int returned = 0; +int return_value = 2; +int result = 2; +int sem = INMAIN; + +void handler() { + // printf("signal was called\n"); + handler_called = 1; + return; +} + +int sigpause_invalid(){ + int return_value = 0; + + return_value = sigpause(-1); + + if (return_value == -1) { + if (errno == EINVAL) { + printf ("Test PASSED: sigpause returned -1 and set errno to EINVAL\n"); + return EXIT_SUCCESS; + } else { + printf ("Test FAILED: sigpause did not set errno to EINVAL\n"); + exit(EXIT_FAILURE); + } + } else { + printf ("Test FAILED: sigpause did not return -1\n"); + if (errno == EINVAL) { + printf ("Test FAILED: sigpause did not set errno to EINVAL\n"); + } + exit(EXIT_FAILURE); + } + return EXIT_SUCCESS; + +} + +int main(){ + sigpause_invalid(); + return EXIT_SUCCESS; +} + diff --git a/tests/signals/sigpause-pause.c b/tests/signals/sigpause-pause.c new file mode 100644 index 0000000000..e2a24c763e --- /dev/null +++ b/tests/signals/sigpause-pause.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// This program verifies that sigpause() removes sig from the signal mask. + +int handler_called = 0; + +void handler() { + handler_called = 1; + return; +} + +void *a_thread_func(void *sig) +{ + int status; + int signum = *(int *)sig; + printf("Pausing signal %s\n", signum); + struct sigaction act; + act.sa_flags = 0; + act.sa_handler = handler; + status = sigemptyset(&act.sa_mask); + ERROR_IF(sigemptyset, status, != 0); + status = sigaction(signum, &act, 0); + ERROR_IF(sigaction, status, != 0); + status = sighold(signum); + ERROR_IF(sighold, status, != 0); + status = sigpause(signum); + ERROR_IF(sigpause, status, != 0); + + return NULL; +} + + + +int sigpause_basic(int signum) +{ + pthread_t new_th; + int status; + status = pthread_create(&new_th, NULL, a_thread_func, (void *)&signum); + ERROR_IF(pthread_create, status, != 0); + + usleep(100000); + + status = pthread_kill(new_th, signum); + ERROR_IF(pthread_kill, status, != 0); + + usleep(100000); + + if (handler_called != 1){ + prinft("handler wasn't called\n"); + exit(EXIT_FAILURE); + } + handler_called = 0; + + return EXIT_SUCCESS; +} + + + +int main(){ + for (int i=1; i +#include +#include +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// This program verifies that sigpause() restores sig to the signal mask before +// returning. + +volatile sig_atomic_t handler_called = false; + +void handler() { + handler_called = true; + return; +} + +void *c_thread_func(void *sig) +{ + int signum = *(int *)sig; + printf("%d !!!\n", signum); + + sigset_t pendingset; + + puts("before sigpause"); + + assert(!handler_called); + + if ((sigpause(signum) != -1) || (errno != EINTR)) { + puts("Test UNRESOLVED: sigpause didn't return -1 and/or didn't set errno correctly."); + exit(2); + return NULL; + } + assert(handler_called); + handler_called = false; + + int status = raise(signum); + ERROR_IF(raise, status, == -1); + + assert(!handler_called); + + sigpending(&pendingset); + if (sigismember(&pendingset, signum) == 1) { + puts("Test PASSED: signal mask was restored when sigpause returned."); + } + + return NULL; + +} + +int sigpause_revert(int signum) { + pthread_t new_th; + int status; + struct sigaction act; + + // Ensure thread inherits mask with signum blocked. + status = sighold(signum); + ERROR_IF(sighold, status, == -1); + + act.sa_flags = 0; + act.sa_handler = handler; + status = sigemptyset(&act.sa_mask); + ERROR_IF(sigemptyset, status, == -1); + status = sigaction(signum, &act, NULL); + ERROR_IF(sigaction, status, == -1); + + if((status = pthread_create(&new_th, NULL, c_thread_func, (void *)&signum)) != 0) + { + errno = status; + perror("Error creating thread"); + exit(EXIT_FAILURE); + } + + usleep(100000); + + if((status = pthread_kill(new_th, signum)) != 0) + { + errno = status; + perror("Test UNRESOLVED: Couldn't send signal to thread"); + exit(EXIT_FAILURE); + } + if ((status = pthread_join(new_th, NULL)) != 0) { + errno = status; + perror("failed to join thread"); + return EXIT_FAILURE; + } + + puts("Test PASSED"); + return EXIT_SUCCESS; +} + + + +int main(){ + for (int i=1; i +#include +#include +#include +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// This program verifies that sigpause() suspends the calling process +// until it receives a signal. + +#define INMAIN 0 +#define INTHREAD 1 + +volatile sig_atomic_t handler_called = 0; +volatile atomic_bool completed = false; + +void handler() { + handler_called = 1; + return; +} +void *b_thread_func(void *code_raw) { + int *code = code_raw; + printf("Pausing signal %s\n", strsignal(*code)); + sigpause(*code); + assert(handler_called != 0); + *code = 0; + atomic_store(&completed, true); + + return NULL; +} + + +int sigpause_suspend(int signum) +{ + atomic_store(&completed, false); + int status; + + struct sigaction act; + act.sa_flags = 0; + act.sa_handler = handler; + sigemptyset(&act.sa_mask); + status = sigaction(signum, &act, 0); + ERROR_IF(sigaction, status, == -1); + + pthread_t new_th; + + int code = signum; + if ((status = pthread_create(&new_th, NULL, b_thread_func, (void *)&code)) != 0) { + errno = status; + perror("failed to create thread"); + return EXIT_FAILURE; + } + usleep(100000); + assert(!atomic_load(&completed)); + + if ((status = pthread_kill(new_th, signum)) != 0) { + errno = status; + perror("failed to kill thread"); + return EXIT_FAILURE; + } + + if ((status = pthread_join(new_th, NULL)) != 0) { + errno = status; + perror("failed to join thread"); + return EXIT_FAILURE; + } + + assert(code == 0); + assert(atomic_load(&completed)); + + return EXIT_SUCCESS; +} + + +int main(){ + // TODO: upper limit for i (gives OOB otherwise) + for (int i=1; i<31; i++){ + int sig = signals_list[i].signal; + if (sig == SIGKILL || sig == SIGSTOP){ + continue; + } + printf("For signal %s (%d)\n", strsignal(sig), sig); + sigpause_suspend(sig); + } + return EXIT_SUCCESS; +} + + + diff --git a/tests/signals/sigprocmask-10.c b/tests/signals/sigprocmask-10.c new file mode 100644 index 0000000000..f14c593bdf --- /dev/null +++ b/tests/signals/sigprocmask-10.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include "../test_helpers.h" + +// The thread's signal mask shall not be changed, if sigprocmask( ) fails. + +#define NUMSIGNALS 24 + +int is_changed(sigset_t set) { + + int i; + int siglist[] = {SIGALRM, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, + SIGPIPE, SIGQUIT, SIGSEGV, + SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, + SIGUSR1, SIGUSR2, SIGPROF, SIGSYS, + SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ }; + + for (i=0; i +#include +#include +#include "../test_helpers.h" + +// sigprocmask( ) shall return 0, Upon successful completion; otherwise, it shall return -1 +// and errno shall be set to indicate the error, and the process' signal mask shall be unchanged. + +int main() +{ + + sigset_t set; + sigaddset (&set, SIGABRT); + + int status; + status = sigprocmask(SIG_SETMASK, &set, NULL); + ERROR_IF(sigprocmask, status, != 0); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/sigprocmask-3.c b/tests/signals/sigprocmask-3.c new file mode 100644 index 0000000000..aee43032ca --- /dev/null +++ b/tests/signals/sigprocmask-3.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +// The resulting set shall be the union of the current set and the signal +// set pointed to by set, if the value of the argument how is SIG_BLOCK. + +volatile sig_atomic_t handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + handler_called = 1; +} + +int sigprocmask_block(int signum) +{ + int defaultsig = SIGALRM; + if (signum == SIGALRM) { + defaultsig = SIGHUP; + } + struct sigaction act; + sigset_t set1, set2, pending_set; + sigemptyset(&set1); + sigemptyset(&set2); + sigaddset(&set1, defaultsig); + sigaddset(&set2, signum); + + act.sa_handler = sig_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + int status; + status = sigaction(signum, &act, 0); + ERROR_IF(sigaction, status, == -1); + + status = sigaction(defaultsig, &act, 0); + ERROR_IF(sigaction, status, == -1); + + status = sigprocmask(SIG_SETMASK, &set1, NULL); + ERROR_IF(sigprocmask, status, == -1); + + status = sigprocmask(SIG_UNBLOCK, &set2, NULL); + ERROR_IF(sigprocmask, status, == -1); + + printf("Raising %s\n", strsignal(signum)); + status = raise(signum); + ERROR_IF(raise, status, == -1); + + ERROR_IF(raise, handler_called, != 1); + handler_called = 0; + + printf("Raising %s\n", strsignal(defaultsig)); + status = raise(defaultsig); + ERROR_IF(raise, defaultsig, == -1); + + ERROR_IF(raise, handler_called, == 1); + + status = sigpending(&pending_set); + ERROR_IF(sigpending, status, == -1); + + status = sigismember(&pending_set, defaultsig); + ERROR_IF(sigismember, status, != 1); + + status = sigismember(&pending_set, signum); + ERROR_IF(sigismemeber, status, != 0); + + // printf("Test PASSED: signal was added to the process's signal mask\n"); + act.sa_handler = SIG_IGN; + sigaction(signum, &act, 0); + sigaction(defaultsig, &act, 0); + + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include + +// The previous mask shall be stored in the location pointed to by oset, if the argument oset is not a null pointer. + + +#define NUMSIGNALS 26 + +int main() +{ + sigset_t oactl, tempset; + int i, j, test_failed=0; + + int siglist[] = {SIGABRT, SIGALRM, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, + SIGPIPE, SIGQUIT, SIGSEGV, + SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, + SIGUSR1, SIGUSR2, SIGPROF, SIGSYS, + SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ }; + + for (i=0; i 0) { + for (j=0; j +#include +#include + +// The value of the argument how is not significant and the process's signal mask shall be unchanged, and +// thus the call can be used to enquire about currently blocked signals, if the argument set is a null +// pointer. + +#define NUMSIGNALS 25 + +int is_changed(sigset_t set, int sig) { + + int i; + int siglist[] = {SIGABRT, SIGALRM, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, + SIGPIPE, SIGQUIT, SIGSEGV, + SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, + SIGUSR1, SIGUSR2, SIGPROF, SIGSYS, + SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ }; + + if (sigismember(&set, sig) != 1) { + return 1; + } + for (i=0; i +#include +#include + + +#define NUMSIGNALS 25 + +int is_changed(sigset_t set, int sig) { + + int i; + int siglist[] = {SIGABRT, SIGALRM, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, + SIGPIPE, SIGQUIT, SIGSEGV, + SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, + SIGUSR1, SIGUSR2, SIGPROF, SIGSYS, + SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ }; + + if (sigismember(&set, sig) != 1) { + return 1; + } + for (i=0; i +#include +#include + +#define NUMSIGNALS 25 + +int is_changed(sigset_t set, int sig) { + + int i; + int siglist[] = {SIGABRT, SIGALRM, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, + SIGPIPE, SIGQUIT, SIGSEGV, + SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, + SIGUSR1, SIGUSR2, SIGPROF, SIGSYS, + SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ }; + + if (sigismember(&set, sig) != 1) { + return 1; + } + for (i=0; i +#include +#include +#include "../test_helpers.h" + +// After the call to sigprocmask(), if there are any pending unblocked signals, at least one of those +// signals shall be delivered before the call to sigprocmask() returns. + +int handler_called = 0; +int sigprocmask_return_val = 1; /* some value that's not a 1 or 0 */ + +void sig_handler(int signo) +{ + (void) signo; + handler_called = 1; + if (sigprocmask_return_val != 1) { + printf("FAIL: sigprocmask() returned before signal was delivered.\n"); + exit(EXIT_FAILURE); + } +} + +int main() +{ + struct sigaction act; + sigset_t blocked_set1; + sigemptyset(&blocked_set1); + sigaddset(&blocked_set1, SIGABRT); + + act.sa_handler = sig_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + int status; + + status = sigaction(SIGABRT, &act, 0); + ERROR_IF(sigaction, status, == -1); + + status = sigprocmask(SIG_SETMASK, &blocked_set1, NULL); + ERROR_IF(sigprocmask, status, == -1); + + status = raise(SIGABRT); + ERROR_IF(raise, status, == -1); + + sigprocmask_return_val = sigprocmask(SIG_UNBLOCK, &blocked_set1, NULL); + + ERROR_IF(sigprocmask, sigprocmask_return_val, != 0); + + ERROR_IF(raise, handler_called, != 1); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/sigprocmask-9.c b/tests/signals/sigprocmask-9.c new file mode 100644 index 0000000000..168eed88c4 --- /dev/null +++ b/tests/signals/sigprocmask-9.c @@ -0,0 +1,28 @@ +#include "../test_helpers.h" +#include +#include +#include +#include + +// Attempt to add SIGKILL and SIGSTOP to the process's signal mask and +// verify that: +// - They do not get added. +// - sigprocmask() does not return -1. + +int main() { + sigset_t set1, set2; + int ret; + + sigemptyset(&set1); + sigemptyset(&set2); + sigaddset(&set1, SIGKILL); + sigaddset(&set1, SIGSTOP); + + ret = sigprocmask(SIG_SETMASK, &set1, NULL); + ERROR_IF(sigprocmask, ret, == -1); + ret = sigprocmask(SIG_SETMASK, NULL, &set2); + ERROR_IF(sigprocmask, ret, == -1); + + assert(!sigismember(&set2, SIGKILL)); + assert(!sigismember(&set2, SIGSTOP)); +} diff --git a/tests/signals/sigprocmask-blocksingle.c b/tests/signals/sigprocmask-blocksingle.c new file mode 100644 index 0000000000..5dd3d5a3c0 --- /dev/null +++ b/tests/signals/sigprocmask-blocksingle.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +int handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + handler_called = 1; +} + +int sigprocmask_block(int signum) +{ + struct sigaction act; + sigset_t blocked_set, pending_set; + sigemptyset(&blocked_set); + sigaddset(&blocked_set, signum); + + act.sa_handler = sig_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + int status; + status = sigaction(signum, &act, 0); + ERROR_IF(sigaction, status, == -1); + + status = sigprocmask(SIG_SETMASK, &blocked_set, NULL); + ERROR_IF(sigprocmask, status, == -1); + + status = raise(signum); + ERROR_IF(raise, status, == -1); + + if (handler_called) { + printf("FAIL: Signal was not blocked\n"); + exit(EXIT_FAILURE); + } + + status = sigpending(&pending_set); + ERROR_IF(sigpending, status, == -1); + + status = sigismember(&pending_set, signum); + ERROR_IF(sigismember, status, != 1); + + act.sa_handler = SIG_IGN; + sigaction(signum, &act, 0); + + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +volatile sig_atomic_t handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + handler_called = 1; +} + +int sigrelse_test(int signum) +{ + // needs to be reset + handler_called = 0; + + struct sigaction act; + + act.sa_handler = sig_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + int status; + status = sigaction(signum, &act, NULL); + ERROR_IF(sigaction, status, == -1); + + status = sighold(signum); + ERROR_IF(sighold, status, == -1); + + assert(handler_called == 0); + + status = raise(signum); + ERROR_IF(raise, status, == -1); + + assert(handler_called == 0); + + status = sigrelse(signum); + ERROR_IF(sigrelse, status, == -1); + + assert(handler_called == 1); + // if (handler_called) { + // printf("PASS: %d successfully removed from signal mask\n", signum); + // handler_called = 0; + // return EXIT_SUCCESS; + // } + // printf("FAIL\n"); + // exit(EXIT_FAILURE); + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include "../test_helpers.h" + +int main() +{ + int status; + status = (int)sigrelse(SIGABRT); + ERROR_IF(sigrelse, status, != 0); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/sigrelse-3.c b/tests/signals/sigrelse-3.c new file mode 100644 index 0000000000..980bca35bd --- /dev/null +++ b/tests/signals/sigrelse-3.c @@ -0,0 +1,14 @@ +#include +#include +#include +#include +#include "../test_helpers.h" + +int main() +{ + int status; + status = (int)sigrelse(100000); + ERROR_IF(sigrelse, status, != -1); + ERROR_IF(sigrelse, errno, != EINVAL); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tests/signals/sigset-1.c b/tests/signals/sigset-1.c new file mode 100644 index 0000000000..fe2f04df1c --- /dev/null +++ b/tests/signals/sigset-1.c @@ -0,0 +1,46 @@ +#include +#include +#include + +int handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + printf("SIGURG called. Inside handler\n"); + handler_called = 1; +} + +int sigset_test(int signum) +{ + + struct sigaction act; + act.sa_handler = sig_handler; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + if (sigaction(signum, &act, 0) != 0) { + perror("Unexpected error while using sigaction()"); + exit(EXIT_FAILURE); + } + + if (sigset(signum,SIG_DFL) != sig_handler) { + perror("Unexpected error while using signal()"); + exit(EXIT_FAILURE); + } + + raise(signum); + + if (handler_called == 1) { + printf("Test FAILED: handler was called even though default was expected\n"); + exit(EXIT_FAILURE); + } + printf("test passed, the signal was ignored\n"); + return EXIT_SUCCESS; +} + +int main(){ + sigset_test(SIGURG); + return EXIT_SUCCESS; +} + diff --git a/tests/signals/sigset-10.c b/tests/signals/sigset-10.c new file mode 100644 index 0000000000..68f8d37808 --- /dev/null +++ b/tests/signals/sigset-10.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include "../test_helpers.h" + +int main() +{ + void (*status) (int); + status = sigset(SIGKILL,SIG_IGN); + assert(status == SIG_ERR); + assert(errno == EINVAL); + // if (sigset(SIGKILL,SIG_IGN) == SIG_ERR) { + // if (errno != EINVAL) { + // printf("Test FAILED: sigset() returned SIG_ERR but didn't set errno to EINVAL\n"); + // exit(EXIT_FAILURE); + // } + // } else { + // printf("Test FAILED: sigset() didn't return SIG_ERROR even though SIGKILL was passed to it\n"); + // exit(EXIT_FAILURE); + // } + // printf("test passed: error was set successfully\n"); + return EXIT_SUCCESS; +} diff --git a/tests/signals/sigset-2.c b/tests/signals/sigset-2.c new file mode 100644 index 0000000000..17ba64392b --- /dev/null +++ b/tests/signals/sigset-2.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +int handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + printf("SIGUSR1 called. Inside handler\n"); + handler_called = 1; +} + +int sigset_test2(int signum) +{ + struct sigaction act; + act.sa_flags = 0; + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + + if (sigaction(signum, &act, 0) != 0) { + perror("Unexpected error while using sigaction()"); + exit(EXIT_FAILURE); + } + + if (sigset(signum,SIG_IGN) != sig_handler) { + perror("Unexpected error while using signal()"); + exit(EXIT_FAILURE); + } + + raise(signum); + + if (handler_called == 1) { + printf("Test FAILED: handler was called even though ignore was expected\n"); + exit(EXIT_FAILURE); + } + printf("test %d passed, the signal was ignored\n", signum); + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +int handler_called = 0; + +void sig_handler(int signo) +{ + (void) signo; + // printf("SIGCHLD called. Inside handler\n"); + handler_called = 1; +} + +int sigset_test3(int signum) +{ + if (sigset(signum, sig_handler) == SIG_ERR) { + perror("Unexpected error while using sigset()"); + exit(EXIT_FAILURE); + } + + raise(signum); + + if (handler_called != 1) { + printf("Test FAILED: handler wasn't called even though it was expected\n"); + exit(EXIT_FAILURE); + } + printf("test %d passed, handler was called\n", signum); + handler_called = 0; + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +int signal_blocked = 0; +int count = 1; + +void sig_handler(int signo) +{ + (void) signo; + // printf("SIGCHLD called. Inside handler\n"); + sigset_t mask; + sigprocmask(SIG_SETMASK, NULL, &mask); + if (count == SIGKILL || count == SIGSTOP){ + count++; + } + if(sigismember(&mask, count)) { + signal_blocked = 1; + } + count++; +} + +int sigset_test4(int signum) +{ + if (sigset(signum, sig_handler) == SIG_ERR) { + perror("Unexpected error while using sigset()"); + exit(EXIT_FAILURE); + } + + raise(signum); + + if (signal_blocked != 1) { + printf("Test FAILED: signal was not added to the mask before the handler was executed\n"); + exit(EXIT_FAILURE); + } + signal_blocked = 0; + printf("test %d passed: the singal was added to the mask before the handler was executed\n", signum); + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +#define NUMSIGNALS 26 + +bool is_empty(sigset_t *set) { + + int i; + int siglist[25] = {SIGABRT, SIGALRM, SIGBUS, SIGCHLD, + SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, + SIGPIPE, SIGQUIT, SIGSEGV, + SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU, + SIGUSR1, SIGUSR2, SIGPROF, SIGSYS, + SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ }; + + for (i=0; i<25; i++) { + if (sigismember(set, siglist[i]) != 0) + return false; + } + return true; +} + +void sig_handler(int signo) +{ + printf("%d called. Inside handler\n", signo); +} + +int sigset_test5(int signum) +{ + sigset_t mask; + sigemptyset(&mask); + + sigprocmask(SIG_SETMASK, &mask, NULL); + void (*status) (int); + + status = sigset(signum, sig_handler); + ERROR_IF(sigset, status, == SIG_ERR); + // if (sigset(signum, sig_handler) == SIG_ERR) { + // perror("Unexpected error while using sigset()"); + // exit(EXIT_FAILURE); + // } + + raise(signum); + sigprocmask(SIG_SETMASK, NULL, &mask); + bool status1 = is_empty(&mask); + assert(status1); + // if (is_empty(&mask) != 1) { + // printf("Test FAILED: signal mask should be empty\n"); + // exit(EXIT_FAILURE); + // } + // printf("sig %d was successfully removed from the mask when handler returned\n", signum); + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include "signals_list.h" +#include "../test_helpers.h" + +void sig_handler(int signo) +{ + (void) signo; + printf("SIGUSR1 called. Inside handler\n"); +} + +int sigset_test9(int signum) +{ + struct sigaction act; + act.sa_flags = 0; + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + + if (sigaction(signum, &act, 0) != 0) { + perror("Unexpected error while using sigaction()"); + exit(EXIT_FAILURE); + } + + if (sigset(signum,SIG_DFL) != sig_handler) { + printf("Test FAILED: sigset didn't return myhandler even though it was SIGUSR1's original disposition\n"); + exit(EXIT_FAILURE); + } + printf("test %d passed \n", signum); + return EXIT_SUCCESS; +} + +int main(){ + for (int i=1; i +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define THE_SIG SIGRTMIN + +volatile sig_atomic_t num = 0; + +int parent; + +void validate(int sig, const siginfo_t *info) +{ + assert(sig == THE_SIG); + assert(info != NULL); + assert(info->si_signo == THE_SIG); + assert(info->si_value.sival_int == num); + assert(info->si_code == SI_QUEUE); + assert(info->si_pid == parent); +} + +void action(int sig, siginfo_t *info, void *context) +{ + (void)context; + assert(context != NULL); + validate(sig, info); + num++; +} + +int main(void) +{ + + int status, fds[2]; + + struct utsname utsname; + + status = uname(&utsname); + if (status == 0) { + if (strncmp(utsname.sysname, "Linux", 6) == 0) { + printf("Test is not supported on Linux, relibc's siginfo_t is not compatible.\n"); + return EXIT_SUCCESS; + } + } + status = pipe(fds); + ERROR_IF(pipe, status, == -1); + + parent = getpid(); + assert(parent != 0); + + sigset_t set, mask; + status = sigfillset(&mask); + ERROR_IF(sigfillset, status, == -1); + status = sigdelset(&mask, SIGSEGV); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&mask, SIGBUS); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&mask, SIGILL); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&mask, SIGFPE); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&mask, SIGINT); + ERROR_IF(sigdelset, status, == -1); + status = sigprocmask(SIG_SETMASK, &mask, NULL); + ERROR_IF(sigprocmask, status, == -1); + + status = sigemptyset(&set); + ERROR_IF(sigemptyset, status, == -1); + status = sigaddset(&set, THE_SIG); + ERROR_IF(sigaddset, status, == -1); + + sigset_t empty_set; + status = sigemptyset(&empty_set); + ERROR_IF(sigemptyset, status, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + status = close(fds[child == 0 ? 0 : 1]); + ERROR_IF(close, status, == -1); + + struct sigaction sa; + memcpy(&sa.sa_mask, &set, sizeof(sigset_t)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = action; + + status = sigaction(THE_SIG, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + if (child == 0) + { + assert(num == 0); + siginfo_t info; + struct timespec t = (struct timespec){.tv_sec = 1, .tv_nsec = 200000000}; + status = sigtimedwait(&set, &info, &t); + ERROR_IF(sigtimedwait, status, == -1); + assert(status == THE_SIG); + validate(THE_SIG, &info); + assert(num == 0); // ensure no signal handler ran + + num++; + + // TODO: check status + status = sigsuspend(&empty_set); + if (status == -1) + { + UNEXP_IF(sigsuspend, errno, != EINTR); + } + + assert(num == 2); // ensure signal handler ran + + status = sigprocmask(SIG_SETMASK, &empty_set, NULL); + ERROR_IF(sigprocmask, status, == -1); + + while (num < 31) + { + } + + status = write(fds[1], "A", 1); + ERROR_IF(write, status, == -1); + } + else + { + struct timespec t = (struct timespec){.tv_sec = 0, .tv_nsec = 100000000}; + status = nanosleep(&t, NULL); + ERROR_IF(nanosleep, status, < 0); + + for (int n = 0; n <= 31; n++) + { + status = sigqueue(child, THE_SIG, (union sigval){.sival_int = n}); + ERROR_IF(sigqueue, status, == -1); + } + char buf[1]; + status = read(fds[0], buf, 1); + ERROR_IF(read, status, == -1); + + pid_t wait_pid = 0; + int wait_status = 0; + wait_pid = wait(&wait_status); + ERROR_IF(wait, wait_pid, < 0); + UNEXP_IF(wait, wait_pid, != child); + if (!WIFEXITED(wait_status) || WEXITSTATUS(wait_status) != EXIT_SUCCESS) + { + fprintf(stderr, "Unexpected result, WIFEXITED %s, WEXITSTATUS %d\n", + WIFEXITED(wait_status) ? "true" : "false", WEXITSTATUS(wait_status)); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/sigsetjmp.c b/tests/sigsetjmp.c new file mode 100644 index 0000000000..0063d21528 --- /dev/null +++ b/tests/sigsetjmp.c @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() { +sigjmp_buf jb; + +if (sigsetjmp(jb, 1)) { +printf("Jump done.\n"); +} else { +printf ("Starting jump...\n"); +siglongjmp(jb, 1); +} +return 0; +} diff --git a/tests/src/main.rs b/tests/src/main.rs new file mode 100644 index 0000000000..69f3024766 --- /dev/null +++ b/tests/src/main.rs @@ -0,0 +1,315 @@ +#![feature(exit_status_error)] + +use std::{ + env, fs, + io::{self, Read, Write}, + path::{Path, PathBuf}, + process::{self, Command, ExitStatus, Stdio}, + sync::mpsc, + thread, + time::{Duration, Instant}, +}; + +fn find_expected_dir() -> Option { + let mut current_dir = env::current_exe() + .ok() + .and_then(|p| p.parent().map(|parent| parent.to_path_buf())) + .unwrap_or_else(|| PathBuf::from(".")); + + let mut found_expected_dir = None; + + while current_dir.pop() { + let check = current_dir.join("expected"); + if check.is_dir() { + found_expected_dir = Some(check); + break; + } + } + + if found_expected_dir.is_none() { + if let Ok(cwd) = env::current_dir() { + let check = cwd.join("expected"); + if check.is_dir() { + found_expected_dir = Some(check); + } + } + } + + found_expected_dir +} + +fn expected( + expected_dir: &Path, + bin: &str, + kind: &str, + generated: &[u8], + status: ExitStatus, +) -> Result<(), String> { + let expect_file = Path::new(bin).with_added_extension(kind); + let components: Vec<_> = expect_file + .components() + .filter_map(|c| c.as_os_str().to_str()) + .rev() + .collect(); + + let mut expected_file = None; + for i in 0..components.len() { + let sub_path: Vec<_> = components[0..=i] + .iter() + .rev() + .map(|s| s.to_string()) + .collect(); + + let check_file = expected_dir.join(sub_path.join("/")); + if check_file.is_file() { + expected_file = Some(fs::read(check_file)); + break; + } + } + + let expect_name = components.first().unwrap(); + let expected = match expected_file { + Some(Ok(ok)) => ok, + Some(Err(err)) => { + return Err(format!("{} failed to read {}: {}", bin, expect_name, err)); + } + None => { + if kind == "stderr" { + // missing stderr file, assume test expect none emitted + vec![] + } else { + return Err(format!("{} expected file not found: {}", bin, expect_name)); + } + } + }; + + if expected != generated { + println!("# {}: {}: expected #", bin, kind); + io::stdout().write(&expected).unwrap(); + + println!("# {}: {}: generated #", bin, kind); + io::stdout().write(generated).unwrap(); + + return Err(format!( + "{} failed - retcode {}, {} mismatch", + bin, status, kind + )); + } + + Ok(()) +} + +const STATUS_ONLY: &str = "-s"; + +fn print_tabbed(output: Vec, name: &str) { + if let Ok(stdout) = String::from_utf8(output) { + let stdout: Vec = stdout.trim().lines().map(|p| format!(" {}", p)).collect(); + let stdout = stdout.join("\n"); + if stdout.as_str() == "" { + println!("{name}: empty content"); + } else { + println!("{name}:\n{}", stdout); + } + } else { + println!("can't print out {name}: not utf8"); + } +} + +#[cfg(target_os = "redox")] +fn print_kernel_version() { + let Ok(uname) = fs::read_to_string("/scheme/sys/uname") else { + eprintln!("kernel version: unable to get one"); + return; + }; + eprintln!( + "kernel version: {}", + uname + .lines() + .last() + .map(|s| s.get(..8)) + .flatten() + .unwrap_or("?") + ); +} + +fn main() { + let mut failures = Vec::new(); + let timeout = Duration::from_secs(10); + let slowtime = Duration::from_secs(1); + let bins: Vec = env::args().skip(1).collect(); + let single_test = bins.len() == 1; + let expected_dir = find_expected_dir(); + + #[cfg(target_os = "redox")] + print_kernel_version(); + + for bin in bins { + let status_only = bin.starts_with(STATUS_ONLY); + let bin = if bin.starts_with(STATUS_ONLY) { + bin.strip_prefix(STATUS_ONLY).unwrap().to_string() + } else { + bin + }; + + println!("# {} #", bin); + + let start_time = Instant::now(); + + let (tx, rx) = mpsc::channel(); + let bin_for_spawn = bin.clone(); + + // There's an issue when pthread hangs, spawn() also hangs, so let's use separate thread to spawn + thread::spawn(move || { + let result = Command::new(&bin_for_spawn) + .arg("test") + .arg("args") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + tx.send(result).expect("Can't send"); + }); + + let mut status = None; + let mut child = None; + + loop { + if start_time.elapsed() > timeout { + let failure = format!( + "\x1b[0;91;49m{}: assumed hangs after {}ms\x1b[0m", + bin, + start_time.elapsed().as_millis() + ); + println!("{}", failure); + failures.push(failure); + break; + } + + if start_time.elapsed() > slowtime { + println!("# waiting {}ms", start_time.elapsed().as_millis()); + } + + let c = match &mut child { + Some(child) => child, + None => match rx.try_recv() { + Ok(Ok(c)) => { + child = Some(c); + continue; + } + Ok(Err(err)) => { + let failure = format!("{}: failed to execute: {}", bin, err); + println!("{}", failure); + failures.push(failure); + break; + } + Err(mpsc::TryRecvError::Disconnected) => { + let failure = format!("{}: failed to execute: thread died", bin); + println!("{}", failure); + failures.push(failure); + break; + } + Err(mpsc::TryRecvError::Empty) => { + thread::sleep(Duration::from_millis(if start_time.elapsed() < slowtime { + 25 + } else { + 500 + })); + continue; + } + }, + }; + + match c.try_wait() { + Ok(Some(s)) => { + status = Some(s); + break; + } + Ok(None) => { + thread::sleep(Duration::from_millis(if start_time.elapsed() < slowtime { + 25 + } else { + 500 + })); + } + Err(e) => { + failures.push(format!("{}: error waiting: {}", bin, e)); + break; + } + } + } + + match (status, child) { + (exit_status, Some(mut child)) => { + if exit_status.is_none() { + match child.kill() { + Ok(_) => {} + Err(e) => { + // if can't be killed then getting the output will hang + println!("Unable to kill, can't get output: {:?}", e); + continue; + } + } + } + let mut stdout = Vec::new(); + let mut stderr = Vec::new(); + child.stdout.unwrap().read_to_end(&mut stdout).unwrap(); + child.stderr.unwrap().read_to_end(&mut stderr).unwrap(); + + let Some(exit_status) = exit_status else { + // hangs + print_tabbed(stdout, "stdout"); + print_tabbed(stderr, "stderr"); + continue; + }; + + if !status_only { + let Some(expected_dir) = &expected_dir else { + eprintln!("Expected directory not found"); + process::exit(1); + }; + + if let Err(failure) = + expected(expected_dir, &bin, "stdout", &stdout, exit_status) + { + println!("{}", failure); + failures.push(failure); + } + if let Err(failure) = + expected(expected_dir, &bin, "stderr", &stderr, exit_status) + { + println!("{}", failure); + failures.push(failure); + } + } + + if let Err(e) = exit_status.exit_ok() { + let failure = format!("# {}: {}", bin, e); + println!("{}", failure); + failures.push(failure); + } + + if single_test { + print_tabbed(stdout, "stdout"); + print_tabbed(stderr, "stderr"); + } + } + (_, _) => { + continue; + } + } + + if start_time.elapsed() > slowtime { + println!( + "\x1b[0;93;49m test exection took too long: {}ms\x1b[0m", + start_time.elapsed().as_millis() + ); + } + } + + if !failures.is_empty() { + println!("\x1b[1;91;49m# FAILURES #\x1b[0m"); + for failure in failures { + println!("{}", failure); + } + process::exit(1); + } +} diff --git a/tests/stdio/all.c b/tests/stdio/all.c new file mode 100644 index 0000000000..4807b584db --- /dev/null +++ b/tests/stdio/all.c @@ -0,0 +1,29 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *f = fopen("stdio/stdio.in", "r"); + ERROR_IF(fopen, f, == NULL); + + int c = fgetc(f); + ERROR_IF(fgetc, c, == EOF); + UNEXP_IF(fgetc, c, < 0); + UNEXP_IF(fgetc, c, > 255); + printf("%c\n", c); + + int u = ungetc('J', f); + ERROR_IF(ungetc, u, == EOF); + + char in[30] = { 0 }; + char *s = fgets(in, 30, f); + ERROR_IF(fgets, s, == NULL); + printf("%s\n", in); + + __attribute__((unused)) int vb = setvbuf(stdout, 0, _IONBF, 0); + //ERROR_IF(setvbuf, vb, > 0); // TODO: Cannot use this, doesn't set errno + //UNEXP_IF(setvbuf, vb, != 0); + + printf("Hello\n"); +} diff --git a/tests/stdio/buffer.c b/tests/stdio/buffer.c new file mode 100644 index 0000000000..69efe8ebac --- /dev/null +++ b/tests/stdio/buffer.c @@ -0,0 +1,12 @@ +#include + +#include "test_helpers.h" + +int main(void) { + // Tests what used to be a bug with buffering + fwrite("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 1, 999, stdout); + fwrite("Test\n", 1, 5, stdout); + fwrite("Hello\nWorld\n", 1, 12, stdout); + puts("It works"); + puts("No buffering issues here"); +} diff --git a/tests/stdio/ctermid.c b/tests/stdio/ctermid.c new file mode 100644 index 0000000000..5a291cf607 --- /dev/null +++ b/tests/stdio/ctermid.c @@ -0,0 +1,22 @@ +#include +#include + +#define DEVTTY "/dev/tty" + +int main(void) +{ + char *name = ctermid(NULL); + if(strcmp(name, DEVTTY) != 0) { + printf("ctermid name differs: expected %s, got: %s\n", DEVTTY, name); + return 1; + } + + char name2[L_ctermid]; + ctermid(name2); + if(strcmp(name, DEVTTY) != 0) { + printf("ctermid name2 differs: expected %s, got: %s\n", DEVTTY, name2); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/tests/stdio/dprintf.c b/tests/stdio/dprintf.c new file mode 100644 index 0000000000..b61d5cfa97 --- /dev/null +++ b/tests/stdio/dprintf.c @@ -0,0 +1,23 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + const char *msg = "Hello, %s"; + + int fd = dup(STDOUT_FILENO); + ERROR_IF(dup, fd, == -1); + + int result = dprintf(fd, msg, "world"); + ERROR_IF(dprintf, result, != sizeof("Hello, world") - 1); + UNEXP_IF(dprintf, result, < 0); + + result = dprintf(fd, "\na\n"); + UNEXP_IF(dprintf, result, < 0); + ERROR_IF(dprintf, result, != sizeof("\na\n") - 1); + + close(fd); + + return 0; +} diff --git a/tests/stdio/fgets.c b/tests/stdio/fgets.c new file mode 100644 index 0000000000..ed02999cde --- /dev/null +++ b/tests/stdio/fgets.c @@ -0,0 +1,24 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *f = fopen("stdio/stdio.in", "r"); + ERROR_IF(fopen, f, == NULL); + + char line[256]; + + while (1) { + if (fgets(line, 256, f)) { + fputs(line, stdout); + } else { + puts("EOF"); + if (!feof(f)) { + puts("feof() not updated!"); + exit(EXIT_FAILURE); + } + break; + } + } +} diff --git a/tests/stdio/fputs.c b/tests/stdio/fputs.c new file mode 100644 index 0000000000..e377bfe8db --- /dev/null +++ b/tests/stdio/fputs.c @@ -0,0 +1,19 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *f = fopen("stdio/fputs.out", "w"); + ERROR_IF(fopen, f, == NULL); + + char *in = "Hello World!"; + + int p = fputs(in, f); + ERROR_IF(fputs, p, == EOF); + UNEXP_IF(fputs, p, < 0); + + int c = fclose(f); + ERROR_IF(fclose, c, == EOF); + UNEXP_IF(fclose, c, != 0); +} diff --git a/tests/stdio/fputs.out b/tests/stdio/fputs.out new file mode 100644 index 0000000000..c57eff55eb --- /dev/null +++ b/tests/stdio/fputs.out @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/tests/stdio/fread.c b/tests/stdio/fread.c new file mode 100644 index 0000000000..cc084d1cd4 --- /dev/null +++ b/tests/stdio/fread.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *fp = fopen("stdio/fread.in", "rb"); + ERROR_IF(fopen, fp, == NULL); + + char buf[33] = { 0 }; + for (int i = 1; i <= 32; ++i) { + size_t nread = fread(buf, 1, i, fp); + if (nread == 0) { + if (feof(fp)) { + fprintf(stderr, "early EOF\n"); + return EXIT_FAILURE; + } else { + perror("fread"); + return EXIT_FAILURE; + } + } + buf[i] = 0; + + printf("%s\n", buf); + } + + fclose(fp); +} diff --git a/tests/stdio/fread.in b/tests/stdio/fread.in new file mode 100644 index 0000000000..94b32220d7 --- /dev/null +++ b/tests/stdio/fread.in @@ -0,0 +1 @@ +122333444455555666666777777788888888999999999AAAAAAAAAABBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDDEEEEEEEEEEEEEEFFFFFFFFFFFFFFF0000000000000000111111111111111112222222222222222223333333333333333333444444444444444444445555555555555555555556666666666666666666666777777777777777777777778888888888888888888888889999999999999999999999999AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 diff --git a/tests/stdio/freopen.c b/tests/stdio/freopen.c new file mode 100644 index 0000000000..f1415399bc --- /dev/null +++ b/tests/stdio/freopen.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int test_reopen_opens_file(void) { + FILE *f = freopen("stdio/stdio.in", "r", stdin); + ERROR_IF(freopen, f, == NULL); + + char in[6]; + fgets(in, 6, stdin); + printf("%s\n", in); // should print Hello + fclose(f); + return 0; +} + +int test_reopen_resets_orientation(void) { + FILE *f = freopen("stdio/stdio.in", "r", stdin); + assert(fwide(f, 0) == 0); + assert(fwide(f, -1) == -1); + + f = freopen("stdio/stdio.in", "r", stdin); + assert(fwide(f, 0) == 0); + + fclose(f); + return 0; +} + +int main(void) { + int(*tests[])(void) = { + &test_reopen_opens_file, + &test_reopen_resets_orientation, + }; + for(size_t i = 0; i < sizeof(tests) / sizeof(int(*)(void)); i++) { + printf("%d\n", (*tests[i])()); + } +} diff --git a/tests/stdio/fscanf.c b/tests/stdio/fscanf.c new file mode 100644 index 0000000000..01d4c9c03b --- /dev/null +++ b/tests/stdio/fscanf.c @@ -0,0 +1,13 @@ +//@ 1 2 3 +//@ SS +#include +int main() { + FILE *f = fopen("stdio/fscanf.c", "r"); + int x, y, z; + fscanf(f, "//@ %d %d %d",&x , &y, &z); + printf("%d %d %d %ld\n", x, y, z, ftell(f)); + while(-1 != fscanf(f, "%*[^\n]")) { + printf("%ld\n", ftell(f)); + getc(f); + } +} \ No newline at end of file diff --git a/tests/stdio/fscanf_offby1.c b/tests/stdio/fscanf_offby1.c new file mode 100644 index 0000000000..c833935793 --- /dev/null +++ b/tests/stdio/fscanf_offby1.c @@ -0,0 +1,8 @@ +//1234 a +#include +int main() { + FILE *f = fopen("stdio/fscanf_offby1.c", "r"); + int x; + fscanf(f, "//%d", &x); + printf("%d, %ld, %d\n", x, ftell(f), fgetc(f)); +} diff --git a/tests/stdio/fseek.c b/tests/stdio/fseek.c new file mode 100644 index 0000000000..9b8bc1b046 --- /dev/null +++ b/tests/stdio/fseek.c @@ -0,0 +1,20 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *f = fopen("stdio/stdio.in", "r"); + ERROR_IF(fopen, f, == NULL); + + int status = fseek(f, 14, SEEK_CUR); + ERROR_IF(fseek, status, == -1); + UNEXP_IF(fseek, status, != 0); + + char buffer[256]; + printf("%s", fgets(buffer, 256, f)); + + off_t pos = ftello(f); + ERROR_IF(ftello, pos, == -1); + printf("ftello: %ld\n", pos); +} diff --git a/tests/stdio/fwrite.c b/tests/stdio/fwrite.c new file mode 100644 index 0000000000..308c9c296a --- /dev/null +++ b/tests/stdio/fwrite.c @@ -0,0 +1,27 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *f = fopen("stdio/fwrite.out", "w"); + ERROR_IF(fopen, f, == NULL); + + const char ptr[] = "Hello World!"; + + if (fwrite(ptr, 0, 17, f)) { + exit(EXIT_FAILURE); + } + + if (fwrite(ptr, 7, 0, f)) { + exit(EXIT_FAILURE); + } + + if (fwrite(ptr, 0, 0, f)) { + exit(EXIT_FAILURE); + } + + fwrite(ptr, sizeof(ptr), 1, f); + fclose(f); +} diff --git a/tests/stdio/fwrite.out b/tests/stdio/fwrite.out new file mode 100644 index 0000000000000000000000000000000000000000..3615520240bf84fe2471475fd53ce54de9600604 GIT binary patch literal 13 UcmeZB&B@7E2+uFdNl|0~03e41J^%m! literal 0 HcmV?d00001 diff --git a/tests/stdio/getc_unget.c b/tests/stdio/getc_unget.c new file mode 100644 index 0000000000..77351c40f1 --- /dev/null +++ b/tests/stdio/getc_unget.c @@ -0,0 +1,15 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + ungetc('h', stdin); + char c; + if ((c = getchar()) == 'h') { + printf("Worked!\n"); + exit(EXIT_SUCCESS); + } + printf("failed :( %c\n", c); + exit(EXIT_FAILURE); +} diff --git a/tests/stdio/getline.c b/tests/stdio/getline.c new file mode 100644 index 0000000000..87a0fabca6 --- /dev/null +++ b/tests/stdio/getline.c @@ -0,0 +1,168 @@ +// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html + +/// ssize_t getdelim(char **restrict lineptr, size_t *restrict n, +/// int delimiter, FILE *restrict stream); +/// ssize_t getline(char **restrict lineptr, size_t *restrict n, +/// FILE *restrict stream); + +#include +#include +#include +#include + +#include "test_helpers.h" + +const char *INFILE_NAME = "stdio/getline.in"; +const size_t INFILE_LINES = 7; +const size_t OVERREAD_LINES = 3; + +void test_null_args(); + +void test_ferror(); + +void test_read_and_overread(); + +int main(void) { + setbuf(stdout, NULL); + + // test if supplying NULL for either pointer correctly yields EINVAL + test_null_args(); + + // test reading stream with error flag set + test_ferror(); + + // "normal" case - read all `INFILE_LINES` lines of the test file and then overread by 3 + test_read_and_overread(); +} + +void test_null_args() { + + + // Basics: NULL handling + // Test all combinations of NULL pointers since we fail on any one. + // + // I can't explicitly find that stream can't be NULL but nothing else + // makes sense, so let's return EINVAL +#define TEST_FOR_EINVAL(x) \ + do \ + { \ + size_t n = 0; \ + char *lineptr = NULL; \ + ssize_t status = 0; \ + FILE *stream = NULL; \ + \ + stream = fopen(INFILE_NAME, "r"); \ + ERROR_IF(fopen, stream, == NULL); \ + ERROR_IF(fopen, ferror(stream), ); \ + \ + status = x; \ + printf("%3zu: %s\n => ", ++counter, #x); \ + CHECK_AND_PRINT_ERRNO(EINVAL); \ + assert(status == -1 && errno == EINVAL); \ + \ + if (stream != NULL) \ + fclose(stream); \ + if (lineptr != NULL) \ + free(lineptr); \ + stream = NULL; \ + errno = 0; \ + (void)n; \ + } while (0); + + static size_t counter = 0; + + TEST_FOR_EINVAL(getline(NULL, NULL, stream)); + + TEST_FOR_EINVAL(getline(NULL, &n, stream)); + TEST_FOR_EINVAL(getline(&lineptr, NULL, stream)); + + // don't always use, as glibc doesn't tolerate stream being NULL or delim being out-of-range for char +#ifdef _RELIBC_STDIO_H + TEST_FOR_EINVAL(getline(NULL, NULL, NULL)); + TEST_FOR_EINVAL(getline(&lineptr, NULL, NULL)); + TEST_FOR_EINVAL(getline(NULL, &n, NULL)); + TEST_FOR_EINVAL(getline(&lineptr, &n, NULL)); + + // test if using delim out of char range correctly causes EINVAL + // POSIX specifies UB, so we try to be more helpful + TEST_FOR_EINVAL(getdelim(&lineptr, &n, 25600, stream)); +#endif + +#undef TEST_FOR_EINVAL + printf("\n"); +} + +void test_ferror() { + // TODO test behavior on error flag set +} + +void test_read_and_overread() { + // Basic use cases + // Read all INFILE_LINES lines of sample input, then overread OVERREAD_LINES times + + size_t n = 0; + char *lineptr = NULL; + ssize_t status; + FILE *stream = NULL; + + stream = fopen(INFILE_NAME, "r"); + + for (size_t i = 0; i < INFILE_LINES; ++i) { + /// "Upon successful completion, the getline() and getdelim() functions + /// shall return the number of bytes written into the buffer, including + /// the delimiter character if one was encountered before EOF, but + /// excluding the terminating NUL character." + printf("%3zu: ", i + 1); + status = getline(&lineptr, &n, stream); + + /// "If the end-of-file indicator for the stream is set, or if no + /// characters were read and the stream is at end-of-file, the + /// end-of-file indicator for the stream shall be set and the function + /// shall return -1." + assert(!(status == -1 && feof(stream))); + + /// "If an error occurs, the error indicator for the stream shall be + /// set, and the function shall return -1 and set errno to indicate the + /// error." + ERROR_IF(getline, status, == -1 && ferror(stream)); + + // This should only execute if the other error cases (with stream flags set) did not abort. + UNEXP_IF(getline, status, == -1); + + // Print length and content to verify. Also test if the buffer is big + // enough and if strlen of input matches the return value. + // Although this doesn't HAVE to be true, as the input could contain + // NUL bytes, ours doesn't. + printf("status = %zi, strlen = %zu, feof = %s, ferror = %s\n |>%s", + status, strlen(lineptr), feof(stream) ? "1" : "0", ferror(stream) ? "1" : "0", lineptr); + // fflush(stdout); + + assert(strlen(lineptr) == (size_t) status); // we can cast to size_t since we + assert(n >= (size_t) status + 1); // UNEXP_IF against status being -1 + } + printf("\n"); + + // OVERREAD + + for (size_t i = 0; i < OVERREAD_LINES; ++i) { + /// "If the end-of-file indicator for the stream is set, or if no + /// characters were read and the stream is at end-of-file, the + /// end-of-file indicator for the stream shall be set and the function + /// shall return -1." + status = getline(&lineptr, &n, stream); + printf("overread %zu, status = %zi, feof = %s, ferror = %s\n", + i + 1, + status, + feof(stream) ? "1" : "0", ferror(stream) ? "1" : "0"); + if (i == 0) { + assert(status == -1); + assert(feof(stream)); + assert(!ferror(stream)); + } + printf("|~%s\n", lineptr); + } + + // cleanup + fclose(stream); + free(lineptr); +} diff --git a/tests/stdio/getline.in b/tests/stdio/getline.in new file mode 100644 index 0000000000..8399a9b4cb --- /dev/null +++ b/tests/stdio/getline.in @@ -0,0 +1,7 @@ +Space: the final frontier. + +These are the voyages of the starship Enterprise. +Its continuing mission: +- to explore strange new worlds; +- to seek out new life and new civilizations; +- to boldly go where no one has gone before! diff --git a/tests/stdio/mutex.c b/tests/stdio/mutex.c new file mode 100644 index 0000000000..dc729d3c5f --- /dev/null +++ b/tests/stdio/mutex.c @@ -0,0 +1,47 @@ +#include +#include +#include + +#include "test_helpers.h" + +FILE *f; + +void* thread_wontlock(void* arg) { + (void)arg; + + int result = ftrylockfile(f); + UNEXP_IF(ftrylockfile, result, == 0); + + return NULL; +} + +void* thread_willlock(void* arg) { + (void)arg; + + int result = ftrylockfile(f); + UNEXP_IF(ftrylockfile, result, != 0); + + return NULL; +} + +int main(void) { + f = fopen("stdio/stdio.in", "r"); + ERROR_IF(fopen, f, == NULL); + flockfile(f); + flockfile(f); + thread_willlock(NULL); + + pthread_t thread; + for (int i = 1; i <= 3; i++) { + pthread_create(&thread, NULL, thread_wontlock, NULL); + pthread_join(thread, NULL); + funlockfile(f); + } + + pthread_create(&thread, NULL, thread_willlock, NULL); + pthread_join(thread, NULL); + + // TODO: glibc refuses to quit test without this + // but in relibc, it will result in EPERM + // funlockfile(f); +} diff --git a/tests/stdio/popen.c b/tests/stdio/popen.c new file mode 100644 index 0000000000..0b9cc519bb --- /dev/null +++ b/tests/stdio/popen.c @@ -0,0 +1,19 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + FILE *fp = popen("ls -1 example_dir", "r"); + ERROR_IF(popen, fp, == NULL); + + int lineno = 0; + char path[256] = { 0 }; + while (fgets(path, 256, fp) != NULL) { + lineno++; + printf("Line %d: %s", lineno, path); + } + + int status = pclose(fp); + ERROR_IF(pclose, status, == -1); +} diff --git a/tests/stdio/printf.c b/tests/stdio/printf.c new file mode 100644 index 0000000000..f794782793 --- /dev/null +++ b/tests/stdio/printf.c @@ -0,0 +1,108 @@ +#include +#include +#include // free() +#include // INFINITY, NAN constants + +int main(void) { + int sofar = 0; + int len = printf( + "percent: %%\nstring: %s\nchar: %c\nchar: %c\nint: %d\n%nuint: %u\nhex: %x\nHEX: %X\nstring: %s\n", + "String", + 'c', + 254, + -16, + &sofar, + 32, + 0xbeef, + 0xC0FFEE, + "end" + ); + printf("%%n returned %d, total len of write: %d\n", sofar, len); + + puts("\nPadding madness:"); + printf("% -5.3d %+3d\n", 1, 2); + printf("%4c\n", 'c'); + printf("%#10.7x\n", 0xFF); + printf("%#4.3o\n", 01); + printf("%#x %#x\n", 0, 1); + printf("%.0d %.0d\n", 0, 1); + printf("(%05d) (%5d)\n", 123, 123); + printf("(%05d) (%5d)\n", -123, -123); + printf("(%13.0d)\n", 0); + printf("%p\n", (void*) 0xABCDEF); + printf("%p\n", (void*) 0); + + puts("\nPositional madness:"); + printf("%3$d %2$d %1$d\n", 2, 3, 4); + printf("%.*3$d\n", 2, 0, 5); + printf("|%-*6$.*5$s|%-*6$.*5$s|%*6$.*5$s|%*6$.*5$s|\n", "Fizz", "Buzz", "FizzBuzz", "TotalBuzz", 3, 8); + printf("int: %*6$d double: %lf %lf %lf %lf\n", 5, 0.1, 0.2, 0.3, 0.4, 10); + printf("%1$d %1$lf\n", 5, 0.1); + printf("%1$d %lf\n", 5, 0.2); + //printf("int: %*6$d no info on middle types\n", 5, 0.1, 0.2, 0.3, 0.4, 10); + + puts("\nFloat madness:"); + printf("%20e\n", 123.456789123); + printf("%20E\n", 0.00001); + printf("%20f\n", 123.456789123); + printf("%20F\n", 0.00001); + printf("%20e\n", -123.456789123); + printf("%020e\n", -123.456789123); + printf("%%.5g: %.5g\n", -123.456789123); + printf("%%.5f: %.5f\n", -123.456789123); + printf("%%.5e: %.5e\n", -123.456789123); + printf("%%.*g: %.*g\n", 2, -123.456789123); + printf("%%.*f: %.*f\n", 2, -123.456789123); + printf("%%.*e: %.*e\n", 2, -123.456789123); + printf("%%.*2$g: %.*2$g\n", -123.456789123, 5); + printf("%%.*2$f: %.*2$f\n", -123.456789123, 5); + printf("%%.*2$e: %.*2$e\n", -123.456789123, 5); + + printf("%g\n", 100000.0); + printf("%g\n", 1000000.0); + printf("%e\n", 1000000.0); + printf("%G\n", 0.0001); + printf("%G\n", 0.00001); + printf("%E\n", 0.00001); + + printf("%2$0*1$.*3$lf\n", 9, 1234.56, 2); + printf("%2$0*1$.*3$Lf\n", 9, 1234.56L, 2); + + double nonfinites[] = {INFINITY, -INFINITY, NAN, -NAN}; + char *float_formats[] = {"%e", "%E", "%f", "%F", "%g", "%G"}; + puts("\nNon-finite float madness:"); + for (size_t i = 0; i < sizeof(float_formats)/sizeof(char *); i++) { + printf("%s:", float_formats[i]); + for (size_t j = 0; j < sizeof(nonfinites)/sizeof(double); j++) { + printf(" "); + printf(float_formats[i], nonfinites[j]); + } + printf("\n"); + } + + puts("Things that have been buggy"); + printf("%s%0*lu\n", "+", 2, 5l); // this format string was found in GDB + + puts("Testing asprintf..."); + char *s = NULL; + int res = asprintf(&s, "test string"); + printf("printed: %s, value: %d\n", s, res); + free(s); + res = asprintf(&s, "test string %d", 2); + printf("printed: %s, value: %d\n", s, res); + free(s); + res = asprintf(&s, "test %s %d", "string", 2); + printf("printed: %s, value: %d\n", s, res); + free(s); + + errno = EOWNERDEAD; + printf("\n%%m: %m\n"); + + puts("\nC23:"); + printf("Binary %%b: %b\n", 4); + printf("Binary %%b alternate: %#b\n", 4); + printf("Binary %%B: %B\n", 4); + printf("Binary %%B alternate: %#B\n", 4); + + return EXIT_SUCCESS; +} diff --git a/tests/stdio/printf_neg_pad.c b/tests/stdio/printf_neg_pad.c new file mode 100644 index 0000000000..7f443034b9 --- /dev/null +++ b/tests/stdio/printf_neg_pad.c @@ -0,0 +1,5 @@ +#include +int main() { + printf ("A%*s%s/\n", 5, "B", "CC"); + printf ("A%*s%s/\n", -5, "B", "CC"); +} diff --git a/tests/stdio/printf_space_pad.c b/tests/stdio/printf_space_pad.c new file mode 100644 index 0000000000..9e9172826d --- /dev/null +++ b/tests/stdio/printf_space_pad.c @@ -0,0 +1,4 @@ +#include +int main() { + printf ("%s%*s\n","A", 3, "B"); +} diff --git a/tests/stdio/putc_unlocked.c b/tests/stdio/putc_unlocked.c new file mode 100644 index 0000000000..693bdafafe --- /dev/null +++ b/tests/stdio/putc_unlocked.c @@ -0,0 +1,23 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) +{ + FILE* fp = tmpfile(); + assert(fp != NULL); + flockfile(fp); + int c = 'c', r = 0; + r = putc_unlocked(c, fp); + ERROR_IF(putc_unlocked, r, == EOF); + r = fflush(fp); + ERROR_IF(fflush, r, == EOF); + funlockfile(fp); + // make sure unlock works + r = putc(c, fp); + ERROR_IF(putc, r, == EOF); + r = fflush(fp); + ERROR_IF(fflush, r, == EOF); + return 0; +} diff --git a/tests/stdio/rename.c b/tests/stdio/rename.c new file mode 100644 index 0000000000..293a9fb302 --- /dev/null +++ b/tests/stdio/rename.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +static char oldpath[] = "old-name.out"; +static char newpath[] = "new-name.out"; +static char str[] = "Hello, World!"; + +// Test that renaming a broken symbolic link works. +// Symlinks should not be resolved by rename. +// One of the problems that arises when links are resolved is that +// broken links can't be renamed. +// +// This test returns EXIT_FAILURE/EXIT_SUCCESS because it needs to clean +// up temporary files on failure. The test helpers call _exit. +int rename_broken_symlink(void) { + int result = EXIT_FAILURE; + + char dir_template[] = "/tmp/sltest.XXXXXX"; + size_t dlen = sizeof(dir_template) - 1; + char* temp_dir = mkdtemp(dir_template); + if (!temp_dir) { + perror("mkdtemp"); + return EXIT_FAILURE; + } + + // TODO: Almost all of the code below can be vastly simplified + // with openat/symlinkat later. + + // Broken link to be created + const char link_name[] = "sym"; + char link_path[PATH_MAX] = {0}; + memcpy(link_path, temp_dir, dlen); + link_path[dlen] = '/'; + memcpy(&link_path[dlen + 1], link_name, sizeof(link_name)); + + // Non-existing target + const char link_target[] = "target"; + char target_path[PATH_MAX] = {0}; + memcpy(target_path, temp_dir, dlen); + target_path[dlen] = '/'; + memcpy(&target_path[dlen + 1], link_target, sizeof(link_target)); + + // New name of link (i.e. mv sym symrename) + const char link_rename[] = "symrename"; + char rename_path[PATH_MAX] = {0}; + memcpy(rename_path, temp_dir, dlen); + rename_path[dlen] = '/'; + memcpy(&rename_path[dlen + 1], link_rename, sizeof(link_rename)); + + // Target most definitely does NOT exist. + // This is a sanity check that shouldn't fail as test uses temp dirs. + int target_fd = open(target_path, O_RDONLY); + if (target_fd != -1) { + fprintf(stderr, + "Target exists when it shouldn't: %s\n", + target_path + ); + // Skip clean up on the very exceptional case that the + // randomized dir and file exists. + goto skip_cleanup; + } + + // Create a broken symlink in a temp directory + if (symlink(target_path, link_path) < 0) { + perror("symlink"); + goto cleanup; + } + + // Rename the link; this should work even if target doesn't exist + if (rename(link_path, rename_path) < 0) { + perror("rename"); + goto cleanup; + } + + // TODO: Assert paths exist (needs openat) + + result = EXIT_SUCCESS; +cleanup: + unlink(link_path); + unlink(rename_path); + rmdir(temp_dir); +skip_cleanup: + return result; +} + +int main(void) { + char buf[14] = { 0 }; + + // Create old file + int fd = creat(oldpath, 0777); + ERROR_IF(creat, fd, == -1); + UNEXP_IF(creat, fd, < 0); + + int written_bytes = write(fd, str, strlen(str)); + ERROR_IF(write, written_bytes, == -1); + + int c1 = close(fd); + ERROR_IF(close, c1, == -1); + UNEXP_IF(close, c1, != 0); + + // Rename old file to new file + int rn_status = rename(oldpath, newpath); + ERROR_IF(rename, rn_status, == -1); + UNEXP_IF(rename, rn_status, != 0); + + // Read new file + fd = open(newpath, O_RDONLY); + ERROR_IF(open, fd, == -1); + UNEXP_IF(open, fd, < 0); + + int read_bytes = read(fd, buf, strlen(str)); + ERROR_IF(read, read_bytes, == -1); + UNEXP_IF(read, read_bytes, < 0); + + int c2 = close(fd); + ERROR_IF(close, c2, == -1); + UNEXP_IF(close, c2, != 0); + + // Remove new file + int rm_status = remove(newpath); + ERROR_IF(remove, rm_status, == -1); + UNEXP_IF(remove, rm_status, != 0); + + // Compare file contents + if (strcmp(str, buf) != 0) { + puts("Comparison failed!"); + exit(EXIT_FAILURE); + } + + int broken_symlink_res = rename_broken_symlink(); + assert(broken_symlink_res == EXIT_SUCCESS); +} diff --git a/tests/stdio/renameat.c b/tests/stdio/renameat.c new file mode 100644 index 0000000000..9fba5fc762 --- /dev/null +++ b/tests/stdio/renameat.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include + +static int temp_file( + const char dir[], + size_t dlen, + char buf[PATH_MAX], + const char name[], + size_t nlen, + bool create +) { + memcpy(buf, dir, dlen); + buf[dlen] = '/'; + memcpy(&buf[dlen + 1], name, nlen); + + if (create) { + int fd = open(buf, O_CREAT); + if (fd == -1) { + perror("open (file A)"); + close(fd); + return -1; + } + close(fd); + } + + return 0; +} + +int main(void) { + int result = EXIT_FAILURE; + + char dir_template[] = "/tmp/mvattest.XXXXXX"; + size_t dlen = sizeof(dir_template) - 1; + if (!mkdtemp(dir_template)) { + perror("mkdtemp"); + goto bye; + } + int dirfd = open(dir_template, O_DIRECTORY | O_PATH); + if (dirfd == -1) { + perror("open (temp directory)"); + goto bye; + } + + char dir_template2[] = "/tmp/mvattest2.XXXXXX"; + size_t dlen2 = sizeof(dir_template2) - 1; + if (!mkdtemp(dir_template2)) { + perror("mkdtemp"); + goto close_dir1; + } + int dirfd2 = open(dir_template2, O_DIRECTORY | O_PATH); + if (dirfd2 == -1) { + perror("open (temp directory)"); + goto close_dir1; + } + + // File A + const char file_a[] = "power"; + char file_a_path[PATH_MAX] = {0}; + if (temp_file(dir_template, dlen, file_a_path, file_a, sizeof(file_a), true) == -1) { + goto close_dir2; + } + + // File B + const char file_b[] = "nyanko"; + char file_b_path[PATH_MAX] = {0}; + if ( + temp_file( + dir_template, + dlen, + file_b_path, + file_b, + sizeof(file_b), + false + ) == -1 + ) { + goto remove_ab; + } + + // File A but in directory 2 + char file_a_dir2[PATH_MAX] = {0}; + if ( + temp_file( + dir_template2, + dlen2, + file_a_dir2, + file_a, + sizeof(file_a), + false + ) == -1 + ) { + goto remove_all; + } + + // Move file A to file B normally (same dir) + if (renameat(dirfd, file_a, dirfd, file_b) == -1) { + perror("renameat (A -> B, basic)"); + goto remove_all; + } + if (access(file_b_path, F_OK) == -1) { + perror("access (file B, basic)"); + goto remove_all; + } + + // Move file B to A (absolute path; same dir) + if (renameat(dirfd, file_b_path, dirfd, file_a) == -1) { + perror("renameat (B -> A, absolute path)"); + goto remove_all; + } + if (access(file_a_path, F_OK) == -1) { + perror("access (file A, absolute path)"); + goto remove_all; + } + + // Move A to B (both absolute) + if (renameat(dirfd, file_a_path, dirfd, file_b_path) == -1) { + perror("renameat (A -> B, both absolute)"); + goto remove_all; + } + if (access(file_b_path, F_OK) == -1) { + perror("access (file B, both absolute)"); + goto remove_all; + } + + // Move B to B + if (renameat(dirfd, file_b, dirfd, file_b) == -1) { + perror("renameat (B -> B)"); + goto remove_all; + } + if (access(file_b_path, F_OK) == -1) { + perror("access (file B)"); + goto remove_all; + } + + // Move file B to A (AT_FDCWD) + char cwd[PATH_MAX] = {0}; + if (!getcwd(cwd, PATH_MAX)) { + perror("getcwd"); + goto remove_all; + } + if (chdir(dir_template) == -1) { + perror("chdir"); + goto remove_all; + } + + if (renameat(AT_FDCWD, file_b, dirfd, file_a) == -1) { + perror("renameat (B -> A, AT_FDCWD)"); + goto remove_all; + } + if (access(file_a_path, F_OK) == -1) { + perror("access (file A, AT_FDCWD)"); + goto remove_all; + } + + // Reset, though it doesn't really matter. + if (chdir(cwd) == -1) { + perror("chdir"); + goto remove_all; + } + + // Move to different directory + if (renameat(dirfd, file_a, dirfd2, file_a) == -1) { + perror("renameat (A -> B, different dirs)"); + goto remove_all; + } + if (access(file_a_dir2, F_OK) == -1) { + perror("access (file A in dir2)"); + goto remove_all; + } + + // Move non-existing file + if (renameat(dirfd, "aki", dirfd, "denji") == 0) { + // Wut? + fputs("renameat succeeded at moving a non-existing file\n", stderr); + goto remove_all; + } + + // RENAME_NOREPLACE + // Create file B in dir 1 first because we moved it earlier. + int fd = open(file_b_path, O_CREAT); + if (fd == -1) { + perror("open (file B in dir 1)"); + close(fd); + goto remove_all; + } + close(fd); + + if (renameat2(dirfd, file_b, dirfd2, file_a, RENAME_NOREPLACE) == 0) { + fputs("renameat2 (B -> A, noreplace) succeeded\n", stderr); + goto remove_all; + } + if (access(file_a_dir2, F_OK) == -1) { + fputs("RENAME_NOREPLACE ate file A in dir 2\n", stderr); + goto remove_all; + } + if (access(file_b_path, F_OK) == -1) { + fputs("RENAME_NOREPLACE ate file B in dir 1\n", stderr); + goto remove_all; + } + + // TODO: RENAME_EXCHANGE (Needs frename support in Redox) + // Notes for later: + // * Write a message to both files + // * Swap + // * Check if the files swapped correctly by strcmp messages + + result = EXIT_SUCCESS; +remove_all: + remove(file_a_dir2); +remove_ab: + remove(file_a_path); + remove(file_b_path); + rmdir(dir_template); + rmdir(dir_template2); +close_dir2: + close(dirfd2); +close_dir1: + close(dirfd); +bye: + return result; +} diff --git a/tests/stdio/scanf.c b/tests/stdio/scanf.c new file mode 100644 index 0000000000..6a02557383 --- /dev/null +++ b/tests/stdio/scanf.c @@ -0,0 +1,83 @@ +#include +#include + +#include "test_helpers.h" + +struct params { + short sa; + int ia; + int ib; + int ic; + float fa; + double da; + int *ptr; + char c; + char string1[20]; + char string2[20]; + char string3[20]; + char string4[20]; +}; + +void test(char* fmt_in, char* input, struct params *p, ...) { + va_list args; + va_start(args, p); + int ret = vsscanf(input, fmt_in, args); + va_end(args); + + printf( + "%d, { sa: %hhd, ia: %d, ib: %d, ic: %d, fa: %f, da: %lf, ptr: %p, char: %c, string1: %s, string2: %s, string3: %s, string4: %s }\n", + ret, p->sa, p->ia, p->ib, p->ic, p->fa, p->da, p->ptr, p->c, p->string1, p->string2, p->string3, p->string4 + ); +} + +int main(void) { + struct params p = { .c = 'a' }; + + test("%hd %d", "12 345", &p, &p.sa, &p.ia); + test("%x %i %i", "12 0x345 010", &p, &p.ia, &p.ib, &p.ic); + test("%f.%lf", "0.1.0.2", &p, &p.fa, &p.da); + test("%p", "0xABCDEF", &p, &p.ptr); + test("%s", "Hello World", &p, &p.string1); + test("%3i", "0xFF", &p, &p.ia); + test("%c%3c", "hello", &p, &p.c, &p.string1); + test("test: %2i%n", "test: 0xFF", &p, &p.ia, &p.ib); + test("hello world%%", "hello world%", &p); + test("hello %5ls %d", "hello world 42", &p, &p.string1, &p.ia); + test("bye %ls %d", "bye planet 84", &p, &p.string2, &p.ia); + test("h%1[ae]ll%1[^a] wor%1[^\n]%[d]", "hello world", &p, &p.string1, &p.string2, &p.string3, &p.string4); + test("h%1[ae]ll%1[^a] wor%1[^\n]%[d]", "halle worfdddddd", &p, &p.string1, &p.string2, &p.string3, &p.string4); + test("h%1[ae]ll%1[^a] wor%1[^\n]%[d]", "halle worfdddddd", &p, &p.string1, &p.string2, &p.string3, &p.string4); + test("%[^a]%[b]", "testbbbb", &p, &p.string1, &p.string2); + + + // Scanf stolen from the url parsing in curl + char protobuf[16]; + char slashbuf[4]; + char hostbuf[100]; + char pathbuf[100]; + + // don't push NUL, make sure scanf does that + memset(protobuf, 97, 16); + memset(slashbuf, 97, 4); + memset(hostbuf, 97, 100); + memset(pathbuf, 97, 100); + + int ret = sscanf( + "https://redox-os.org\0# extra garbage for nul test", "%15[^\n/:]:%3[/]%[^\n/?#]%[^\n]", + &protobuf, &slashbuf, &hostbuf, &pathbuf + ); + if (ret < 4) { + *pathbuf = 0; + } + if (ret < 3) { + *hostbuf = 0; + } + if (ret < 2) { + *slashbuf = 0; + } + if (ret < 1) { + *protobuf = 0; + } + + printf("%d \"%s\" \"%s\" \"%s\" \"%s\"\n", ret, &protobuf, &slashbuf, &hostbuf, &pathbuf); +} diff --git a/tests/stdio/setvbuf.c b/tests/stdio/setvbuf.c new file mode 100644 index 0000000000..2e79590aba --- /dev/null +++ b/tests/stdio/setvbuf.c @@ -0,0 +1,15 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + setvbuf(stdout, 0, _IONBF, 0); + FILE *f = fopen("stdio/stdio.in", "r"); + setvbuf(f, 0, _IONBF, 0); + printf("%c\n", fgetc(f)); + ungetc('H', f); + char *in = malloc(30); + printf("%s\n", fgets(in, 30, f)); + printf("Hello\n"); +} diff --git a/tests/stdio/sprintf.c b/tests/stdio/sprintf.c new file mode 100644 index 0000000000..0c29fe291b --- /dev/null +++ b/tests/stdio/sprintf.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char buffer[72]; + + int ret = sprintf( + buffer, + "This string fits in the buffer because it is only %d bytes in length", + 68 + ); + if (ret != 68) { + printf("Failed! Return value was %d\n", ret); + exit(EXIT_FAILURE); + } + + memset(buffer, 0, sizeof(buffer)); + + ret = snprintf( + buffer, + 10, + "This string is way longer and does not fit in the buffer because it %d bytes in length", + 86 + ); + if (ret != 86) { + printf("Failed! Return value was %d\n", ret); + exit(EXIT_FAILURE); + } + + puts(buffer); +} diff --git a/tests/stdio/stdio.in b/tests/stdio/stdio.in new file mode 100644 index 0000000000..e4d4901390 --- /dev/null +++ b/tests/stdio/stdio.in @@ -0,0 +1,30 @@ +Hello World! + +Line 2 + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg +hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj +kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll +mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm +nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp +qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr +sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss +ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz diff --git a/tests/stdio/tempnam.c b/tests/stdio/tempnam.c new file mode 100644 index 0000000000..8fd7edf6d0 --- /dev/null +++ b/tests/stdio/tempnam.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#include "test_helpers.h" + +static void test_prefix(const char *prefix); +static void test_dir(const char *dir); +static void test_dir_and_prefix(const char *dir, const char *prefix); + +int main(void) { + char *first_null = tempnam(NULL, NULL); + if(first_null == NULL) { + // NOTE: assuming that we can at least get one file name + puts("tempnam(NULL, NULL) returned NULL on first try"); + exit(EXIT_FAILURE); + } + printf("%s\n", first_null); + + char *second_null = tempnam(NULL, NULL); + if(second_null == NULL) { + // NOTE: assuming that we can at least get one file name + puts("tempnam(NULL, NULL) returned NULL on second try"); + free(first_null); + exit(EXIT_FAILURE); + } + printf("%s\n", second_null); + + free(first_null); + free(second_null); + + if(first_null == second_null) { + puts("tempnam(NULL, NULL) returns the same address"); + exit(EXIT_FAILURE); + } + + // Ensure the "prefix" argument works + test_prefix("this_is_a_test_prefix"); + test_prefix("exact"); + test_prefix("hi"); + test_prefix(""); + + // Ensure the "dir" argument works + + // NOTE: needed because TMPDIR is the first directory checked + unsetenv("TMPDIR"); + + test_dir("/tmp"); + test_dir(""); + // NOTE: assumes /root is NOT writable + test_dir("/root"); + + // Ensure "prefix" and "dir" work together + test_dir_and_prefix("/tmp", "this_is_a_prefix"); + test_dir_and_prefix("/tmp", "exact"); + test_dir_and_prefix("/root", "exact"); + test_dir_and_prefix("/root", "long_prefix"); + test_dir_and_prefix("", "prefix"); + test_dir_and_prefix("/tmp", "test"); + + return 0; +} + +static void test_prefix(const char *prefix) { + test_dir_and_prefix(NULL, prefix); +} + +static void test_dir(const char *dir) { + test_dir_and_prefix(dir, NULL); +} + +static void test_dir_and_prefix(const char *dir, const char *prefix) { + char funcbuf[256]; + if(dir && prefix) { + snprintf(funcbuf, sizeof(funcbuf), "tempnam(\"%s\", \"%s\")", dir, prefix); + } else if(dir) { + snprintf(funcbuf, sizeof(funcbuf), "tempnam(\"%s\", NULL)", dir); + } else if(prefix) { + snprintf(funcbuf, sizeof(funcbuf), "tempnam(NULL, \"%s\")", prefix); + } else { + strcpy(funcbuf, "tempnam(NULL, NULL)"); + } + + char *result = tempnam(dir, prefix); + if(!result) { + printf("%s failed\n", funcbuf); + exit(EXIT_FAILURE); + } + printf("%s\n", result); + + if(prefix) { + char buf[7] = { '/' }; + strncpy(&buf[1], prefix, sizeof(buf) - 2); + buf[6] = 0; + + char *prev = NULL; + char *substr = result; + do { + prev = substr; + substr = strstr(&substr[1], buf); + } while(substr); + substr = prev; + + if(!substr) { + printf("%s did not add the full (5 bytes at most) prefix\n", funcbuf); + free(result); + exit(EXIT_FAILURE); + } else if(strlen(substr) != strlen(&buf[1]) + L_tmpnam) { + printf("%s has the wrong length\n", funcbuf); + free(result); + exit(EXIT_FAILURE); + } + } + + if(dir) { + char *substr = strstr(result, dir); + char *other_substr = strstr(result, P_tmpdir); + if(!substr && !other_substr) { + printf("%s is in an unexpected directory\n", funcbuf); + free(result); + exit(EXIT_FAILURE); + } + } + + free(result); +} + diff --git a/tests/stdio/tmpnam.c b/tests/stdio/tmpnam.c new file mode 100644 index 0000000000..02c07d6724 --- /dev/null +++ b/tests/stdio/tmpnam.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *first_null = tmpnam(NULL); + if(first_null == NULL) { + // NOTE: assuming that we can at least get one file name + puts("tmpnam(NULL) returned NULL on first try"); + exit(EXIT_FAILURE); + } + printf("%s\n", first_null); + + char *second_null = tmpnam(NULL); + if(second_null == NULL) { + // NOTE: assuming that we can at least get one file name + puts("tmpnam(NULL) returned NULL on second try"); + exit(EXIT_FAILURE); + } + printf("%s\n", second_null); + + if(first_null != second_null) { + puts("tmpnam(NULL) returns different addresses"); + exit(EXIT_FAILURE); + } + + char buffer[L_tmpnam + 1]; + char *buf_result = tmpnam(buffer); + if(buf_result == NULL) { + puts("tmpnam(buffer) failed"); + exit(EXIT_FAILURE); + } else if(buf_result != buffer) { + puts("tmpnam(buffer) did not return buffer's address"); + exit(EXIT_FAILURE); + } + printf("%s\n", buffer); + + return 0; +} diff --git a/tests/stdio/ungetc_ftell.c b/tests/stdio/ungetc_ftell.c new file mode 100644 index 0000000000..10be6a3057 --- /dev/null +++ b/tests/stdio/ungetc_ftell.c @@ -0,0 +1,37 @@ +#include +int main() { + FILE *f = fopen("stdio/ungetc_ftell.c", "r"); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + ungetc('\n', f);ungetc('d', f); + ungetc('l', f); ungetc('r', f); + ungetc('o', f); ungetc('w', f); + ungetc(' ', f); ungetc('o', f); + ungetc('l', f); ungetc('l', f); + ungetc('e', f); ungetc('h', f); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); + printf("%c, %ld\n", getc(f), ftell(f)); +} diff --git a/tests/stdio/ungetc_multiple.c b/tests/stdio/ungetc_multiple.c new file mode 100644 index 0000000000..611430489a --- /dev/null +++ b/tests/stdio/ungetc_multiple.c @@ -0,0 +1,27 @@ +#include +int main() { + ungetc('\n', stdin); + ungetc('d', stdin); + ungetc('l', stdin); + ungetc('r', stdin); + ungetc('o', stdin); + ungetc('w', stdin); + ungetc(' ', stdin); + ungetc('o', stdin); + ungetc('l', stdin); + ungetc('l', stdin); + ungetc('e', stdin); + ungetc('h', stdin); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); + putchar(getchar()); +} diff --git a/tests/stdlib/a64l.c b/tests/stdlib/a64l.c new file mode 100644 index 0000000000..2696c34664 --- /dev/null +++ b/tests/stdlib/a64l.c @@ -0,0 +1,53 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char * s = "azAZ9."; // test boundaries + long l = a64l(s); + if (l != 194301926) { + printf("Invalid result: a64l(%s) = %ld\n", s, l); + exit(EXIT_FAILURE); + } + printf("Correct a64l: %s = %ld\n", s, l); + + + s = "azA"; // test null terminated string + l = a64l(s); + if (l != 53222) { + printf("Invalid result: a64l(%s) = %ld\n", s, l); + exit(EXIT_FAILURE); + } + printf("Correct a64l: %s = %ld\n", s, l); + + /* Test near boundaries of digit character mapping, and near + * boundaries for number of digits */ + long l64a_test_values[] = {0, 1, 2, 11, 12, 37, \ + 38, 63, \ + 64, 65, 4095, \ + 4096, 262143, \ + 262144, 16777215, \ + 16777216, 1073741823, \ + 1073741824, 2147483647}; + + // l64a tests + for (size_t i = 0; i < sizeof(l64a_test_values)/sizeof(long); i++) { + printf("l64a(%ld): %s\n", l64a_test_values[i], l64a(l64a_test_values[i])); + } + + // a64l(l64a(x)) round-trip tests + for (size_t i = 0; i < sizeof(l64a_test_values)/sizeof(long); i++) { + printf("a64l(l64a(%ld)): %ld\n", l64a_test_values[i], a64l(l64a(l64a_test_values[i]))); + } + + /* For testing 32-bit truncation behavior (for platforms where long + * is larger than 32 bits). Note that the behavior for a64l() and + * l64a() is unspecified for negative values. */ + int64_t test_value_64bit = 0x7edcba9876543210; + printf("l64a(x) (lower 32 bits of x are %ld): %s\n", ((long)test_value_64bit) & 0xffffffff, l64a((long)test_value_64bit)); + + /* Test for trunctation in l64a(a64(x)) round trip (POSIX says the + * result of that is "x in the low-order 32-bits". */ + printf("a64l(l64a(x)) (lower 32 bits of x are %ld): %ld\n", ((long)test_value_64bit) & 0xffffffff, a64l(l64a((long)test_value_64bit))); +} diff --git a/tests/stdlib/alloc.c b/tests/stdlib/alloc.c new file mode 100644 index 0000000000..2e51c144c6 --- /dev/null +++ b/tests/stdlib/alloc.c @@ -0,0 +1,341 @@ +#include /* for pvalloc() */ +#include +#include +#include +#include /* for size_t */ +#include /* for SIZE_MAX */ +#include /* for strerror() */ +#include /* for sysconf() */ + +#include "test_helpers.h" + +/* For regular allocations that should succeed without particular + * alignment requirements. */ +void test_non_null(void *ptr, int error_val) { + if (ptr != NULL) { + // Constant output for successful case + printf("pointer: (not NULL), "); + } + else { + printf("pointer: %p, ", ptr); + } + printf("error value: %d = %s\n", + error_val, strerror(error_val)); +} + +/* For testing functions that should return pointers with a particular + * alignment (successful case). */ +void test_valid_aligned(void *ptr, size_t alignment, int error_val) { + /* Cast to uintptr_t to allow taking modulo of address. The + * uintptr_t type is guaranteed to be able to hold any valid object + * address. */ + uintptr_t ptr_alignment_rem = (uintptr_t)ptr % (uintptr_t)alignment; + + if (ptr != NULL && ptr_alignment_rem == 0) { + // Constant output for successful case + printf("pointer: (alignment OK), "); + } + else { + printf("pointer: %p, ", ptr); + } + printf("error value: %d = %s\n", + error_val, strerror(error_val)); +} + +/* For testing functions that should return pointers with a particular + * alignment. With invalid alignment, we expect constant output (a NULL + * pointer and EINVAL). */ +void test_invalid_aligned(void *ptr, int error_val) { + printf("pointer: %p, error value: %d = %s\n", + ptr, error_val, strerror(error_val)); +} + +/* For testing size-0 allocation requests. */ +void test_size_zero(void *ptr, size_t alignment, int error_val) { + /* Facilitates checking alignment upon non-NULL return */ + uintptr_t ptr_alignment_rem = (uintptr_t)ptr % (uintptr_t)alignment; + + /* For allocation functions, POSIX permits returning either a NULL + * pointer and optionally an implementation-defined error value, or + * succeeding with a non-NULL pointer. */ + if (ptr == NULL || (ptr_alignment_rem == 0 && error_val == 0)) { + // Constant output for successful case + printf("(OK)\n"); + } + else { + printf("pointer: %p, error value: %d = %s\n", + ptr, error_val, strerror(error_val)); + } +} + +/* For cases where we expect allocation to fail, returning a NULL + * pointer and indicating ENOMEM. */ +void test_cannot_alloc(void *ptr, int error_val) { + printf("pointer: %p, error value: %d = %s\n", + ptr, error_val, strerror(error_val)); +} + +int main(void) { + size_t sample_alloc_size = 256; + size_t sample_realloc_size = sample_alloc_size + 1; + + /* ensure values are mapped to variables */ + size_t zero_size = 0; + size_t max_size = SIZE_MAX; + size_t page_size = (size_t)sysconf(_SC_PAGESIZE); + size_t aligned_alloc_alignment = 128; + size_t aligned_alloc_goodsize = 256; + size_t aligned_alloc_badsize = 257; + size_t nonpow2_mul_voidptr_size = 3*sizeof(void *); + size_t pow2_mul_voidptr_size = 4*sizeof(void *); + + size_t i; + + errno = 0; + char * ptr_zerosize_malloc = (char *)malloc(zero_size); + int malloc_zerosize_errno = errno; + printf("malloc (size 0): "); + test_size_zero(ptr_zerosize_malloc, 1, malloc_zerosize_errno); + free(ptr_zerosize_malloc); + + errno = 0; + char * ptr_malloc = (char *)malloc(sample_alloc_size); + int malloc_errno = errno; + printf("malloc: "); + test_non_null(ptr_malloc, malloc_errno); + for(i = 0; i < sample_alloc_size; i++) { + ptr_malloc[i] = (char)i; + } + free(ptr_malloc); + + errno = 0; + char * ptr_malloc_maxsize = (char *)malloc(max_size); + int malloc_maxsize_errno = errno; + printf("malloc (SIZE_MAX): "); + test_cannot_alloc(ptr_malloc_maxsize, malloc_maxsize_errno); + free(ptr_malloc_maxsize); + + errno = 0; + char * ptr_zerosize_calloc = (char *)calloc(zero_size, 1); + int calloc_zerosize_errno = errno; + printf("calloc (size 0): "); + test_size_zero(ptr_zerosize_calloc, 1, calloc_zerosize_errno); + free(ptr_zerosize_calloc); + + errno = 0; + char * ptr_calloc = (char *)calloc(sample_alloc_size, 1); + int calloc_errno = errno; + printf("calloc: "); + test_non_null(ptr_calloc, calloc_errno); + for(i = 0; i < sample_alloc_size; i++) { + ptr_calloc[i] = (char)i; + } + free(ptr_calloc); + + errno = 0; + char * ptr_calloc_overflow = (char *)calloc(max_size, max_size); + int calloc_overflow_errno = errno; + printf("calloc (overflowing): "); + test_cannot_alloc(ptr_calloc_overflow, calloc_overflow_errno); + free(ptr_calloc_overflow); + + char * ptr_realloc_size0 = (char *)malloc(sample_alloc_size); + errno = 0; + ptr_realloc_size0 = (char *)realloc(ptr_realloc_size0, zero_size); + int realloc_size0_errno = errno; + printf("realloc (size 0): "); + test_size_zero(ptr_realloc_size0, 1, realloc_size0_errno); + free(ptr_realloc_size0); + + char * ptr_realloc = (char *)malloc(sample_alloc_size); + errno = 0; + ptr_realloc = (char *)realloc(ptr_realloc, sample_realloc_size); + int realloc_errno = errno; + printf("realloc: "); + test_non_null(ptr_realloc, realloc_errno); + for(i = 0; i < sample_realloc_size; i++) { + ptr_realloc[i] = (char)i; + } + free(ptr_realloc); + + char * ptr_realloc_maxsize = (char *)malloc(sample_alloc_size); + errno = 0; + ptr_realloc_maxsize = (char *)realloc(ptr_realloc_maxsize, max_size); + int realloc_maxsize_errno = errno; + printf("realloc (SIZE_MAX): "); + test_cannot_alloc(ptr_realloc_maxsize, realloc_maxsize_errno); + free(ptr_realloc_maxsize); + + errno = 0; + char * ptr_reallocarray_maxsize = (char *)malloc(sample_alloc_size); + ptr_reallocarray_maxsize = (char *)reallocarray(ptr_reallocarray_maxsize, 2, sample_alloc_size); + int reallocarray_errno = errno; + printf("reallocarray: "); + test_non_null(ptr_reallocarray_maxsize, reallocarray_errno); + for(i = 0; i < sample_realloc_size; i++) { + ptr_realloc[i] = (char)i; + } + errno = 0; + ptr_reallocarray_maxsize = (char *)reallocarray(ptr_reallocarray_maxsize, 2, max_size); + reallocarray_errno = errno; + printf("reallocarray (SIZE_MAX): "); + test_cannot_alloc(ptr_reallocarray_maxsize, reallocarray_errno); + free(ptr_reallocarray_maxsize); + + // Warning for deprecated functions is expected so silence -Werror + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + errno = 0; + char * ptr_memalign_size0 = (char *)memalign(aligned_alloc_alignment, zero_size); + int memalign_size0_errno = errno; + printf("memalign (size 0): "); + test_size_zero(ptr_memalign_size0, aligned_alloc_alignment, memalign_size0_errno); + free(ptr_memalign_size0); + + errno = 0; + char * ptr_memalign = (char *)memalign(aligned_alloc_alignment, sample_alloc_size); + int memalign_errno = errno; + printf("memalign: "); + test_valid_aligned(ptr_memalign, aligned_alloc_alignment, memalign_errno); + for(i = 0; i < sample_alloc_size; i++) { + ptr_memalign[i] = (char)i; + } + free(ptr_memalign); + + errno = 0; + char * ptr_memalign_maxsize = (char *)memalign(aligned_alloc_alignment, max_size); + int memalign_maxsize_errno = errno; + printf("memalign (SIZE_MAX): "); + test_cannot_alloc(ptr_memalign_maxsize, memalign_maxsize_errno); + free(ptr_memalign_maxsize); + + errno = 0; + char * ptr_memalign_align0 = (char *)memalign(0, sample_alloc_size); + int memalign_align0_errno = errno; + printf("memalign (alignment 0): "); + test_invalid_aligned(ptr_memalign_align0, memalign_align0_errno); + free(ptr_memalign_align0); + + errno = 0; + char * ptr_memalign_align3 = (char *)memalign(3, sample_alloc_size); + int memalign_align3_errno = errno; + printf("memalign (alignment 3): "); + test_invalid_aligned(ptr_memalign_align3, memalign_align3_errno); + free(ptr_memalign_align3); + + #pragma GCC diagnostic pop + + errno = 0; + char * ptr_aligned_alloc_goodsize = (char *)aligned_alloc(aligned_alloc_alignment, aligned_alloc_goodsize); + int aligned_alloc_goodsize_errno = errno; + printf("aligned_alloc (size %% alignment == 0): "); + test_valid_aligned(ptr_aligned_alloc_goodsize, aligned_alloc_alignment, aligned_alloc_goodsize_errno); + free(ptr_aligned_alloc_goodsize); + + errno = 0; + char * ptr_aligned_alloc_badsize = (char *)aligned_alloc(aligned_alloc_alignment, aligned_alloc_badsize); + int aligned_alloc_badsize_errno = errno; + printf("aligned_alloc (size %% alignment != 0): "); + test_invalid_aligned(ptr_aligned_alloc_badsize, aligned_alloc_badsize_errno); + free(ptr_aligned_alloc_badsize); + + // Warning for deprecated functions is expected so silence -Werror + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + + errno = 0; + char * ptr_valloc_size0 = (char *)valloc(zero_size); + int valloc_size0_errno = errno; + printf("valloc (size 0): "); + test_size_zero(ptr_valloc_size0, page_size, valloc_size0_errno); + free(ptr_valloc_size0); + + errno = 0; + char * ptr_valloc = (char *)valloc(sample_alloc_size); + int valloc_errno = errno; + printf("valloc: "); + test_valid_aligned(ptr_valloc, page_size, valloc_errno); + free(ptr_valloc); + + errno = 0; + char * ptr_valloc_maxsize = (char *)valloc(max_size); + int valloc_maxsize_errno = errno; + printf("valloc (SIZE_MAX): "); + test_cannot_alloc(ptr_valloc_maxsize, valloc_maxsize_errno); + free(ptr_valloc_maxsize); + + errno = 0; + void * ptr_posix_memalign = NULL; + int posix_memalign_return = posix_memalign(&ptr_posix_memalign, pow2_mul_voidptr_size, sample_alloc_size); + printf("posix_memalign: "); + test_valid_aligned(ptr_posix_memalign, pow2_mul_voidptr_size, posix_memalign_return); + free(ptr_posix_memalign); + + errno = 0; + void * ptr_posix_memalign_align0 = NULL; + int posix_memalign_align0_return = posix_memalign(&ptr_posix_memalign_align0, zero_size, sample_alloc_size); + printf("posix_memalign (alignment 0): "); + test_invalid_aligned(ptr_posix_memalign_align0, posix_memalign_align0_return); + free(ptr_posix_memalign_align0); + + errno = 0; + void * ptr_posix_memalign_nonpow2mul = NULL; + int posix_memalign_nonpow2mul_return = posix_memalign(&ptr_posix_memalign_nonpow2mul, nonpow2_mul_voidptr_size, sample_alloc_size); + printf("posix_memalign (non-power-of-two multiple of sizeof(void *)): "); + test_invalid_aligned(ptr_posix_memalign_nonpow2mul, posix_memalign_nonpow2mul_return); + free(ptr_posix_memalign_nonpow2mul); + + errno = 0; + void * ptr_posix_memalign_size0 = NULL; + int posix_memalign_size0_return = posix_memalign(&ptr_posix_memalign_size0, pow2_mul_voidptr_size, zero_size); + printf("posix_memalign (size 0): "); + test_size_zero(ptr_posix_memalign_size0, pow2_mul_voidptr_size, posix_memalign_size0_return); + free(ptr_posix_memalign_size0); + + errno = 0; + void * ptr_posix_memalign_maxsize = NULL; + int posix_memalign_maxsize_return = posix_memalign(&ptr_posix_memalign_maxsize, pow2_mul_voidptr_size, max_size); + printf("posix_memalign (SIZE_MAX): "); + test_cannot_alloc(ptr_posix_memalign_maxsize, posix_memalign_maxsize_return); + free(ptr_posix_memalign_maxsize); + + errno = 0; + char * ptr_pvalloc_size0 = (char *)pvalloc(zero_size); + int pvalloc_size0_errno = errno; + printf("pvalloc (size 0): "); + test_size_zero(ptr_pvalloc_size0, page_size, pvalloc_size0_errno); + free(ptr_pvalloc_size0); + + errno = 0; + char * ptr_pvalloc = (char *)pvalloc(sample_alloc_size); + int pvalloc_errno = errno; + printf("pvalloc: "); + test_valid_aligned(ptr_pvalloc, page_size, pvalloc_errno); + /* Check that the alloc size is rounded up to nearest page-size + * multiple */ + for(i = 0; i < page_size; i++) { + ptr_pvalloc[i] = (char)i; + } + free(ptr_pvalloc); + + errno = 0; + char * ptr_pvalloc_multipage = (char *)pvalloc(page_size + 1); + int pvalloc_multipage_errno = errno; + printf("pvalloc (2 pages): "); + test_valid_aligned(ptr_pvalloc_multipage, page_size, pvalloc_multipage_errno); + /* Check that the alloc size is rounded up to nearest page-size + * multiple */ + for(i = 0; i < 2*page_size; i++) { + ptr_pvalloc_multipage[i] = (char)i; + } + free(ptr_pvalloc_multipage); + + errno = 0; + char * ptr_pvalloc_maxsize = (char *)pvalloc(max_size); + int pvalloc_maxsize_errno = errno; + printf("pvalloc (SIZE_MAX): "); + test_cannot_alloc(ptr_pvalloc_maxsize, pvalloc_maxsize_errno); + free(ptr_pvalloc_maxsize); + + #pragma GCC diagnostic pop +} diff --git a/tests/stdlib/atof.c b/tests/stdlib/atof.c new file mode 100644 index 0000000000..f9783467b7 --- /dev/null +++ b/tests/stdlib/atof.c @@ -0,0 +1,12 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + double d = atof("-3.14"); + printf("%f\n", d); + + d = atof("INF"); + printf("%f\n", d); +} diff --git a/tests/stdlib/atoi.c b/tests/stdlib/atoi.c new file mode 100644 index 0000000000..26497f27f4 --- /dev/null +++ b/tests/stdlib/atoi.c @@ -0,0 +1,13 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("%d\n", atoi(" -42")); + printf("%d\n", atoi(" +555")); + printf("%d\n", atoi(" 1234567890 ")); + printf("%ld\n", atol(" -42")); + printf("%ld\n", atol(" +555")); + printf("%ld\n", atol(" 1234567890 ")); +} diff --git a/tests/stdlib/bsearch.c b/tests/stdlib/bsearch.c new file mode 100644 index 0000000000..139cb53d65 --- /dev/null +++ b/tests/stdlib/bsearch.c @@ -0,0 +1,56 @@ +#include +#include + +#include "test_helpers.h" + +int int_cmp(const void* a, const void* b) { + return *(const int*) a - *(const int*) b; +} + +#define BSEARCH_TEST_INT(key, arr, len, expect) \ + do { \ + void* res = bsearch((const void*) &key, (void*) arr, len, sizeof(int), int_cmp); \ + if (res != expect) { \ + printf("FAIL bsearch for %d in [", key); \ + size_t i = 0; \ + for (; i < len; ++i) printf("%d,", arr[i]); \ + printf("] expected %p but got %p\n", (void*) expect, res); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +int main(void) { + int x = 0; + int y = 1024; + + // TODO: Zero sized arrays are a non-standard GNU extension + //int empty[] = {}; + //BSEARCH_TEST_INT(x, empty, 0, NULL); + + int singleton[] = {42}; + printf("%p\n%p\n", singleton, &singleton[1]); + BSEARCH_TEST_INT(x, singleton, 1, NULL); + BSEARCH_TEST_INT(singleton[0], singleton, 1, &singleton[0]); + BSEARCH_TEST_INT(y, singleton, 1, NULL); + + int two[] = {14, 42}; + BSEARCH_TEST_INT(x, two, 2, NULL); + BSEARCH_TEST_INT(y, two, 2, NULL); + BSEARCH_TEST_INT(two[0], two, 2, &two[0]); + BSEARCH_TEST_INT(two[0], two, 1, &two[0]); + BSEARCH_TEST_INT(two[1], two, 2, &two[1]); + BSEARCH_TEST_INT(two[1], two, 1, NULL); + + int three[] = {-5, -1, 4}; + BSEARCH_TEST_INT(three[0], three, 3, &three[0]); + BSEARCH_TEST_INT(three[1], three, 3, &three[1]); + BSEARCH_TEST_INT(three[2], three, 3, &three[2]); + + int big[] = {-19, -13, -7, -3, 2, 5, 11}; + BSEARCH_TEST_INT(big[0], big, 7, big); + BSEARCH_TEST_INT(big[6], big, 7, &big[6]); + BSEARCH_TEST_INT(big[3], big, 7, &big[3]); + BSEARCH_TEST_INT(x, big, 7, NULL); + + printf("PASS bsearch\n"); +} diff --git a/tests/stdlib/div.c b/tests/stdlib/div.c new file mode 100644 index 0000000000..5a768b8ed8 --- /dev/null +++ b/tests/stdlib/div.c @@ -0,0 +1,23 @@ +#include + +#include "test_helpers.h" + +volatile float f; +volatile long double ld; +volatile unsigned long long ll; +lldiv_t mydivt; + +int main(void) { + char* tmp; + f = strtof("gnu", &tmp); + ld = strtold("gnu", &tmp); + ll = strtoll("gnu", &tmp, 10); + ll = strtoull("gnu", &tmp, 10); + ll = llabs(10); + mydivt = lldiv(10,1); + ll = mydivt.quot; + ll = mydivt.rem; + ll = atoll("10"); + _Exit(0); +} + diff --git a/tests/stdlib/env.c b/tests/stdlib/env.c new file mode 100644 index 0000000000..b5524067b1 --- /dev/null +++ b/tests/stdlib/env.c @@ -0,0 +1,45 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + //puts(getenv("SHELL")); + //puts(getenv("CC")); + + char* owned = malloc(26); // "TEST=Updates accordingly." + NUL + strcpy(owned, "TEST=It's working!!"); + + putenv(owned); + puts(getenv("TEST")); + + strcpy(owned, "TEST=Updates accordingly."); + puts(getenv("TEST")); + + // Allocation is reused + setenv("TEST", "in place", 1); + puts(getenv("TEST")); + puts(owned); + + // Allocation is not reused + setenv("TEST", "Value overwritten and not in place because it's really long", 1); + puts(getenv("TEST")); + puts(owned); + + // Value is not overwritten + setenv("TEST", "Value not overwritten", 0); + puts(getenv("TEST")); + + unsetenv("TEST"); + char* env = getenv("TEST"); + if (env) { + puts("This should be null, but isn't"); + puts(env); + exit(EXIT_FAILURE); + } else { + puts("Value deleted successfully!"); + } + + free(owned); +} diff --git a/tests/stdlib/getsubopt.c b/tests/stdlib/getsubopt.c new file mode 100644 index 0000000000..4717f65fcd --- /dev/null +++ b/tests/stdlib/getsubopt.c @@ -0,0 +1,55 @@ +#include "test_helpers.h" + +int main(void) { + char *const tokens[] = { + "ro", + "rw", + "foo", + "baz", + NULL + }; + + // getsubopt modifies the string in-place + char opt_str[] = "ro,foo=bar,bool,baz=,rw"; + char *options = opt_str; + char *value = NULL; + int idx; + + idx = getsubopt(&options, tokens, &value); + UNEXP_IF(getsubopt, idx, != 0); + if (value != NULL) { + printf("getsubopt failed: expected NULL value for 'ro', got '%s'\n", value); + exit(EXIT_FAILURE); + } + + idx = getsubopt(&options, tokens, &value); + UNEXP_IF(getsubopt, idx, != 2); + if (value == NULL || strcmp(value, "bar") != 0) { + printf("getsubopt failed: expected 'bar', got '%s'\n", value ? value : "NULL"); + exit(EXIT_FAILURE); + } + + idx = getsubopt(&options, tokens, &value); + UNEXP_IF(getsubopt, idx, != -1); + if (value == NULL || strcmp(value, "bool") != 0) { + printf("getsubopt failed: expected 'bool' in value, got '%s'\n", value ? value : "NULL"); + exit(EXIT_FAILURE); + } + + idx = getsubopt(&options, tokens, &value); + UNEXP_IF(getsubopt, idx, != 3); + if (value == NULL || strcmp(value, "") != 0) { + printf("getsubopt failed: expected empty string value, got '%s'\n", value ? value : "NULL"); + exit(EXIT_FAILURE); + } + + idx = getsubopt(&options, tokens, &value); + UNEXP_IF(getsubopt, idx, != 1); + if (value != NULL) { + printf("getsubopt failed: expected NULL value for 'rw', got '%s'\n", value); + exit(EXIT_FAILURE); + } + + idx = getsubopt(&options, tokens, &value); + UNEXP_IF(getsubopt, idx, != -1); +} diff --git a/tests/stdlib/mkostemps.c b/tests/stdlib/mkostemps.c new file mode 100644 index 0000000000..370ffd009a --- /dev/null +++ b/tests/stdlib/mkostemps.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char* file_name = (char*) calloc(18, sizeof(char)); + strcpy(file_name, "tempXXXXXX.suffix"); + int fd = mkostemps(file_name, 7, 0); + FILE* fp = fdopen(fd, "w+"); + printf("Start unchanged: %d\n", strncmp(file_name, "temp", 4)); + printf("End unchanged: %d\n", strcmp(file_name + 4 + 6, ".suffix")); + + char* write = "Writing to file"; + fputs(write, fp); + + char buffer[sizeof write]; + memset(buffer, 0, sizeof buffer); + fgets(buffer, strlen(buffer), fp); + if (strcmp(write, buffer)) { + printf("Read & Write Successful\n"); + } else { + printf("Read & Write Failed\n"); + } + fclose(fp); + remove(file_name); +} diff --git a/tests/stdlib/mktemp.c b/tests/stdlib/mktemp.c new file mode 100644 index 0000000000..577fb51fc2 --- /dev/null +++ b/tests/stdlib/mktemp.c @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char* string = (char*) calloc(20, sizeof(char)); + strcpy(string, "tempXXXXXX"); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + mktemp(string); + #pragma GCC diagnostic pop + printf("%s\n",string); + free(string); +} diff --git a/tests/stdlib/ptsname.c b/tests/stdlib/ptsname.c new file mode 100644 index 0000000000..82d2687d84 --- /dev/null +++ b/tests/stdlib/ptsname.c @@ -0,0 +1,89 @@ +/* Test for ptsname/ptsname_r. + Copyright (C) 2014-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include + +#define DEV_TTY "/dev/tty" +#define PTSNAME_EINVAL "./ptsname-einval" + +int do_single_test(int fd, char * buf, size_t buflen, int expected_err) { + + int ret = ptsname_r(fd, buf, buflen); + int err = errno; + + if (expected_err == 0) { + if (ret != 0) { + printf("ptsname_r: expected: return = 0\n"); + printf(" got: return = %d, errno = %d (%s)\n", + ret, err, strerror(err)); + return 1; + } + } else { + if (ret == 0 || errno != expected_err) { + printf("ptsname_r: expected: return = %d, errno = %d (%s)\n", + -1, expected_err, strerror(expected_err)); + printf(" got: return = %d, errno = %d (%s)\n", + ret, err, strerror(err)); + return 1; + } + } + + return 0; +} + +int main(void) { + char buf[512] = {0}; + int result = 0; + errno = 0; + /* Tests with a real PTS master. */ + int fd = posix_openpt(O_RDWR); + if (fd != -1) { + result |= do_single_test(fd, buf, sizeof(buf), 0); + result |= do_single_test(fd, buf, 1, ERANGE); + close(fd); + } else + printf("posix_openpt (O_RDWR) failed\nerrno %d (%s)\n", + errno, strerror(errno)); + + // TODO: open(DEV_TTY, O_RDONLY) gives error on CI + // /* Test with a terminal device which is not a PTS master. */ + // fd = open(DEV_TTY, O_RDONLY); + // if (fd != -1) { + // result |= do_single_test(fd, buf, sizeof(buf), ENOTTY); + // close(fd); + // } else + // printf("open (\"%s\", O_RDWR) failed\nerrno %d (%s)\n", + // DEV_TTY, errno, strerror(errno)); + + /* Test with a file. */ + fd = open(PTSNAME_EINVAL, O_RDWR | O_CREAT, 0600); + if (fd != -1) { + result |= do_single_test(fd, buf, sizeof(buf), ENOTTY); + close(fd); + unlink(PTSNAME_EINVAL); + } else + printf("open (\"%s\", O_RDWR | OCREAT) failed\nerrno %d (%s)\n", + PTSNAME_EINVAL, errno, strerror(errno)); + + return result; +} \ No newline at end of file diff --git a/tests/stdlib/qsort.c b/tests/stdlib/qsort.c new file mode 100644 index 0000000000..0de6d141b5 --- /dev/null +++ b/tests/stdlib/qsort.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +int values[] = { 23, 16, 8, 4, 42, 15 }; + +int cmpfunc (const void * a_ptr, const void * b_ptr) { + int a = *(const int *)a_ptr; + int b = *(const int *)b_ptr; + return a - b; +} + +int main () { + size_t i; + + printf("Before: "); + for(i = 0; i < ARRAY_SIZE(values); i++) { + printf("%d ", values[i]); + } + printf("\n"); + + qsort(values, ARRAY_SIZE(values), sizeof(int), cmpfunc); + + printf("After: "); + for(i = 0; i < ARRAY_SIZE(values); i++) { + printf("%d ", values[i]); + } + printf("\n"); + + return 0; +} diff --git a/tests/stdlib/rand.c b/tests/stdlib/rand.c new file mode 100644 index 0000000000..29791888d8 --- /dev/null +++ b/tests/stdlib/rand.c @@ -0,0 +1,70 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + // Uninitialized generator + int rand_uninit = rand(); + printf("%d\n", rand_uninit); + + // Testing the reproducibility of values + srand(259); + int rand_seed259_1 = rand(); + printf("%d\n", rand_seed259_1); + + srand(259); + int rand_seed259_2 = rand(); + printf("%d\n", rand_seed259_2); + + if (rand_seed259_1 != rand_seed259_2) { + puts("rand() doesn't return reproducible values"); + exit(EXIT_FAILURE); + } + + // Seed value 1 should return the same values as the ininitialized generator + srand(1); + int rand_seed1 = rand(); + printf("%d\n", rand_seed1); + + if (rand_uninit != rand_seed1) { + puts("srand(1) doesn't work"); + exit(EXIT_FAILURE); + } + + // Ensure rand_r() fails with NULL input + if (rand_r(NULL) != EINVAL) { + puts("rand_r(NULL) doesn't return EINVAL"); + exit(EXIT_FAILURE); + } + + // Ensure rand_r() produces unique values + int seed = 259; + int rand_r_seed259_1 = rand_r((unsigned *)&seed); + printf("%d\n", rand_r_seed259_1); + + int rand_r_seed259_2 = rand_r((unsigned *)&seed); + printf("%d\n", rand_r_seed259_2); + + if (rand_r_seed259_1 == rand_r_seed259_2) { + puts("rand_r() fails to produce unique values"); + exit(EXIT_FAILURE); + } + + // Ensure rand_r() returns reproducible values + seed = 259; + int rand_r_seed259_1_2 = rand_r((unsigned *)&seed); + printf("%d\n", rand_r_seed259_1_2); + + int rand_r_seed259_2_2 = rand_r((unsigned *)&seed); + printf("%d\n", rand_r_seed259_2_2); + + if (rand_r_seed259_1 != rand_r_seed259_1_2 + || rand_r_seed259_2 != rand_r_seed259_2_2) + { + puts("rand_r() is not reproducible"); + exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/tests/stdlib/rand48.c b/tests/stdlib/rand48.c new file mode 100644 index 0000000000..a1685f27fd --- /dev/null +++ b/tests/stdlib/rand48.c @@ -0,0 +1,137 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + long x_l, x_m; + double x_d; + long seedval = 0xcafebeef; + unsigned short seed[3] = {0xfedc, 0xba98, 0x7654}; + unsigned short xsubi[3] = {0xabcd, 0xef42, 0x5678}; + unsigned short lcong48_params[7] = {0x0123, 0x4567, 0x89ab, 0xcdef, 0x4242, 0xf000, 0xbaaa}; + unsigned short xsubi_from_seed48[3] = {0, 0, 0}; + + /* Test uninitialized behavior */ + printf("lrand48 (uninitialized):"); + for (int i = 0; i < 10; i++) + { + x_l = lrand48(); + printf(" %ld", x_l); + } + printf("\n"); + + /* Test different output types with same seed, builtin X_i and + * default multiplier and addend */ + srand48(seedval); + printf("drand48 (seeded with srand48):"); + for (int i = 0; i < 10; i++) + { + x_d = drand48(); + printf(" %lf", x_d); + } + printf("\n"); + + srand48(seedval); + printf("lrand48 (seeded with srand48):"); + for (int i = 0; i < 10; i++) + { + x_l = lrand48(); + printf(" %ld", x_l); + } + printf("\n"); + + srand48(seedval); + printf("mrand48 (seeded with srand48):"); + for (int i = 0; i < 10; i++) + { + x_m = mrand48(); + printf(" %ld", x_m); + } + printf("\n"); + + /* Test corresponding functions taking user-supplied X_i, with + * default multiplier and addend */ + printf("erand48:"); + for (int i = 0; i < 10; i++) + { + x_d = erand48(xsubi); + printf(" %lf", x_d); + } + printf("\n"); + + printf("nrand48:"); + for (int i = 0; i < 10; i++) + { + x_l = nrand48(xsubi); + printf(" %ld", x_l); + } + printf("\n"); + + printf("jrand48:"); + for (int i = 0; i < 10; i++) + { + x_l = jrand48(xsubi); + printf(" %ld", x_l); + } + printf("\n"); + + /* Test seed48() "stashing" behavior. */ + unsigned short *seed48_return = seed48(seed); + printf("seed48_return: [%x, %x, %x]\n", + seed48_return[0], seed48_return[1], seed48_return[2]); + + /* Test seeding behavior of seed48() */ + printf("lrand48 (seeded with seed48):"); + for (int i = 0; i < 10; i++) + { + x_l = lrand48(); + printf(" %ld", x_l); + } + printf("\n"); + + /* Test restore from seed48()'s "stashed" value */ + xsubi_from_seed48[0] = seed48_return[0]; + xsubi_from_seed48[1] = seed48_return[1]; + xsubi_from_seed48[2] = seed48_return[2]; + printf("xsubi restored froom seed48 return value: [%x, %x, %x]\n", + xsubi_from_seed48[0], xsubi_from_seed48[1], xsubi_from_seed48[2]); + printf("nrand48 (from xsubi value returned by seed48):"); + for (int i = 0; i < 10; i++) + { + x_l = nrand48(xsubi_from_seed48); + printf(" %ld", x_l); + } + printf("\n"); + + /* Test behavior with all-user-defined parameters */ + lcong48(lcong48_params); + printf("lrand48 (with parameters from lcong48):"); + for (int i = 0; i < 10; i++) + { + x_l = lrand48(); + printf(" %ld", x_l); + } + printf("\n"); + + /* Test multiplier- and addend-restoring behavior of srand48() */ + srand48(seedval); + printf("lrand48 (seeded with srand48 after lcong48 call):"); + for (int i = 0; i < 10; i++) + { + x_l = lrand48(); + printf(" %ld", x_l); + } + printf("\n"); + + /* Test multiplier- and addend-restoring behavior of seed48() */ + lcong48(lcong48_params); + seed48(seed); + printf("lrand48 (seeded with seed48 after lcong48 call):"); + for (int i = 0; i < 10; i++) + { + x_l = lrand48(); + printf(" %ld", x_l); + } + printf("\n"); +} diff --git a/tests/stdlib/random.c b/tests/stdlib/random.c new file mode 100644 index 0000000000..1c46861346 --- /dev/null +++ b/tests/stdlib/random.c @@ -0,0 +1,100 @@ +#include +#include + +#include "test_helpers.h" + +/* The output of these tests are checked against that from musl. Other + * algorithms may yield different results and still comply with the + * POSIX requirements. */ + +int main(void) { + /* Should be enough to exercise the rollover branching in random() + * for all possible state array sizes (up to 256 bytes, i.e. 64 + * 32-bit values). */ + size_t test_seq_len = 70; + + long random_result; + + // Should give same result as with seed 1 + puts("Uninitialized:"); + for (size_t i = 0; i < test_seq_len; i++) { + random_result = random(); + printf("%ld\n", random_result); + } + + puts("\nSeed 1:"); + srandom(1); + for (size_t i = 0; i < test_seq_len; i++) { + random_result = random(); + printf("%ld\n", random_result); + } + + puts("\nSeed 1337:"); + srandom(1337); + for (size_t i = 0; i < test_seq_len; i++) { + random_result = random(); + printf("%ld\n", random_result); + } + + /* 256 bytes (as below) is the largest possible amount of state + * data. Created as a uint32_t to avoid possible alignment issues + * with char. */ + uint32_t new_state[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned int seed = 42; + + // Should exercise the different branches in initstate() + size_t sizes[] = {8, 31, 32, 63, 64, 127, 128, 255, 256}; + + for (size_t j = 0; j < sizeof(sizes)/sizeof(size_t); j++) { + size_t size = sizes[j]; + printf("\nSeed %d, size %ld:\n", seed, size); + initstate(seed, (char *)new_state, size); + + for (size_t i = 0; i < test_seq_len; i++) { + random_result = random(); + printf("%ld\n", random_result); + } + } + + /* Test that setstate() allows the use of a different state array, + * and that it correctly returns the old value. */ + uint32_t other_new_state[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + initstate(seed, (char *)other_new_state, 32); + printf("\nSeed %d, other state array:\n", seed); + for (size_t i = 0; i < test_seq_len; i++) { + random_result = random(); + printf("%ld\n", random_result); + } + + char *should_be_other_new_state_ptr = setstate((char *)new_state); + if (should_be_other_new_state_ptr == (char *)other_new_state) { + puts("\nState data pointer restored correctly by setstate()."); + } + else { + puts("\nState data pointer NOT restored correctly by setstate()."); + } + printf("\nSeed %d, back to first state array:\n", seed); + for (size_t i = 0; i < test_seq_len; i++) { + random_result = random(); + printf("%ld\n", random_result); + } + + // Should yield NULL + char *state_with_size_less_than_8 = initstate(seed, (char *)new_state, 7); + printf("\nPointer returned by initstate with size < 8: %p\n", + state_with_size_less_than_8); + + return 0; +} diff --git a/tests/stdlib/realpath.c b/tests/stdlib/realpath.c new file mode 100644 index 0000000000..7abe26527d --- /dev/null +++ b/tests/stdlib/realpath.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *path_res = realpath("stdlib/realpath.c", NULL); + ERROR_IF(realpath, path_res, == NULL); + puts(path_res); + free(path_res); + + char path_arg[PATH_MAX] = { 0 }; + char *res = realpath("stdlib/realpath.c", path_arg); + ERROR_IF(realpath, res, == NULL); + puts(path_arg); +} diff --git a/tests/stdlib/strtod.c b/tests/stdlib/strtod.c new file mode 100644 index 0000000000..8b0450259a --- /dev/null +++ b/tests/stdlib/strtod.c @@ -0,0 +1,81 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char* endptr = 0; + double d; + + char* inputs[] = { + "a 1 hello", " 1 hello", "1 hello 2", + "10.123", "010.123", "-5.3", + "0x10.123", "0x1.23", "0x3.21", + + "1e5", "1e+5", "1e-5", + "1e5 ", "1e+5 ", "1e-5 ", + + "1e10", "1eXXXX", "1e", "1e ", + "1e+10", "1e+XXXX", "1e+", "1e+ ", + "1e-10", "1e-XXXX", "1e-", "1e- ", + + "-1e5", "-1e+5", "-1e-5", + "-1e5 ", "-1e+5 ", "-1e-5 ", + + "-1e10", "-1eXXXX", "-1e", "-1e ", + "-1e+10", "-1e+XXXX", "-1e+", "-1e+ ", + "-1e-10", "-1e-XXXX", "-1e-", "-1e- ", + + "12.34e5", "12.34e+5", "12.34e-5", + "12.34e5 ", "12.34e+5 ", "12.34e-5 ", + + "12.34e10", "12.34eXXXX", "12.34e", "12.34e ", + "12.34e+10", "12.34e+XXXX", "12.34e+", "12.34e+ ", + "12.34e-10", "12.34e-XXXX", "12.34e-", "12.34e- ", + + "-12.34e5", "-12.34e+5", "-12.34e-5", + "-12.34e5 ", "-12.34e+5 ", "-12.34e-5 ", + + "-12.34e10", "-12.34eXXXX", "-12.34e", "-12.34e ", + "-12.34e+10", "-12.34e+XXXX", "-12.34e+", "-12.34e+ ", + "-12.34e-10", "-12.34e-XXXX", "-12.34e-", "-12.34e- ", + + "0x0.3p10", "-0x0.3p10", "0x0.3p-5", "-0x0.3p-5", + "0x1.4p3", "0x1.4p-3", "-0x1.4p3", "-0x1.4p-3", + "0x10.1p0", "0x10.1p-0", "-0x10.1p0", "-0x10.1p-0", + + "0.5e0", "0.5e1", "0.5e2", "0.5e3", "0.5e4", + "0.5e5", "0.5e6", "0.5e7", "0.5e8", "0.5e9", + "0.5e10", "0.5e11", "0.5e12", "0.5e13", "0.5e14", + "0.5e15", "0.5e16", "0.5e17", "0.5e18", "0.5e19", + "0.5e20", "0.5e21", "0.5e22", "0.5e23", "0.5e24", + "0.5e25", "0.5e26", "0.5e27", "0.5e28", "0.5e29", + "0.5e30", "0.5e31", "0.5e32", "0.5e33", "0.5e34", + "0.5e35", "0.5e36", "0.5e37", "0.5e38", + + "-0.5e0", "-0.5e1", "-0.5e2", "-0.5e3", "-0.5e4", + "-0.5e5", "-0.5e6", "-0.5e7", "-0.5e8", "-0.5e9", + "-0.5e10", "-0.5e11", "-0.5e12", "-0.5e13", "-0.5e14", + "-0.5e15", "-0.5e16", "-0.5e17", "-0.5e18", "-0.5e19", + "-0.5e20", "-0.5e21", "-0.5e22", "-0.5e23", "-0.5e24", + "-0.5e25", "-0.5e26", "-0.5e27", "-0.5e28", "-0.5e29", + "-0.5e30", "-0.5e31", "-0.5e32", "-0.5e33", "-0.5e34", + "-0.5e35", "-0.5e36", "-0.5e37", "-0.5e38", + + "-0", + + "INF", "inf", "iNf", "Inf foobarbaz", + "+INF", "+inf", "+iNf", "+Inf foobarbaz", + "-INF", "-inf", "-iNf", "-Inf foobarbaz", + + "NaN0.1e5", "nan-37", "nAn1.05", "Nan foo bar baz", + "+NaN0.1e5", "+nan-37", "+nAn1.05", "+Nan foo bar baz", + "-NaN0.1e5", "-nan-37", "-nAn1.05", "-Nan foo bar baz", + + }; + for (size_t i = 0; i < sizeof(inputs) / sizeof(char*); i += 1) { + d = strtod(inputs[i], &endptr); + printf("d: %f Endptr: \"%s\"\n", d, endptr); + } +} diff --git a/tests/stdlib/strtol.c b/tests/stdlib/strtol.c new file mode 100644 index 0000000000..118d635de4 --- /dev/null +++ b/tests/stdlib/strtol.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *endptr; + + printf("%ld\n", strtol(" -42", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtol(" +555", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtol(" 1234567890 ", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + + printf("%ld\n", strtol(" -42", &endptr, 10)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtol(" +555", &endptr, 10)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtol(" 1234567890 ", &endptr, 10)); + printf("endptr \"%s\"\n", endptr); + + printf("%lx\n", strtol(" 0x38Acfg", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%lx\n", strtol("0Xabcdef12", &endptr, 16)); + printf("endptr \"%s\"\n", endptr); + printf("%lx\n", strtol("cafebeef", &endptr, 16)); + printf("endptr \"%s\"\n", endptr); + + printf("%lo\n", strtol(" 073189", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%lo\n", strtol(" 073189", &endptr, 8)); + printf("endptr \"%s\"\n", endptr); + + printf("%lo\n", strtol(" 0b", &endptr, 8)); + if(errno != 0) { + printf("errno is not 0 (%d), something went wrong\n", errno); + } + printf("endptr \"%s\"\n", endptr); + printf("%lo\n", strtol(" 0b", &endptr, 0)); + if(errno != 0) { + printf("errno is not 0 (%d), something went wrong\n", errno); + } + printf("endptr \"%s\"\n", endptr); +} diff --git a/tests/stdlib/strtoul.c b/tests/stdlib/strtoul.c new file mode 100644 index 0000000000..b558f0c170 --- /dev/null +++ b/tests/stdlib/strtoul.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *endptr; + + printf("%ld\n", strtoul(" -42", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtoul(" +555", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtoul(" 1234567890 ", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + + printf("%ld\n", strtoul(" -42", &endptr, 10)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtoul(" +555", &endptr, 10)); + printf("endptr \"%s\"\n", endptr); + printf("%ld\n", strtoul(" 1234567890 ", &endptr, 10)); + printf("endptr \"%s\"\n", endptr); + + printf("%lx\n", strtoul(" 0x38Acfg", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%lx\n", strtoul("0Xabcdef12", &endptr, 16)); + printf("endptr \"%s\"\n", endptr); + printf("%lx\n", strtoul("0x21000004", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + + printf("%lo\n", strtoul(" 073189", &endptr, 0)); + printf("endptr \"%s\"\n", endptr); + printf("%lo\n", strtoul(" 073189", &endptr, 8)); + printf("endptr \"%s\"\n", endptr); + + printf("%lo\n", strtoul(" 0b", &endptr, 8)); + if(errno != 0) { + printf("errno is not 0 (%d), something went wrong\n", errno); + } + printf("endptr \"%s\"\n", endptr); + printf("%lo\n", strtoul(" 0b", NULL, 0)); + if(errno != 0) { + printf("errno is not 0 (%d), something went wrong\n", errno); + } + printf("endptr \"%s\"\n", endptr); +} diff --git a/tests/stdlib/system.c b/tests/stdlib/system.c new file mode 100644 index 0000000000..821c216f6c --- /dev/null +++ b/tests/stdlib/system.c @@ -0,0 +1,18 @@ +#include + +#include "test_helpers.h" + +int main(void) { + // testing shell detection + // this means, because we don't detect if a shell actually exists but just assume it does, this test case breaks in + // environments without `sh`. I think that is a reasonable tradeoff. + // (And if there isn't a shell, system() won't work anyways) + int status = system(NULL); + printf("shell found: %i\n", status); + fflush(stdout); + ERROR_IF(system, status, == 0); + + // base case + status = system("echo test of system"); + ERROR_IF(system, status, == -1); +} diff --git a/tests/string/mem.c b/tests/string/mem.c new file mode 100644 index 0000000000..ed27237237 --- /dev/null +++ b/tests/string/mem.c @@ -0,0 +1,48 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + puts("# mem #"); + char arr[100]; + memset(arr, 0, 100); // Compiler builtin, should work + arr[50] = 1; + if ((size_t)memchr((void *)arr, 1, 100) - (size_t)arr != 50) { + puts("Incorrect memchr"); + exit(EXIT_FAILURE); + } + puts("Correct memchr"); + if ((size_t)memrchr((void *)arr, 1, 100) - (size_t)arr != 50) { + puts("Incorrect memrchr"); + exit(EXIT_FAILURE); + } + puts("Correct memrchr"); + char arr2[51]; + memset(arr2, 0, 51); // Compiler builtin, should work + memccpy((void *)arr2, (void *)arr, 1, 51); + if (arr[50] != 1) { + puts("Incorrect memccpy"); + exit(EXIT_FAILURE); + } + puts("Correct memccpy"); + int res; + if ((res = memcmp("hello world", "hello world", 11))) { + printf("Incorrect memcmp (1), expected 0 found %d\n", res); + exit(EXIT_FAILURE); + } + if ((res = memcmp("hello world", "hello worlt", 11)) >= 0) { + printf("Incorrect memcmp (2), expected -, found %d\n", res); + exit(EXIT_FAILURE); + } + if ((res = memcmp("hello world", "hallo world", 5)) <= 0) { + printf("Incorrect memcmp (3), expected +, found %d\n", res); + exit(EXIT_FAILURE); + } + if ((res = memcmp("hello world", "henlo world", 5)) >= 0) { + printf("Incorrect memcmp (4), expected -, found %d\n", res); + exit(EXIT_FAILURE); + } + puts("Correct memcmp"); +} diff --git a/tests/string/memcpy.c b/tests/string/memcpy.c new file mode 100644 index 0000000000..f9978dd697 --- /dev/null +++ b/tests/string/memcpy.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +int main(void) { + const uint8_t UNTOUCHED_BYTE = 0x00; + const uint8_t TOUCHED_BYTE = 0xff; + // In order to fully exercise the implementation, this should be at least 3 times the largest possible chunk size + const size_t BUFFER_LEN = 64; + + uint8_t *s1_buffer = malloc(BUFFER_LEN); + uint8_t *s2_buffer = malloc(BUFFER_LEN); + + // Loop through all possible combinations of s1 and s2 alignments and slice length within the buffers allocated + for (size_t s1_offset = 0; s1_offset < BUFFER_LEN; s1_offset++) { + for (size_t s2_offset = 0; s2_offset < BUFFER_LEN; s2_offset++) { + size_t n_max = BUFFER_LEN - MAX(s1_offset, s2_offset); + for (size_t n = 1; n <= n_max; n++) { + // Clear buffers + memset(s1_buffer, UNTOUCHED_BYTE, BUFFER_LEN); + memset(s2_buffer, UNTOUCHED_BYTE, BUFFER_LEN); + + // Fill s2 subslice + memset(s2_buffer + s2_offset, TOUCHED_BYTE, n); + + // Do the actual memcpy of the slice of interest + memcpy(s1_buffer + s1_offset, s2_buffer + s2_offset, n); + + // Check that area below the slice of interest is untouched + for (size_t i = 0; i < s1_offset; i++) { + assert(s1_buffer[i] == UNTOUCHED_BYTE); + } + + // Check that the slice of interest was copied + assert(memcmp(s1_buffer + s1_offset, s2_buffer + s2_offset, n) == 0); + + // Check that area above the slice of interest is untouched + for (size_t i = s1_offset + n; i < BUFFER_LEN; i++) { + assert(s1_buffer[i] == UNTOUCHED_BYTE); + } + } + } + } + + free(s1_buffer); + free(s2_buffer); +} \ No newline at end of file diff --git a/tests/string/memmem.c b/tests/string/memmem.c new file mode 100644 index 0000000000..c3ff546428 --- /dev/null +++ b/tests/string/memmem.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + uint8_t haystack[] = {1, 1, 2, 1, 2, 3, 1, 2, 3, 4}; + uint8_t present_needle[] = {1, 2, 3}; + uint8_t absent_needle[] = {1, 2, 3, 4, 5}; + uint8_t long_needle[] = {1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1}; + + size_t haystacklen = sizeof(haystack); + size_t present_needlelen = sizeof(present_needle); + size_t absent_needlelen = sizeof(absent_needle); + size_t long_needlelen = sizeof(long_needle); + + uint8_t *present_needle_match_ptr = memmem(haystack, haystacklen, present_needle, present_needlelen); + assert(present_needle_match_ptr == haystack + 3); + + uint8_t *absent_needle_match_ptr = memmem(haystack, haystacklen, absent_needle, absent_needlelen); + assert(absent_needle_match_ptr == NULL); + + // Explicitly specified to return haystack for needlelen == 0. + uint8_t *zero_needle_match_ptr = memmem(haystack, haystacklen, present_needle, 0); + assert(zero_needle_match_ptr == haystack); + + uint8_t *long_needle_match_ptr = memmem(haystack, haystacklen, long_needle, long_needlelen); + assert(long_needle_match_ptr == NULL); +} diff --git a/tests/string/stpcpy.c b/tests/string/stpcpy.c new file mode 100644 index 0000000000..9d10d41199 --- /dev/null +++ b/tests/string/stpcpy.c @@ -0,0 +1,26 @@ +#include +#include + +void test_stpcpy(const char *src, char *dest) { + char *end = stpcpy(dest, src); + assert(strcmp(dest, src) == 0); + assert(*end == '\0'); + assert(end == dest + strlen(dest)); +} + +int main() { + char dest[20]; + + test_stpcpy("Hello, World!", dest); + + // Test case 2: Empty string + test_stpcpy("", dest); + + // Test case 3: String with special characters + test_stpcpy("Special chars: !@#$%^&*()", dest); + + // Test case 4: String with spaces + test_stpcpy("A string with spaces", dest); + + return 0; +} diff --git a/tests/string/stpncpy.c b/tests/string/stpncpy.c new file mode 100644 index 0000000000..27b558f088 --- /dev/null +++ b/tests/string/stpncpy.c @@ -0,0 +1,40 @@ +#include +#include + +int main() { + // Define some test strings + const char *src = "Hello, World!"; + char dest[50]; + + // Test 1: Copy exactly 5 characters from src to dest + char *result = stpncpy(dest, src, 5); + dest[5] = '\0'; // Ensure the destination string is null-terminated + assert(strcmp(dest, "Hello") == 0); // Verify the string in dest + assert(result == &dest[5]); // Ensure the return pointer points to the null terminator + + // Test 2: Copy 15 characters from src to dest (more than the length of src) + result = stpncpy(dest, src, 15); + dest[13] = '\0'; // Ensure the destination string is null-terminated + assert(strcmp(dest, "Hello, World!") == 0); // Verify the string in dest + assert(result == &dest[13]); // Ensure the return pointer points to the null terminator + + // Test 3: Copy 3 characters from src to dest + result = stpncpy(dest, src, 3); + dest[3] = '\0'; // Ensure the destination string is null-terminated + assert(strcmp(dest, "Hel") == 0); // Verify the string in dest + assert(result == &dest[3]); // Ensure the return pointer points to the null terminator + + // Test 4: Copy 0 characters from src to dest + result = stpncpy(dest, src, 0); + dest[0] = '\0'; // Ensure the destination is explicitly null-terminated + assert(dest[0] == '\0'); // Ensure the destination is an empty string + assert(result == dest); // Ensure the return pointer points to the start of dest + + // Test 5: Copy exactly the length of the source string + result = stpncpy(dest, src, strlen(src)); + dest[strlen(src)] = '\0'; // Ensure the destination string is null-terminated + assert(strcmp(dest, "Hello, World!") == 0); // Verify the string in dest + assert(result == &dest[strlen(src)]); // Ensure the return pointer points to the null terminator + + return 0; +} diff --git a/tests/string/strcat.c b/tests/string/strcat.c new file mode 100644 index 0000000000..cbfe52218b --- /dev/null +++ b/tests/string/strcat.c @@ -0,0 +1,12 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char dest1[12] = "hello"; + printf("%s\n", strcat(dest1, " world")); // should be hello world + + char dest2[12] = "hello"; + printf("%s\n", strncat(dest2, " world foobar", 6)); // should be hello world +} diff --git a/tests/string/strchr.c b/tests/string/strchr.c new file mode 100644 index 0000000000..51f4b7aa56 --- /dev/null +++ b/tests/string/strchr.c @@ -0,0 +1,11 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("%s\n", strchr("hello", 'e')); // should be ello + printf("%s\n", strchr("world", 'l')); // should be ld + printf("%s\n", strchr("world", '\0')); // should be an empty, nul-terminated string + printf("%p\n", strchr("world", 'x')); // should be a null pointer +} diff --git a/tests/string/strchrnul.c b/tests/string/strchrnul.c new file mode 100644 index 0000000000..9911e03608 --- /dev/null +++ b/tests/string/strchrnul.c @@ -0,0 +1,31 @@ +#include +#include + + +int main() { + // Test 1: Character is present in the string + const char *str1 = "Hello, World!"; + const char *result1 = strchrnul(str1, 'o'); + assert(result1 == &str1[4]); // 'o' is at position 4 in "Hello, World!" + + // Test 2: Character is not in the string (should return the null terminator) + const char *str2 = "Hello, World!"; + const char *result2 = strchrnul(str2, 'z'); + assert(result2 == &str2[13]); // 'z' is not present, so it returns the null terminator + + // Test 3: Character is the first character in the string + const char *str3 = "abcdef"; + const char *result3 = strchrnul(str3, 'a'); + assert(result3 == &str3[0]); // 'a' is at position 0 + + // Test 4: Character is the last character in the string + const char *str4 = "abcdef"; + const char *result4 = strchrnul(str4, 'f'); + assert(result4 == &str4[5]); // 'f' is at position 5, the last character + + // Test 5: Searching for the null terminator itself + const char *str5 = "abcdef"; + const char *result5 = strchrnul(str5, '\0'); + assert(result5 == &str5[6]); // The null terminator is at position 6 (end of the string) + return 0; +} diff --git a/tests/string/strcpy.c b/tests/string/strcpy.c new file mode 100644 index 0000000000..25b0276317 --- /dev/null +++ b/tests/string/strcpy.c @@ -0,0 +1,29 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char dst[20]; + + strcpy(dst, "strcpy works!"); + puts(dst); + strncpy(dst, "strncpy works!", 20); + puts(dst); + + // Make sure no NUL is placed + memset(dst, 'a', 20); + dst[19] = 0; + strncpy(dst, "strncpy should work here too", 10); + puts(dst); + + // The string should be properly terminated regardless + char ndst[28]; + + size_t r = strlcpy(ndst, "strlcpy works!", 28); + puts(ndst); + printf("copied %lu\n", r); + r = strlcat(ndst, " and strlcat!", 28); + puts(ndst); + printf("copied %lu\n", r); +} diff --git a/tests/string/strcspn.c b/tests/string/strcspn.c new file mode 100644 index 0000000000..969d6ec663 --- /dev/null +++ b/tests/string/strcspn.c @@ -0,0 +1,10 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *world = "world"; + printf("%ld\n", strcspn("hello", world)); // should be 2 + printf("%ld\n", strcspn("banana", world)); // should be 6 +} diff --git a/tests/string/strlen.c b/tests/string/strlen.c new file mode 100644 index 0000000000..d5125c52dc --- /dev/null +++ b/tests/string/strlen.c @@ -0,0 +1,76 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char dest1[13] = "hello world!"; + int dest1_len = strlen(dest1); + printf("%d\n", dest1_len); + if(dest1_len != 12) { + puts("strlen(\"hello world!\") failed"); + exit(EXIT_FAILURE); + } + + char empty[1] = { 0 }; + int empty_len = strlen(empty); + printf("%d\n", empty_len); + if(empty_len != 0) { + puts("strlen(\"\") failed"); + exit(EXIT_FAILURE); + } + + dest1_len = strnlen(dest1, sizeof(dest1)); + printf("%d\n", dest1_len); + if(dest1_len != 12) { + puts("strnlen(\"hello world!\", 13) failed"); + exit(EXIT_FAILURE); + } + + dest1_len = strnlen(dest1, sizeof(dest1) - 1); + printf("%d\n", dest1_len); + if(dest1_len != 12) { + puts("strnlen(\"hello world!\", 12) failed"); + exit(EXIT_FAILURE); + } + + dest1_len = strnlen(dest1, 0); + printf("%d\n", dest1_len); + if(dest1_len != 0) { + puts("strnlen(\"hello world!\", 0) failed"); + exit(EXIT_FAILURE); + } + + dest1_len = strnlen(dest1, 300); + printf("%d\n", dest1_len); + if(dest1_len != 12) { + puts("strnlen(\"hello world!\", 300) failed"); + exit(EXIT_FAILURE); + } + +// no strnlen_s on glibc +#ifndef __GLIBC__ + dest1_len = strnlen_s(dest1, 6); + printf("%d\n", dest1_len); + if(dest1_len != 6) { + puts("strnlen_s(\"hello world!\", 6) failed"); + exit(EXIT_FAILURE); + } + + dest1_len = strnlen_s(dest1, 20); + printf("%d\n", dest1_len); + if(dest1_len != 12) { + puts("strnlen_s(\"hello world!\", 20) failed"); + exit(EXIT_FAILURE); + } + + int null_len = strnlen_s(NULL, 100); + printf("%d\n", null_len); + if(null_len != 0) { + puts("strnlen_s(NULL, 100) failed"); + exit(EXIT_FAILURE); + } +#endif + + return 0; +} diff --git a/tests/string/strncmp.c b/tests/string/strncmp.c new file mode 100644 index 0000000000..d42b3e39a5 --- /dev/null +++ b/tests/string/strncmp.c @@ -0,0 +1,13 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("%d\n", strncmp("a", "aa", 2)); + printf("%d\n", strncmp("a", "aä", 2)); + printf("%d\n", strncmp("\xFF", "\xFE", 2)); + printf("%d\n", strncmp("", "\xFF", 1)); + printf("%d\n", strncmp("a", "c", 1)); + printf("%d\n", strncmp("a", "a", 2)); +} diff --git a/tests/string/strpbrk.c b/tests/string/strpbrk.c new file mode 100644 index 0000000000..88e62f7463 --- /dev/null +++ b/tests/string/strpbrk.c @@ -0,0 +1,20 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char* source = "The quick drawn fix jumps over the lazy bug"; + + // should be "The quick drawn fix jumps over the lazy bug" + char* res1 = strpbrk(source, "From The Very Beginning"); + printf("%s\n", (res1) ? res1 : "NULL"); + + // should be "lazy bug" + char* res2 = strpbrk(source, "lzbg"); + printf("%s\n", (res2) ? res2 : "NULL"); + + // should be "NULL" + char* res3 = strpbrk(source, "404"); + printf("%s\n", (res3) ? res3 : "NULL"); +} diff --git a/tests/string/strrchr.c b/tests/string/strrchr.c new file mode 100644 index 0000000000..1013e76100 --- /dev/null +++ b/tests/string/strrchr.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char s0[] = "hello, world"; + char *ptr = strrchr(s0, 'l'); + if (ptr != &s0[10]) { + printf("%p != %p\n", ptr, &s0[10]); + puts("strrchr FAIL"); + exit(EXIT_FAILURE); + } + + char s1[] = ""; + ptr = strrchr(s1, 'a'); + if (ptr != NULL) { + printf("%p != 0\n", ptr); + puts("strrchr FAIL"); + exit(EXIT_FAILURE); + } + + puts("strrch PASS"); +} diff --git a/tests/string/strsep.c b/tests/string/strsep.c new file mode 100644 index 0000000000..bfa6c4a11b --- /dev/null +++ b/tests/string/strsep.c @@ -0,0 +1,68 @@ +#include +#include + +int main() { + // Test case 1: Basic case with multiple tokens + char str1[] = "apple,orange,banana"; + char *delim = ","; + char *token = str1; + char *result = NULL; + + // First token should be "apple" + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "apple") == 0); + + // Second token should be "orange" + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "orange") == 0); + + // Third token should be "banana" + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "banana") == 0); + + // No more tokens + result = strsep(&token, delim); + assert(result == NULL); + + // Test case 2: Empty string + char str2[] = ""; + token = str2; + result = strsep(&token, delim); + + // Test case 3: String with no delimiter + char str3[] = "apple"; + token = str3; + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "apple") == 0); + assert(token == NULL); // No more tokens + + // Test case 4: String starts with delimiter + char str4[] = ",apple,orange"; + token = str4; + result = strsep(&token, delim); + assert(result != NULL && strlen(result) == 0); // First token should be an empty string ("") + + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "apple") == 0); + + // Test case 5: Multiple delimiters in a row + char str5[] = "apple,,orange"; + token = str5; + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "apple") == 0); + result = strsep(&token, delim); + assert(result != NULL && strlen(result) == 0); // Empty token due to consecutive delimiters + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "orange") == 0); + + // Test case 6: Delimiters at the end of the string + char str6[] = "apple,orange,"; + token = str6; + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "apple") == 0); + result = strsep(&token, delim); + assert(result != NULL && strcmp(result, "orange") == 0); + result = strsep(&token, delim); + + return 0; +} diff --git a/tests/string/strsignal.c b/tests/string/strsignal.c new file mode 100644 index 0000000000..490d11f38c --- /dev/null +++ b/tests/string/strsignal.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + puts("# strsignal #"); + const char *x = strsignal(SIGHUP); + if (strcmp(x, "Hangup")) { + printf("Incorrect strsignal (1), found: .%s.\n", x); + exit(EXIT_FAILURE); + } + x = strsignal(0); + if (strcmp(x, "Unknown signal")) { + printf("Incorrect strsignal (2), found: .%s.\n", x); + exit(EXIT_FAILURE); + } + x = strsignal(100); + if (strcmp(x, "Unknown signal")) { + printf("Incorrect strsignal (3), found: .%s.\n", x); + exit(EXIT_FAILURE); + } + + +} diff --git a/tests/string/strspn.c b/tests/string/strspn.c new file mode 100644 index 0000000000..2f5ae33e9d --- /dev/null +++ b/tests/string/strspn.c @@ -0,0 +1,13 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char *hello = "hello"; + char *world = "world"; + char *banana = "banana"; + printf("%lu\n", strspn(hello, "hello")); // should be 5 + printf("%lu\n", strspn(world, "wendy")); // should be 1 + printf("%lu\n", strspn(banana, "apple")); // should be 0 +} diff --git a/tests/string/strstr.c b/tests/string/strstr.c new file mode 100644 index 0000000000..aa097864ca --- /dev/null +++ b/tests/string/strstr.c @@ -0,0 +1,12 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("%s\n", strstr("In relibc we trust", "rust")); + printf("%s\n", strstr("In relibc we trust", "libc")); + printf("%s\n", strstr("In relibc we trust", "bugs")); + printf("%s\n", strstr("IN RELIBC WE TRUST", "rust")); + printf("%s\n", strcasestr("IN RELIBC WE TRUST", "rust")); +} diff --git a/tests/string/strtok.c b/tests/string/strtok.c new file mode 100644 index 0000000000..4781968e82 --- /dev/null +++ b/tests/string/strtok.c @@ -0,0 +1,17 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char source[] = "I'd just like to interject for a moment. What you're referring to as Linux, " + "is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux.\n"; + + char* token = strtok(source, " "); + while (token) { + printf("%s", token); + if ((token = strtok(NULL, " "))) { + printf("_"); + } + } +} diff --git a/tests/string/strtok_r.c b/tests/string/strtok_r.c new file mode 100644 index 0000000000..1c188c76b1 --- /dev/null +++ b/tests/string/strtok_r.c @@ -0,0 +1,18 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + char source[] = "I'd just like to interject for a moment. What you're referring to as Linux, " + "is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux.\n"; + char* sp; + + char* token = strtok_r(source, " ", &sp); + while (token) { + printf("%s", token); + if ((token = strtok_r(NULL, " ", &sp))) { + printf("_"); + } + } +} diff --git a/tests/strings.c b/tests/strings.c new file mode 100644 index 0000000000..ede5d6289c --- /dev/null +++ b/tests/strings.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + assert(!bcmp("hello", "hehe", 2)); + assert(bcmp("hello", "haha", 2)); + + char* new = malloc(3); + bcopy("hi", new, 3); // include nul byte + + assert(!strcasecmp("hi", new)); + assert(strcasecmp("he", new)); + + assert(strcasecmp("hello", "HEllO") == 0); + assert(strcasecmp("hello", "HEllOo") < 0); + assert(strcasecmp("5", "5") == 0); + assert(strcasecmp("5", "4") > 0); + assert(strcasecmp("5", "6") < 0); + assert(strncasecmp("hello", "Hello World", 5) == 0); + assert(strncasecmp("FLOOR0_1", "FLOOR0_1FLOOR4_1", 8) == 0); + assert(strncasecmp("FL00RO_1", "FLOOR0_1FLOOR4_1", 8) < 0); + + // Ensure we aren't relying on the 5th (lowercase) bit on non-alpha characters + assert(strcasecmp("{[", "[{") > 0); + + bzero(new, 1); + assert(*new == 0); + assert(*(new+1) == 'i'); + assert(*(new+2) == 0); + + assert(ffs(1) == 1); + assert(ffs(2) == 2); + assert(ffs(3) == 1); + assert(ffs(10) == 2); + + char* str = "hihih"; + assert(index(str, 'i') == str + 1); + assert(rindex(str, 'i') == str + 3); + + char buf[] = "password"; + explicit_bzero(buf, sizeof(buf)); + assert(buf[0] == 0); +} diff --git a/tests/sys_epoll/epoll.c b/tests/sys_epoll/epoll.c new file mode 100644 index 0000000000..fa367e07cf --- /dev/null +++ b/tests/sys_epoll/epoll.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include + +#include "../test_helpers.h" + +int reader(int fd) { + // Create an epoll file + int epollfd = epoll_create1(EPOLL_CLOEXEC); + if (epollfd < 0) { + perror("epoll_create1"); + return 1; + } + + // Register for events from the reader file + struct epoll_event ev; + ev.events = EPOLLIN; + ev.data.fd = fd; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) { + perror("epoll_ctl"); + return 1; + } + + struct epoll_event events[8]; + + // Check that epoll returns error on a zero or negative number of events + int nfds0 = epoll_wait(epollfd, events, 0, -1); + if (nfds0 != -1 || errno != EINVAL) { + perror("epoll_wait"); + return 1; + } + +#ifndef __GLIBC__ + int nfds_n1 = epoll_wait(epollfd, events, -1, -1); +#else + // glibc does not support events len -1 + int nfds_n1 = epoll_wait(epollfd, events, 8, -1); +#endif + if (nfds_n1 != -1 || errno != EINVAL) { + perror("epoll_wait"); + return 1; + } + + // Process exactly 1024 events + for (int i = 0; i < 1024; i++) { + // Wait for the next event + int nfds = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1); + if (nfds < 0) { + perror("epoll_wait"); + return 1; + } + + // For each event received + for (int n = 0; n < nfds; n++) { + // If the event is the reader file + if (events[n].data.fd == fd) { + // Read the current event count + int writer_i; + ssize_t status = read(fd, &writer_i, sizeof(writer_i)); + ERROR_IF(read, status, == -1); + size_t count = (size_t)status; + + if (count < sizeof(writer_i)) { + fprintf(stderr, "read %zu instead of %d\n", count, sizeof(writer_i)); + return 1; + } + // Make sure the writer's event count matches our own + if (i != writer_i) { + fprintf(stderr, "received event count %d instead of %d\n", writer_i, i); + return 1; + } + printf("%d == %d\n", i, writer_i); + } else { + // Otherwise, return an error + fprintf(stderr, "unknown fd %d\n", events[n].data.fd); + return 1; + } + } + } + + return 0; +} + +int writer(int fd) { + // Create an epoll file + int epollfd = epoll_create1(EPOLL_CLOEXEC); + if (epollfd < 0) { + perror("epoll_create1"); + return 1; + } + + // Register for events from the writer file + struct epoll_event ev; + ev.events = EPOLLOUT; + ev.data.fd = fd; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) { + perror("epoll_ctl"); + return 1; + } + + // Process exactly 1024 events + struct epoll_event events[8]; + for (int i = 0; i < 1024; i++) { + // Wait for the next event + int nfds = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1); + if (nfds < 0) { + perror("epoll_wait"); + return 1; + } + + // For each event received + for (int n = 0; n < nfds; n++) { + // If the event is the writer file + if (events[n].data.fd == fd) { + // Write the current event count + ssize_t status = write(fd, &i, sizeof(i)); + ERROR_IF(write, status, == -1); + size_t count = (size_t)status; + + if (count < sizeof(i)) { + fprintf(stderr, "wrote %zu instead of %d\n", count, sizeof(i)); + return 1; + } + } else { + // Otherwise, return an error + fprintf(stderr, "unknown fd %d\n", events[n].data.fd); + return 1; + } + } + } + + return 0; +} + +int main(void) { + // Create a non-blocking pipe to use for epoll testing + int pipefd[2]; + if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) < 0) { + perror("pipe2"); + return 1; + } + + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + return 1; + } else if (pid == 0) { + // Child process will read events + close(pipefd[1]); + return reader(pipefd[0]); + } else { + // Parent process will write events + close(pipefd[0]); + int ret = writer(pipefd[1]); + + // Wait for child process + int status = 0; + if (waitpid(pid, &status, 0) != pid) { + perror("waitpid"); + return 1; + } + + // If writer failed, return exit status + if (ret != 0) { + return ret; + } + + // If child exited with exit status + if (WIFEXITED(status)) { + // Return the child's exit status + return WEXITSTATUS(status); + } else { + // Otherwise, return 1 + return 1; + } + } +} diff --git a/tests/sys_epoll/epollet.c b/tests/sys_epoll/epollet.c new file mode 100644 index 0000000000..7c0646730b --- /dev/null +++ b/tests/sys_epoll/epollet.c @@ -0,0 +1,46 @@ +// test code found on dbus, currently hangs on redox + +#include +#include +#include +#include + +#define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object)))) + +int +main (void) +{ + struct epoll_event input; + struct epoll_event output; + int epfd = epoll_create1 (EPOLL_CLOEXEC); + + int pipefds[2]; + pipe(pipefds); + close(pipefds[1]); + int fd = pipefds[0]; + + int ret; + + _DBUS_ZERO (input); + + input.events = EPOLLHUP | EPOLLET; + ret = epoll_ctl (epfd, EPOLL_CTL_ADD, fd, &input); + printf ("ctl ADD: %d\n", ret); + + ret = epoll_wait (epfd, &output, 1, -1); + printf ("wait for HUP, edge-triggered: %d\n", ret); + + ret = epoll_wait (epfd, &output, 1, 100); + printf ("wait for HUP again: %d\n", ret); + + input.events = EPOLLHUP; + ret = epoll_ctl (epfd, EPOLL_CTL_MOD, fd, &input); + printf ("ctl MOD: %d\n", ret); + + ret = epoll_wait (epfd, &output, 1, -1); + printf ("wait for HUP: %d\n", ret); + + close(fd); + close(epfd); + return 0; +} \ No newline at end of file diff --git a/tests/sys_mman/fmap.c b/tests/sys_mman/fmap.c new file mode 100644 index 0000000000..002f5cc5c4 --- /dev/null +++ b/tests/sys_mman/fmap.c @@ -0,0 +1,104 @@ +// copied from autoconf mmap test, currently panicking kernel + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include +#include +#include +#include +#include + +int +main (void) +{ + char *data, *data2, *data3; + const char *cdata2; + int i, pagesize; + int fd, fd2; + + pagesize = sysconf(_SC_PAGESIZE); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + return 1; + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + return 2; + if (write (fd, data, pagesize) != pagesize) + return 3; + close (fd); + + /* Next, check that the tail of a page is zero-filled. File must have + non-zero length, otherwise we risk SIGBUS for entire page. */ + fd2 = open ("conftest.txt", O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd2 < 0) + return 4; + cdata2 = ""; + if (write (fd2, cdata2, 1) != 1) + return 5; + data2 = (char *) mmap (0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0L); + if (data2 == MAP_FAILED) + return 6; + for (i = 0; i < pagesize; ++i) + if (*(data2 + i)) + return 7; + close (fd2); + if (munmap (data2, pagesize)) + return 8; + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + return 9; + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + return 10; + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + return 11; + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + return 12; + if (read (fd, data3, pagesize) != pagesize) + return 13; + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + return 14; + close (fd); + free (data); + free (data3); + return 0; +} \ No newline at end of file diff --git a/tests/sys_mman/mmap.c b/tests/sys_mman/mmap.c new file mode 100644 index 0000000000..81b868ca74 --- /dev/null +++ b/tests/sys_mman/mmap.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main() { + int page_size = getpagesize(); + printf("Page size: %d\n", page_size); + + puts("Mapping 1 page of memory..."); + char *map1 = mmap((void *) 0x200000000, (size_t) page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0); + puts("Mapping 3 pages of memory..."); + char *map2 = mmap(map1 + page_size, (size_t) page_size * 4, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0); + + ERROR_IF(mmap, map1, == MAP_FAILED); + ERROR_IF(mmap, map2, == MAP_FAILED); + + (void)madvise(map1 + page_size + 3, page_size, PROT_NONE); + + puts("Randomizing mapping #1"); + for (int i = 0; i < page_size; ++i) { + map1[i] = (char) (rand() & 0xFF); + } + puts("Randomizing mapping #2"); + for (int i = 0; i < page_size * 3; ++i) { + map2[i] = (char) (rand() & 0xFF); + } + + puts("Unmapping page 2 of map2"); + munmap(map2 + page_size, page_size); + + puts("Randomizing page 1 of mapping #2"); + for (int i = 0; i < page_size; ++i) { + map2[i] = (char) (rand() % 256); + } + + puts("Randomizing page 3 of mapping #2"); + for (int i = 0; i < page_size; ++i) { + map2[page_size * 2 + i] = (char) (rand() % 256); + } + + // Page fault: + // map2[page_size] = 0; + + puts("Unmapping it all at once!"); + munmap(map1, (size_t) page_size * 5); + + // Page fault: + // *map2 = 0; +} diff --git a/tests/sys_resource/constants.c b/tests/sys_resource/constants.c new file mode 100644 index 0000000000..7a93b7f4d5 --- /dev/null +++ b/tests/sys_resource/constants.c @@ -0,0 +1,27 @@ +#include +#include + +int main(void) { + // Checks availability of constants specified in + // https://pubs.opengroup.org/onlinepubs/7908799/xsh/sysresource.h.html + printf("PRIO_PROCESS: %d\n", PRIO_PROCESS); + printf("PRIO_PGRP: %d\n", PRIO_PGRP); + printf("PRIO_USER: %d\n", PRIO_USER); + + printf("RLIM_INFINITY: %lld\n", RLIM_INFINITY); + printf("RLIM_SAVED_MAX: %lld\n", RLIM_SAVED_MAX); + printf("RLIM_SAVED_CUR: %lld\n", RLIM_SAVED_CUR); + + printf("RUSAGE_SELF: %lld\n", RUSAGE_SELF); + printf("RUSAGE_CHILDREN: %lld\n", RUSAGE_CHILDREN); + + printf("RLIMIT_CORE: %d\n", RLIMIT_CORE); + printf("RLIMIT_CPU: %d\n", RLIMIT_CPU); + printf("RLIMIT_DATA: %d\n", RLIMIT_DATA); + printf("RLIMIT_FSIZE: %d\n", RLIMIT_FSIZE); + printf("RLIMIT_NOFILE: %d\n", RLIMIT_NOFILE); + printf("RLIMIT_STACK: %d\n", RLIMIT_STACK); + printf("RLIMIT_AS: %d\n", RLIMIT_AS); + + return 0; +} diff --git a/tests/sys_resource/getrusage.c b/tests/sys_resource/getrusage.c new file mode 100644 index 0000000000..4a5c51e39e --- /dev/null +++ b/tests/sys_resource/getrusage.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +void ptimeval(struct timeval* val) { + printf("{ tv_sec: %ld, tv_usec: %ld }\n", val->tv_sec, val->tv_usec); +} + +int main(void) { + struct rusage r_usage; + + int status = getrusage(RUSAGE_SELF, &r_usage); + ERROR_IF(getrusage, status, == -1); + UNEXP_IF(getrusage, status, != 0); + + printf("ru_utime:"); + ptimeval(&r_usage.ru_utime); + + printf("ru_stime:"); + ptimeval(&r_usage.ru_utime); + + printf("ru_maxrss: %ld\n", r_usage.ru_maxrss); + printf("ru_ixrss: %ld\n", r_usage.ru_ixrss); + printf("ru_idrss: %ld\n", r_usage.ru_idrss); + printf("ru_isrss: %ld\n", r_usage.ru_isrss); + printf("ru_minflt: %ld\n", r_usage.ru_minflt); + printf("ru_majflt: %ld\n", r_usage.ru_majflt); + printf("ru_nswap: %ld\n", r_usage.ru_nswap); + printf("ru_inblock: %ld\n", r_usage.ru_inblock); + printf("ru_oublock: %ld\n", r_usage.ru_oublock); + printf("ru_msgsnd: %ld\n", r_usage.ru_msgsnd); + printf("ru_msgrcv: %ld\n", r_usage.ru_msgrcv); + printf("ru_nsignals: %ld\n", r_usage.ru_nsignals); + printf("ru_nvcsw: %ld\n", r_usage.ru_nvcsw); + printf("ru_nivcsw: %ld\n", r_usage.ru_nivcsw); +} diff --git a/tests/sys_resource/rlimit_roundtrip.c b/tests/sys_resource/rlimit_roundtrip.c new file mode 100644 index 0000000000..c90a6b794a --- /dev/null +++ b/tests/sys_resource/rlimit_roundtrip.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include + +int main(void) { + struct rlimit original; + struct rlimit current; + struct rlimit invalid; + + errno = 0; + assert(getrlimit(RLIMIT_NOFILE, &original) == 0); + + errno = 0; + assert(getrlimit(RLIMIT_NLIMITS, ¤t) == -1); + assert(errno == EINVAL); + + errno = 0; + assert(getrlimit(RLIMIT_NOFILE, NULL) == -1); + assert(errno == EFAULT); + + errno = 0; + assert(getrusage(RUSAGE_SELF, NULL) == -1); + assert(errno == EFAULT); + + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, NULL) == -1); + assert(errno == EFAULT); + + invalid.rlim_cur = original.rlim_max; + invalid.rlim_max = original.rlim_cur; + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, &invalid) == -1); + assert(errno == EINVAL); + + if (original.rlim_max != RLIM_INFINITY) { + invalid.rlim_cur = original.rlim_max + 1; + invalid.rlim_max = original.rlim_max + 1; + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, &invalid) == -1); + assert(errno == EPERM); + } + + current.rlim_cur = original.rlim_cur > 16 ? original.rlim_cur - 16 : original.rlim_cur; + current.rlim_max = original.rlim_max; + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, ¤t) == 0); + + struct rlimit roundtrip; + errno = 0; + assert(getrlimit(RLIMIT_NOFILE, &roundtrip) == 0); + assert(roundtrip.rlim_cur == current.rlim_cur); + assert(roundtrip.rlim_max == current.rlim_max); + + long open_max = sysconf(_SC_OPEN_MAX); + if (current.rlim_cur == RLIM_INFINITY) { + assert(open_max == -1); + } else if (current.rlim_cur > LONG_MAX) { + assert(open_max == LONG_MAX); + } else { + assert(open_max == (long)current.rlim_cur); + } + + if (current.rlim_cur > INT_MAX) { + assert(getdtablesize() == INT_MAX); + } else { + assert(getdtablesize() == (int)current.rlim_cur); + } + + errno = 0; + assert(setrlimit(RLIMIT_NOFILE, &original) == 0); + assert(getrlimit(RLIMIT_NOFILE, &roundtrip) == 0); + assert(roundtrip.rlim_cur == original.rlim_cur); + assert(roundtrip.rlim_max == original.rlim_max); + + puts("rlimit roundtrip ok"); + return 0; +} diff --git a/tests/sys_socket/getpeername.c b/tests/sys_socket/getpeername.c new file mode 100644 index 0000000000..98eb2fa28a --- /dev/null +++ b/tests/sys_socket/getpeername.c @@ -0,0 +1,77 @@ +#include + +#include "test_helpers.h" + +void print_sockaddr(char *ctx, struct sockaddr *addr) { + char ip_string[INET6_ADDRSTRLEN]; + void *raw_ip_addr; + char *fam; + in_port_t port; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; + raw_ip_addr = &(ipv4->sin_addr); + port = ntohs(ipv4->sin_port); + fam = "AF_INET"; + } else if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; + raw_ip_addr = &(ipv6->sin6_addr); + port = ntohs(ipv6->sin6_port); + fam = "AF_INET6"; + } else { + printf("Unknown address family: %d\n", addr->sa_family); + exit(1); + } + + if (inet_ntop(addr->sa_family, raw_ip_addr, ip_string, sizeof(ip_string)) == NULL) { + perror("inet_ntop failed"); + exit(1); + } + + printf("%s [%s]: %s:%u\n", ctx, fam, ip_string, port); +} + +int main(void) +{ + int status; + int listen_fd = socket(AF_INET, SOCK_STREAM, 0); + ERROR_IF(socket, listen_fd, == -1); + + struct sockaddr_in addr = + { + .sin_family = AF_INET, + .sin_addr = { .s_addr = htonl(0x7F000001 /* 127.0.0.1 */) }, + .sin_port = htons(0), + }; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addr_len = sizeof(struct sockaddr_in); + status = bind(listen_fd, saddr, addr_len); + ERROR_IF(bind, status, == -1); + + status = listen(listen_fd, 1); + ERROR_IF(bind, status, == -1); + + status = getsockname(listen_fd, saddr, &addr_len); + ERROR_IF(getsockname, status, == -1); + print_sockaddr("listen sockname", saddr); + + int client_fd = socket(AF_INET, SOCK_STREAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, saddr, addr_len); + ERROR_IF(connect, status, == -1); + + struct sockaddr_in peer; + struct sockaddr* speer = (struct sockaddr*)&peer; + socklen_t peer_len = sizeof(struct sockaddr_in); + status = getpeername(client_fd, speer, &peer_len); + ERROR_IF(getpeername, status, == -1); + print_sockaddr("client peername", speer); + + status = memcmp(saddr, speer, sizeof(struct sockaddr_in)); + UNEXP_IF(memcmp, status, != 0); + + status = getsockname(client_fd, saddr, &addr_len); + ERROR_IF(getsockname, status, == -1); + print_sockaddr("client sockname", saddr); +} diff --git a/tests/sys_socket/recv.c b/tests/sys_socket/recv.c new file mode 100644 index 0000000000..058fea14d9 --- /dev/null +++ b/tests/sys_socket/recv.c @@ -0,0 +1,50 @@ +#include + +#include "test_helpers.h" + +int main(void) +{ + int status; + int listen_fd = socket(AF_INET, SOCK_STREAM, 0); + ERROR_IF(socket, listen_fd, == -1); + + struct sockaddr_in addr = + { + .sin_family = AF_INET, + .sin_addr = { .s_addr = htonl(0x7F000001 /* 127.0.0.1 */) }, + .sin_port = htons(0), + }; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addr_len = sizeof(struct sockaddr_in); + status = bind(listen_fd, saddr, addr_len); + ERROR_IF(bind, status, == -1); + + status = listen(listen_fd, 1); + ERROR_IF(listen, status, == -1); + + status = getsockname(listen_fd, saddr, &addr_len); + ERROR_IF(getsockname, status, == -1); + + int client_fd = socket(AF_INET, SOCK_STREAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, saddr, addr_len); + ERROR_IF(connect, status, == -1); + + int server_fd = accept(listen_fd, NULL, NULL); + ERROR_IF(accept, status, == -1); + + char *c = "foo"; + status = send(server_fd, c, 4, 0); + ERROR_IF(send, status, == -1); + + char x[4]; + ssize_t amount = recv(client_fd, x, 4, 0); + ERROR_IF(recv, amount, == -1); + UNEXP_IF(recv, amount, != 4); + + status = strcmp(c, x); + printf("send %s\n", c); + printf("recv %s\n", x); + UNEXP_IF(strcmp, status, != 0); +} diff --git a/tests/sys_socket/recvfrom.c b/tests/sys_socket/recvfrom.c new file mode 100644 index 0000000000..2b57597280 --- /dev/null +++ b/tests/sys_socket/recvfrom.c @@ -0,0 +1,56 @@ +#include + +#include +#include + +#include "test_helpers.h" + +int main(void) +{ + int status; + int server_fd = socket(AF_INET, SOCK_DGRAM, 0); + ERROR_IF(socket, server_fd, == -1); + + struct sockaddr_in addr = + { + .sin_family = AF_INET, + .sin_addr = { .s_addr = htonl(0x7F000001 /* 127.0.0.1 */) }, + .sin_port = htons(0), + }; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addr_len = sizeof(struct sockaddr_in); + status = bind(server_fd, saddr, addr_len); + ERROR_IF(bind, status, == -1); + + status = getsockname(server_fd, saddr, &addr_len); + ERROR_IF(getsockname, status, == -1); + + int client_fd = socket(AF_INET, SOCK_DGRAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, saddr, addr_len); + ERROR_IF(connect, status, == -1); + + struct sockaddr_in name; + struct sockaddr* sname = (struct sockaddr*)&name; + socklen_t name_len = sizeof(struct sockaddr_in); + status = getsockname(client_fd, sname, &name_len); + ERROR_IF(getsockname, status, == -1); + + char *c = "bar"; + status = sendto(server_fd, c, 4, 0, sname, name_len); + ERROR_IF(sendto, status, == -1); + + struct sockaddr_in from; + struct sockaddr* sfrom = (struct sockaddr*)&from; + socklen_t from_len = sizeof(struct sockaddr_in); + char x[4]; + ssize_t amount = recvfrom(client_fd, x, 4, 0, sfrom, &from_len); + ERROR_IF(recvfrom, amount, == -1); + UNEXP_IF(recvfrom, amount, != 4); + + status = strcmp(c, x); + printf("sendto %s\n", c); + printf("recvfrom %s\n", x); + UNEXP_IF(strcmp, status, != 0); +} diff --git a/tests/sys_socket/shutdown.c b/tests/sys_socket/shutdown.c new file mode 100644 index 0000000000..9653633ddc --- /dev/null +++ b/tests/sys_socket/shutdown.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int sv[2]; + pid_t pid; + char buf[64]; + const char *parent_msg = "ping"; + const char *child_msg = "pong"; + + int status = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); + ERROR_IF(socketpair, status, == -1); + + pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + close(sv[0]); + + memset(buf, 0, sizeof(buf)); + ssize_t n = recv(sv[1], buf, sizeof(buf), 0); + ERROR_IF(recv, n, == -1); + printf("child: received '%s'\n", buf); + + n = recv(sv[1], buf, sizeof(buf), 0); + if (n != 0) { + fprintf( + stderr, + "FAILURE: Expected EOF (0) after parent shutdown, got %ld\n", + (long)n); + exit(EXIT_FAILURE); + } + printf("child: verified EOF from parent (SHUT_WR worked)\n"); + + status = send(sv[1], child_msg, 5, 0); + ERROR_IF(send, status, == -1); + + close(sv[1]); + exit(0); + } else { + close(sv[1]); + + status = send(sv[0], parent_msg, 5, 0); + ERROR_IF(send, status, == -1); + + status = shutdown(sv[0], SHUT_WR); + ERROR_IF(shutdown, status, == -1); + printf("parent: shutdown(SHUT_WR) performed\n"); + + memset(buf, 0, sizeof(buf)); + ssize_t n = recv(sv[0], buf, sizeof(buf), 0); + ERROR_IF(recv, n, == -1); + + if (n == 0) { + fprintf(stderr, + "FAILURE: Parent received EOF, but expected 'pong'\n"); + exit(EXIT_FAILURE); + } + printf("parent: received '%s' (Shutdown allows reading)\n", buf); + + status = send(sv[0], "garbage", 7, MSG_NOSIGNAL); + if (status != -1 || errno != EPIPE) { + fprintf(stderr, + "WARNING: Expected EPIPE on write after shutdown, got " + "status %d\n", + status); + } else { + printf("parent: verified EPIPE on write after shutdown\n"); + } + + close(sv[0]); + wait(NULL); + } + + return 0; +} diff --git a/tests/sys_socket/unixpeername.c b/tests/sys_socket/unixpeername.c new file mode 100644 index 0000000000..cef0c0453e --- /dev/null +++ b/tests/sys_socket/unixpeername.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +void print_sockaddr(char *ctx, struct sockaddr *addr, socklen_t len) { + if (addr->sa_family == AF_UNIX) { + struct sockaddr_un *un = (struct sockaddr_un *)addr; + if (len <= sizeof(sa_family_t)) { + printf("%s [AF_UNIX]: (unnamed)\n", ctx); + } else { + printf("%s [AF_UNIX]: %s\n", ctx, un->sun_path); + } + } else { + printf("%s: Unknown address family %d\n", ctx, addr->sa_family); + exit(1); + } +} + +int main(void) +{ + int status; + // TODO: in linux the sockname shown exactly like this string, not the case with redox. + const char* socket_path = "unixpeername.sock"; + unlink(socket_path); + + int listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, listen_fd, == -1); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addr_len = sizeof(struct sockaddr_un); + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + status = bind(listen_fd, saddr, addr_len); + ERROR_IF(bind, status, == -1); + + // TODO: If this commented out, connect() should error with connection refused + status = listen(listen_fd, 1); + ERROR_IF(listen, status, == -1); + + struct sockaddr_un check; + struct sockaddr* scheck = (struct sockaddr*)✓ + status = getsockname(listen_fd, scheck, &addr_len); + ERROR_IF(getsockname, status, == -1); + print_sockaddr("listen sockname", scheck, addr_len); + + int client_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, saddr, addr_len); + ERROR_IF(connect, status, == -1); + + struct sockaddr_un peer; + struct sockaddr* speer = (struct sockaddr*)&peer; + socklen_t peer_len = sizeof(struct sockaddr_un); + status = getpeername(client_fd, speer, &peer_len); + ERROR_IF(getpeername, status, == -1); + print_sockaddr("client peername", speer, peer_len); + + status = strcmp(check.sun_path, peer.sun_path); + UNEXP_IF(strcmp, status, != 0); + + status = getsockname(client_fd, scheck, &addr_len); + ERROR_IF(getsockname, status, == -1); + print_sockaddr("client sockname", scheck, addr_len); + + close(client_fd); + close(listen_fd); + unlink(socket_path); + + return 0; +} diff --git a/tests/sys_socket/unixrecv.c b/tests/sys_socket/unixrecv.c new file mode 100644 index 0000000000..5d02f896f4 --- /dev/null +++ b/tests/sys_socket/unixrecv.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) +{ + int status; + const char* socket_path = "unix_stream.sock"; + unlink(socket_path); + + int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, server_fd, == -1); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + status = bind(server_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)); + ERROR_IF(bind, status, == -1); + + status = listen(server_fd, 5); + ERROR_IF(listen, status, == -1); + + pid_t pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + // to test that blocking in accept() works + usleep(500000); + + int client_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)); + ERROR_IF(connect, status, == -1); + + char *msg = "ipsum"; + printf("send %s\n", msg); + status = send(client_fd, msg, 6, 0); + ERROR_IF(send, status, == -1); + + close(client_fd); + return 0; + } else { + int accepted_fd = accept(server_fd, NULL, NULL); + ERROR_IF(accept, accepted_fd, == -1); + + char x[6]; + ssize_t amount = recv(accepted_fd, x, 6, 0); + ERROR_IF(recv, amount, == -1); + + printf("recv %s\n", x); + close(accepted_fd); + close(server_fd); + + wait(NULL); + unlink(socket_path); + } + + return 0; +} \ No newline at end of file diff --git a/tests/sys_socket/unixrecvearly.c b/tests/sys_socket/unixrecvearly.c new file mode 100644 index 0000000000..6c496f53f2 --- /dev/null +++ b/tests/sys_socket/unixrecvearly.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +// similar to "unixrecv", but this test if recv() works before accept(). +int main(void) +{ + int status; + const char* socket_path = "unix_read.sock"; + unlink(socket_path); + + int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, server_fd, == -1); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + status = bind(server_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)); + ERROR_IF(bind, status, == -1); + + status = listen(server_fd, 5); + ERROR_IF(listen, status, == -1); + + pid_t pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + int client_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)); + ERROR_IF(connect, status, == -1); + + char x[6]; + ssize_t amount = recv(client_fd, x, 6, 0); + ERROR_IF(recv, amount, == -1); + printf("recv %s\n", x); + + close(client_fd); + return 0; + } else { + // to test that blocking in recv() works + usleep(500000); + + int accepted_fd = accept(server_fd, NULL, NULL); + ERROR_IF(accept, accepted_fd, == -1); + + char *msg = "ipsum"; + printf("send %s\n", msg); + status = send(accepted_fd, msg, 6, 0); + ERROR_IF(send, status, == -1); + + close(accepted_fd); + close(server_fd); + + wait(NULL); + unlink(socket_path); + } + + return 0; +} \ No newline at end of file diff --git a/tests/sys_socket/unixrecvfrom.c b/tests/sys_socket/unixrecvfrom.c new file mode 100644 index 0000000000..8d11bd32b7 --- /dev/null +++ b/tests/sys_socket/unixrecvfrom.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) +{ + int status; + const char* socket_path = "unixrecvfrom.sock"; + + unlink(socket_path); + + int server_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + ERROR_IF(socket, server_fd, == -1); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + status = bind(server_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)); + ERROR_IF(bind, status, == -1); + + int client_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)); + ERROR_IF(connect, status, == -1); + + char *c = "lorem"; + status = send(client_fd, c, 6, 0); + ERROR_IF(send, status, == -1); + + char x[6]; + struct sockaddr_un from; + socklen_t from_len = sizeof(struct sockaddr_un); + + ssize_t amount = recvfrom(server_fd, x, 6, 0, (struct sockaddr*)&from, &from_len); + ERROR_IF(recvfrom, amount, == -1); + UNEXP_IF(recvfrom, amount, != 6); + + status = strcmp(c, x); + printf("send %s\n", c); + printf("recvfrom %s\n", x); + UNEXP_IF(strcmp, status, != 0); + + close(server_fd); + close(client_fd); + unlink(socket_path); +} \ No newline at end of file diff --git a/tests/sys_socket/unixsocketpair.c b/tests/sys_socket/unixsocketpair.c new file mode 100644 index 0000000000..0f5aa0f618 --- /dev/null +++ b/tests/sys_socket/unixsocketpair.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +void socketpair_test(int flags) { + int sv[2]; + pid_t pid; + char buf[64]; + const char *parent_msg = "ping"; + const char *child_msg = "pong"; + + int status = socketpair(AF_UNIX, flags, 0, sv); + ERROR_IF(socketpair, status, == -1); + + pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + close(sv[0]); + + ssize_t n = recv(sv[1], buf, sizeof(buf), 0); + ERROR_IF(recv, n, == -1); + printf("child: %s\n", buf); + + status = send(sv[1], child_msg, 5, 0); + ERROR_IF(send, status, == -1); + + close(sv[1]); + exit(0); + } else { + close(sv[1]); + + status = send(sv[0], parent_msg, 5, 0); + ERROR_IF(send, status, == -1); + + ssize_t n = recv(sv[0], buf, sizeof(buf), 0); + ERROR_IF(recv, n, == -1); + printf("parent: %s\n", buf); + + close(sv[0]); + wait(NULL); + } +} + +int main(void) { + printf("SOCK_STREAM\n"); + socketpair_test(SOCK_STREAM); + printf("SOCK_DGRAM\n"); + socketpair_test(SOCK_DGRAM); + return 0; +} \ No newline at end of file diff --git a/tests/sys_socket/unixwrite.c b/tests/sys_socket/unixwrite.c new file mode 100644 index 0000000000..4386c66be2 --- /dev/null +++ b/tests/sys_socket/unixwrite.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +// similar to "unixrecv", but this test if write() works before accept(). +// "unixrecvfrom" is the similar test to this one, but using SOCK_DGRAM. +// "unixrecvearly" is also the similar test to this one, but using recv(). +int main() { + int status; + const char* socket_path = "unix_write.sock"; + unlink(socket_path); + + int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, server_fd, == -1); + + struct sockaddr_un addr = { .sun_family = AF_UNIX }; + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + unlink(socket_path); + + status = bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); + ERROR_IF(bind, status, == -1); + + status = listen(server_fd, 1); + ERROR_IF(listen, status, == -1); + + int client_fd = socket(AF_UNIX, SOCK_STREAM, 0); + ERROR_IF(socket, client_fd, == -1); + + status = connect(client_fd, (struct sockaddr*)&addr, sizeof(addr)); + ERROR_IF(connect, status, == -1); + + char *msg = "yes"; + ssize_t ret = write(client_fd, msg, 4); + ERROR_IF(write, ret, == -1); + UNEXP_IF(raise, ret, != 4); + + int accepted_fd = accept(server_fd, NULL, NULL); + ERROR_IF(accept, accepted_fd, == -1); + + char x[4]; + ssize_t amount = read(accepted_fd, x, 4); + ERROR_IF(read, amount, == -1); + + close(accepted_fd); + close(client_fd); + close(server_fd); + return 0; +} diff --git a/tests/sys_stat/chmod.c b/tests/sys_stat/chmod.c new file mode 100644 index 0000000000..30be2a6f19 --- /dev/null +++ b/tests/sys_stat/chmod.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #if defined(__linux__) && !defined(__redox__) */ +/* #include */ +/* #else */ +/* #define KERNEL_VERSION(x,y,z) (0) */ +/* #endif */ + +__attribute__((nonnull(2))) +static bool check_mode( + int dir, + const char path[], + int flags, + mode_t expected +) { + struct stat stat = {0}; + if (fstatat(dir, path, &stat, flags) == -1) { + perror("fstatat"); + return false; + } + + if ((stat.st_mode & 0777) != expected) { + fprintf( + stderr, + "Mode mismatch\n\tExpected: %o\n\tActual: %o\n", + expected, + stat.st_mode & 0777 + ); + return false; + } + + return true; +} + +int main(void) { + int status = EXIT_FAILURE; + + char template[] = "/tmp/chmtest.XXXXXX"; + if (!mkdtemp(template)) { + perror("mkdtemp"); + goto bye; + } + size_t len = sizeof(template) - 1; + + // Create a file and a link to chmod. + const char file_name[] = "king_crimson"; + char file_path[PATH_MAX] = {0}; + memcpy(file_path, template, len); + file_path[len] = '/'; + memcpy(&file_path[len + 1], file_name, sizeof(file_name)); + + int filefd = open(file_path, O_CREAT); + if (filefd == -1) { + perror("open (file, O_CREAT)"); + goto rmtempdir; + } + + const char link_name[] = "red"; + char link_path[PATH_MAX] = {0}; + memcpy(link_path, template, len); + link_path[len] = '/'; + memcpy(&link_path[len + 1], link_name, sizeof(link_name)); + + if (symlink(file_path, link_path) == -1) { + perror("symlink"); + goto rmfiles; + } + #ifdef __redox__ + int linkfd = open(link_path, O_PATH | O_NOFOLLOW | O_SYMLINK); + if (linkfd == -1) { + perror("open (link, O_NOFOLLOW)"); + goto rmfiles; + } + #endif + + int dir = open(template, O_DIRECTORY); + if (dir == -1) { + perror("open (temp dir)"); + goto closelink; + } + + // chmod + + // File + if (chmod(file_path, 0666) == -1) { + perror("chmod file 0666"); + goto closetemp; + } + if (!check_mode(dir, file_name, 0, 0666)) { + fprintf(stderr, "Context: chmod %s\n", file_name); + goto closetemp; + } + + // Link (deferenced) + if (chmod(link_path, 0777) == -1) { + perror("chmod link 0777"); + goto closetemp; + } + if (!check_mode(dir, file_name, 0, 0777)) { + fprintf(stderr, "Context: chmod %s\n", link_name); + goto closetemp; + } + + // Dir + if (chmod(template, 0766) == -1) { + perror("chmod directory 0766"); + goto closetemp; + } + if (!check_mode(dir, "", AT_EMPTY_PATH, 0766)) { + fprintf(stderr, "Context: chmod %s\n", template); + goto closetemp; + } + + // fchmod + + // File + if (fchmod(filefd, 0644) == -1) { + perror("fchmod file 0644"); + goto closetemp; + } + if (!check_mode(dir, file_name, 0, 0644)) { + fprintf(stderr, "Context: fchmod %s\n", file_name); + goto closetemp; + } + + // Link (not followed) + #ifdef __redox__ + if (fchmod(linkfd, 0666) == -1) { + perror("fchmod link 0666"); + goto closetemp; + } + if (!check_mode(dir, link_name, AT_SYMLINK_NOFOLLOW, 0666)) { + fprintf(stderr, "Context: fchmod %s\n", link_name); + goto closetemp; + } + #endif + + // Directory + if (fchmod(dir, 0777) == -1) { + perror("fchmod directory 0777"); + goto closetemp; + } + if (!check_mode(dir, "", AT_EMPTY_PATH, 0777)) { + fprintf(stderr, "Context: fchmod %s\n", template); + goto closetemp; + } + + // fchmodat + + // File (relative) + if (fchmodat(dir, file_name, 0666, 0) == -1) { + perror("fchmodat directory 0666"); + goto closetemp; + } + if (!check_mode(dir, file_name, 0, 0666)) { + fprintf(stderr, "Context: fchmodat %s\n", file_name); + goto closetemp; + } + + // File (absolute) + if (fchmodat(dir, file_path, 0777, 0) == -1) { + perror("fchmodat file 0777"); + goto closetemp; + } + if (!check_mode(dir, file_name, 0, 0777)) { + fprintf(stderr, "Context: fchmodat %s\n", file_path); + } + + // Link (followed) + if (fchmodat(dir, link_name, 0666, 0) == -1) { + perror("fchmodat link 0666"); + goto closetemp; + } + if (!check_mode(dir, file_name, 0, 0666)) { + fprintf(stderr, "Context: fchmodat %s\n", link_name); + goto closetemp; + } + + // Link (not followed) + #ifdef __redox__ + if (fchmodat(dir, link_name, 0777, AT_SYMLINK_NOFOLLOW) == -1) { + perror("fchmodat link 0777"); + goto closetemp; + } + if (!check_mode(dir, link_name, AT_SYMLINK_NOFOLLOW, 0777)) { + fprintf(stderr, "Context: fchmodat %s\n", link_name); + goto closetemp; + } + #endif + + // Directory + // AT_EMPTY_PATH support is relatively new so this fails in CI. + #if defined(__redox__) // || LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0) + if (fchmodat(dir, "", 0700, AT_EMPTY_PATH) == -1) { + perror("fchmodat directory 0700"); + goto closetemp; + } + if (!check_mode(dir, "", AT_EMPTY_PATH, 0700)) { + fprintf(stderr, "Context: fchmodat %s\n", template); + goto closetemp; + } + + // Directory (cwd) + char old_cwd[PATH_MAX] = {0}; + if (!getcwd(old_cwd, PATH_MAX)) { + perror("getcwd"); + goto closetemp; + } + if (chdir(template) == -1) { + perror("chdir (temp dir)"); + goto closetemp; + } + + if (fchmodat(AT_FDCWD, "", 0777, AT_EMPTY_PATH) == -1) { + perror("fchmodat cwd 0777"); + goto closetemp; + } + if (!check_mode(AT_FDCWD, "", AT_EMPTY_PATH, 0777)) { + fprintf(stderr, "Context: fchmodat %s\n", template); + goto closetemp; + } + + if (chdir(old_cwd) == -1) { + perror("chdir (old cwd)"); + goto closetemp; + } + #endif + + status = EXIT_SUCCESS; +closetemp: + close(dir); +closelink: + #ifdef __redox__ + close(linkfd); + #endif +rmfiles: + close(filefd); + unlink(file_path); + unlink(link_path); +rmtempdir: + rmdir(template); +bye: + return status; +} diff --git a/tests/sys_stat/fstatat.c b/tests/sys_stat/fstatat.c new file mode 100644 index 0000000000..a3a2b075c0 --- /dev/null +++ b/tests/sys_stat/fstatat.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +// Create file `dir/name` and fill it with `contents`. +__attribute__((nonnull)) +static const char* mktestfile( + const char dir[], + const char name[], + const char contents[] +) { + size_t dir_len = strlen(dir); + size_t name_len = strlen(name); + + // dir + / name + \0 + char* path = malloc(dir_len + name_len + 2); + if (!path) { + perror("malloc"); + return NULL; + } + // memcpy is faster/recommended but I'm lazy. + strcpy(path, dir); + strcat(path, "/"); + strcat(path, name); + + int fd = creat(path, 0700); + if (fd < 0) { + perror("creat"); + free(path); + return NULL; + } + + size_t contents_len = strlen(contents); + // Assumes that the handful of bytes of contents are fully written. + if (write(fd, contents, contents_len) < 0) { + perror("write"); + free(path); + close(fd); + return NULL; + } + + close(fd); + return path; +} + +// Create a symlink, `dir/name`, to `target` and return its path. +__attribute__((nonnull)) +static const char* mktestlink( + const char dir[], + const char name[], + const char target[] +) { + char* path = malloc(strlen(dir) + strlen(name) + 2); + if (!path) { + perror("malloc"); + return NULL; + } + + strcpy(path, dir); + strcat(path, "/"); + strcat(path, name); + + if (symlink(target, path) == -1) { + perror("symlink"); + free(path); + return NULL; + } + + return path; +} + +__attribute__((nonnull)) +static int run_test( + int fd, + int flags, + const char name[], + const char path[], + size_t expected, + mode_t mode +) { + struct stat stat = {0}; + if (fstatat(fd, name, &stat, flags) == -1) { + perror("fstatat"); + return -1; + } + + if ((size_t) stat.st_size != expected) { + fprintf( + stderr, + "fstatat invalid stats\n\tFile: %s\n\tExpected: %zu Actual: %ld\n", + path, + expected, + stat.st_size + ); + return -1; + } + if ((stat.st_mode & S_IFMT) != mode) { + fprintf( + stderr, + "fstatat invalid mode\n\tFile: %s\n", + path + ); + return -1; + } + + // We create the temp dirs/files therefore we should own them. + uid_t uid = getuid(); + gid_t gid = getgid(); + if (stat.st_uid != uid || stat.st_gid != gid) { + fputs("fstatat invalid UID or GID", stderr); + fprintf( + stderr, + "\n\tExpected UID: %u\n\tActual UID: %u\n", + uid, stat.st_uid + ); + fprintf( + stderr, + "\n\tExpected GID: %u\n\tActual GID: %u\n", + gid, stat.st_gid + ); + + } + + return 0; +} + +int main(void) { + int status = EXIT_FAILURE; + + char dir_template_A[] = "/tmp/fsatest.XXXXXXX"; + char* dir_A = mkdtemp(dir_template_A); + if (!dir_A) { + perror("mkdtemp (dir A)"); + goto bye; + } + + char dir_template_B[] = "/tmp/fsatest.XXXXXXX"; + char* dir_B = mkdtemp(dir_template_B); + if (!dir_B) { + perror("mkdtemp (dir B)"); + goto clean_dir_a; + } + + // File in directory A + const char name[] = "file"; + const char cont_A[] = "Flying Dutchman"; + const char* file_A = mktestfile(dir_A, name, cont_A); + if (!file_A) { + goto clean_dir_b; + } + // Link from directory A to file A + const char link_A_name[] = "alink"; + const char* link_A = mktestlink(dir_A, link_A_name, file_A); + if (!link_A) { + goto clean_file_a; + } + + // File in directory B + const char cont_B[] = "Every Villain is Lemons"; + const char* file_B = mktestfile(dir_B, name, cont_B); + if (!file_B) { + goto unlink_link_A; + } + const char link_B_name[] = "blink"; + // Link from directory A to file B in directory B + const char* link_B = mktestlink(dir_A, link_B_name, file_B); + if (!link_B) { + goto unlink_file_B; + } + + // TESTS + // The size of the file is used as a proxy for checking that stat worked. + int dir_a_fd = open(dir_A, O_DIRECTORY); + if (dir_a_fd == -1) { + perror("open (dir A)"); + goto unlink_link_B; + } + + // fstatat works (basic) + size_t len_cont_A = strlen(cont_A); + if (run_test(dir_a_fd, 0, name, file_A, len_cont_A, S_IFREG) == -1) { + goto close_dir_a_fd; + } + + // fstatat follows symlinks (same dir) + if (run_test(dir_a_fd, 0, link_A_name, link_A, len_cont_A, S_IFREG) == -1) { + fprintf(stderr, "Context: link %s -> %s\n", link_A, file_A); + goto close_dir_a_fd; + } + + // fstatat follows symlinks (to diff dir) + size_t len_cont_B = strlen(cont_B); + if (run_test(dir_a_fd, 0, link_B_name, link_B, len_cont_B, S_IFREG) == -1) { + fprintf(stderr, "Context: link %s -> %s\n", link_B, file_B); + goto close_dir_a_fd; + } + + // AT_SYMLINK_NOFOLLOW + if ( + run_test( + dir_a_fd, + AT_SYMLINK_NOFOLLOW, + link_A_name, + link_A, + strlen(file_A), + S_IFLNK + ) == -1 + ) { + fprintf(stderr, "Context: link %s (no follow)\n", link_A); + goto close_dir_a_fd; + } + + // TODO: O_SEARCH (no Redox support) + + // AT_FDCWD + char old_cwd[PATH_MAX] = {0}; + if (!getcwd(old_cwd, PATH_MAX)) { + perror("getcwd"); + goto close_dir_a_fd; + } + if (chdir(dir_A) == -1) { + perror("chdir"); + goto close_dir_a_fd; + } + if (run_test(AT_FDCWD, 0, name, "./", len_cont_A, S_IFREG) == -1) { + fputs("Context: AT_FDCWD\n", stderr); + goto close_dir_a_fd; + } + if (chdir(old_cwd) == -1) { + perror("chdir"); + goto close_dir_a_fd; + } + + // Absolute path + if (run_test(dir_a_fd, 0, file_A, file_A, len_cont_A, S_IFREG) == -1) { + fprintf(stderr, "Context: absolute path %s\n", file_A); + goto close_dir_a_fd; + } + + // Relative path that traverses dir boundaries + // The path isn't resolved beneath the directory but rather resolved + // relative to it. Directory traversal is allowed if AT_RESOLVE_BENEATH + // isn't used. + const char nested_name[] = "nested"; + char* nested_dir = malloc(strlen(dir_A) + strlen(nested_name) + 2); + if (!nested_dir) { + perror("malloc"); + goto close_dir_a_fd; + } + strcpy(nested_dir, dir_A); + strcat(nested_dir, "/"); + strcat(nested_dir, nested_name); + if (mkdir(nested_dir, 0700) == -1) { + perror("mkdir"); + goto clean_nested_dir; + } + + int nested_fd = open(nested_dir, O_DIRECTORY); + if (nested_fd < 0) { + perror("open"); + goto remove_nested_dir; + } + char rel_nested[sizeof(name) + 5] = "../"; + strcat(rel_nested, name); + if ( + run_test( + nested_fd, + 0, + rel_nested, + rel_nested, + len_cont_A, + S_IFREG + ) == -1 + ) { + fprintf( + stderr, + "Context: relative path from %s to ../%s\n", + nested_dir, + name + ); + goto close_nested_dir; + } + + // TODO: AT_RESOLVE_BENEATH + + // AT_EMPTY_PATH (empty path) + struct stat stat = {0}; + if (fstatat(dir_a_fd, "", &stat, AT_EMPTY_PATH) == -1) { + fputs("Context: AT_EMPTY_PATH with empty path\n", stderr); + goto close_dir_a_fd; + } + if ((stat.st_mode & S_IFMT) != S_IFDIR) { + fputs("Context: AT_EMPTY_PATH should stat dir (empty path)\n", stderr); + goto close_dir_a_fd; + } + + // AT_EMPTY_PATH (non-empty path) + if ( + run_test( + dir_a_fd, + AT_EMPTY_PATH, + link_A_name, + link_A, + len_cont_A, + S_IFREG + ) == -1 + ) { + fputs("Context: AT_EMPTY_PATH with path should stat path\n", stderr); + goto close_dir_a_fd; + } + + // TODO: Swapped directories resolves correctly + + // Failure conditions: + // Empty path without AT_EMPTY_PATH + stat = (struct stat) {0}; + if (fstatat(dir_a_fd, "", &stat, 0) == 0) { + fputs("Context: empty path should fail\n", stderr); + goto close_dir_a_fd; + } + + // Clean up is LIFO where the last created resource is the first cleaned. + status = EXIT_SUCCESS; +close_nested_dir: + close(nested_fd); +remove_nested_dir: + rmdir(nested_dir); +clean_nested_dir: + free((void*) nested_dir); +close_dir_a_fd: + close(dir_a_fd); +unlink_link_B: + unlink(link_B); + free((void*) link_B); +unlink_file_B: + unlink(file_B); + free((void*) file_B); +unlink_link_A: + unlink(link_A); + free((void*) link_A); +clean_file_a: + unlink(file_A); + free((void*) file_A); +clean_dir_b: + rmdir(dir_B); +clean_dir_a: + rmdir(dir_A); +bye: + return status; +} diff --git a/tests/sys_stat/lstat.c b/tests/sys_stat/lstat.c new file mode 100644 index 0000000000..9dd4dd05c1 --- /dev/null +++ b/tests/sys_stat/lstat.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(void) { + int status = EXIT_FAILURE; + + char tempdir_path[] = "/tmp/lstat_test.XXXXXX"; + if (!mkdtemp(tempdir_path)) { + perror("mkdtemp"); + goto bye; + } + int tempdir = open(tempdir_path, O_DIRECTORY); + if (tempdir == -1) { + perror("open (tempdir)"); + goto rmtemp; + } + + // Source + const char src_name[] = "kerouac"; + char src_buf[PATH_MAX] = {0}; + strcpy(src_buf, tempdir_path); + strcat(src_buf, "/"); + strcat(src_buf, src_name); + // TODO: Use openat + int source = open(src_buf, O_CREAT); + if (source == -1) { + perror("open"); + goto close_dir; + } + + // Dest (link) + const char dst_name[] = "on_the_road"; + char dst_buf[PATH_MAX] = {0}; + strcpy(dst_buf, tempdir_path); + strcat(dst_buf, "/"); + strcat(dst_buf, dst_name); + if (symlink(src_buf, dst_buf) == -1) { + perror("symlink"); + goto rm_source; + } + + struct stat src_stat = {0}; + if (fstatat(tempdir, dst_name, &src_stat, 0) == -1) { + perror("fstatat"); + goto rm_dest; + } + // Quick consistency check + uid_t uid = getuid(); + gid_t gid = getgid(); + if (src_stat.st_uid != uid) { + fprintf( + stderr, + "uid:\n\tExpected %ld\n\tActual: %ld\n", + (intmax_t) uid, + (intmax_t) gid + ); + goto rm_dest; + } + if (src_stat.st_nlink != 1) { + fputs("nlink: Expected one hard link\n", stderr); + goto rm_dest; + } + if ((src_stat.st_mode & S_IFMT) != S_IFREG) { + fputs("mode: Expected a normal file\n", stderr); + goto rm_dest; + } + + struct stat dst_stat = {0}; + if (lstat(dst_buf, &dst_stat) == -1) { + perror("lstat"); + goto rm_dest; + } + if (memcmp(&src_stat, &dst_stat, sizeof(struct stat)) == 0) { + fputs("lstat incorrectly followed the symlink\n", stderr); + goto rm_dest; + } + if ((dst_stat.st_mode & S_IFMT) != S_IFLNK) { + fputs("lstat did not stat a link\n", stderr); + goto rm_dest; + } + + status = EXIT_SUCCESS; +rm_dest: + unlink(dst_buf); +rm_source: + unlink(src_buf); +close_dir: + close(tempdir); +rmtemp: + rmdir(tempdir_path); +bye: + return status; +} diff --git a/tests/sys_stat/stat.c b/tests/sys_stat/stat.c new file mode 100644 index 0000000000..95fadb1751 --- /dev/null +++ b/tests/sys_stat/stat.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("sizeof(struct stat): %ld\n", sizeof(struct stat)); + + struct stat buf = {0}; + + int stat_status = stat("sys_stat/stat.c", &buf); + ERROR_IF(stat, stat_status, == -1); + UNEXP_IF(stat, stat_status, != 0); + + printf("st_size: %lu\n", buf.st_size); + printf("st_blksize: %lu\n", buf.st_blksize); + printf("st_dev: %lu\n", buf.st_dev); + printf("st_ino: %lu\n", buf.st_ino); + printf("st_mode: %o\n", buf.st_mode); + printf("st_nlink: %lu\n", buf.st_nlink); + printf("st_uid: %u\n", buf.st_uid); + printf("st_gid: %u\n", buf.st_gid); + + assert(buf.st_size > 600); + assert(buf.st_nlink == 1); + + return EXIT_SUCCESS; +} diff --git a/tests/sys_statvfs/statvfs.c b/tests/sys_statvfs/statvfs.c new file mode 100644 index 0000000000..62785f1bfa --- /dev/null +++ b/tests/sys_statvfs/statvfs.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include + +int test_statvfs(const char *path) { + struct statvfs buf; + + printf("Testing statvfs on: %s\n", path); + + if (statvfs(path, &buf) != 0) { + perror("statvfs failed"); + return EXIT_FAILURE; + } + + printf(" Filesystem block size: %lu\n", buf.f_bsize); + printf(" Fragment size: %lu\n", buf.f_frsize); + printf(" Total blocks: %lu\n", buf.f_blocks); + printf(" Free blocks: %lu\n", buf.f_bfree); + printf(" Available blocks: %lu\n", buf.f_bavail); + printf(" Total inodes: %lu\n", buf.f_files); + printf(" Free inodes: %lu\n", buf.f_ffree); + + if (buf.f_bsize == 0 || buf.f_frsize == 0) { + fprintf(stderr, "ERROR: Block size or fragment size is zero.\n"); + return EXIT_FAILURE; + } + + if (buf.f_blocks == 0) { + fprintf(stderr, "ERROR: Total number of blocks is zero.\n"); + return EXIT_FAILURE; + } + + return 0; +} + +int main(void) { + const char *test_path = "/"; + + return test_statvfs(test_path); +} diff --git a/tests/sys_syslog/syslog.c b/tests/sys_syslog/syslog.c new file mode 100644 index 0000000000..f7a5a2e75a --- /dev/null +++ b/tests/sys_syslog/syslog.c @@ -0,0 +1,46 @@ +#include +#include + +int main(void) { + // Test that syslog succeeds without explicitly calling openlog + syslog(LOG_INFO, "Testing syslog; disregard"); + closelog(); + + // The rest of the tests use LOG_PERROR so we get verifiable output on stderr + openlog("relibc_test", + LOG_CONS | LOG_PERROR | LOG_NDELAY, + LOG_LOCAL2 + ); + + // Basic + syslog(LOG_EMERG, "This is a test message with formatting: %d", 5); + + // Only alert should print + setlogmask(LOG_MASK(LOG_ALERT)); + syslog(LOG_ALERT, "Hank Hill"); + syslog(LOG_EMERG, "Sells propane and propane accessories"); + + // Squelch all logs with a priority less than WARNING. + setlogmask(LOG_UPTO(LOG_WARNING)); + // First line should be emitted while the second shouldn't. + syslog(LOG_WARNING, "Foo has been bar'd"); + syslog(LOG_NOTICE, "I am a very spammy log message. Ha!"); + + // All of these should print + setlogmask(LOG_UPTO(LOG_DEBUG)); + syslog(LOG_DEBUG, "And now, Eeveelutions"); + syslog(LOG_DEBUG, "Espeon"); + syslog(LOG_INFO, "Umbreon"); + syslog(LOG_NOTICE, "Sylveon"); + syslog(LOG_WARNING, "Glaceon"); + syslog(LOG_ERR, "Leafeon"); + syslog(LOG_CRIT, "Vaporeon"); + syslog(LOG_ALERT, "Flareon"); + syslog(LOG_EMERG, "Jolteon"); + + // The log file should automatically open even if closed. + closelog(); + syslog(LOG_INFO, "Bye from relibc's syslog tests!"); + + return EXIT_SUCCESS; +} diff --git a/tests/sys_utsname/uname.c b/tests/sys_utsname/uname.c new file mode 100644 index 0000000000..836fef71fd --- /dev/null +++ b/tests/sys_utsname/uname.c @@ -0,0 +1,19 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + struct utsname system_info; + + int result = uname(&system_info); + ERROR_IF(uname, result, == -1); + UNEXP_IF(uname, result, < 0); + + printf("sysname: '%s'\n", system_info.sysname); + printf("nodename: '%s'\n", system_info.nodename); + printf("release: '%s'\n", system_info.release); + printf("version: '%s'\n", system_info.version); + printf("machine: '%s'\n", system_info.machine); + //printf("domainname: '%s'\n", system_info.domainname); +} diff --git a/tests/termios.c b/tests/termios.c new file mode 100644 index 0000000000..36436811d6 --- /dev/null +++ b/tests/termios.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include + +int main(void) { + // Check that the tty supports VDISABLE + int vdisable = pathconf("/dev/tty", _PC_VDISABLE); + assert(vdisable == _POSIX_VDISABLE); + + int termfd = open("/dev/tty", O_RDWR, O_NDELAY | O_NOCTTY); + if (termfd < 0) { + perror("open"); + return EXIT_FAILURE; + } + + // Currently set/default options + struct termios termios = {0}; + if (tcgetattr(termfd, &termios) < 0) { + perror("tcgetattr"); + close(termfd); + return EXIT_FAILURE; + } + struct termios term_restore = termios; + + const cc_t verase = termios.c_cc[VERASE]; + assert(verase != _POSIX_VDISABLE); + // Disable backspace key control char + termios.c_cc[VERASE] = _POSIX_VDISABLE; + + if (tcsetattr(termfd, TCSANOW, &termios) < 0) { + perror("tcsetattr (setting VDISABLE)"); + close(termfd); + return EXIT_FAILURE; + } + + // Check that it was actually set + struct termios term_vdisable = {0}; + if (tcgetattr(termfd, &term_vdisable) < 0) { + perror("tcgetattr (after setting VDISABLE)"); + close(termfd); + return EXIT_FAILURE; + } + assert(term_vdisable.c_cc[VERASE] == _POSIX_VDISABLE); + + // Restore old config + if (tcsetattr(termfd, TCSAFLUSH, &term_restore) < 0) { + perror("tcsetattr (restoring settings)"); + close(termfd); + return EXIT_FAILURE; + } + + close(termfd); + return EXIT_SUCCESS; +} diff --git a/tests/test_helpers.h b/tests/test_helpers.h new file mode 100644 index 0000000000..de4a360951 --- /dev/null +++ b/tests/test_helpers.h @@ -0,0 +1,132 @@ +#ifndef _TEST_HELPERS +#define _TEST_HELPERS + +#include +#include +#include +#include +#include +#include + +// Throws errors on a well-defined API error values. +// +// Only use with API functions that sets the errno variable. +// Do not pass functions as the status or condition arguments, they might be +// evaluated multiple times. +// +// Usage example: +// +// > Upon successful completion, fclose() returns 0. +// > Otherwise, it returns EOF and sets errno to indicate the error. +// +// int status = fclose(fp); +// ERROR_IF(fclose, status, == EOF); +// +// Use it only for checking the API error values. +// Do not use it for checking the correctness of the results. If you need to +// do that, print the values to the standard output and use the expected outputs +// directory. +// +// For example: +// +// int c = fgetc(f); // !!! DO NOT USE THIS WAY !!! +// ERROR_IF(fgetc, c, != 'H'); // !!! DO NOT USE THIS WAY !!! +// +// Correct usage: +// +// int c = fgetc(f); // OK +// ERROR_IF(fgetc, c, == EOF); // OK +// printf("result: %c\n", c); // OK +// +#define ERROR_IF(func, status, condition) \ + do { \ + if (status condition) { \ + fprintf(stderr, "%s:%s:%d: '%s' failed: %s (%d)\n", \ + __FILE__, __func__, __LINE__, #func, strerror(errno), errno); \ + _exit(EXIT_FAILURE); \ + } \ + } while(0) + +#define ERROR_IF2(func, status, condition) \ + do { \ + if (status condition) { \ + fprintf(stderr, "%s:%s:%d: '%s' failed: %s (%d)\n", \ + __FILE__, __func__, __LINE__, #func, strerror(status), status); \ + _exit(EXIT_FAILURE); \ + } \ + } while(0) + +// Throws errors on API return values not defined by the standards. +// +// Do not pass functions as the status or condition arguments, they might be +// evaluated multiple times. +// +// Use it only for detecting return values that should have never been returned +// in any case by the API functions. +// +// Usage example: +// +// > The fgetc() function obtains the next byte as an unsigned char +// > converted to an int. +// +// int c = fgetc(f); +// UNEXP_IF(fgetc, c, < 0); +// UNEXP_IF(fgetc, c, > 255); +// +#define UNEXP_IF(func, status, condition) \ + do { \ + if (status condition) { \ + fprintf(stderr, "%s:%s:%d: '%s' returned a non-standard value: ", \ + __FILE__, __func__, __LINE__, #func); \ + fprintf(stderr, _Generic((status), \ + char *: "char*(%p) = \"%1$s\"", \ + void *: "void*(%p)", \ + ssize_t: "%li", \ + default: "%i" \ + ), status); \ + fprintf(stderr, "\n"); \ + _exit(EXIT_FAILURE); \ + } \ + } while (0) + +// A convenience macro to show where the test fail. +#define exit(code) \ + do { \ + if (code != EXIT_SUCCESS) { \ + fprintf(stderr, "%s:%s:%d: Test failed with exit(%s)\n", \ + __FILE__, __func__, __LINE__, #code); \ + } \ + _exit(code); \ + } while(0) + +// Duplicate of lrand48() logic but suitable for multithreaded use +int random_bool() { + _Thread_local static uint64_t xsubi = 0; + xsubi = (0x5deece66d * xsubi + 0xb) & (0xffffffffffff); + return (xsubi >> 17) % 2 == 0; +} + +// Quick helper for checking desired errno status. +// Use as macro: CHECK_AND_PRINT_ERRNO(); +// If errno is as expected, prints this; otherwise, prints expected vs. actual and exit. +void printf_errno(int errnoval, char *errnoname) { + + printf("%d (%s)", + errno, strerror(errno)); + if (errno != errnoval) { + printf("\n^^^^^ FAILURE ^^^^^ (SHOULD BE %d - %s (%s))\n", + errnoval, errnoname, strerror(errnoval)); + } else { + printf(" - %s\n", errnoname); + } +} + +#define CHECK_AND_PRINT_ERRNO(errnoval) \ + do { \ + printf_errno(errnoval, #errnoval); \ + if (errnoval != errno) { \ + exit(EXIT_FAILURE); \ + } \ + } while(0); + +#endif /* _TEST_HELPERS */ diff --git a/tests/time/asctime.c b/tests/time/asctime.c new file mode 100644 index 0000000000..2525eddf0a --- /dev/null +++ b/tests/time/asctime.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + time_t unix_epoch = 0; + struct tm *unix_epoch_tm_ptr = gmtime(&unix_epoch); + + char *time_string = NULL; + + /* Min/max non-UB-causing values according to ISO C11 and newer. */ + struct tm iso_c_min_tm = {.tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = 1, .tm_mon = 0, .tm_year = 1000-1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = 0, .tm_gmtoff = 0, .tm_zone = NULL}; + struct tm iso_c_max_tm = {.tm_sec = 60, .tm_min = 59, .tm_hour = 23, .tm_mday = 31, .tm_mon = 11, .tm_year = 9999-1900, .tm_wday = 6, .tm_yday = 0, .tm_isdst = 0, .tm_gmtoff = 0, .tm_zone = NULL}; + + /* Min/max non-UB-causing values according to POSIX (issue 7). These + * will cause UB according to the ISO standard! */ + struct tm posix_min_tm = {.tm_sec = 0, .tm_min = 0, .tm_hour = 0, .tm_mday = -99, .tm_mon = 0, .tm_year = -999-1900, .tm_wday = 0, .tm_yday = 0, .tm_isdst = 0, .tm_gmtoff = 0, .tm_zone = NULL}; + struct tm posix_max_tm = {.tm_sec = 99, .tm_min = 99, .tm_hour = 99, .tm_mday = 999, .tm_mon = 11, .tm_year = 9999-1900, .tm_wday = 6, .tm_yday = 0, .tm_isdst = 0, .tm_gmtoff = 0, .tm_zone = NULL}; + + time_string = asctime(unix_epoch_tm_ptr); + printf("%s", time_string); + + time_string = asctime(&iso_c_min_tm); + printf("%s", time_string); + + time_string = asctime(&iso_c_max_tm); + printf("%s", time_string); + + time_string = asctime(&posix_min_tm); + printf("%s", time_string); + + time_string = asctime(&posix_max_tm); + printf("%s", time_string); + + return 0; +} diff --git a/tests/time/constants.c b/tests/time/constants.c new file mode 100644 index 0000000000..b29f13f253 --- /dev/null +++ b/tests/time/constants.c @@ -0,0 +1,12 @@ +#include +#include + +int main(void) { + /* TODO: ensure that it is really time.h supplying the NULL constant */ + printf("%p\n", NULL); + + /* Cast to long to avoid format string mismatch in case CLOCKS_PER_SEC is + defined as some other type. The expected value (1 million) will always fit + in a long and will always have that value on conforming systems. */ + printf("%ld\n", (long)CLOCKS_PER_SEC); +} diff --git a/tests/time/gettimeofday.c b/tests/time/gettimeofday.c new file mode 100644 index 0000000000..e43b38dc86 --- /dev/null +++ b/tests/time/gettimeofday.c @@ -0,0 +1,14 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + struct timeval tv; + + // gettimeofday always returns 0, no errors are defined + int gtod = gettimeofday(&tv, NULL); + UNEXP_IF(gettimeofday, gtod, != 0); + + printf("%ld: %ld\n", tv.tv_sec, tv.tv_usec); +} diff --git a/tests/time/gmtime.c b/tests/time/gmtime.c new file mode 100644 index 0000000000..da4bdb08d7 --- /dev/null +++ b/tests/time/gmtime.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +void print_tm(const struct tm *tm_ptr) { + printf(" tm_sec = %d\n", tm_ptr->tm_sec); + printf(" tm_min = %d\n", tm_ptr->tm_min); + printf(" tm_hour = %d\n", tm_ptr->tm_hour); + printf(" tm_mday = %d\n", tm_ptr->tm_mday); + printf(" tm_mon = %d\n", tm_ptr->tm_mon); + printf(" tm_year = %d\n", tm_ptr->tm_year); + printf(" tm_wday = %d\n", tm_ptr->tm_wday); + printf(" tm_yday = %d\n", tm_ptr->tm_yday); + printf(" tm_isdst = %d\n", tm_ptr->tm_isdst); + printf(" tm_gmtoff = %ld\n", tm_ptr->tm_gmtoff); + printf(" tm_zone = %s\n", tm_ptr->tm_zone); +} + +int main(void) { + time_t unix_epoch_seconds = 0; + + // Exercise the different branches of the leap year logic + // 1970: common year + time_t y1970_mar01_seconds = 5097600; // 1970-03-01 00:00:00 + time_t y1970_dec31_seconds = 31535999; // 1970-12-31 23:59:59 + + // 1972: leap year + time_t y1972_feb29_seconds = 68255999; // 1972-02-29 23:59:59 + time_t y1972_dec31_seconds = 94694399; // 1972-12-31 23:59:59 + + // 2000: leap year + time_t y2000_feb29_seconds = 951868799; // 2000-02-29 23:59:59 + time_t y2000_dec31_seconds = 978307199; // 2000-12-31 23:59:59 + + // 2100: common year + time_t y2100_mar01_seconds = 4107542400; // 2100-03-01 00:00:00 + time_t y2100_dec31_seconds = 4133980799; // 2100-12-31 23:59:59 + + // Year 2038-related corner cases + time_t y2038_pre_seconds = 2147483647; + time_t y2038_post_seconds = 2147483648; + time_t y2106_pre_seconds = 4294967295; + time_t y2106_post_seconds = 4294967296; + + struct tm *result_tm = NULL; + + puts("Unix epoch:"); + result_tm = gmtime(&unix_epoch_seconds); + print_tm(result_tm); + + puts(""); + puts("1970-03-01 00:00:00:"); + result_tm = gmtime(&y1970_mar01_seconds); + print_tm(result_tm); + + puts(""); + puts("1970-12-31 23:59:59:"); + result_tm = gmtime(&y1970_dec31_seconds); + print_tm(result_tm); + + puts(""); + puts("1972-02-29 23:59:59:"); + result_tm = gmtime(&y1972_feb29_seconds); + print_tm(result_tm); + + puts(""); + puts("1972-12-31 23:59:59:"); + result_tm = gmtime(&y1972_dec31_seconds); + print_tm(result_tm); + + puts(""); + puts("2000-02-29 23:59:59:"); + result_tm = gmtime(&y2000_feb29_seconds); + print_tm(result_tm); + + puts(""); + puts("2000-12-31 23:59:59:"); + result_tm = gmtime(&y2000_dec31_seconds); + print_tm(result_tm); + + puts(""); + puts("2100-03-01 00:00:00:"); + result_tm = gmtime(&y2100_mar01_seconds); + print_tm(result_tm); + + puts(""); + puts("2100-12-31 23:59:59:"); + result_tm = gmtime(&y2100_dec31_seconds); + print_tm(result_tm); + + puts(""); + puts("Year 2038, pre-i32 overflow:"); + result_tm = gmtime(&y2038_pre_seconds); + print_tm(result_tm); + + puts(""); + puts("Year 2038, post-i32 overflow:"); + result_tm = gmtime(&y2038_post_seconds); + print_tm(result_tm); + + puts(""); + puts("Year 2106, pre-u32 overflow:"); + result_tm = gmtime(&y2106_pre_seconds); + print_tm(result_tm); + + puts(""); + puts("Year 2106, post-u32 overflow:"); + result_tm = gmtime(&y2106_post_seconds); + print_tm(result_tm); +} diff --git a/tests/time/localtime.c b/tests/time/localtime.c new file mode 100644 index 0000000000..9d1098fd40 --- /dev/null +++ b/tests/time/localtime.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int day = 60 * 60 * 24; + time_t inputs[] = { -(day * 33), -day, -1, -500, 0, 1, 1531454950 }; + for (size_t i = 0; i < (sizeof(inputs) / sizeof(time_t)); i += 1) { + struct tm* t = localtime(&inputs[i]); + + printf( + "Year %d, Day of year: %d, Month %d, Day of month: %d, Day of week: %d, %d:%d:%d\n", + t->tm_year, t->tm_yday, t->tm_mon, t->tm_mday, t->tm_wday, t->tm_hour, t->tm_min, t->tm_sec + ); + } + + time_t input = 1531461823; + fputs(ctime(&input), stdout); // Omit newline + + char ctime_r_buffer[26]; + /* ctime_r() generally returns the address of the provided buffer, + * but may return NULL upon error according to the spec. */ + char *ctime_r_result = ctime_r(&input, ctime_r_buffer); + if (ctime_r_result == ctime_r_buffer) { + fputs(ctime_r_result, stdout); + } else { + printf("Unexpected pointer from ctime_r: %p\n", ctime_r_result); + } +} diff --git a/tests/time/localtime_r.c b/tests/time/localtime_r.c new file mode 100644 index 0000000000..a874daee1e --- /dev/null +++ b/tests/time/localtime_r.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +void test_localtime_r_epoch() { + time_t t = 0; // Unix epoch (1970-01-01 00:00:00) + struct tm result; + + setenv("TZ", "UTC", 1); + tzset(); + + assert(localtime_r(&t, &result) != NULL); + assert(result.tm_year == 70); // Year 1970 - 1900 + assert(result.tm_mon == 0); // January + assert(result.tm_mday == 1); // 1st of January + assert(result.tm_hour == 0); // Midnight + assert(result.tm_min == 0); + assert(result.tm_sec == 0); + + assert(result.tm_isdst == -1 || result.tm_isdst == 0); +} + +void test_localtime_r_non_epoch() { + time_t t = 1609459200; // January 1, 2021 00:00:00 UTC (New Year 2021) + struct tm result; + + setenv("TZ", "UTC", 1); + tzset(); + + assert(localtime_r(&t, &result) != NULL); + assert(result.tm_year == 121); // Year 2021 - 1900 + assert(result.tm_mon == 0); // January + assert(result.tm_mday == 1); // 1st of January + assert(result.tm_hour == 0); // Midnight + assert(result.tm_min == 0); + assert(result.tm_sec == 0); +} + +void test_localtime_r_dst() { + time_t t = 1615708800; // March 14 2021 08:00:00 UTC + struct tm result; + + setenv("TZ", "UTC", 1); + tzset(); + + assert(localtime_r(&t, &result) != NULL); // Ensure localtime_r does not fail + assert(result.tm_year == 121); // Year 2021 - 1900 + assert(result.tm_mon == 2); // March + assert(result.tm_mday == 14); // 14th + assert(result.tm_hour == 8); // 08:00:00 local time + assert(result.tm_min == 0); + assert(result.tm_sec == 0); +} + +void test_localtime_r_large_time() { + time_t t = 32503680000; // A large value: 1 January 3000 (UTC) + struct tm result; + + setenv("TZ", "UTC", 1); + tzset(); + + assert(localtime_r(&t, &result) != NULL); // Ensure localtime_r does not fail + assert(result.tm_year == 1100); // Year 3000 - 1900 = 1100 + assert(result.tm_mon == 0); // January + assert(result.tm_mday == 1); // 1st of January + assert(result.tm_hour == 0); // Midnight + assert(result.tm_min == 0); + assert(result.tm_sec == 0); +} + +void test_dst_transition() { + time_t t = 1615809600; // March 15 2021, 08:00:00 UTC + struct tm result; + + setenv("TZ", "America/New_York", 1); + tzset(); + + localtime_r(&t, &result); + + assert(result.tm_year == 121); // Year 2021 - 1900 = 121 + assert(result.tm_mon == 2); // March (0-based index) + assert(result.tm_mday == 15); // 15th + assert(result.tm_hour == 8); // 00:00:00 local time + assert(result.tm_min == 0); + assert(result.tm_sec == 0); + + assert(result.tm_isdst == 1); // DST should be active + + assert(strcmp(tzname[0], "EST") == 0 || strcmp(tzname[0], "EDT") == 0); // Standard or DST name + assert(strcmp(tzname[1], "EDT") == 0); // Should be the DST version of the timezone +} + +void test_standard_time() { + time_t t = 1609459200; // January 1, 2021, 00:00:00 UTC + struct tm result; + + setenv("TZ", "Asia/Tokyo", 1); + tzset(); + + localtime_r(&t, &result); + + assert(result.tm_year == 121); // Year 2021 - 1900 = 121 + assert(result.tm_mon == 0); // January (0-based index) + assert(result.tm_mday == 1); // 1st + assert(result.tm_hour == 9); // 09:00:00 local time + assert(result.tm_min == 0); + assert(result.tm_sec == 0); + + assert(result.tm_isdst == 0); // DST should NOT be active + + assert(strcmp(tzname[0], "JST") == 0); // Standard time name + assert(strcmp(tzname[1], "EST") != 0); // Should NOT be in DST +} + +int main() { + test_localtime_r_epoch(); + test_localtime_r_non_epoch(); + test_localtime_r_dst(); + test_localtime_r_large_time(); + test_dst_transition(); + test_standard_time(); + return 0; +} diff --git a/tests/time/macros.c b/tests/time/macros.c new file mode 100644 index 0000000000..60e2f96e7c --- /dev/null +++ b/tests/time/macros.c @@ -0,0 +1,38 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + struct timeval x = { .tv_usec = 15 }; + struct timeval y = { 0 }; + struct timeval z = { 0 }; + struct timeval one_usec = { .tv_usec = 1 }; + struct timeval max_usec = { .tv_usec = 999999 }; + struct timeval one_sec = { .tv_sec = 1 }; + + assert(!timerisset(&y)); + assert(timerisset(&x)); + timerclear(&x); + assert(!timerisset(&x)); + + assert(timercmp(&x, &y, ==)); + timeradd(&y, &one_usec, &z); + assert(!timercmp(&x, &z, ==)); + assert(timercmp(&x, &z, <)); + + timeradd(&z, &max_usec, &y); + assert(y.tv_sec == 1); + assert(y.tv_usec == 0); + timersub(&y, &one_usec, &z); + assert(z.tv_sec == 0); + assert(z.tv_usec == 999999); + timeradd(&z, &one_sec, &y); + assert(y.tv_sec == 1); + assert(y.tv_usec == 999999); + for (int i = 0; i < 3; i += 1) { + timersub(&y, &one_sec, &y); + } + assert(y.tv_sec == -2); + assert(y.tv_usec == 999999); +} diff --git a/tests/time/mktime.c b/tests/time/mktime.c new file mode 100644 index 0000000000..bc8c9c755d --- /dev/null +++ b/tests/time/mktime.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int check(time_t input) { + struct tm *t = localtime(&input); + ERROR_IF(localtime, t, == NULL); + + time_t output = mktime(t); + ERROR_IF(mktime, output, == (time_t)-1); + + printf("%ld = %ld\n", input, output); + + if (input != output) { + printf( + "Year %d, Day of year: %d, Month %d, Day of month: %d, Day of week: %d, %d:%d:%d\n", + t->tm_year, t->tm_yday, t->tm_mon, t->tm_mday, t->tm_wday, t->tm_hour, t->tm_min, t->tm_sec + ); + puts("Failed!"); + return 1; + } + return 0; +} + +int main(void) { + struct tm t = { 0 }; + + t.tm_year = 71; + t.tm_mday = 1; + + printf("%ld\n", mktime(&t)); + + int day = 60 * 60 * 24; + time_t inputs[] = { -(day * 33), -day, -500, 0, 1531454950 }; + for (int i = 0; i < 5; i += 1) { + if (check(inputs[i])) { + exit(EXIT_FAILURE); + } + } + + srand(time(NULL)); + + for (int i = 0; i < 10; i += 1) { + time_t input = (time_t) rand(); + + struct tm *time = localtime(&input); + ERROR_IF(localtime, time, == NULL); + + time_t output = mktime(time); + ERROR_IF(mktime, output, == (time_t)-1); + + if (input != output) { + // asctime has newline + printf("Comparison %ld == %ld failed. Time: %s", input, output, asctime(time)); + } + } +} diff --git a/tests/time/strftime.c b/tests/time/strftime.c new file mode 100644 index 0000000000..629f86f838 --- /dev/null +++ b/tests/time/strftime.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include "test_helpers.h" + +#define PRINT_BUFSZ 50 +#define V_BUFSZ 3 +#define BUFS 30 + +void print(time_t timestamp, char* fmt) { + char out[PRINT_BUFSZ] = {0}; + size_t n = strftime(out, PRINT_BUFSZ, fmt, localtime(×tamp)); + printf("%zu: %s\n", n, out); + +} + +void strftime_mix(void) { + setenv("TZ", "Australia/Melbourne", 1); + tzset(); + + // Initialize a struct tm with a known fixed date and time. + // December 31, 2020 23:59:59, which is a Thursday. + struct tm t = {0}; + t.tm_year = 2020 - 1900; // Years since 1900. + t.tm_mon = 11; // December (0-based, so 11 means December). + t.tm_mday = 31; + t.tm_hour = 23; + t.tm_min = 59; + t.tm_sec = 59; + t.tm_wday = 4; // Thursday (0 = Sunday, so Thursday = 4). + t.tm_yday = 365; + t.tm_gmtoff = 39600; // GMT offset for AEDT (UTC+11). + t.tm_isdst = 11; // Daylight saving time is in effect. + t.tm_zone = "AEDT"; // Time zone name. + char buf[BUFS]; + + strftime(buf, BUFS, "%a", &t); + assert(strcmp(buf, "Thu") == 0); + + strftime(buf, BUFS, "%A", &t); + assert(strcmp(buf, "Thursday") == 0); + + strftime(buf, BUFS, "%b", &t); + assert(strcmp(buf, "Dec") == 0); + + strftime(buf, BUFS, "%B", &t); + assert(strcmp(buf, "December") == 0); + + strftime(buf, BUFS, "%d", &t); + assert(strcmp(buf, "31") == 0); + + strftime(buf, BUFS, "%H", &t); + assert(strcmp(buf, "23") == 0); + + strftime(buf, BUFS, "%I", &t); + assert(strcmp(buf, "11") == 0); + + // Day of the year as a zero-padded decimal number (001-366). + strftime(buf, BUFS, "%j", &t); + // Even though t.tm_yday is 365 (zero-based), %j is one-based: "366" + assert(strcmp(buf, "366") == 0); + + strftime(buf, BUFS, "%m", &t); + assert(strcmp(buf, "12") == 0); + + strftime(buf, BUFS, "%M", &t); + assert(strcmp(buf, "59") == 0); + + strftime(buf, BUFS, "%p", &t); + assert(strcmp(buf, "PM") == 0); + + strftime(buf, BUFS, "%S", &t); + assert(strcmp(buf, "59") == 0); + + strftime(buf, BUFS, "%U", &t); + assert(strcmp(buf, "52") == 0); + + strftime(buf, BUFS, "%w", &t); + assert(strcmp(buf, "4") == 0); + + strftime(buf, BUFS, "%W", &t); + assert(strcmp(buf, "52") == 0); + + strftime(buf, BUFS, "%y", &t); + assert(strcmp(buf, "20") == 0); + + strftime(buf, BUFS, "%Y", &t); + assert(strcmp(buf, "2020") == 0); + + strftime(buf, BUFS, "%Z", &t); + assert(strcmp(buf, "AEDT") == 0); + + strftime(buf, BUFS, "%z", &t); + assert(strcmp(buf, "+1100") == 0); + + strftime(buf, BUFS, "%F", &t); + assert(strcmp(buf, "2020-12-31") == 0); + + strftime(buf, BUFS, "%R", &t); + assert(strcmp(buf, "23:59") == 0); + + strftime(buf, BUFS, "%r", &t); + assert(strcmp(buf, "11:59:59 PM") == 0); + + strftime(buf, BUFS, "%T", &t); + assert(strcmp(buf, "23:59:59") == 0); +} + +void test_v(void) { + char buf[V_BUFSZ] = {0}; + struct tm time = {0}; + + // Example dates copied from Wikipedia + // Saturday 2005-01-01 + time.tm_yday = 0; + time.tm_wday = 6; + time.tm_year = 2005; + strftime(buf, V_BUFSZ, "%V", &time); + puts(buf); + + // Saturday 2005-12-31 + time.tm_yday = 365; + time.tm_wday = 6; + strftime(buf, V_BUFSZ, "%V", &time); + puts(buf); + + // Sunday 2006-01-01 + time.tm_yday = 0; + time.tm_wday = 0; + time.tm_year = 2006; + strftime(buf, V_BUFSZ, "%V", &time); + puts(buf); + + // Sunday 2008-12-28 + time.tm_yday = 362; + time.tm_wday = 0; + time.tm_year = 2008; + strftime(buf, V_BUFSZ, "%V", &time); + puts(buf); + + // Friday 2010-01-01 + time.tm_yday = 0; + time.tm_wday = 5; + time.tm_year = 2010; + strftime(buf, V_BUFSZ, "%V", &time); + puts(buf); +} + +int main(void) { + print(1531808742, "%a %A %b %B"); + print(1531808742, "The %Cst century"); + print(1531808742, "%I:%M:%S %p"); + print(1531839600, "%r"); + print(1531839600, "%R"); + print(1531839600, "%H %s %u"); + print(1531839600, "%j %U"); + print(1531839600, "%+"); + print(1533669431, "%+%+%+%+%+"); // will overflow 50 characters + + // ISO-8601 tests + test_v(); + + strftime_mix(); + + return EXIT_SUCCESS; +} diff --git a/tests/time/strptime.c b/tests/time/strptime.c new file mode 100644 index 0000000000..af96064513 --- /dev/null +++ b/tests/time/strptime.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +__attribute__((nonnull)) +static void strptime_test(const char* restrict time, + const char* restrict format, + struct tm expected, + const char* restrict exp_str) { + struct tm actual = {0}; + const char* result = strptime(time, format, &actual); + + // This struct is packed and zeroed beforehand so it shouldn't + // have any holes to throw off memcmp. + // + // If relibc implements the GNU extensions that store timezone, + // then this will need to be modified to memcmp up to + // sizeof(struct tm) - sizeof(char*) followed by a strcmp. + // + // glibc and musl differ a bit which is why some calls to + // this function comment out tm_yday and tm_wday. + // glibc seems to set these fields even if they're not specified, + // which is a cool extra but implementation specific behavior. + if(memcmp(&expected, &actual, sizeof(struct tm))) { + puts("struct tm expected versus actual\n"); + printf("%-8s %d %4d\n", "tm_year", expected.tm_year, actual.tm_year); + printf("%-8s %d %4d\n", "tm_mon", expected.tm_mon, actual.tm_mon); + printf("%-8s %d %4d\n", "tm_mday", expected.tm_mday, actual.tm_mday); + printf("%-8s %d %4d\n", "tm_hour", expected.tm_hour, actual.tm_hour); + printf("%-8s %d %4d\n", "tm_min", expected.tm_min, actual.tm_min); + printf("%-8s %d %4d\n", "tm_sec", expected.tm_sec, actual.tm_sec); + printf("%-8s %d %4d\n", "tm_wday", expected.tm_wday, actual.tm_wday); + printf("%-8s %d %4d\n", "tm_yday", expected.tm_yday, actual.tm_yday); + printf("%-8s %d %4d\n", "tm_isdst", expected.tm_isdst, actual.tm_isdst); + + exit(EXIT_FAILURE); + } + + // Safety: + // `time` our static string which definitely ends with NUL + // `result` is from strptime and ends with a NUL because `time` does + size_t diff = strlen(time) - strlen(result); + assert(!strncmp(exp_str, result, diff)); +} + +int main(void) { + const char emi[] = "02:24:14"; + struct tm emi_expect = { + .tm_hour = 2, + .tm_min = 24, + .tm_sec = 14, + }; + strptime_test(emi, "%T", emi_expect, ""); + + const char daydream[] = "1981-11-18 Daydream Nation"; + struct tm daydream_expect = { + .tm_year = 81, + .tm_mon = 10, + .tm_mday = 18, + /* .tm_wday = 3, */ + /* .tm_yday = 321, */ + }; + const char* daydream_rem = &daydream[10]; + strptime_test(daydream, + "%Y-%m-%d", + daydream_expect, + daydream_rem + ); + + // strptime(3): "(This is the American style date, very confusing to + // non-Americans [...])" + const char america[] = "07/04/76 AMERICA! (Can't use 1776 here :( ))"; + struct tm america_expect = { + .tm_year = 76, + .tm_mon = 6, + .tm_mday = 4, + /* .tm_yday = 185, */ + }; + const char* america_rem = &america[9]; + strptime_test(america, + "%D%n", + america_expect, + america_rem + ); + + const char percent[] = "%"; + struct tm percent_expect = {0}; + strptime_test(percent, + "%%", + percent_expect, + "" + ); + + // TODO: Locale offset + const char redox[] = "Mon Oct 31 11:19:57 2016"; + struct tm redox_expect = { + .tm_year = 116, + .tm_mon = 9, + .tm_mday = 31, + .tm_hour = 11, + .tm_min = 19, + .tm_sec = 57, + /* .tm_yday = 304, */ + .tm_wday = 1, + }; + strptime_test(redox, + "%a%t%b%t%d%t%T%t%Y", + redox_expect, + ""); + + // Roundtrip + const char roundtrip[] = "2012-01-19 13:37:00"; + const char roundtrip_fmt[] = "%Y-%m-%d %H:%M:%S"; + struct tm roundtrip_tm = {0}; + strptime(roundtrip, roundtrip_fmt, &roundtrip_tm); + + char rt_actual[32]; + size_t rt_res = strftime(rt_actual, 32, roundtrip_fmt, &roundtrip_tm); + size_t rt_len = strnlen(roundtrip, 32); + assert(rt_res == rt_len); + assert(!strncmp(roundtrip, rt_actual, rt_len)); + + return EXIT_SUCCESS; +} diff --git a/tests/time/time.c b/tests/time/time.c new file mode 100644 index 0000000000..0655cc50a3 --- /dev/null +++ b/tests/time/time.c @@ -0,0 +1,44 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + struct timespec tm = {0}; + + int cgt = clock_gettime(CLOCK_REALTIME, &tm); + ERROR_IF(clock_gettime, cgt, == -1); + + cgt = clock_getres(CLOCK_REALTIME, &tm); + ERROR_IF(clock_getres, cgt, == -1); + + cgt = timespec_get(&tm, TIME_UTC); + if (cgt != TIME_UTC) { + errx( + EXIT_FAILURE, + "timespec_get should have returned %d but returned %d\n", + TIME_UTC, + cgt + ); + } + + cgt = timespec_getres(&tm, TIME_UTC); + if (cgt != TIME_UTC) { + errx( + EXIT_FAILURE, + "timespec_getres should have returned %d but returned %d\n", + TIME_UTC, + cgt + ); + } + + time_t t = time(NULL); + ERROR_IF(time, t, == (time_t)-1); + + // TODO: Support clock() on Redox + // clock_t c = clock(); + // ERROR_IF(clock, c, == (clock_t)-1); + + return EXIT_SUCCESS; +} diff --git a/tests/time/timegm.c b/tests/time/timegm.c new file mode 100644 index 0000000000..9fbfa89fac --- /dev/null +++ b/tests/time/timegm.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +int main(void) { + setenv("TZ", "UTC", 1); + tzset(); + + struct { + time_t timestamp; + const char *expected_asctime; + } test_cases[] = { + {0, "Thu Jan 1 00:00:00 1970\n"}, + {5097600, "Sun Mar 1 00:00:00 1970\n"}, + {31535999, "Thu Dec 31 23:59:59 1970\n"}, + {68255999, "Tue Feb 29 23:59:59 1972\n"}, // 1972 is a leap year + {94694399, "Sun Dec 31 23:59:59 1972\n"}, + {951868799, "Tue Feb 29 23:59:59 2000\n"}, // 2000 is a leap year + {978307199, "Sun Dec 31 23:59:59 2000\n"}, + {4107542400, "Mon Mar 1 00:00:00 2100\n"}, // 2100 is not a leap year + {4133980799, "Fri Dec 31 23:59:59 2100\n"}, + {2147483647, "Tue Jan 19 03:14:07 2038\n"}, // 32-bit overflow point + {2147483648, "Tue Jan 19 03:14:08 2038\n"}, + {4294967295, "Sun Feb 7 06:28:15 2106\n"}, // Unsigned 32-bit overflow + {4294967296, "Sun Feb 7 06:28:16 2106\n"} + }; + int num_tests = sizeof(test_cases) / sizeof(test_cases[0]); + + for (int i = 0; i < num_tests; i++) { + time_t orig = test_cases[i].timestamp; + const char *expected_asctime = test_cases[i].expected_asctime; + + struct tm *tm_ptr = gmtime(&orig); + assert(tm_ptr != NULL); + + struct tm local_tm; + memcpy(&local_tm, tm_ptr, sizeof(struct tm)); + + char *asc_time = asctime(&local_tm); + assert(asc_time != NULL); + assert(strcmp(asc_time, expected_asctime) == 0); + + time_t computed = timegm(&local_tm); + assert(computed == orig); + } + + return 0; +} diff --git a/tests/time/times.c b/tests/time/times.c new file mode 100644 index 0000000000..018f9423e7 --- /dev/null +++ b/tests/time/times.c @@ -0,0 +1,17 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + struct tms tms; + + int status = times(&tms); + ERROR_IF(times, status, == (time_t)-1); + + printf("tm_utime: %ld\n", tms.tms_utime); + printf("tm_stime: %ld\n", tms.tms_stime); + printf("tm_cutime: %ld\n", tms.tms_cutime); + printf("tm_cstime: %ld\n", tms.tms_cstime); +} diff --git a/tests/time/tzset.c b/tests/time/tzset.c new file mode 100644 index 0000000000..87fa15f719 --- /dev/null +++ b/tests/time/tzset.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +extern int daylight; +extern long timezone; +extern char *tzname[2]; +void tzset(void); + +int main(void) { + tzset(); + printf("tzname[0] %s, tzname[1] %s, daylight %d, timezone %ld\n", + tzname[0], tzname[1], daylight, timezone); + return 0; +} diff --git a/tests/tls.c b/tests/tls.c new file mode 100644 index 0000000000..2be0006d3e --- /dev/null +++ b/tests/tls.c @@ -0,0 +1,10 @@ +#include + +_Thread_local int tbss = 0; +_Thread_local int tdata = 1; + +int main(void) { + printf("%d == 0\n", tbss); + printf("%d == 1\n", tdata); + return 0; +} diff --git a/tests/unistd/access.c b/tests/unistd/access.c new file mode 100644 index 0000000000..87ee7c9fbc --- /dev/null +++ b/tests/unistd/access.c @@ -0,0 +1,17 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + if (access("example_dir/1-never-gonna-give-you-up", R_OK | W_OK)) { + perror("access"); + exit(EXIT_FAILURE); + } + if (!access("example_dir/1-never-gonna-give-you-up", X_OK)) { + puts("Accessing a file with X_OK worked even though it... probably... shouldn't?"); + puts("Please run `chmod 644 example_dir/*` and try again."); + exit(EXIT_FAILURE); + } +} diff --git a/tests/unistd/alarm.c b/tests/unistd/alarm.c new file mode 100644 index 0000000000..4b0b2f96d6 --- /dev/null +++ b/tests/unistd/alarm.c @@ -0,0 +1,80 @@ +/* + * Tests for alarm(2) - POSIX: https://pubs.opengroup.org/onlinepubs/9799919799/functions/alarm.html + * + * Verifies: + * 1. alarm(0) with no pending alarm returns 0 + * 2. alarm(1) delivers SIGALRM after ~1 second (tested via pause()) + * 3. alarm(0) cancels a pending alarm + * 4. re-arming alarm returns the remaining seconds from the previous alarm + */ + +#include +#include +#include +#include + +#include "test_helpers.h" + +static volatile sig_atomic_t alarm_count = 0; + +static void handler(int sig) { + (void)sig; + alarm_count++; +} + +int main(void) { + struct sigaction sa; + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + int r = sigaction(SIGALRM, &sa, NULL); + ERROR_IF(sigaction, r, == -1); + + /* alarm(0) with no existing alarm must return 0 */ + unsigned prev = alarm(0); + UNEXP_IF(alarm, (int)prev, != 0); + puts("alarm(0) baseline: ok"); + + /* alarm(1): SIGALRM must fire; pause() blocks until the signal arrives */ + alarm_count = 0; + alarm(1); + pause(); + if (alarm_count != 1) { + fprintf(stderr, "SIGALRM did not fire (count=%d)\n", (int)alarm_count); + return EXIT_FAILURE; + } + puts("alarm(1) fires: ok"); + + /* alarm(0) must cancel a pending alarm and return remaining secs > 0 */ + alarm_count = 0; + alarm(10); + unsigned remaining = alarm(0); + if (remaining == 0) { + fprintf(stderr, "alarm(0) cancel: expected remaining > 0\n"); + return EXIT_FAILURE; + } + /* Wait longer than original alarm; SIGALRM must NOT fire */ + sleep(2); + if (alarm_count != 0) { + fprintf(stderr, "SIGALRM fired after alarm(0) cancel\n"); + return EXIT_FAILURE; + } + puts("alarm(0) cancel: ok"); + + /* + * Re-arming: arm with 10s, sleep 1s, then re-arm with 3s. + * The return value must be the remaining time (~9s) which is > 0. + */ + alarm_count = 0; + alarm(10); + sleep(1); + remaining = alarm(3); + if (remaining == 0) { + fprintf(stderr, "re-arm: expected remaining > 0, got 0\n"); + return EXIT_FAILURE; + } + alarm(0); /* disarm so SIGALRM doesn't fire after the test */ + puts("alarm re-arm returns remaining: ok"); + + return EXIT_SUCCESS; +} diff --git a/tests/unistd/brk.c b/tests/unistd/brk.c new file mode 100644 index 0000000000..0dc7566ccb --- /dev/null +++ b/tests/unistd/brk.c @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + // sbrk report current brk + void * current = sbrk(0); + ERROR_IF(sbrk, current, == (void *)-1); + + // sbrk increment and report previous brk + void * prev = current; + current = sbrk(4096); + ERROR_IF(sbrk, current, != prev); + + // sbrk report current break + prev = current; + current = sbrk(0); + ERROR_IF(sbrk, current, != (void*)((uintptr_t)prev + 4096)); + + // brk set break to new value + int status = brk((void*)((uintptr_t)current + 4096)); + ERROR_IF(brk, status, == -1); + UNEXP_IF(brk, status, != 0); +} diff --git a/tests/unistd/chdir.c b/tests/unistd/chdir.c new file mode 100644 index 0000000000..70e5881afb --- /dev/null +++ b/tests/unistd/chdir.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char cwd[PATH_MAX] = { 0 }; + char *cwd_result = NULL; + + cwd_result = getcwd(cwd, PATH_MAX); + ERROR_IF(getcwd, cwd_result, == NULL); + UNEXP_IF(getcwd, cwd_result, != cwd); + + printf("getcwd before chdir: %s\n", cwd); + + int status = chdir(".."); + ERROR_IF(chdir, status, == -1); + UNEXP_IF(chdir, status, != 0); + + cwd_result = getcwd(cwd, PATH_MAX); + ERROR_IF(getcwd, cwd_result, == NULL); + UNEXP_IF(getcwd, cwd_result, != cwd); + + printf("getcwd after chdir: %s\n", cwd); +} diff --git a/tests/unistd/confstr.c b/tests/unistd/confstr.c new file mode 100644 index 0000000000..d9abde8811 --- /dev/null +++ b/tests/unistd/confstr.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +#define CSPATHSZ 9 +#define BADSIZ 4 + +int main(void) { + char actual[CSPATHSZ] = {0}; + assert(confstr(_CS_PATH, actual, CSPATHSZ) == CSPATHSZ); + const char expected[] = "/usr/bin"; + assert(strncmp(expected, actual, CSPATHSZ) == 0); + + // The constants other than _CS_PATH just return an empty str (no support). + char empty[] = ""; + assert( + confstr( + _CS_POSIX_V6_LP64_OFF64_LIBS, + empty, + 0 + ) == 1 + ); + + // Buffers that are too small should return the expected size. + char small[BADSIZ] = {0}; + assert(confstr(_CS_PATH, small, BADSIZ) == CSPATHSZ); + + // Null buffer and zero length should return the expected size + // for the constant. + assert(confstr(_CS_PATH, NULL, 0) == CSPATHSZ); + + return EXIT_SUCCESS; +} diff --git a/tests/unistd/constants.c b/tests/unistd/constants.c new file mode 100644 index 0000000000..46549c54a3 --- /dev/null +++ b/tests/unistd/constants.c @@ -0,0 +1,123 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + // Constants specified in https://pubs.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html + + printf("_POSIX_VERSION: %ld\n", _POSIX_VERSION); + /* TODO + printf("_POSIX2_VERSION: %ld\n", _POSIX2_VERSION); + printf("_POSIX2_C_VERSION: %ld\n", _POSIX2_C_VERSION); + printf("_XOPEN_VERSION: %d\n", _XOPEN_VERSION); + + printf("_XOPEN_XCU_VERSION: %d\n", _XOPEN_XCU_VERSION); + + printf("_XOPEN_XPG2: %d\n", _XOPEN_XPG2); + printf("_XOPEN_XPG3: %d\n", _XOPEN_XPG3); + printf("_XOPEN_XPG4: %d\n", _XOPEN_XPG4); + printf("_XOPEN_UNIX: %d\n", _XOPEN_UNIX); + + printf("_POSIX_CHOWN_RESTRICTED: %d\n", _POSIX_CHOWN_RESTRICTED); + printf("_POSIX_NO_TRUNC: %d\n", _POSIX_NO_TRUNC); + printf("_POSIX_VDISABLE: %d\n", _POSIX_VDISABLE); + printf("_POSIX_SAVED_IDS: %d\n", _POSIX_SAVED_IDS); + printf("_POSIX_JOB_CONTROL: %d\n", _POSIX_JOB_CONTROL); + printf("_POSIX_THREADS: %ld\n", _POSIX_THREADS); + printf("_POSIX_THREAD_ATTR_STACKADDR: %ld\n", _POSIX_THREAD_ATTR_STACKADDR); + printf("_POSIX_THREAD_ATTR_STACKSIZE: %ld\n", _POSIX_THREAD_ATTR_STACKSIZE); + printf("_POSIX_THREAD_PROCESS_SHARED: %ld\n", _POSIX_THREAD_PROCESS_SHARED); + printf("_POSIX_THREAD_SAFE_FUNCTIONS: %ld\n", _POSIX_THREAD_SAFE_FUNCTIONS); + + printf("_POSIX2_C_BIND: %ld\n", _POSIX2_C_BIND); + printf("_POSIX2_C_DEV: %ld\n", _POSIX2_C_DEV); + printf("_POSIX2_CHAR_TERM: %ld\n", _POSIX2_CHAR_TERM); + printf("_POSIX2_FORT_DEV: %d\n", _POSIX2_FORT_DEV); + printf("_POSIX2_FORT_RUN: %d\n", _POSIX2_FORT_RUN); + printf("_POSIX2_LOCALEDEF: %ld\n", _POSIX2_LOCALEDEF); + printf("_POSIX2_SW_DEV: %ld\n", _POSIX2_SW_DEV); + printf("_POSIX2_UPE: %d\n", _POSIX2_UPE); + printf("_XOPEN_CRYPT: %d\n", _XOPEN_CRYPT); + printf("_XOPEN_ENH_I18N: %d\n", _XOPEN_ENH_I18N); + printf("_XOPEN_LEGACY: %d\n", _XOPEN_LEGACY); + printf("_XOPEN_REALTIME: %d\n", _XOPEN_REALTIME); + printf("_XOPEN_REALTIME_THREADS: %d\n", _XOPEN_REALTIME_THREADS); + printf("_XOPEN_SHM: %d\n", _XOPEN_SHM); + printf("_XBS5_ILP32_OFF32: %d\n", _XBS5_ILP32_OFF32); + printf("_XBS5_ILP32_OFFBIG: %d\n", _XBS5_ILP32_OFFBIG); + printf("_XBS5_LP64_OFF64: %d\n", _XBS5_LP64_OFF64); + printf("_XBS5_LPBIG_OFFBIG: %d\n", _XBS5_LPBIG_OFFBIG); + + printf("_POSIX_ASYNCHRONOUS_IO: %ld\n", _POSIX_ASYNCHRONOUS_IO); + printf("_POSIX_MEMLOCK: %ld\n", _POSIX_MEMLOCK); + printf("_POSIX_MEMLOCK_RANGE: %ld\n", _POSIX_MEMLOCK_RANGE); + printf("_POSIX_MESSAGE_PASSING: %ld\n", _POSIX_MESSAGE_PASSING); + printf("_POSIX_PRIORITY_SCHEDULING: %ld\n", _POSIX_PRIORITY_SCHEDULING); + printf("_POSIX_REALTIME_SIGNALS: %ld\n", _POSIX_REALTIME_SIGNALS); + printf("_POSIX_SEMAPHORES: %ld\n", _POSIX_SEMAPHORES); + printf("_POSIX_SHARED_MEMORY_OBJECTS: %ld\n", _POSIX_SHARED_MEMORY_OBJECTS); + printf("_POSIX_SYNCHRONIZED_IO: %ld\n", _POSIX_SYNCHRONIZED_IO); + printf("_POSIX_TIMERS: %ld\n", _POSIX_TIMERS); + + printf("_POSIX_FSYNC: %ld\n", _POSIX_FSYNC); + printf("_POSIX_MAPPED_FILES: %ld\n", _POSIX_MAPPED_FILES); + printf("_POSIX_MEMORY_PROTECTION: %ld\n", _POSIX_MEMORY_PROTECTION); + + printf("_POSIX_PRIORITIZED_IO: %ld\n", _POSIX_PRIORITIZED_IO); + + printf("_POSIX_THREAD_PRIORITY_SCHEDULING: %ld\n", _POSIX_THREAD_PRIORITY_SCHEDULING); + printf("_POSIX_THREAD_PRIO_INHERIT: %ld\n", _POSIX_THREAD_PRIO_INHERIT); + printf("_POSIX_THREAD_PRIO_PROTECT: %ld\n", _POSIX_THREAD_PRIO_PROTECT); + + printf("_POSIX_ASYNC_IO: %d\n", _POSIX_ASYNC_IO); + printf("_POSIX_PRIO_IO: %d\n", _POSIX_PRIO_IO); + printf("_POSIX_SYNC_IO: %d\n", _POSIX_SYNC_IO); + */ + + printf("NULL: %p\n", NULL); + + printf("R_OK: %d\n", R_OK); + printf("W_OK: %d\n", W_OK); + printf("X_OK: %d\n", X_OK); + printf("F_OK: %d\n", F_OK); + + /* TODO: confstr() constants: + printf("_CS_PATH: %d\n", _CS_PATH); + printf("_CS_XBS5_ILP32_OFF32_CFLAGS: %d\n", _CS_XBS5_ILP32_OFF32_CFLAGS); + printf("_CS_XBS5_ILP32_OFF32_LDFLAGS: %d\n", _CS_XBS5_ILP32_OFF32_LDFLAGS); + printf("_CS_XBS5_ILP32_OFF32_LIBS: %d\n", _CS_XBS5_ILP32_OFF32_LIBS); + printf("_CS_XBS5_ILP32_OFF32_LINTFLAGS: %d\n", _CS_XBS5_ILP32_OFF32_LINTFLAGS); + printf("_CS_XBS5_ILP32_OFFBIG_CFLAGS: %d\n", _CS_XBS5_ILP32_OFFBIG_CFLAGS); + printf("_CS_XBS5_ILP32_OFFBIG_LDFLAGS: %d\n", _CS_XBS5_ILP32_OFFBIG_LDFLAGS); + printf("_CS_XBS5_ILP32_OFFBIG_LIBS: %d\n", _CS_XBS5_ILP32_OFFBIG_LIBS); + printf("_CS_XBS5_ILP32_OFFBIG_LINTFLAGS: %d\n", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS); + printf("_CS_XBS5_LP64_OFF64_CFLAGS: %d\n", _CS_XBS5_LP64_OFF64_CFLAGS); + printf("_CS_XBS5_LP64_OFF64_LDFLAGS: %d\n", _CS_XBS5_LP64_OFF64_LDFLAGS); + printf("_CS_XBS5_LP64_OFF64_LIBS: %d\n", _CS_XBS5_LP64_OFF64_LIBS); + printf("_CS_XBS5_LP64_OFF64_LINTFLAGS: %d\n", _CS_XBS5_LP64_OFF64_LINTFLAGS); + printf("_CS_XBS5_LPBIG_OFFBIG_CFLAGS: %d\n", _CS_XBS5_LPBIG_OFFBIG_CFLAGS); + printf("_CS_XBS5_LPBIG_OFFBIG_LDFLAGS: %d\n", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS); + printf("_CS_XBS5_LPBIG_OFFBIG_LIBS: %d\n", _CS_XBS5_LPBIG_OFFBIG_LIBS); + printf("_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS: %d\n", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS); + */ + + printf("SEEK_SET: %d\n", SEEK_SET); + printf("SEEK_CUR: %d\n", SEEK_CUR); + printf("SEEK_END: %d\n", SEEK_END); + + // sysconf() constants (_SC_*) are tested separately + + printf("F_LOCK: %d\n", F_LOCK); + printf("F_ULOCK: %d\n", F_ULOCK); + printf("F_TEST: %d\n", F_TEST); + printf("F_TLOCK: %d\n", F_TLOCK); + + // pathconf() constants (_PC_*) are tested separately + + printf("STDIN_FILENO: %d\n", STDIN_FILENO); + printf("STDOUT_FILENO: %d\n", STDOUT_FILENO); + printf("STDERR_FILENO: %d\n", STDERR_FILENO); + + return 0; +} diff --git a/tests/unistd/dup.c b/tests/unistd/dup.c new file mode 100644 index 0000000000..cb7449e577 --- /dev/null +++ b/tests/unistd/dup.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int fd0 = creat("dup.out", 0777); + ERROR_IF(creat, fd0, == -1); + UNEXP_IF(creat, fd0, < 0); + + int fd1 = open("dup.out", 0); + ERROR_IF(open, fd1, == -1); + UNEXP_IF(open, fd1, < 0); + + int fd2 = dup(fd1); + ERROR_IF(dup, fd2, == -1); + UNEXP_IF(dup, fd2, < 0); + + printf("duped fd is %d greater than the original fd\n", fd2 - fd1); + + int c1 = close(fd1); + ERROR_IF(close, c1, == -1); + UNEXP_IF(close, c1, != 0); + + int c2 = close(fd2); + ERROR_IF(close, c2, == -1); + UNEXP_IF(close, c2, != 0); + + int fd3 = open("dup.out", O_RDWR); + + ERROR_IF(open, fd3, == -1); + UNEXP_IF(open, fd3, < 0); + + int fd4 = dup2(fd3, 1); + ERROR_IF(dup2, fd4, == -1); + UNEXP_IF(dup2, fd4, < 0); + + int p = printf("hello fd %d", fd3); + ERROR_IF(printf, p, == -1); + UNEXP_IF(printf, p, < 0); + + int c3 = close(fd3); + ERROR_IF(close, c3, == -1); + UNEXP_IF(close, c3, != 0); +} diff --git a/tests/unistd/exec.c b/tests/unistd/exec.c new file mode 100644 index 0000000000..00d13b3a84 --- /dev/null +++ b/tests/unistd/exec.c @@ -0,0 +1,12 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char* args[] = {"sh", "-c", "echo 'exec works :D'", NULL}; + + int status = execv("/bin/sh", args); + ERROR_IF(execv, status, == -1); +} diff --git a/tests/unistd/fchdir.c b/tests/unistd/fchdir.c new file mode 100644 index 0000000000..d80db6ea25 --- /dev/null +++ b/tests/unistd/fchdir.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int fd = open("/usr", O_DIRECTORY); + ERROR_IF(open, fd, == -1); + UNEXP_IF(open, fd, < 0); + + int status = fchdir(fd); + ERROR_IF(fchdir, status, == -1); + UNEXP_IF(fchdir, status, != 0); + + int c = close(fd); + ERROR_IF(close, c, == -1); + UNEXP_IF(close, c, != 0); +} diff --git a/tests/unistd/fork.c b/tests/unistd/fork.c new file mode 100644 index 0000000000..225ead36e2 --- /dev/null +++ b/tests/unistd/fork.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +void prepare() { + puts("Hello from prepare"); +} +void parent() { + // Make sure we print in the right order and also don't exit + // before the fork does. + int us_status = usleep(1000); + ERROR_IF(usleep, us_status, == -1); + UNEXP_IF(usleep, us_status, != 0); + + puts("Hello from parent"); +} +void child() { + puts("Hello from child"); +} + +int main(void) { + int status = pthread_atfork(prepare, parent, child); + ERROR_IF(pthread_atfork, status, == -1); + + int pid = fork(); + ERROR_IF(fork, pid, == -1); +} diff --git a/tests/unistd/fsync.c b/tests/unistd/fsync.c new file mode 100644 index 0000000000..b53ffd68e6 --- /dev/null +++ b/tests/unistd/fsync.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int fd = open("example_dir/1-never-gonna-give-you-up", O_RDWR); + ERROR_IF(open, fd, == -1); + UNEXP_IF(open, fd, < 0); + + int status = fsync(fd); + ERROR_IF(fsync, status, == -1); + UNEXP_IF(fsync, status, != 0); + + int c = close(fd); + ERROR_IF(close, c, == -1); + UNEXP_IF(close, c, != 0); +} diff --git a/tests/unistd/ftruncate.c b/tests/unistd/ftruncate.c new file mode 100644 index 0000000000..c1b35cefa5 --- /dev/null +++ b/tests/unistd/ftruncate.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int fd = creat("ftruncate.out", 0777); + ERROR_IF(creat, fd, == -1); + UNEXP_IF(creat, fd, < 0); + + int status = ftruncate(fd, 100); + ERROR_IF(ftruncate, status, == -1); + UNEXP_IF(ftruncate, status, != 0); + + int c = close(fd); + ERROR_IF(close, c, == -1); + UNEXP_IF(close, c, != 0); + + status = truncate("ftruncate.out", 100); + ERROR_IF(truncate, status, == -1); + UNEXP_IF(truncate, status, != 0); +} diff --git a/tests/unistd/getcwd.c b/tests/unistd/getcwd.c new file mode 100644 index 0000000000..1df201e4f9 --- /dev/null +++ b/tests/unistd/getcwd.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char first[PATH_MAX] = { 0 }; + getcwd(first, PATH_MAX); + puts(first); + + char* second = getcwd(NULL, 0); + puts(second); + + if (strcmp(first, second)) { + puts("Not matching"); + free(second); + exit(EXIT_FAILURE); + } + + free(second); +} diff --git a/tests/unistd/gethostname.c b/tests/unistd/gethostname.c new file mode 100644 index 0000000000..aa13ed6b83 --- /dev/null +++ b/tests/unistd/gethostname.c @@ -0,0 +1,15 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + char hostname[256] = { 0 }; + + int status = gethostname(hostname, 256); + ERROR_IF(gethostname, status, == -1); + UNEXP_IF(gethostname, status, != 0); + + printf("Hostname: %s\n", hostname); +} diff --git a/tests/unistd/getid.c b/tests/unistd/getid.c new file mode 100644 index 0000000000..fdd8eb285d --- /dev/null +++ b/tests/unistd/getid.c @@ -0,0 +1,16 @@ +#include +#include + +#include "test_helpers.h" + +int main(void) { + gid_t egid = getegid(); + uid_t euid = geteuid(); + gid_t gid = getgid(); + pid_t pgid = getpgid(0); + pid_t pid = getpid(); + pid_t ppid = getppid(); + uid_t uid = getuid(); + printf("egid: %d, euid: %d, gid: %d, pgid: %d, pid: %d, ppid %d, uid %d\n", + egid, euid, gid, pgid, pid, ppid, uid); +} diff --git a/tests/unistd/getopt.c b/tests/unistd/getopt.c new file mode 100644 index 0000000000..2505788a25 --- /dev/null +++ b/tests/unistd/getopt.c @@ -0,0 +1,71 @@ +#include +#include + +#include "test_helpers.h" + +#define RUN(...) \ + do { \ + optind = 1; \ + optarg = NULL; \ + opterr = 1; \ + optopt = -1; \ + char *args_arr[] = { __VA_ARGS__ }; \ + printf("result: %d\n", runner(sizeof(args_arr) / sizeof(args_arr[0]), args_arr)); \ + } while (0) + +int runner(int argc, char *argv[]) { + int c; + int bflg = 0, aflg = 0, errflg = 0; + char *ifile = ""; + char *ofile = ""; + + while((c = getopt(argc, argv, ":abf:o:")) != -1) { + switch(c) { + case 'a': + if(bflg) + errflg++; + else + aflg++; + break; + case 'b': + if(aflg) + errflg++; + else + bflg++; + break; + case 'f': + ifile = optarg; + break; + case 'o': + ofile = optarg; + break; + case ':': + printf("Option -%c requires an operand\n", optopt); + errflg++; + break; + case '?': + printf("Unrecognized option: -%c\n", optopt); + errflg++; + } + } + printf("bflg: %d\n", bflg); + printf("aflg: %d\n", aflg); + printf("errflg: %d\n", errflg); + printf("ifile: %s\n", ifile); + printf("ofile: %s\n", ofile); + if(errflg) { + printf("Usage: info goes here\n"); + return 2; + } + return 0; +} + +int main(void) { + RUN("test", "-ao", "arg", "path", "path"); + RUN("test", "-a", "-o", "arg", "path", "path"); + RUN("test", "-o", "arg", "-a", "path", "path"); + RUN("test", "-a", "-o", "arg", "--", "path", "path"); + RUN("test", "-a", "-oarg", "path", "path"); + RUN("test", "-aoarg", "path", "path"); + RUN("test"); +} diff --git a/tests/unistd/getopt_long.c b/tests/unistd/getopt_long.c new file mode 100644 index 0000000000..92716068ce --- /dev/null +++ b/tests/unistd/getopt_long.c @@ -0,0 +1,66 @@ +#include +#include + +#include "test_helpers.h" + +#define RUN(...) \ + do { \ + optind = 1; \ + optarg = NULL; \ + opterr = 1; \ + optopt = -1; \ + char *args_arr[] = { __VA_ARGS__ }; \ + runner(sizeof(args_arr) / sizeof(char*), args_arr); \ + } while (0) + +void runner(int argc, char *argv[]) { + printf("--- Running:"); + for (int i = 0; i < argc; i += 1) { + printf(" %s", argv[i]); + } + puts(""); + + static int flag = 0; + + static struct option long_options[] = { + {"test0", no_argument, NULL, 1}, + {"test1", no_argument, &flag, 2}, + {"test2", optional_argument, NULL, 3}, + {"test3", required_argument, NULL, 4}, + {NULL, 0, NULL, 5}, + }; + + int option_index = 0; + int c; + while((c = getopt_long(argc, argv, ":a", long_options, &option_index)) != -1) { + switch(c) { + case 'a': + printf("Option -a with value %s\n", optarg); + break; + case ':': + printf("unrecognized argument: -%c\n", optopt); + break; + case '?': + printf("error: -%c\n", optopt); + break; + default: + printf("getopt_long returned %d, ", c); + if (flag) { + printf("set flag to %d, ", flag); + flag = 0; + } + printf("argument %s=%s\n", long_options[option_index].name, optarg); + break; + } + } +} + +int main(void) { + RUN("test", "--test0", "-a"); + RUN("test", "--test1", "-a"); + RUN("test", "--test2", "-a"); + RUN("test", "--test2=arg", "-a"); + RUN("test", "--test3", "-a"); + RUN("test", "--test3=arg", "-a"); + RUN("test", "--test3", "arg", "-a"); +} diff --git a/tests/unistd/getpagesize.c b/tests/unistd/getpagesize.c new file mode 100644 index 0000000000..e7f2b43924 --- /dev/null +++ b/tests/unistd/getpagesize.c @@ -0,0 +1,11 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int getpagesize_result = getpagesize(); + + printf("Page size: %d\n", getpagesize_result); +} diff --git a/tests/unistd/getpass.c b/tests/unistd/getpass.c new file mode 100644 index 0000000000..07c8e5c9a5 --- /dev/null +++ b/tests/unistd/getpass.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +// #include "test_helpers.h" + +int main(void) +{ + const char *pass = "pass"; + const char *prompt = "Enter password: "; + + char *result = getpass(prompt); + + if(strcmp(pass, result)) { + printf("incorrect password\n"); + exit(EXIT_FAILURE); + } + + const char *pass_127_chars = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + result = getpass(prompt); + + if(strcmp(pass_127_chars, result)) { + printf("incorrect password\n"); + exit(EXIT_FAILURE); + } + + const char *pass_empty = ""; + result = getpass(prompt); + + if(strcmp(pass_empty, result)) { + printf("incorrect password\n"); + exit(EXIT_FAILURE); + } + + printf("matching passwords\n", result); + + return 0; +} \ No newline at end of file diff --git a/tests/unistd/getpass.exp b/tests/unistd/getpass.exp new file mode 100644 index 0000000000..a707a04655 --- /dev/null +++ b/tests/unistd/getpass.exp @@ -0,0 +1,18 @@ +#!/usr/bin/expect + +set testgetpass [lindex $argv 0]; + +spawn $testgetpass +expect "Enter password: " +send -- "pass\r" + +expect "Enter password: " +send -- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r" + +expect "Enter password: " +send -- "\r" + +expect { + "incorrect password" { exit 123 } + eof +} \ No newline at end of file diff --git a/tests/unistd/isatty.c b/tests/unistd/isatty.c new file mode 100644 index 0000000000..5f7edfe886 --- /dev/null +++ b/tests/unistd/isatty.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int status = isatty(STDOUT_FILENO); + + if (status == 1) { + puts("'Tis a tty :D"); + } else if (status == 0) { + if (errno == ENOTTY) { + // I wouldn't consider stdout not being a TTY an error + // (CI runners, etc.) + puts("Whatever a tty is, it's not me"); + } else { + perror("isatty"); + exit(EXIT_FAILURE); + } + } else { + printf("isatty returned %d, unexpected result\n", status); + exit(EXIT_FAILURE); + } +} diff --git a/tests/unistd/link.c b/tests/unistd/link.c new file mode 100644 index 0000000000..5c09c12606 --- /dev/null +++ b/tests/unistd/link.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + printf("sizeof(struct stat): %ld\n", sizeof(struct stat)); + + struct stat buf; + + // Stat for the inode + if (stat("unistd/link.c", &buf)) { + perror("stat"); + exit(EXIT_FAILURE); + } + unsigned long inode = buf.st_ino; + printf("%ld\n", inode); + + // Create the link + if (link("unistd/link.c", "link.out")) { + perror("link"); + exit(EXIT_FAILURE); + } + + // Make sure inodes match + if (stat("link.out", &buf)) { + perror("stat"); + } + printf("%ld\n", inode); + printf("%ld\n", buf.st_ino); + if (inode != buf.st_ino) { + puts("Created file is not a link."); + printf("unistd/link.c inode: %ld\n", inode); + printf("link.out inode: %ld\n", buf.st_ino); + } + + // Remove link + if (unlink("link.out")) { + perror("unlink"); + exit(EXIT_FAILURE); + } + if (!stat("link.out", &buf) || errno != ENOENT) { + perror("stat"); + exit(EXIT_FAILURE); + } +} diff --git a/tests/unistd/pathconf.c b/tests/unistd/pathconf.c new file mode 100644 index 0000000000..bbbf922b79 --- /dev/null +++ b/tests/unistd/pathconf.c @@ -0,0 +1,35 @@ +#include +#include +#include + +#include "test_helpers.h" + +#define PC(N) \ + do { \ + errno = 0; \ + printf("%s (%d): %ld (%d)\n", #N, _PC_ ## N, fpathconf(0, _PC_ ## N), errno); \ + } while (0) + +int main(void) { + PC(LINK_MAX); + PC(MAX_CANON); + PC(MAX_INPUT); + PC(NAME_MAX); + PC(PATH_MAX); + PC(PIPE_BUF); + PC(CHOWN_RESTRICTED); + PC(NO_TRUNC); + PC(VDISABLE); + PC(SYNC_IO); + PC(ASYNC_IO); + PC(PRIO_IO); + PC(SOCK_MAXBUF); + PC(FILESIZEBITS); + PC(REC_INCR_XFER_SIZE); + PC(REC_MAX_XFER_SIZE); + PC(REC_MIN_XFER_SIZE); + PC(REC_XFER_ALIGN); + PC(ALLOC_SIZE_MIN); + PC(SYMLINK_MAX); + PC(2_SYMLINKS); +} diff --git a/tests/unistd/pipe.c b/tests/unistd/pipe.c new file mode 100644 index 0000000000..9f4b79a62e --- /dev/null +++ b/tests/unistd/pipe.c @@ -0,0 +1,76 @@ +//http://www2.cs.uregina.ca/~hamilton/courses/330/notes/unix/pipes/pipes.html +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int pip[2]; + char instring[20]; + char *outstring = "Hello World!"; + + int pipe_status = pipe(pip); + ERROR_IF(pipe, pipe_status, == -1); + UNEXP_IF(pipe, pipe_status, != 0); + + int pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + // child: sends message to parent + // close read end + int cr = close(pip[0]); + ERROR_IF(close, cr, == -1); + UNEXP_IF(close, cr, != 0); + + // send 7 characters in the string, including end-of-string + ssize_t bytes = write(pip[1], outstring, strlen(outstring)); + ERROR_IF(write, bytes, == -1); + + // check result + if ((size_t)bytes != strlen(outstring)) { + fprintf(stderr, "pipe write: %d != %ld\n", bytes, strlen(outstring)); + exit(EXIT_FAILURE); + } + + // close write end + int cw = close(pip[1]); + ERROR_IF(close, cw, == -1); + UNEXP_IF(close, cw, != 0); + + exit(EXIT_SUCCESS); + } else { + // parent: receives message from child + // close write end + int cw = close(pip[1]); + ERROR_IF(close, cw, == -1); + UNEXP_IF(close, cw, != 0); + + // clear memory + memset(instring, 0, sizeof(instring)); + + // read from the pipe + ssize_t bytes = read(pip[0], instring, sizeof(instring) - 1); + ERROR_IF(read, bytes, == -1); + + // check result + if ((size_t)bytes != strlen(outstring)) { + fprintf(stderr, "pipe read: %d != %ld\n", bytes, strlen(outstring)); + exit(EXIT_FAILURE); + } else if (memcmp(instring, outstring, strlen(outstring)) != 0) { + fprintf(stderr, "pipe read does not match pipe write\n"); + exit(EXIT_FAILURE); + } else { + printf("%s\n", instring); + } + + // close read end + int cr = close(pip[0]); + ERROR_IF(close, cr, == -1); + UNEXP_IF(close, cr, != 0); + + exit(EXIT_SUCCESS); + } +} diff --git a/tests/unistd/readlinkat.c b/tests/unistd/readlinkat.c new file mode 100644 index 0000000000..9009543438 --- /dev/null +++ b/tests/unistd/readlinkat.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include + +__attribute__((nonnull)) +static int run_test( + int dir, + const char name[], + char buf[PATH_MAX], + const char expected[] +) { + if (readlinkat(dir, name, buf, PATH_MAX) == -1) { + perror("readlinkat"); + return -1; + } + if (strncmp(expected, buf, PATH_MAX) != 0) { + fprintf( + stderr, + "readlinkat mismatch\n\tExpected: %s\n\tActual: %s\n", + expected, + buf + ); + return -1; + } + + return 0; +} + +int main(void) { + int status = EXIT_FAILURE; + + char template[] = "/tmp/rlatest.XXXXXX"; + if (!mkdtemp(template)) { + perror("mkdtemp"); + goto bye; + } + int dir = open(template, O_DIRECTORY); + if (dir == -1) { + perror("open"); + goto rmtempdir; + } + + // Set up file and link. + size_t len = sizeof(template) - 1; + const char file_name[] = "miku"; + char file_path[PATH_MAX] = {0}; + memcpy(file_path, template, len); + file_path[len] = '/'; + memcpy(&file_path[len + 1], file_name, sizeof(file_name)); + int file = open(file_path, O_CREAT | O_WRONLY); + if (file == -1) { + perror("open"); + goto close_dir; + } + + // Writing a sentinel message to the target file is useful in case + // readlinkat reads the file instead of the link. If that happens, + // it will self-evident instead of printing an empty string. + // (n.b. this totally happened to me so I KNOW it's useful) + const char msg[] = "readlinkat read the file instead of the link...oops"; + const ssize_t msg_len = sizeof(msg); + assert(msg_len < PATH_MAX); + if (write(file, msg, msg_len) < msg_len) { + perror("write"); + goto rmfiles; + } + if (close(file) == -1) { + perror("close"); + goto rmfiles; + } + file = -1; + + const char link_name[] = "link"; + char link_path[PATH_MAX] = {0}; + memcpy(link_path, template, len); + link_path[len] = '/'; + memcpy(&link_path[len + 1], link_name, len); + if (symlink(file_path, link_path) == -1) { + perror("symlink"); + goto rmfiles; + } + + // Relative path + char buf[PATH_MAX] = {0}; + if (run_test(dir, link_name, buf, file_path) == -1) { + fputs("Context: Basic test (relative path)\n", stderr); + goto rmfiles; + } + + // Absolute path + memset(buf, 0, PATH_MAX); + if (run_test(dir, link_path, buf, file_path) == -1) { + fputs("Context: Absolute path\n", stderr); + goto rmfiles; + } + + // AT_FDCWD + memset(buf, 0, PATH_MAX); + char old_cwd[PATH_MAX] = {0}; + if (!getcwd(old_cwd, PATH_MAX)) { + perror("getcwd"); + goto rmfiles; + } + if (chdir(template) == -1) { + perror("chdir"); + goto rmfiles; + } + if (run_test(AT_FDCWD, link_name, buf, file_path) == -1) { + fputs("Context: AT_FDCWD\n", stderr); + goto rmfiles; + } + if (chdir(old_cwd) == -1) { + perror("chdir"); + goto rmfiles; + } + + // Not a dir + memset(buf, 0, PATH_MAX); + file = open(file_path, O_PATH); + if (file == -1) { + perror("open"); + goto rmfiles; + } + if (readlinkat(file, "", buf, PATH_MAX) != -1) { + fputs("Context: Using a file for dirfd should fail\n", stderr); + fprintf(stderr, "readlinkat wrote this into buf: %s\n", buf); + goto close_file; + } + + status = EXIT_SUCCESS; +close_file: + close(file); +rmfiles: + unlink(file_path); + unlink(link_path); +close_dir: + close(dir); +rmtempdir: + rmdir(template); +bye: + return status; +} diff --git a/tests/unistd/rmdir.c b/tests/unistd/rmdir.c new file mode 100644 index 0000000000..be9e7be919 --- /dev/null +++ b/tests/unistd/rmdir.c @@ -0,0 +1,15 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int mk_status = mkdir("foo", 0); + ERROR_IF(mkdir, mk_status, == -1); + UNEXP_IF(mkdir, mk_status, != 0); + + int rm_status = rmdir("foo"); + ERROR_IF(rmdir, rm_status, == -1); + UNEXP_IF(rmdir, rm_status, != 0); +} diff --git a/tests/unistd/setid.c b/tests/unistd/setid.c new file mode 100644 index 0000000000..c5184bddd2 --- /dev/null +++ b/tests/unistd/setid.c @@ -0,0 +1,28 @@ +/* + * The process joins process group 0. + */ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + int pg_status = setpgid(getpid(), 0); + ERROR_IF(setpgid, pg_status, == -1); + UNEXP_IF(setpgid, pg_status, != 0); + + printf("%d belongs to process group %d\n", getpid(), getpgrp()); + + int reg_status = setregid(-1, -1); + ERROR_IF(setregid, reg_status, == -1); + UNEXP_IF(setregid, reg_status, != 0); + + printf("%d has egid %d and gid %d\n", getpid(), getegid(), getgid()); + + int reu_status = setreuid(-1, -1); + ERROR_IF(setreuid, reu_status, == -1); + UNEXP_IF(setreuid, reu_status, != 0); + + printf("%d has euid %d and uid %d\n", getpid(), geteuid(), getuid()); +} diff --git a/tests/unistd/sleep.c b/tests/unistd/sleep.c new file mode 100644 index 0000000000..92b254e79f --- /dev/null +++ b/tests/unistd/sleep.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int usleep(useconds_t); + +void handler(int _s) +{ + (void)_s; +} + +int main(void) +{ + // sleep has no error codes and doesn't set errno + unsigned int unslept = sleep(1); + printf("unslept: %u\n", unslept); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigaction(SIGALRM, &sa, NULL); + // TODO: This test is unreliable, overlapping use of alarm and sleep is not recommended. + // alarm(1); + + // unslept = sleep(10); + // if (unslept < 7) + // { + // printf("after alarm, unslept too short: %u\n", unslept); + // exit(EXIT_FAILURE); + // } + + int us_status = usleep(100000); + ERROR_IF(usleep, us_status, == -1); + UNEXP_IF(usleep, us_status, != 0); + + struct timespec tm = {0, 10000}; + int ns_status = nanosleep(&tm, NULL); + ERROR_IF(nanosleep, ns_status, == -1); + UNEXP_IF(nanosleep, ns_status, != 0); +} diff --git a/tests/unistd/swab.c b/tests/unistd/swab.c new file mode 100644 index 0000000000..dd4603913a --- /dev/null +++ b/tests/unistd/swab.c @@ -0,0 +1,53 @@ +#include +#include + +int main(void) { + const char source[] = {0, 1, 2, 3, 4, 5, 6}; + char destination[] = {0, 0, 0, 0, 0, 0}; + const char flipped_source[] = {1, 0, 3, 2, 5, 4}; + const char first_two_source_bytes_flipped[] = {1, 0}; + +#ifndef __GLIBC__ + swab(source, destination, /* nbytes */ -3); + for (size_t i = 0; i < sizeof(destination); ++i) { + if (destination[i] != 0) { + puts("If nbytes is negative destionation shouldn't be modified"); + return 1; + } + } +#endif + + swab(source, destination, /* nbytes */ 0); + for (size_t i = 0; i < sizeof(destination); ++i) { + if (destination[i] != 0) { + puts("If nbytes is zero destionation shouldn't be modified"); + return 1; + } + } + + swab(source, destination, /* nbytes */ 3); + for (size_t i = 0; i < sizeof(first_two_source_bytes_flipped); ++i) { + if (destination[i] != first_two_source_bytes_flipped[i]) { + puts("copied bytes don't match expected values"); + return 1; + } + } + // If nbytes is not even it's not specified what should happen to the + // last byte so the third byte in destination is not checked. + for (size_t i = sizeof(first_two_source_bytes_flipped) + 1; i < sizeof(destination); ++i) { + if (destination[i] != 0) { + puts("swab modified too many bytes in destination"); + return 1; + } + } + + swab(source, destination, /* nbytes */ sizeof(destination)); + for (size_t i = 0; i < sizeof(destination); ++i) { + if (destination[i] != flipped_source[i]) { + puts("copied bytes don't match expected values"); + return 1; + } + } + + return 0; +} diff --git a/tests/unistd/sysconf.c b/tests/unistd/sysconf.c new file mode 100644 index 0000000000..05b1ed1b66 --- /dev/null +++ b/tests/unistd/sysconf.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "test_helpers.h" + +#define SC(N) \ + do { \ + errno = 0; \ + printf("%s (%d): %ld (%d)\n", #N, _SC_ ## N, sysconf(_SC_ ## N), errno); \ + } while (0) + +int main(void) { + SC(ARG_MAX); + SC(CHILD_MAX); + SC(CLK_TCK); + SC(NGROUPS_MAX); + SC(OPEN_MAX); + SC(STREAM_MAX); + SC(TZNAME_MAX); + SC(VERSION); + SC(PAGESIZE); + SC(RE_DUP_MAX); + SC(LOGIN_NAME_MAX); + SC(TTY_NAME_MAX); + SC(SYMLOOP_MAX); + SC(HOST_NAME_MAX); + SC(NPROCESSORS_CONF); + SC(NPROCESSORS_ONLN); + SC(PHYS_PAGES); + SC(AVPHYS_PAGES); +} diff --git a/tests/unistd/write.c b/tests/unistd/write.c new file mode 100644 index 0000000000..6810af3855 --- /dev/null +++ b/tests/unistd/write.c @@ -0,0 +1,8 @@ +#include + +#include "test_helpers.h" + +int main(void) { + int written = write(STDOUT_FILENO, "Hello World!\n", 13); + ERROR_IF(write, written, == -1); +} diff --git a/tests/waitid.c b/tests/waitid.c new file mode 100644 index 0000000000..5dc7063ccd --- /dev/null +++ b/tests/waitid.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +static void wait_until_child_exits(pid_t pid) { + siginfo_t info; + for (int i = 0; i < 50; ++i) { + info.si_pid = 0; + int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT); + ERROR_IF(waitid, ret, == -1); + if (info.si_pid == pid) { + assert(info.si_code == CLD_EXITED); + assert(info.si_status == 42); + return; + } + usleep(10000); + } + assert(!"waitid never observed child exit"); +} + +int main(void) { + pid_t pid = fork(); + ERROR_IF(fork, pid, == -1); + + if (pid == 0) { + usleep(50000); + _Exit(42); + } + + siginfo_t info; + info.si_pid = 0; + int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT); + ERROR_IF(waitid, ret, == -1); + assert(info.si_pid == 0); + + wait_until_child_exits(pid); + + int status = 0; + pid_t waited = waitpid(pid, &status, 0); + ERROR_IF(waitpid, waited, == -1); + assert(waited == pid); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 42); + + return EXIT_SUCCESS; +} diff --git a/tests/waitpid.c b/tests/waitpid.c new file mode 100644 index 0000000000..4d89c078ca --- /dev/null +++ b/tests/waitpid.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +void for_code(int code) { + pid_t pid = fork(); + ERROR_IF(fork, pid, == -1); + + // Testing successful exit + if (pid == 0) { + // child + usleep(100000); + _Exit(code); + } + printf("Testing waitpid of child %d for code %d\n", pid, code); + // parent + int status; + pid_t wid = waitpid(pid, &status, 0); + ERROR_IF(waitpid, wid, == -1); + + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == code); + + puts("Success"); +} + +int main(void) { + for_code(EXIT_SUCCESS); + for_code(EXIT_FAILURE); + for_code(42); + for_code(255); + // TODO: Also add coverage for e.g. WIFSTOPPED, WSTOPSIG, WTERMSIG, etc + + return EXIT_SUCCESS; +} diff --git a/tests/waitpid_multiple.c b/tests/waitpid_multiple.c new file mode 100644 index 0000000000..f787c84e04 --- /dev/null +++ b/tests/waitpid_multiple.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + // Spawn three childrens, one with same pgid and two with pgid set to its own + // pid, so the exit order is two different pgid, then followed with same pgid + + pid_t pid_samepgid = fork(); + ERROR_IF(fork, pid_samepgid, == -1); + + if (pid_samepgid == 0) { + // child + usleep(300000); + _Exit(2); + } + pid_t pid_diffpgids[2]; + for (int i = 0; i < 2; i++) { + pid_diffpgids[i] = fork(); + ERROR_IF(fork, pid_diffpgids[i], == -1); + + if (pid_diffpgids[i] == 0) { + int ret = setpgid(0, 0); + ERROR_IF(setpgid, ret, == -1); + + // child + usleep((i + 1) * 100000); + _Exit(i); + } + } + int status; + pid_t wid; + + // First, check that the first different-pgid proc is recognized. + wid = waitpid(-1, &status, 0); + ERROR_IF(waitpid, wid, == -1); + assert(wid == pid_diffpgids[0]); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 0); + + // Then, check that the longest-waiting proc with the same pgid is properly matched. + wid = waitpid(0, &status, 0); + ERROR_IF(waitpid, wid, == -1); + assert(wid == pid_samepgid); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 2); + + // Finally, the last different-pgid must have completed. + wid = waitpid(-1, &status, WNOHANG); + ERROR_IF(waitpid, wid, == -1); + assert(wid == pid_diffpgids[1]); + assert(WIFEXITED(status)); + assert(WEXITSTATUS(status) == 1); +} diff --git a/tests/wchar/fgetwc.c b/tests/wchar/fgetwc.c new file mode 100644 index 0000000000..1aeeff16ff --- /dev/null +++ b/tests/wchar/fgetwc.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int main(void) +{ + setlocale(LC_ALL, ""); + wint_t wc; + FILE *fp = fopen("wchar/fgetwc.in", "r"); + if (!fp) { + return 1; + } + + while (WEOF != (wc = fgetwc(fp))) + printf("%lc", wc); + + fclose(fp); + return 0; +} diff --git a/tests/wchar/fgetwc.in b/tests/wchar/fgetwc.in new file mode 100644 index 0000000000..e7cabf537e --- /dev/null +++ b/tests/wchar/fgetwc.in @@ -0,0 +1,3 @@ +êçã +こんにちは世界 +Привет мир \ No newline at end of file diff --git a/tests/wchar/fwide.c b/tests/wchar/fwide.c new file mode 100644 index 0000000000..12eceb75a0 --- /dev/null +++ b/tests/wchar/fwide.c @@ -0,0 +1,60 @@ +#include +#include +#include + +int test_initial_orientation(void) { + FILE *f = tmpfile(); + assert(fwide(f, 0) == 0); + return 0; +} + +int test_manual_byte_orientation(void) { + FILE *f = tmpfile(); + + // set manually to byte orientation + assert(fwide(f, -483) == -1); + + // Cannot change to wchar orientation + assert(fwide(f, 1) == -1); + + fclose(f); + return 0; +} + +int test_manual_wchar_orientation(void) { + FILE *f = tmpfile(); + + // set manually to wchar orientation + assert(fwide(f, 483) == 1); + + // Cannot change to byte orientation + assert(fwide(f, -1) == 1); + + fclose(f); + return 0; +} + +int test_orientation_after_fprintf(void) { + // open file and write bytes; implicitly setting the bytes orientation + FILE *f = tmpfile(); + assert(fprintf(f, "blah\n") == 5); + + // Check that bytes orientation is set + assert(fwide(f, 0) == -1); + + fclose(f); + return 0; +} + +int main() { + int(*tests[])(void) = { + &test_initial_orientation, + &test_manual_byte_orientation, + &test_manual_wchar_orientation, + &test_orientation_after_fprintf, + }; + for(size_t i = 0; i < sizeof(tests) / sizeof(int(*)(void)); i++) { + printf("%d\n", (*tests[i])()); + } +} + diff --git a/tests/wchar/mbrtowc.c b/tests/wchar/mbrtowc.c new file mode 100644 index 0000000000..0a1824429a --- /dev/null +++ b/tests/wchar/mbrtowc.c @@ -0,0 +1,31 @@ +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + mbstate_t state; + memset(&state, 0, sizeof state); + char in[] = u8"z\u00df\u6c34\U0001F34C"; // or u8"zß水🍌" + size_t in_sz = sizeof in / sizeof *in; + + printf("Processing %zu UTF-8 code units: [ ", in_sz); //Should be 11 regular chars + for(size_t n = 0; n < in_sz; ++n) printf("%#x ", (unsigned char)in[n]); + puts("]"); + + wchar_t out[in_sz]; + char *p_in = in, *end = in + in_sz; + wchar_t *p_out = out; + int rc; + while((rc = mbrtowc(p_out, p_in, end - p_in, &state)) > 0) + { + p_in += rc; + p_out += 1; + } + + size_t out_sz = p_out - out + 1; + printf("into %zu wchar_t units: [ ", out_sz); //Should be 5 wchars + for(size_t x = 0; x < out_sz; ++x) printf("%#x ", out[x]); + puts("]"); +} diff --git a/tests/wchar/mbsrtowcs.c b/tests/wchar/mbsrtowcs.c new file mode 100644 index 0000000000..6db79da51a --- /dev/null +++ b/tests/wchar/mbsrtowcs.c @@ -0,0 +1,25 @@ +#include +#include +#include + +#include "test_helpers.h" + +void print_as_wide(const char* mbstr) +{ + mbstate_t state; + memset(&state, 0, sizeof state); + size_t len = 1 + mbsrtowcs(NULL, &mbstr, 0, &state); + wchar_t wstr[len]; + mbsrtowcs(&wstr[0], &mbstr, len, &state); + + //Should be 5 + printf("The length, including '\\0': %li \n",len); + + //missing wprintf to print this wide string + //wprintf(L"The wide string: %ls \n", &wstr[0]); +} + +int main(void) { + const char* mbstr = u8"z\u00df\u6c34\U0001f34c"; // or u8"zß水🍌" + print_as_wide(mbstr); +} diff --git a/tests/wchar/printf-on-wchars.c b/tests/wchar/printf-on-wchars.c new file mode 100644 index 0000000000..20860c74f1 --- /dev/null +++ b/tests/wchar/printf-on-wchars.c @@ -0,0 +1,22 @@ +#include +#include +#include + +int main() { + wint_t a = L'1'; + wint_t b = L'2'; + wint_t c = L'a'; + wint_t d = L'b'; + printf("This is a few one-byte chars: %lc %lc %lc %lc\n", a, b, c, d); + wchar_t *s = L"Hello World"; + printf("Long one-byte string: %ls\n", s); + + a = L'❤'; + b = L'R'; + c = L'😠'; + d = L'C'; + printf("This is a few multi-byte chars: %lc %lc %lc %lc\n", a, b, c, d); + + s = L"👉😎👉 Zoop!"; + printf("Long multi-byte string: %ls\n", s); +} diff --git a/tests/wchar/putwchar.c b/tests/wchar/putwchar.c new file mode 100644 index 0000000000..fc25911468 --- /dev/null +++ b/tests/wchar/putwchar.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + wchar_t *wcs = L"zß水🍌"; + + for (int i = 0; wcs[i] != L'\0'; i++) { + wint_t status = putwchar(wcs[i]); + ERROR_IF(putwchar, status, == WEOF); + } +} diff --git a/tests/wchar/ungetwc.c b/tests/wchar/ungetwc.c new file mode 100644 index 0000000000..26f2649a4d --- /dev/null +++ b/tests/wchar/ungetwc.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include + +int main() +{ + setlocale(LC_ALL, ""); + FILE *stream; + wint_t wc; + wint_t wc2; + + if (NULL == (stream = fopen("wchar/ungetwc.in", "r+"))) + return 1; + + while (WEOF != (wc = fgetwc(stream)) && iswdigit(wc)) {} + + if (WEOF != wc) + ungetwc(wc, stream); + + wc2 = fgetwc(stream); + assert(WEOF != wc2); + assert(wc == wc2); + + return 0; +} \ No newline at end of file diff --git a/tests/wchar/ungetwc.in b/tests/wchar/ungetwc.in new file mode 100644 index 0000000000..a86807323c --- /dev/null +++ b/tests/wchar/ungetwc.in @@ -0,0 +1 @@ +123çA \ No newline at end of file diff --git a/tests/wchar/wcpcpy.c b/tests/wchar/wcpcpy.c new file mode 100644 index 0000000000..c65421220d --- /dev/null +++ b/tests/wchar/wcpcpy.c @@ -0,0 +1,16 @@ +#include +#include +#include + +int main() { + wchar_t src[] = L"こんにちは世界!"; + wchar_t dest[9]; + + wchar_t* result = wcpcpy(dest, src); + + assert(wcscmp(dest, src) == 0); + assert(*result == L'\0'); + assert(result == dest + wcslen(src)); + + return 0; +} \ No newline at end of file diff --git a/tests/wchar/wcpncpy.c b/tests/wchar/wcpncpy.c new file mode 100644 index 0000000000..62c0bdb4e2 --- /dev/null +++ b/tests/wchar/wcpncpy.c @@ -0,0 +1,37 @@ +#include +#include +#include + +int main() { + wchar_t src[] = L"Привет мир"; + + size_t n_input = 10; + size_t n_short = 6; + size_t n_long = 15; + + // Initialize with sentinel values to detect exactly how much is overwritten + wchar_t dest_short[] = L"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x90\x12\x34\x56\x78"; + wchar_t dest_long[] = L"\x12\x34\x56\x78\x90\x12\x34\x56\x78\x90\x12\x34\x56\x78"; + + wchar_t expected_short[] = L"Привет\x34\x56\x78\x90\x12\x34\x56\x78"; + + // The "short" test should copy exactly n_short characters without terminating null + wchar_t* result_short = wcpncpy(dest_short, src, n_short); + assert(wcsncmp(dest_short, src, n_short) == 0); + assert(result_short == dest_short + n_short); + for (size_t i = n_short; i < n_long; i++) { + // Check that the sentinel characters have not been overwritten + assert(dest_short[i] == expected_short[i]); + } + + // The "long" test should write the input string, with nulls appended up to n_long + wchar_t* result_long = wcpncpy(dest_long, src, n_long); + assert(wcsncmp(dest_long, src, n_long) == 0); + assert(result_long == dest_long + n_input); + for (size_t i = n_input; i < n_long; i++) { + // Check that nulls are written up to n_long + assert(dest_long[i] == L'\0'); + } + + return 0; +} diff --git a/tests/wchar/wcrtomb.c b/tests/wchar/wcrtomb.c new file mode 100644 index 0000000000..9978da5ab4 --- /dev/null +++ b/tests/wchar/wcrtomb.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +#include "test_helpers.h" + +int main(void) { + mbstate_t state; + memset(&state, 0, sizeof state); + wchar_t in[] = L"zß水🍌"; // or "z\u00df\u6c34\U0001F34C" + size_t in_sz = sizeof in / sizeof *in; + + printf("Processing %zu wchar_t units: [ ", in_sz); + for(size_t n = 0; n < in_sz; ++n) printf("%#x ", (unsigned int)in[n]); + puts("]"); + + char out[MB_CUR_MAX * in_sz]; + char *p = out; + for(size_t n = 0; n < in_sz; ++n) { + int rc = wcrtomb(p, in[n], &state); + if(rc == -1) break; + p += rc; + } + + size_t out_sz = p - out; + printf("into %zu UTF-8 code units: [ ", out_sz); + for(size_t x = 0; x < out_sz; ++x) printf("%#x ", +(unsigned char)out[x]); + puts("]"); +} diff --git a/tests/wchar/wcscasecmp.c b/tests/wchar/wcscasecmp.c new file mode 100644 index 0000000000..7241be198a --- /dev/null +++ b/tests/wchar/wcscasecmp.c @@ -0,0 +1,11 @@ +#include +#include + +int main() { + wchar_t *s1 = L"ThIs Is StRiNg 1."; + wchar_t *s2 = L"tHiS iS sTrInG 2."; + printf("wcscasecmp(s1, s1) = %d\n", wcscasecmp(s1, s1)); + printf("wcscasecmp(s1, s2) = %d\n", wcscasecmp(s1, s2)); + printf("wcscasecmp(s2, s1) = %d\n", wcscasecmp(s2, s1)); + printf("wcscasecmp(s2, s2) = %d\n", wcscasecmp(s2, s2)); +} diff --git a/tests/wchar/wcschr.c b/tests/wchar/wcschr.c new file mode 100644 index 0000000000..2b396d4763 --- /dev/null +++ b/tests/wchar/wcschr.c @@ -0,0 +1,13 @@ +#include +#include + +int main(void) { + wchar_t *haystack = L"Hello World!"; + + assert(wcschr(haystack, L'H') == haystack); + assert(wcschr(haystack, L'W') == &haystack[6]); + assert(wcschr(haystack, L'\0') == &haystack[12]); // the terminating nul is considered part of the string + assert(wcschr(haystack, L'X') == NULL); + + return 0; +} diff --git a/tests/wchar/wcscspn.c b/tests/wchar/wcscspn.c new file mode 100644 index 0000000000..f61adae236 --- /dev/null +++ b/tests/wchar/wcscspn.c @@ -0,0 +1,16 @@ +#include +#include + +int main(void) { + + assert(wcscspn(L"", L"") == 0); + assert(wcscspn(L"", L"h") == 0); + assert(wcscspn(L"a", L"a") == 0); + + assert(wcscspn(L"ba", L"ab") == 0); + assert(wcscspn(L"ba", L"a") == 1); + + assert(wcscspn(L"abcdefghijkl$\"£$%", L"zxqrst,./$w") == 12); + + return 0; +} diff --git a/tests/wchar/wcsdup.c b/tests/wchar/wcsdup.c new file mode 100644 index 0000000000..27d4a2c580 --- /dev/null +++ b/tests/wchar/wcsdup.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +int main() { + wchar_t src[] = L"こんにちは世界Привет мир"; + + wchar_t* dup = wcsdup(src); + + assert(wcscmp(dup, src) == 0); + assert(dup != src); + free(dup); + + return 0; +} diff --git a/tests/wchar/wcsncasecmp.c b/tests/wchar/wcsncasecmp.c new file mode 100644 index 0000000000..d0e815fe6e --- /dev/null +++ b/tests/wchar/wcsncasecmp.c @@ -0,0 +1,12 @@ +#include +#include + +int main() { + wchar_t *s1 = L"This is string 1."; + wchar_t *s2 = L"This is string 2."; + printf("wcsncasecmp(s1, s1, 17) = %d\n", wcsncasecmp(s1, s1, 17)); + printf("wcsncasecmp(s1, s2, 17) = %d\n", wcsncasecmp(s1, s2, 17)); + printf("wcsncasecmp(s2, s1, 17) = %d\n", wcsncasecmp(s2, s1, 17)); + printf("wcsncasecmp(s2, s1, 15) = %d\n", wcsncasecmp(s2, s1, 15)); + printf("wcsncasecmp(s1, s2, 0) = %d\n", wcsncasecmp(s1, s2, 0)); +} diff --git a/tests/wchar/wcsnlen.c b/tests/wchar/wcsnlen.c new file mode 100644 index 0000000000..ff1c705fbf --- /dev/null +++ b/tests/wchar/wcsnlen.c @@ -0,0 +1,9 @@ +#include +#include + +int main() { + // size is 17 + wchar_t* str1 = L"こんにちは世界Привет мир"; + assert(wcsnlen(str1, 10) == 10); + assert(wcsnlen(str1, 100) == 17); +} \ No newline at end of file diff --git a/tests/wchar/wcsnrtombs.c b/tests/wchar/wcsnrtombs.c new file mode 100644 index 0000000000..eb2a859267 --- /dev/null +++ b/tests/wchar/wcsnrtombs.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +int main() { + setlocale(LC_ALL, ""); + + const wchar_t *src = L"こんにちは世界Привет мир"; + char dst[20] = {0}; + mbstate_t ps; + + size_t result = wcsnrtombs(dst, &src, 4, sizeof(dst), &ps); + assert(result == 12); + assert(strcmp(dst, "こんにち") == 0); + + return 0; +} diff --git a/tests/wchar/wcsrchr.c b/tests/wchar/wcsrchr.c new file mode 100644 index 0000000000..08b43ea0fc --- /dev/null +++ b/tests/wchar/wcsrchr.c @@ -0,0 +1,24 @@ +#include +#include + +int main() { + wchar_t *s; + + assert(wcsrchr(L"", L'a') == NULL); + + s = L"a"; + assert(wcsrchr(s, L'a') == s); + + s = L"aa"; + assert(wcsrchr(s, L'a') == s + 1); + + s = L"aab"; + assert(wcsrchr(s, L'a') == s + 1); + + s = L"abcdef!\"£$%^e&*"; + assert(wcsrchr(s, L'g') == NULL); + assert(wcsrchr(s, L'\"') == s + 7); + assert(wcsrchr(s, L'e') == s + 12); + + return 0; +} diff --git a/tests/wchar/wcsrtombs.c b/tests/wchar/wcsrtombs.c new file mode 100644 index 0000000000..087a8862ae --- /dev/null +++ b/tests/wchar/wcsrtombs.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +int main() { + const wchar_t *wcs1 = L""; + size_t mb_len1 = wcsrtombs(NULL, &wcs1, 0, NULL); + assert(mb_len1 == 0); + + const wchar_t *wcs2 = L"Hello, world!"; + size_t mb_len2 = wcsrtombs(NULL, &wcs2, 0, NULL); + assert(mb_len2 == strlen("Hello, world!")); + + // wcsrtombs resets wcs2 to zero, so we need to fill it again + wcs2 = L"Hello, world!"; + char *mbs2 = malloc(mb_len2 + 1); + wcsrtombs(mbs2, &wcs2, mb_len2 + 1, NULL); + assert(strcmp(mbs2, "Hello, world!") == 0); + free(mbs2); + + return 0; +} \ No newline at end of file diff --git a/tests/wchar/wcsstr.c b/tests/wchar/wcsstr.c new file mode 100644 index 0000000000..b49a4baac2 --- /dev/null +++ b/tests/wchar/wcsstr.c @@ -0,0 +1,27 @@ +#include +#include + +int main(void) { + wchar_t *haystack = L"Hello, World!"; + wchar_t *haystack_empty = L""; + + wchar_t *needle_expected = L"World"; + wchar_t *needle_expected_multiple = L"l"; + wchar_t *needle_not_expected = L"Rust"; + wchar_t *needle_too_long = L"Hello, World!!!"; + wchar_t *needle_empty = L""; + + assert(wcsstr(haystack, needle_expected) == haystack + 7); + assert(wcsstr(haystack, needle_expected_multiple) == haystack + 2); + assert(wcsstr(haystack, needle_not_expected) == NULL); + assert(wcsstr(haystack, needle_too_long) == NULL); + assert(wcsstr(haystack, needle_empty) == haystack); + + assert(wcsstr(haystack_empty, needle_expected) == NULL); + assert(wcsstr(haystack_empty, needle_expected_multiple) == NULL); + assert(wcsstr(haystack_empty, needle_not_expected) == NULL); + assert(wcsstr(haystack_empty, needle_too_long) == NULL); + assert(wcsstr(haystack_empty, needle_empty) == haystack_empty); + + return 0; +} diff --git a/tests/wchar/wcstod.c b/tests/wchar/wcstod.c new file mode 100644 index 0000000000..dd71e42191 --- /dev/null +++ b/tests/wchar/wcstod.c @@ -0,0 +1,20 @@ +#include +#include +// TODO: remove glibc? +#ifdef __GLIBC__ +#include +#endif + +void attempt(wchar_t *s) { + wchar_t *end; + double result = wcstod(s, &end); + printf("strtod(%lls) = (%f, %lls)\n", s, result, end); +} +int main() { + attempt(L"1.2345wowzah"); + attempt(L"53"); + attempt(L"-254352.5..."); + attempt(L" 19.2 wat"); + attempt(L"365.24 29.53"); + attempt(L" 29.53"); +} diff --git a/tests/wchar/wcstoimax.c b/tests/wchar/wcstoimax.c new file mode 100644 index 0000000000..546314d063 --- /dev/null +++ b/tests/wchar/wcstoimax.c @@ -0,0 +1,13 @@ +#include +#include +#include + +int main(void) { + wchar_t* endptr; + wprintf(L"%ld\n", wcstoimax(L" -123junk", &endptr, 10)); /* base 10 */ + wprintf(L"%ld\n", wcstoimax(L"11111111", &endptr, 2)); /* base 2 */ + wprintf(L"%ld\n", wcstoimax(L"XyZ", &endptr, 36)); /* base 36 */ + wprintf(L"%ld\n", wcstoimax(L"010", &endptr, 0)); /* octal auto-detection */ + wprintf(L"%ld\n", wcstoimax(L"10", &endptr, 0)); /* decimal auto-detection */ + wprintf(L"%ld\n", wcstoimax(L"0x10", &endptr, 0)); /* hexadecimal auto-detection */ +} \ No newline at end of file diff --git a/tests/wchar/wcstok.c b/tests/wchar/wcstok.c new file mode 100644 index 0000000000..0fe7483b2a --- /dev/null +++ b/tests/wchar/wcstok.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + wchar_t wcs[] = L"Hello from the otter\tslide.\nMust have gone down a\t\t\t \n thousand tiiiiiimeeeees...\n\n\n"; + wchar_t *token; + wchar_t *state; + for (token = wcstok(wcs, L" \t\n", &state); + token != NULL; + token = wcstok(NULL, L" \t\n", &state)) { + fputws(token, stdout); + fputwc(L'\n', stdout); + } +} diff --git a/tests/wchar/wcstol.c b/tests/wchar/wcstol.c new file mode 100644 index 0000000000..e33b5343ab --- /dev/null +++ b/tests/wchar/wcstol.c @@ -0,0 +1,17 @@ +// from http://www.cplusplus.com/reference/cwchar/wcstol/ +#include +#include + +int main () { + wchar_t wsNumbers[] = L"2001 60c0c0 -1101110100110100100000 0x6fffff"; + wchar_t * pEnd; + long int li1, li2, li3, li4; + + li1 = wcstol(wsNumbers,&pEnd,10); + li2 = wcstol(pEnd,&pEnd,16); + li3 = wcstol(pEnd,&pEnd,2); + li4 = wcstol(pEnd,NULL,0); + + printf("The decimal equivalents are: %ld, %ld, %ld and %ld.\n", li1, li2, li3, li4); + return 0; +} diff --git a/tests/wchar/wcstoumax.c b/tests/wchar/wcstoumax.c new file mode 100644 index 0000000000..8f4c80b8aa --- /dev/null +++ b/tests/wchar/wcstoumax.c @@ -0,0 +1,15 @@ +#include +#include +#include + +int main(void) { + wchar_t *nptr; + wchar_t *endptr; + uintmax_t j; + int base = 10; + nptr = L"10110134932"; + printf("nptr = `%ls`\n", nptr); + j = wcstoumax(nptr, &endptr, base); + printf("wcstoumax = %ju\n", j); + printf("Stopped scan at `%ls`\n", endptr); +} \ No newline at end of file diff --git a/tests/wchar/wcswidth.c b/tests/wchar/wcswidth.c new file mode 100644 index 0000000000..79aeb921cb --- /dev/null +++ b/tests/wchar/wcswidth.c @@ -0,0 +1,10 @@ +#include +#include + +int main () { + wchar_t *wcs = L"relibc"; + size_t len = wcslen(wcs); + int width = wcswidth(wcs, len); + printf("wcswidth(L\"%ls\", %d) = %d\n", wcs, len, width); + return 0; +} diff --git a/tests/wchar/wprintf.c b/tests/wchar/wprintf.c new file mode 100644 index 0000000000..c635fff661 --- /dev/null +++ b/tests/wchar/wprintf.c @@ -0,0 +1,93 @@ +#include +#include // free() +#include +#include // INFINITY, NAN constants + +int main(void) { + int sofar = 0; + int len = wprintf( + L"percent: %%\nwstring: %ls\nchar: %lc\nint: %d\n%nuint: %u\nhex: %x\nHEX: %X\nstring: %s\n", + L"String", + L'c', + -16, + &sofar, + 32, + 0xbeef, + 0xC0FFEE, + "end" + ); + wprintf(L"%%n returned %d, total len of write: %d\n", sofar, len); + + wprintf(L"\nPadding madness:\n"); + wprintf(L"% -5.3d %+3d\n", 1, 2); + wprintf(L"%4c\n", 'c'); + wprintf(L"%#10.7x\n", 0xFF); + wprintf(L"%#4.3o\n", 01); + wprintf(L"%#x %#x\n", 0, 1); + wprintf(L"%.0d %.0d\n", 0, 1); + wprintf(L"(%05d) (%5d)\n", 123, 123); + wprintf(L"(%05d) (%5d)\n", -123, -123); + wprintf(L"(%13.0d)\n", 0); + wprintf(L"%p\n", (void*) 0xABCDEF); + wprintf(L"%p\n", (void*) 0); + + wprintf(L"\nPositional madness:\n"); + wprintf(L"%3$d %2$d %1$d\n", 2, 3, 4); + wprintf(L"%.*3$d\n", 2, 0, 5); + wprintf(L"|%-*6$.*5$s|%-*6$.*5$s|%*6$.*5$s|%*6$.*5$s|\n", "Fizz", "Buzz", "FizzBuzz", "TotalBuzz", 3, 8); + wprintf(L"int: %*6$d double: %lf %lf %lf %lf\n", 5, 0.1, 0.2, 0.3, 0.4, 10); + wprintf(L"%1$d %1$lf\n", 5, 0.1); + wprintf(L"%1$d %lf\n", 5, 0.2); + //wprintf(L"int: %*6$d no info on middle types\n", 5, 0.1, 0.2, 0.3, 0.4, 10); + + wprintf(L"\nFloat madness:\n"); + wprintf(L"%20e\n", 123.456789123); + wprintf(L"%20E\n", 0.00001); + wprintf(L"%20f\n", 123.456789123); + wprintf(L"%20F\n", 0.00001); + wprintf(L"%20e\n", -123.456789123); + wprintf(L"%020e\n", -123.456789123); + wprintf(L"%%.5g: %.5g\n", -123.456789123); + wprintf(L"%%.5f: %.5f\n", -123.456789123); + wprintf(L"%%.5e: %.5e\n", -123.456789123); + wprintf(L"%%.*g: %.*g\n", 2, -123.456789123); + wprintf(L"%%.*f: %.*f\n", 2, -123.456789123); + wprintf(L"%%.*e: %.*e\n", 2, -123.456789123); + wprintf(L"%%.*2$g: %.*2$g\n", -123.456789123, 5); + wprintf(L"%%.*2$f: %.*2$f\n", -123.456789123, 5); + wprintf(L"%%.*2$e: %.*2$e\n", -123.456789123, 5); + + wprintf(L"%g\n", 100000.0); + wprintf(L"%g\n", 1000000.0); + wprintf(L"%e\n", 1000000.0); + wprintf(L"%G\n", 0.0001); + wprintf(L"%G\n", 0.00001); + wprintf(L"%E\n", 0.00001); + + double nonfinites[] = {INFINITY, -INFINITY, NAN, -NAN}; + wchar_t *float_formats[] = {L"%e", L"%E", L"%f", L"%F", L"%g", L"%G"}; + wprintf(L"\nNon-finite float madness:\n"); + for (size_t i = 0; i < sizeof(float_formats)/sizeof(char *); i++) { + wprintf(L"%ls:", float_formats[i]); + for (size_t j = 0; j < sizeof(nonfinites)/sizeof(double); j++) { + wprintf(L" "); + wprintf(float_formats[i], nonfinites[j]); + } + wprintf(L"\n"); + } + + wprintf(L"Things that have been buggy\n"); + wprintf(L"%s%0*lu\n", "+", 2, 5l); // this format string was found in GDB + + wprintf(L"Testing asprintf...\n"); + char *s = NULL; + int res = asprintf(&s, "test string"); + wprintf(L"printed: %s, value: %d\n", s, res); + free(s); + res = asprintf(&s, "test string %d", 2); + wprintf(L"printed: %s, value: %d\n", s, res); + free(s); + res = asprintf(&s, "test %s %d", "string", 2); + wprintf(L"printed: %s, value: %d\n", s, res); + free(s); +} diff --git a/tests/wchar/wscanf.c b/tests/wchar/wscanf.c new file mode 100644 index 0000000000..01962484f0 --- /dev/null +++ b/tests/wchar/wscanf.c @@ -0,0 +1,102 @@ +/* swscanf example */ +#include +#include +// TODO: remove glibc? +#ifdef __GLIBC__ +#include +#endif + +#include "test_helpers.h" + +struct params { + short sa; + int ia; + int ib; + int ic; + float fa; + double da; + int *ptr; + char c; + wchar_t wc; + char string1[20]; + char string2[20]; + char string3[20]; + char string4[20]; + wchar_t wstring1[20]; + wchar_t wstring2[20]; + wchar_t wstring3[20]; + wchar_t wstring4[20]; +}; + + +void test(wchar_t* fmt_in, wchar_t* input, struct params *p, ...) { + va_list args; + va_start(args, p); + wint_t ret = vswscanf(input, fmt_in, args); + va_end(args); + + wprintf( + L"%d, { sa: %hhd, ia: %d, ib: %d, ic: %d, fa: %f, da: %lf, ptr: %p, char: %c, wide char: %lc, string1: %s, string2: %s, string3: %s, string4: %s, wstring1: %ls, wstring2: %ls, wstring3: %ls, wstring4: %ls }\n", + ret, p->sa, p->ia, p->ib, p->ic, p->fa, p->da, p->ptr, p->c, p->wc, p->string1, p->string2, p->string3, p->string4, p->wstring1, p->wstring2, p->wstring3, p->wstring4 + ); +} + +int main () +{ + struct params p = { .c = 'a' }; + + test(L"%hd %d", L"12 345", &p, &p.sa, &p.ia); + test(L"%x %i %i", L"12 0x345 010", &p, &p.ia, &p.ib, &p.ic); + test(L"%f.%lf", L"0.1.0.2", &p, &p.fa, &p.da); + test(L"%p", L"0xABCDEF", &p, &p.ptr); + test(L"%s", L"Hello World", &p, &p.string1); + test(L"%3i", L"0xFF", &p, &p.ia); + test(L"%c%3c", L"hello", &p, &p.c, &p.string1); + test(L"%lc", L"β", &p, &p.wc); + test(L"%lc %f", L"π 3.14", &p, &p.wc, &p.fa); + test(L"test: %2i%n", L"test: 0xFF", &p, &p.ia, &p.ib); + test(L"hello world%%", L"hello world%", &p); + test(L"hello %5ls %d", L"hello world 42", &p, &p.wstring1, &p.ia); + test(L"bye %ls %d", L"bye planet 84", &p, &p.wstring2, &p.ia); + test(L"h%1[ae]ll%1[^a] wor%1[^\n]%[d]", L"hello world", &p, &p.string1, &p.string2, &p.string3, &p.string4); + test(L"h%1[ae]ll%1[^a] wor%1[^\n]%[d]", L"halle worfdddddd", &p, &p.string1, &p.string2, &p.string3, &p.string4); + test(L"%[^a]%[b]", L"testbbbb", &p, &p.string1, &p.string2); + test(L"%ls %ls", L"Привет мир", &p, &p.wstring1, &p.wstring2); + test(L"%ls %ls", L"こんにちは 世界", &p, &p.wstring1, &p.wstring2); + test(L"%ls %d %ls %d", L"αβγ 123 δεζ 456", &p, &p.wstring1, &p.ia, &p.wstring2, &p.ib); + test(L"%ls %s %ls %s", L"αβγ test1 δεζ test2", &p, &p.wstring1, &p.string1, &p.wstring2, &p.string2); + test(L"%ls %ls %ls %ls", L"z ß 水 🍌", &p, &p.wstring1, &p.wstring2, &p.wstring3, &p.wstring4); + + // Scanf stolen from the url parsing in curl + wchar_t protobuf[16]; + wchar_t slashbuf[4]; + wchar_t hostbuf[100]; + wchar_t pathbuf[100]; + + // don't push NUL, make sure scanf does that + memset(protobuf, 97, 16); + memset(slashbuf, 97, 4); + memset(hostbuf, 97, 100); + memset(pathbuf, 97, 100); + + int ret = swscanf( + L"https://redox-os.org\0# extra garbage for nul test", L"%15[^\n/:]:%3[/]%[^\n/?#]%[^\n]", + &protobuf, &slashbuf, &hostbuf, &pathbuf + ); + if (ret < 4) { + *pathbuf = 0; + } + if (ret < 3) { + *hostbuf = 0; + } + if (ret < 2) { + *slashbuf = 0; + } + if (ret < 1) { + *protobuf = 0; + } + + wprintf(L"%d \"%s\" \"%s\" \"%s\" \"%s\"\n", ret, &protobuf, &slashbuf, &hostbuf, &pathbuf); + + return 0; +} diff --git a/tests/wctype/towlower.c b/tests/wctype/towlower.c new file mode 100644 index 0000000000..93767c82c9 --- /dev/null +++ b/tests/wctype/towlower.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +int main() { + wchar_t *str = L"HaLf WiDe ChAr StRiNg!\n"; + fputws(str, stdout); + for (size_t i = 0; i < wcslen(str); i++) { + putwchar(towctrans(str[i], wctrans("tolower"))); + } + return 0; +} diff --git a/tests/wctype/towupper.c b/tests/wctype/towupper.c new file mode 100644 index 0000000000..a9bb4fbfe5 --- /dev/null +++ b/tests/wctype/towupper.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +int main() { + wchar_t *str = L"HaLf WiDe ChAr StRiNg!\n"; + fputws(str, stdout); + for (size_t i = 0; i < wcslen(str); i++) { + putwchar(towctrans(str[i], wctrans("toupper"))); + } + return 0; +}