diff options
113 files changed, 3642 insertions, 703 deletions
diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index 53dd06d..3c3cd93 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -29,7 +29,7 @@ jobs: # install build tools - name: Install build tools run: | - dnf install -y autoconf automake make patch perl mingw${{ matrix.pkgarch }}-gcc-c++ mingw${{ matrix.pkgarch }}-winpthreads-static mingw${{ matrix.pkgarch }}-zlib-static + dnf install -y autoconf automake gawk make patch perl mingw${{ matrix.pkgarch }}-gcc-c++ mingw${{ matrix.pkgarch }}-winpthreads-static mingw${{ matrix.pkgarch }}-zlib-static # enable 'dnf copr' - name: Enable 'dnf copr' @@ -107,7 +107,10 @@ jobs: - run: git config --global core.autocrlf input # remove inheritable permissions since they break assumptions testsuite # makes about file modes - - run: icacls . /inheritance:r + - name: adjust permissions + run: | + icacls . /inheritance:r + icacls . /grant Administrators:F - uses: actions/checkout@v3 # install cygwin and build tools @@ -148,16 +151,35 @@ jobs: - name: Build Cygwin run: >- export PATH=/usr/bin:$(cygpath ${SYSTEMROOT})/system32 && + export DESTDIR=$(realpath $(pwd)/install) && mkdir build install && (cd winsup; ./autogen.sh) && cd build && - ../configure --prefix=$(realpath $(pwd)/../install) -v && + ../configure --prefix=/usr -v && export MAKEFLAGS=-j$(nproc) && make && - make install && + export CYGWIN=winsymlinks:sys && + make install -j1 tooldir=/usr gcc_tooldir=/usr DESTDIR=${DESTDIR} && (cd */newlib; make info man) && - (cd */newlib; make install-info install-man) - shell: C:\cygwin\bin\bash.exe --noprofile --norc -eo pipefail '{0}' + (cd */newlib; make install-info install-man tooldir=/usr gcc_tooldir=/usr DESTDIR=${DESTDIR}) + shell: bash --noprofile --norc -eo pipefail '{0}' + + # adjust install so it matches the physical arrangement of directories when + # unpacked by setup + - name: Rearrange for default mountpoints + run: | + mv -v install/usr/bin install/bin + mv -v install/usr/lib install/lib + shell: bash --noprofile --norc -o igncr -eo pipefail '{0}' + + # upload installed cygwin as an artifact, for subsequent use in + # test job(s) + - name: Make Cygwin installation artifact + uses: actions/upload-artifact@v4 + with: + name: cygwin-install-${{ matrix.pkgarch }} + path: | + install # test - name: Test Cygwin @@ -167,7 +189,7 @@ jobs: cd build && (export PATH=${{ matrix.target }}/winsup/testsuite/testinst/bin:${PATH} && cmd /c $(cygpath -wa ${{ matrix.target }}/winsup/cygserver/cygserver) &) && (cd ${{ matrix.target }}/winsup; make check AM_COLOR_TESTS=always) - shell: C:\cygwin\bin\bash.exe --noprofile --norc -eo pipefail '{0}' + shell: bash --noprofile --norc -eo pipefail '{0}' # upload test logs to facilitate investigation of problems - name: Upload test logs @@ -182,3 +204,143 @@ jobs: # workaround problems with actions/checkout post-run step using cygwin git - name: Avoid actions/checkout post-run step using Cygwin git run: bash -c 'rm /usr/bin/git.exe' + + windows-stress-test: + needs: windows-build + + strategy: + fail-fast: false + matrix: + include: + - pkgarch: x86_64 + runarch: x86_64 + runner: windows-latest + - pkgarch: x86_64 + runarch: arm64 + runner: windows-11-arm + runs-on: ${{ matrix.runner }} + name: stress tests ${{ matrix.pkgarch }} on ${{ matrix.runarch }} Windows + + steps: + - run: git config --global core.autocrlf input + - uses: actions/checkout@v3 + + # install cygwin + - name: Install Cygwin + id: cygwin-install + uses: cygwin/cygwin-install-action@master + with: + platform: ${{ matrix.pkgarch }} + packages: | + procps-ng + stress-ng + + # fetch the just-built cygwin installation artifact + - name: Unpack just-built Cygwin artifact + uses: actions/download-artifact@v4 + with: + name: cygwin-install-${{ matrix.pkgarch }} + # use the install-dir of cygwin-install-action above, so we unpack the + # artifact over it + path: ${{ steps.cygwin-install.outputs.root }} + + # This isn't quite right, as it just overwrites existing files, it doesn't + # remove anything which is no longer provided. Ideally, we'd make a cygwin + # package of the just-built cygwin version and install it here, but tools + # don't exist (yet) to let us do that... + + # run stress-test + - name: Run stress tests + run: | + export PATH=/usr/bin:$(cygpath ${SYSTEMROOT})/system32 + uname -a + export LOGDIR=$(cygpath -a logs) + winsup/testsuite/stress/cygstress CI + shell: bash --noprofile --norc -o igncr -eo pipefail '{0}' + continue-on-error: ${{ matrix.runarch == 'arm64' }} + + # upload logs artifact + - name: Capture logs artifact + uses: actions/upload-artifact@v4 + with: + name: stress-logs-${{ matrix.pkgarch }}-on-${{ matrix.runarch }} + path: | + logs + if: ${{ !cancelled() }} + + windows-stc-regression-test: + needs: windows-build + + strategy: + fail-fast: false + matrix: + include: + - pkgarch: x86_64 + runarch: x86_64 + runner: windows-latest + - pkgarch: x86_64 + runarch: arm64 + runner: windows-11-arm + runs-on: ${{ matrix.runner }} + name: stc tests ${{ matrix.pkgarch }} on ${{ matrix.runarch }} Windows + + steps: + # install cygwin + - name: Install Cygwin + id: cygwin-install + uses: cygwin/cygwin-install-action@master + with: + platform: ${{ matrix.pkgarch }} + packages: | + gcc-core + git + meson + ninja + + # The download-artifact action currently seems to fail with EPERM when it + # tries to unpack over a symlink. Remove the only such instance. + - name: Workaround download-artifact issue + run: | + cd /d ${{ steps.cygwin-install.outputs.root }}\lib + bash -c 'rm libg.a' + shell: cmd + + # fetch the just-built cygwin installation artifact + - name: Unpack just-built Cygwin artifact + uses: actions/download-artifact@v4 + with: + name: cygwin-install-${{ matrix.pkgarch }} + # use the install-dir of cygwin-install-action above, so we unpack the + # artifact over it + path: ${{ steps.cygwin-install.outputs.root }} + + # This isn't quite right, as it just overwrites existing files, it doesn't + # remove anything which is no longer provided. Ideally, we'd make a cygwin + # package of the just-built cygwin version and install it here, but tools + # don't exist (yet) to let us do that... + + # fetch and build stc test + - name: Fetch and build stc tests + run: | + export PATH=/usr/bin:$(cygpath ${SYSTEMROOT})/system32 + uname -a + git clone https://cygwin.com/git/cygwin-apps/stc.git --depth 1 + meson setup _build stc + ninja -C _build + shell: bash --noprofile --norc -o igncr -eo pipefail '{0}' + + # run stc test + - name: Run stc tests + run: | + export PATH=/usr/bin:$(cygpath ${SYSTEMROOT})/system32 + meson test -C _build + shell: bash --noprofile --norc -o igncr -eo pipefail '{0}' + + # upload logs artifact + - name: Capture logs artifact + uses: actions/upload-artifact@v4 + with: + name: stc-logs-${{ matrix.pkgarch }}-on-${{ matrix.runarch }} + path: | + _build/meson-logs + if: ${{ !cancelled() }} diff --git a/libgloss/doc/porting.texi b/libgloss/doc/porting.texi index 1f68757..dd5e29e 100644 --- a/libgloss/doc/porting.texi +++ b/libgloss/doc/porting.texi @@ -50,9 +50,7 @@ into another language, under the above conditions for modified versions. @end titlepage @direntry -START-INFO-DIR-ENTRY * Embed with GNU: (porting-). Embed with GNU -END-INFO-DIR-ENTRY @end direntry @ifnottex diff --git a/libgloss/riscv/internal_syscall.h b/libgloss/riscv/internal_syscall.h index 080c8c8..254880b 100644 --- a/libgloss/riscv/internal_syscall.h +++ b/libgloss/riscv/internal_syscall.h @@ -24,7 +24,7 @@ __syscall_error(long a0) static inline long __internal_syscall(long n, int argc, long _a0, long _a1, long _a2, long _a3, long _a4, long _a5) { -#ifdef __riscv_32e +#ifdef __riscv_abi_rve register long syscall_id asm("t0") = n; #else register long syscall_id asm("a7") = n; diff --git a/libgloss/rs6000/mbx-print.c b/libgloss/rs6000/mbx-print.c index 64472ee..591ca2d 100644 --- a/libgloss/rs6000/mbx-print.c +++ b/libgloss/rs6000/mbx-print.c @@ -13,6 +13,8 @@ * they apply. */ +extern int write(int fd, const void *buf, unsigned int count); + /* * print -- do a raw print of a string */ diff --git a/libgloss/rs6000/mvme-print.c b/libgloss/rs6000/mvme-print.c index 8d19542..d8f4c50 100644 --- a/libgloss/rs6000/mvme-print.c +++ b/libgloss/rs6000/mvme-print.c @@ -12,6 +12,9 @@ * the new terms are clearly indicated on the first page of each file where * they apply. */ +extern void __pcrlf(void); +extern void __outln(char *, char *); +extern void __outstr(char *, char *); /* * write -- write some bytes to the output device. diff --git a/libgloss/rs6000/mvme-read.c b/libgloss/rs6000/mvme-read.c index 1c823b4..9d1e7d3 100644 --- a/libgloss/rs6000/mvme-read.c +++ b/libgloss/rs6000/mvme-read.c @@ -14,7 +14,7 @@ */ extern int inbyte (); -extern char * __inln (); +extern char * __inln (char *); /* * read -- read bytes from the serial port. Ignore fd, since diff --git a/libgloss/rs6000/sim-abort.c b/libgloss/rs6000/sim-abort.c index e1b59cf..931f886 100644 --- a/libgloss/rs6000/sim-abort.c +++ b/libgloss/rs6000/sim-abort.c @@ -14,6 +14,9 @@ * they apply. */ +extern int write(int fd, const void *buf, unsigned int count); +extern void exit(int status); + void abort(void) { write (2, "Abort called.\n", sizeof("Abort called.\n")-1); diff --git a/libgloss/rs6000/sim-inbyte.c b/libgloss/rs6000/sim-inbyte.c index 787b689..fe1ac2a 100644 --- a/libgloss/rs6000/sim-inbyte.c +++ b/libgloss/rs6000/sim-inbyte.c @@ -13,6 +13,7 @@ * the new terms are clearly indicated on the first page of each file where * they apply. */ +extern int read(int fd, void *buf, unsigned int count); int inbyte () diff --git a/libgloss/rs6000/sim-print.c b/libgloss/rs6000/sim-print.c index c0b9adc..49596af 100644 --- a/libgloss/rs6000/sim-print.c +++ b/libgloss/rs6000/sim-print.c @@ -13,6 +13,8 @@ * they apply. */ +extern int write(int fd, const void *buf, unsigned int count); + /* * print -- do a raw print of a string */ diff --git a/libgloss/rs6000/sim-sbrk.c b/libgloss/rs6000/sim-sbrk.c index 5c8bd65..0bbfe33 100644 --- a/libgloss/rs6000/sim-sbrk.c +++ b/libgloss/rs6000/sim-sbrk.c @@ -13,6 +13,7 @@ * the new terms are clearly indicated on the first page of each file where * they apply. */ +extern int brk(void *addr); extern char _end[]; static char *curbrk = _end; diff --git a/newlib/MAINTAINERS b/newlib/MAINTAINERS index 4dfef27..499791e 100644 --- a/newlib/MAINTAINERS +++ b/newlib/MAINTAINERS @@ -41,7 +41,6 @@ mn10300 Nick Clifton nickc@redhat.com moxie Anthony Green green@moxielogic.com risc-v Kito Cheng kito.cheng@gmail.com aarch64 Richard Earnshaw richard.earnshaw@arm.com - Marcus Shawcroft marcus.shawcroft@arm.com msp430 Nick Clifton nickc@redhat.com pru Dimitar Dimitrov dimitar@dinux.eu diff --git a/newlib/Makefile.in b/newlib/Makefile.in index 52b5d80..5a1987b 100644 --- a/newlib/Makefile.in +++ b/newlib/Makefile.in @@ -856,7 +856,7 @@ check_PROGRAMS = @HAVE_LIBC_MACHINE_PRU_TRUE@am__append_113 = libc/machine/pru/setjmp.s @HAVE_LIBC_MACHINE_RISCV_TRUE@am__append_114 = \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/memmove.S libc/machine/riscv/memmove-stub.c libc/machine/riscv/memset.S libc/machine/riscv/memcpy-asm.S libc/machine/riscv/memcpy.c libc/machine/riscv/strlen.c \ -@HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/strcpy.c libc/machine/riscv/stpcpy.c libc/machine/riscv/strcmp.S libc/machine/riscv/setjmp.S libc/machine/riscv/ieeefp.c libc/machine/riscv/ffs.c +@HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/strcpy.c libc/machine/riscv/stpcpy.c libc/machine/riscv/strcmp.S libc/machine/riscv/memchr.c libc/machine/riscv/memrchr.c libc/machine/riscv/setjmp.S libc/machine/riscv/ieeefp.c libc/machine/riscv/ffs.c @HAVE_LIBC_MACHINE_RL78_TRUE@am__append_115 = libc/machine/rl78/setjmp.S @HAVE_LIBC_MACHINE_RX_TRUE@am__append_116 = \ @@ -2185,6 +2185,8 @@ am__objects_51 = libc/ssp/libc_a-chk_fail.$(OBJEXT) \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-strcpy.$(OBJEXT) \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-stpcpy.$(OBJEXT) \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-strcmp.$(OBJEXT) \ +@HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-memchr.$(OBJEXT) \ +@HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-memrchr.$(OBJEXT) \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-setjmp.$(OBJEXT) \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-ieeefp.$(OBJEXT) \ @HAVE_LIBC_MACHINE_RISCV_TRUE@ libc/machine/riscv/libc_a-ffs.$(OBJEXT) @@ -4002,7 +4004,6 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ -runstatedir = @runstatedir@ sbindir = @sbindir@ shared_machine_dir = @shared_machine_dir@ sharedstatedir = @sharedstatedir@ @@ -9185,6 +9186,12 @@ libc/machine/riscv/libc_a-stpcpy.$(OBJEXT): \ libc/machine/riscv/libc_a-strcmp.$(OBJEXT): \ libc/machine/riscv/$(am__dirstamp) \ libc/machine/riscv/$(DEPDIR)/$(am__dirstamp) +libc/machine/riscv/libc_a-memchr.$(OBJEXT): \ + libc/machine/riscv/$(am__dirstamp) \ + libc/machine/riscv/$(DEPDIR)/$(am__dirstamp) +libc/machine/riscv/libc_a-memrchr.$(OBJEXT): \ + libc/machine/riscv/$(am__dirstamp) \ + libc/machine/riscv/$(DEPDIR)/$(am__dirstamp) libc/machine/riscv/libc_a-setjmp.$(OBJEXT): \ libc/machine/riscv/$(am__dirstamp) \ libc/machine/riscv/$(DEPDIR)/$(am__dirstamp) @@ -13139,10 +13146,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/powerpc/$(DEPDIR)/libc_a-vfscanf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-ffs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-ieeefp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memcpy-asm.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memcpy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memmove-stub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memmove.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-memset.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-setjmp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@libc/machine/riscv/$(DEPDIR)/libc_a-stpcpy.Po@am__quote@ @@ -34819,6 +34828,34 @@ libc/machine/riscv/libc_a-stpcpy.obj: libc/machine/riscv/stpcpy.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/machine/riscv/libc_a-stpcpy.obj `if test -f 'libc/machine/riscv/stpcpy.c'; then $(CYGPATH_W) 'libc/machine/riscv/stpcpy.c'; else $(CYGPATH_W) '$(srcdir)/libc/machine/riscv/stpcpy.c'; fi` +libc/machine/riscv/libc_a-memchr.o: libc/machine/riscv/memchr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/machine/riscv/libc_a-memchr.o -MD -MP -MF libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Tpo -c -o libc/machine/riscv/libc_a-memchr.o `test -f 'libc/machine/riscv/memchr.c' || echo '$(srcdir)/'`libc/machine/riscv/memchr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Tpo libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libc/machine/riscv/memchr.c' object='libc/machine/riscv/libc_a-memchr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/machine/riscv/libc_a-memchr.o `test -f 'libc/machine/riscv/memchr.c' || echo '$(srcdir)/'`libc/machine/riscv/memchr.c + +libc/machine/riscv/libc_a-memchr.obj: libc/machine/riscv/memchr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/machine/riscv/libc_a-memchr.obj -MD -MP -MF libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Tpo -c -o libc/machine/riscv/libc_a-memchr.obj `if test -f 'libc/machine/riscv/memchr.c'; then $(CYGPATH_W) 'libc/machine/riscv/memchr.c'; else $(CYGPATH_W) '$(srcdir)/libc/machine/riscv/memchr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Tpo libc/machine/riscv/$(DEPDIR)/libc_a-memchr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libc/machine/riscv/memchr.c' object='libc/machine/riscv/libc_a-memchr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/machine/riscv/libc_a-memchr.obj `if test -f 'libc/machine/riscv/memchr.c'; then $(CYGPATH_W) 'libc/machine/riscv/memchr.c'; else $(CYGPATH_W) '$(srcdir)/libc/machine/riscv/memchr.c'; fi` + +libc/machine/riscv/libc_a-memrchr.o: libc/machine/riscv/memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/machine/riscv/libc_a-memrchr.o -MD -MP -MF libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Tpo -c -o libc/machine/riscv/libc_a-memrchr.o `test -f 'libc/machine/riscv/memrchr.c' || echo '$(srcdir)/'`libc/machine/riscv/memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Tpo libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libc/machine/riscv/memrchr.c' object='libc/machine/riscv/libc_a-memrchr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/machine/riscv/libc_a-memrchr.o `test -f 'libc/machine/riscv/memrchr.c' || echo '$(srcdir)/'`libc/machine/riscv/memrchr.c + +libc/machine/riscv/libc_a-memrchr.obj: libc/machine/riscv/memrchr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/machine/riscv/libc_a-memrchr.obj -MD -MP -MF libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Tpo -c -o libc/machine/riscv/libc_a-memrchr.obj `if test -f 'libc/machine/riscv/memrchr.c'; then $(CYGPATH_W) 'libc/machine/riscv/memrchr.c'; else $(CYGPATH_W) '$(srcdir)/libc/machine/riscv/memrchr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Tpo libc/machine/riscv/$(DEPDIR)/libc_a-memrchr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libc/machine/riscv/memrchr.c' object='libc/machine/riscv/libc_a-memrchr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -c -o libc/machine/riscv/libc_a-memrchr.obj `if test -f 'libc/machine/riscv/memrchr.c'; then $(CYGPATH_W) 'libc/machine/riscv/memrchr.c'; else $(CYGPATH_W) '$(srcdir)/libc/machine/riscv/memrchr.c'; fi` + libc/machine/riscv/libc_a-ieeefp.o: libc/machine/riscv/ieeefp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libc_a_CPPFLAGS) $(CPPFLAGS) $(libc_a_CFLAGS) $(CFLAGS) -MT libc/machine/riscv/libc_a-ieeefp.o -MD -MP -MF libc/machine/riscv/$(DEPDIR)/libc_a-ieeefp.Tpo -c -o libc/machine/riscv/libc_a-ieeefp.o `test -f 'libc/machine/riscv/ieeefp.c' || echo '$(srcdir)/'`libc/machine/riscv/ieeefp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libc/machine/riscv/$(DEPDIR)/libc_a-ieeefp.Tpo libc/machine/riscv/$(DEPDIR)/libc_a-ieeefp.Po diff --git a/newlib/doc/makedocbook.py b/newlib/doc/makedocbook.py index 9c5615f..b5ef596 100755 --- a/newlib/doc/makedocbook.py +++ b/newlib/doc/makedocbook.py @@ -198,7 +198,7 @@ def function(c, l): descr = line_markup_convert(', '.join(descrlist)) # fpclassify includes an 'and' we need to discard - namelist = map(lambda v: re.sub(r'^and ', r'', v.strip(), 1), namelist) + namelist = map(lambda v: re.sub(r'^and ', r'', v.strip(), count=1), namelist) # strip off << >> surrounding name namelist = map(lambda v: v.strip().lstrip('<').rstrip('>'), namelist) # instantiate list to make it subscriptable @@ -563,7 +563,7 @@ def t_TABLEEND(t): def t_ITEM(t): r'o\s.*\n' - t.value = re.sub(r'o\s', r'', lexer.lexmatch.group(0), 1) + t.value = re.sub(r'o\s', r'', lexer.lexmatch.group(0), count=1) t.value = line_markup_convert(t.value) return t diff --git a/newlib/libc/ctype/ctype_.c b/newlib/libc/ctype/ctype_.c index 32ce4f3..869945f 100644 --- a/newlib/libc/ctype/ctype_.c +++ b/newlib/libc/ctype/ctype_.c @@ -95,21 +95,12 @@ char _ctype_b[128 + 256] = { /* For backward compatibility */ char __EXPORT *__ctype_ptr__ = DEFAULT_CTYPE_PTR; -# ifdef __x86_64__ __asm__ (" \n\ .data \n\ .globl _ctype_ \n\ .set _ctype_,_ctype_b+127 \n\ .text \n\ "); -# else -__asm__ (" \n\ - .data \n\ - .globl __ctype_ \n\ - .set __ctype_,__ctype_b+127 \n\ - .text \n\ -"); -# endif # else /* !__CYGWIN__ */ const char _ctype_[1 + 256] = { diff --git a/newlib/libc/include/pthread.h b/newlib/libc/include/pthread.h index c99ad39..05ff315 100644 --- a/newlib/libc/include/pthread.h +++ b/newlib/libc/include/pthread.h @@ -87,11 +87,11 @@ int pthread_mutex_timedlock (pthread_mutex_t *__mutex, #endif /* _POSIX_TIMEOUTS */ -#if __GNU_VISIBLE +#if (__GNU_VISIBLE || __POSIX_VISIBLE >= 202405) /* The Issue 8 standard adds pthread_mutex_clocklock() */ int pthread_mutex_clocklock(pthread_mutex_t *__restrict, clockid_t, const struct timespec *__restrict); -#endif /* __GNU_VISIBLE */ +#endif /* __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 */ /* Condition Variable Initialization Attributes, P1003.1c/Draft 10, p. 96 */ @@ -133,12 +133,12 @@ int pthread_cond_timedwait (pthread_cond_t *__cond, pthread_mutex_t *__mutex, const struct timespec *__abstime); -#if __GNU_VISIBLE +#if (__GNU_VISIBLE || __POSIX_VISIBLE >= 202405) /* The Issue 8 standard adds pthread_cond_clockwait() */ int pthread_cond_clockwait(pthread_cond_t *__restrict, pthread_mutex_t *__restrict, clockid_t, const struct timespec *__restrict); -#endif /* __GNU_VISIBLE */ +#endif /* __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 */ #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) @@ -436,14 +436,14 @@ int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock); int pthread_rwlock_timedwrlock (pthread_rwlock_t *__rwlock, const struct timespec *__abstime); -#if __GNU_VISIBLE +#if (__GNU_VISIBLE || __POSIX_VISIBLE >= 202405) /* The Issue 8 standard adds pthread_rwlock_clockrdlock() * and pthread_rwlock_clockwrlock()*/ int pthread_rwlock_clockrdlock(pthread_rwlock_t *__restrict, clockid_t, const struct timespec *__restrict); int pthread_rwlock_clockwrlock(pthread_rwlock_t *__restrict, clockid_t, const struct timespec *__restrict); -#endif /* __GNU_VISIBLE */ +#endif /* __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 */ #endif /* defined(_POSIX_READER_WRITER_LOCKS) */ diff --git a/newlib/libc/include/search.h b/newlib/libc/include/search.h index ed321b0..70a1a20 100644 --- a/newlib/libc/include/search.h +++ b/newlib/libc/include/search.h @@ -36,6 +36,8 @@ typedef struct node { } node_t; #endif +typedef void posix_tnode; + struct hsearch_data { struct internal_head *htable; @@ -54,11 +56,11 @@ ENTRY *hsearch(ENTRY, ACTION); int hcreate_r(size_t, struct hsearch_data *); void hdestroy_r(struct hsearch_data *); int hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *); -void *tdelete(const void *__restrict, void **__restrict, __compar_fn_t); +void *tdelete(const void *__restrict, posix_tnode **__restrict, __compar_fn_t); void tdestroy (void *, void (*)(void *)); -void *tfind(const void *, void **, __compar_fn_t); -void *tsearch(const void *, void **, __compar_fn_t); -void twalk(const void *, void (*)(const void *, VISIT, int)); +posix_tnode *tfind(const void *, posix_tnode *const *, __compar_fn_t); +posix_tnode *tsearch(const void *, posix_tnode **, __compar_fn_t); +void twalk(const posix_tnode *, void (*)(const posix_tnode *, VISIT, int)); __END_DECLS #endif /* !_SEARCH_H_ */ diff --git a/newlib/libc/include/stdlib.h b/newlib/libc/include/stdlib.h index 0690a03..55b20fa 100644 --- a/newlib/libc/include/stdlib.h +++ b/newlib/libc/include/stdlib.h @@ -333,10 +333,13 @@ extern long double strtold (const char *__restrict, char **__restrict); #if __ISO_C_VISIBLE >= 2011 void * aligned_alloc(size_t, size_t) __malloc_like __alloc_align(1) __alloc_size(2) __result_use_check; +#endif /* __ISO_C_VISIBLE >= 2011 */ + +#if (__ISO_C_VISIBLE >= 2011 || __POSIX_VISIBLE >= 202405) int at_quick_exit(void (*)(void)); _Noreturn void quick_exit(int); -#endif /* __ISO_C_VISIBLE >= 2011 */ +#endif /* __ISO_C_VISIBLE >= 2011 || __POSIX_VISIBLE >= 202405 */ _END_STD_C diff --git a/newlib/libc/include/sys/reent.h b/newlib/libc/include/sys/reent.h index 5ce5387..eafac96 100644 --- a/newlib/libc/include/sys/reent.h +++ b/newlib/libc/include/sys/reent.h @@ -709,7 +709,8 @@ struct _reent {0, {0}}, \ {0, {0}}, \ {0, {0}}, \ - {0, {0}} \ + {0, {0}}, \ + 0 \ } \ }, \ _REENT_INIT_RESERVED_6_7 \ diff --git a/newlib/libc/include/sys/unistd.h b/newlib/libc/include/sys/unistd.h index 771a4bd..4cf9f06 100644 --- a/newlib/libc/include/sys/unistd.h +++ b/newlib/libc/include/sys/unistd.h @@ -215,7 +215,7 @@ int setpgrp (void); #if defined(__CYGWIN__) && __BSD_VISIBLE /* Stub for Linux libbsd compatibility. */ #define initsetproctitle(c, a, e) setproctitle_init((c), (a), (e)) -static inline void setproctitle_init (int _c, char *_a[], char *_e[]) {} +static __inline void setproctitle_init (int _c, char *_a[], char *_e[]) {} void setproctitle (const char *, ...) _ATTRIBUTE ((__format__ (__printf__, 1, 2))); diff --git a/newlib/libc/include/time.h b/newlib/libc/include/time.h index ab34913..a2df4f7 100644 --- a/newlib/libc/include/time.h +++ b/newlib/libc/include/time.h @@ -57,6 +57,11 @@ clock_t clock (void); double difftime (time_t _time2, time_t _time1); time_t mktime (struct tm *_timeptr); time_t time (time_t *_timer); +#if (__ISO_C_VISIBLE >= 2011 || __POSIX_VISIBLE >= 202405) +#define TIME_UTC 1 + +int timespec_get(struct timespec *ts, int base); +#endif #ifndef _REENT_ONLY char *asctime (const struct tm *_tblock); char *ctime (const time_t *_time); diff --git a/newlib/libc/machine/riscv/Makefile.inc b/newlib/libc/machine/riscv/Makefile.inc index 4d6c046..85bed91 100644 --- a/newlib/libc/machine/riscv/Makefile.inc +++ b/newlib/libc/machine/riscv/Makefile.inc @@ -1,3 +1,3 @@ libc_a_SOURCES += \ %D%/memmove.S %D%/memmove-stub.c %D%/memset.S %D%/memcpy-asm.S %D%/memcpy.c %D%/strlen.c \ - %D%/strcpy.c %D%/stpcpy.c %D%/strcmp.S %D%/setjmp.S %D%/ieeefp.c %D%/ffs.c + %D%/strcpy.c %D%/stpcpy.c %D%/strcmp.S %D%/memchr.c %D%/memrchr.c %D%/setjmp.S %D%/ieeefp.c %D%/ffs.c diff --git a/newlib/libc/machine/riscv/memchr.c b/newlib/libc/machine/riscv/memchr.c new file mode 100644 index 0000000..62a7d19 --- /dev/null +++ b/newlib/libc/machine/riscv/memchr.c @@ -0,0 +1,152 @@ +/* +FUNCTION + <<memchr>>---find character in memory + +INDEX + memchr + +SYNOPSIS + #include <string.h> + void *memchr(const void *<[src]>, int <[c]>, size_t <[length]>); + +DESCRIPTION + This function searches memory starting at <<*<[src]>>> for the + character <[c]>. The search only ends with the first + occurrence of <[c]>, or after <[length]> characters; in + particular, <<NUL>> does not terminate the search. + +RETURNS + If the character <[c]> is found within <[length]> characters + of <<*<[src]>>>, a pointer to the character is returned. If + <[c]> is not found, then <<NULL>> is returned. + +PORTABILITY +<<memchr>> is ANSI C. + +<<memchr>> requires no supporting OS subroutines. + +QUICKREF + memchr ansi pure +*/ + +#include <sys/asm.h> +#include <stddef.h> +#include "rv_string.h" + +// Move size +#if __riscv_zilsd +#define MV_SZ 8 +#else +#define MV_SZ SZREG +#endif + + +void * +memchr (const void *src_void, + int c, + size_t length) +{ + const unsigned char *src = (const unsigned char *) src_void; + unsigned char d = c; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + size_t align = (uintptr_t) src & (MV_SZ - 1); + + if (align) + { + align = MV_SZ - align; + + if (length < align) align = length; + + switch (align) + { +#if MV_SZ == 8 + case 7: + if (*src++ == d) return (void *) (src - 1); + case 6: + if (*src++ == d) return (void *) (src - 1); + case 5: + if (*src++ == d) return (void *) (src - 1); + case 4: + if (*src++ == d) return (void *) (src - 1); +#endif /* MV_SZ == 8 */ + case 3: + if (*src++ == d) return (void *) (src - 1); + case 2: + if (*src++ == d) return (void *) (src - 1); + case 1: + if (*src++ == d) return (void *) (src - 1); + } + + length -= align; + } + + const unsigned char *end_addr = src + (length & ~(MV_SZ - 1)); + + if (src < end_addr) + { + uintxlen_t mask = __libc_splat_byte(d); + + do + { + uintlslen_t val = *(uintlslen_t*) src; + uintxlen_t word1 = val ^ mask; + + if (__libc_detect_null(word1)) + { +#if __riscv_zbb + word1 = ~__LIBC_RISCV_ZBB_ORC_B(word1); + word1 = __LIBC_RISCV_ZBB_CNT_Z(word1); + + return (void *) (src + (word1 >> 3)); +#else /* not __riscv_zbb */ + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); +#if __riscv_xlen == 64 + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); +#endif /* __riscv_xlen == 64 */ + return (void *) src; +#endif /* __riscv_zbb */ + } +#if __riscv_zilsd + uintxlen_t word2 = (val >> 32); + word2 ^= mask; + + if (__libc_detect_null(word2)) + { + src += MV_SZ / 2; +#if __riscv_zbb + word2 = ~__LIBC_RISCV_ZBB_ORC_B(word2); + word2 = __LIBC_RISCV_ZBB_CNT_Z(word2); + + return (void *) (src + (word2 >> 3)); +#else /* not __riscv_zbb */ + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); + if (*src++ == d) return (void *) (src - 1); + return (void *) src; +#endif /* __riscv_zbb */ + } +#endif /* __riscv_zilsd */ + + src += MV_SZ; + } while (src < end_addr); + + length &= MV_SZ - 1; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (length--) + { + if (*src == d) + return (void *) src; + src++; + } + + return NULL; +} diff --git a/newlib/libc/machine/riscv/memcpy-asm.S b/newlib/libc/machine/riscv/memcpy-asm.S index 5571e47..2771285 100644 --- a/newlib/libc/machine/riscv/memcpy-asm.S +++ b/newlib/libc/machine/riscv/memcpy-asm.S @@ -14,15 +14,15 @@ .global memcpy .type memcpy, @function memcpy: - mv t1, a0 + mv a3, a0 beqz a2, 2f 1: - lb t2, 0(a1) - sb t2, 0(t1) - add a2, a2, -1 - add t1, t1, 1 - add a1, a1, 1 + lbu a4, 0(a1) + sb a4, 0(a3) + addi a2, a2, -1 + addi a3, a3, 1 + addi a1, a1, 1 bnez a2, 1b 2: diff --git a/newlib/libc/machine/riscv/memcpy.c b/newlib/libc/machine/riscv/memcpy.c index e1a34a8..5d6b2f3 100644 --- a/newlib/libc/machine/riscv/memcpy.c +++ b/newlib/libc/machine/riscv/memcpy.c @@ -1,4 +1,5 @@ /* Copyright (c) 2017 SiFive Inc. All rights reserved. + Copyright (c) 2025 Mahmoud Abumandour <ma.mandourr@gmail.com> This copyrighted material is made available to anyone wishing to use, modify, copy, or redistribute it subject to the terms and conditions @@ -10,83 +11,137 @@ */ #if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) -//memcpy defined in memcpy-asm.S +// memcpy defined in memcpy-asm.S #else -#include <string.h> -#include <stdint.h> #include "../../string/local.h" +#include "xlenint.h" +#include <string.h> +#include <sys/asm.h> #define unlikely(X) __builtin_expect (!!(X), 0) -void * -__inhibit_loop_to_libcall -memcpy(void *__restrict aa, const void *__restrict bb, size_t n) +static inline void +__libc_memcpy_bytewise (unsigned char *dst, const unsigned char *src, + const size_t sz) { - #define BODY(a, b, t) { \ - t tt = *b; \ - a++, b++; \ - *(a - 1) = tt; \ - } + const unsigned char *end = dst + sz; + while (dst < end) + *dst++ = *src++; +} - char *a = (char *)aa; - const char *b = (const char *)bb; - char *end = a + n; - uintptr_t msk = sizeof (long) - 1; -#if __riscv_misaligned_slow || __riscv_misaligned_fast - if (n < sizeof (long)) -#else - if (unlikely ((((uintptr_t)a & msk) != ((uintptr_t)b & msk)) - || n < sizeof (long))) +#if defined(__riscv_misaligned_slow) || defined(__riscv_misaligned_avoid) +static uintxlen_t +__libc_load_xlen (const void *src) +{ + const unsigned char *p = (const unsigned char *)src; + uintxlen_t ret = 0; + unsigned char b0 = *p++; + unsigned char b1 = *p++; + unsigned char b2 = *p++; + unsigned char b3 = *p++; + ret = (uintxlen_t)b0 | ((uintxlen_t)b1 << 8) | ((uintxlen_t)b2 << 16) + | ((uintxlen_t)b3 << 24); +#if __riscv_xlen == 64 + unsigned char b4 = *p++; + unsigned char b5 = *p++; + unsigned char b6 = *p++; + unsigned char b7 = *p++; + ret |= ((uintxlen_t)b4 << 32) | ((uintxlen_t)b5 << 40) + | ((uintxlen_t)b6 << 48) | ((uintxlen_t)b7 << 56); +#endif + return ret; +} #endif + +void * +__inhibit_loop_to_libcall +memcpy (void *__restrict aa, const void *__restrict bb, size_t n) +{ + unsigned char *a = (unsigned char *)aa; + const unsigned char *b = (const unsigned char *)bb; + unsigned char *end = a + n; + uintptr_t msk = SZREG - 1; + if (n < SZREG) { -small: if (__builtin_expect (a < end, 1)) - while (a < end) - BODY (a, b, char); + __libc_memcpy_bytewise (a, b, n); return aa; } +/* + * If misaligned access is slow or prohibited, and the alignments of the source + * and destination are different, we align the destination to do XLEN stores. + * This uses only one aligned store for every four (or eight for XLEN == 64) + * bytes of data. + */ +#if defined(__riscv_misaligned_slow) || defined(__riscv_misaligned_avoid) + if (unlikely ((((uintptr_t)a & msk) != ((uintptr_t)b & msk)))) + { + size_t dst_pad = (uintptr_t)a & msk; + dst_pad = (SZREG - dst_pad) & msk; + __libc_memcpy_bytewise (a, b, dst_pad); + a += dst_pad; + b += dst_pad; + + uintxlen_t *la = (uintxlen_t *)a; + const unsigned char *cb = (const unsigned char *)b; + uintxlen_t *lend = (uintxlen_t *)((uintptr_t)end & ~msk); + + while (la < lend) + { + *la++ = __libc_load_xlen (cb); + cb += SZREG; + } + a = (unsigned char *)la; + b = (const unsigned char *)cb; + if (unlikely (a < end)) + __libc_memcpy_bytewise (a, b, end - a); + return aa; + } +#endif + if (unlikely (((uintptr_t)a & msk) != 0)) - while ((uintptr_t)a & msk) - BODY (a, b, char); + { + size_t pad = SZREG - ((uintptr_t)a & msk); + __libc_memcpy_bytewise (a, b, pad); + a += pad; + b += pad; + } - long *la = (long *)a; - const long *lb = (const long *)b; - long *lend = (long *)((uintptr_t)end & ~msk); + uintxlen_t *la = (uintxlen_t *)a; + const uintxlen_t *lb = (const uintxlen_t *)b; + uintxlen_t *lend = (uintxlen_t *)((uintptr_t)end & ~msk); if (unlikely (lend - la > 8)) { while (lend - la > 8) - { - long b0 = *lb++; - long b1 = *lb++; - long b2 = *lb++; - long b3 = *lb++; - long b4 = *lb++; - long b5 = *lb++; - long b6 = *lb++; - long b7 = *lb++; - long b8 = *lb++; - *la++ = b0; - *la++ = b1; - *la++ = b2; - *la++ = b3; - *la++ = b4; - *la++ = b5; - *la++ = b6; - *la++ = b7; - *la++ = b8; - } + { + uintxlen_t b0 = *lb++; + uintxlen_t b1 = *lb++; + uintxlen_t b2 = *lb++; + uintxlen_t b3 = *lb++; + uintxlen_t b4 = *lb++; + uintxlen_t b5 = *lb++; + uintxlen_t b6 = *lb++; + uintxlen_t b7 = *lb++; + uintxlen_t b8 = *lb++; + *la++ = b0; + *la++ = b1; + *la++ = b2; + *la++ = b3; + *la++ = b4; + *la++ = b5; + *la++ = b6; + *la++ = b7; + *la++ = b8; + } } - while (la < lend) - BODY (la, lb, long); - - a = (char *)la; - b = (const char *)lb; + a = (unsigned char *)la; + b = (const unsigned char *)lb; if (unlikely (a < end)) - goto small; + __libc_memcpy_bytewise (a, b, end - a); return aa; } #endif diff --git a/newlib/libc/machine/riscv/memmove.S b/newlib/libc/machine/riscv/memmove.S index 66d9cd4..061472c 100644 --- a/newlib/libc/machine/riscv/memmove.S +++ b/newlib/libc/machine/riscv/memmove.S @@ -14,26 +14,26 @@ .global memmove .type memmove, @function memmove: - beqz a2, 2f + beqz a2, .Ldone /* in case there are 0 bytes to be copied, return immediately */ - mv t1, a0 + mv a4, a0 /* copy the destination address over to a4, since memmove should return that address in a0 at the end */ li a3, 1 - bgtu a1, a0, 1f + bgtu a1, a0, .Lcopy /* in case of source address > destination address, copy from start to end of the specified memory area */ - li a3, -1 - addi a4, a2 , -1 - add t1, t1, a4 - add a1, a1, a4 + li a3, -1 /* otherwhise, start copying from the end of the specified memory area in order to prevent data loss in case of overlapping memory areas.*/ + add a4, a4, a2 /* add the number of bytes to be copied to both addresses. this gives us the address one byte past the end of the memory area we want to copy, */ + add a1, a1, a2 /* therefore we need to subtract 1 from both addresses in the next step before starting the copying process. */ -1: - lb t2, 0(a1) - sb t2, 0(t1) - add a2, a2, -1 - add t1, t1, a3 +.Lincrement: + add a4, a4, a3 /* in case of source address < destination address, increment both addresses by -1 before copying any data to obtain the correct start addresses */ add a1, a1, a3 - bnez a2, 1b +.Lcopy: + lbu a5, 0(a1) + addi a2, a2, -1 /* copy bytes as long as a2 (= the number of bytes to be copied) > 0. the increment is done here to relax the RAW dependency between load and store */ + sb a5, 0(a4) + bnez a2, .Lincrement -2: +.Ldone: ret .size memmove, .-memmove diff --git a/newlib/libc/machine/riscv/memrchr.c b/newlib/libc/machine/riscv/memrchr.c new file mode 100644 index 0000000..47e1023 --- /dev/null +++ b/newlib/libc/machine/riscv/memrchr.c @@ -0,0 +1,172 @@ +/* +FUNCTION + <<memrchr>>---reverse search for character in memory + +INDEX + memrchr + +SYNOPSIS + #include <string.h> + void *memrchr(const void *<[src]>, int <[c]>, size_t <[length]>); + +DESCRIPTION + This function searches memory starting at <[length]> bytes + beyond <<*<[src]>>> backwards for the character <[c]>. + The search only ends with the first occurrence of <[c]>; in + particular, <<NUL>> does not terminate the search. + +RETURNS + If the character <[c]> is found within <[length]> characters + of <<*<[src]>>>, a pointer to the character is returned. If + <[c]> is not found, then <<NULL>> is returned. + +PORTABILITY +<<memrchr>> is a GNU extension. + +<<memrchr>> requires no supporting OS subroutines. + +QUICKREF + memrchr +*/ + +#include <sys/asm.h> +#include <stddef.h> +#include "rv_string.h" + +// Move size +#if __riscv_zilsd +#define MV_SZ 8 + +// Offset is only 4 bytes for Zilsd/Zclsd since each register is 32 bits +#define OFFSET 4 +#else +#define MV_SZ SZREG +#define OFFSET SZREG +#endif + + +void * +memrchr (const void *src_void, + int c, + size_t length) +{ + const unsigned char *src = (const unsigned char *) src_void; + unsigned char d = c; + + if (length) src += length - 1; + +#if !defined(PREFER_SIZE_OVER_SPEED) && !defined(__OPTIMIZE_SIZE__) + + /* + We add one to the address because even if an address is already aligned, + when loading words the bytes preceding this address are read, so check + the single byte. + + If the address has all the least significant bits set equaling MV_SZ - 1, + and has a length of at least MV_SZ, we can read a word starting from + src & ~(MV_SZ - 1) because no alignment is actually required + */ + size_t align = (uintptr_t) (src + 1) & (MV_SZ - 1); + + if (align) + { + if (length < align) align = length; + + switch (align) + { +#if MV_SZ == 8 + case 7: + if (*src-- == d) return (void *) (src + 1); + case 6: + if (*src-- == d) return (void *) (src + 1); + case 5: + if (*src-- == d) return (void *) (src + 1); + case 4: + if (*src-- == d) return (void *) (src + 1); +#endif /* MV_SZ == 8 */ + case 3: + if (*src-- == d) return (void *) (src + 1); + case 2: + if (*src-- == d) return (void *) (src + 1); + case 1: + if (*src-- == d) return (void *) (src + 1); + } + + length -= align; + } + + const unsigned char *end_addr = src - (length & ~(MV_SZ - 1)); + + if (src > end_addr) + { + src -= MV_SZ - 1; + + uintxlen_t mask = __libc_splat_byte(d); + + do + { + uintlslen_t val = *(uintlslen_t*) src; + +#if __riscv_zilsd + uintxlen_t word2 = val >> 32; + word2 ^= mask; + + if (__libc_detect_null(word2)) + { +#if __riscv_zbb + src += OFFSET; + word2 = ~__LIBC_RISCV_ZBB_ORC_B(word2); + word2 = __LIBC_RISCV_ZBB_CNT_Z_REV(word2); + + return (void *) (src + OFFSET - 1 - (word2 >> 3)); +#else /* not __riscv_zbb */ + src += MV_SZ - 1; + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); + return (void *) src; +#endif /* __riscv_zbb */ + } +#endif /* __riscv_zilsd */ + uintxlen_t word1 = val ^ mask; + + if (__libc_detect_null(word1)) + { +#if __riscv_zbb + word1 = ~__LIBC_RISCV_ZBB_ORC_B(word1); + word1 = __LIBC_RISCV_ZBB_CNT_Z_REV(word1); + + return (void *) (src + OFFSET - 1 - (word1 >> 3)); +#else /* not __riscv_zbb */ + src += OFFSET - 1; + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); +#if __riscv_xlen == 64 + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); + if (*src-- == d) return (void *) (src + 1); +#endif /* __riscv_xlen == 64 */ + return (void *) src; +#endif /* __riscv_zbb */ + } + + src -= MV_SZ; + } while (src > end_addr); + + length &= MV_SZ - 1; + src = end_addr; + } + +#endif /* not PREFER_SIZE_OVER_SPEED */ + + while (length--) + { + if (*src == d) + return (void *) src; + src--; + } + + return NULL; +} diff --git a/newlib/libc/machine/riscv/memset.S b/newlib/libc/machine/riscv/memset.S index a717ae7..533f667 100644 --- a/newlib/libc/machine/riscv/memset.S +++ b/newlib/libc/machine/riscv/memset.S @@ -9,105 +9,296 @@ http://www.opensource.org/licenses. */ +#include <sys/asm.h> + + +#define BYTE_TBL_SZ 31 +#define WORD_TBL_SZ 32 + +#if __riscv_zilsd +/* Move size */ +#define MV_SZ 8 + +/* Store instruction */ +#define RG_ST sd + +/* Zilsd and Zclsd require an even numbered register */ +#define REG_SPLAT a4 +#else +#define MV_SZ SZREG +#define RG_ST REG_S +#define REG_SPLAT a1 +#endif + +/* + Use an extended register for Zilsd and Zclsd if available + since a5 is used for the odd numbered register, in order + to eliminate an li instruction +*/ +#if __riscv_zilsd && !__riscv_abi_rve +#define REG_TABLE a6 +#else +#define REG_TABLE a5 +#endif + + .text .global memset -.type memset, @function +.type memset, @function + +/* void *memset(void *s, int c, size_t n); */ + + memset: #if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) - mv t1, a0 - beqz a2, 2f + mv a3, a0 + beqz a2, .Ldone -1: - sb a1, 0(t1) - add a2, a2, -1 - add t1, t1, 1 - bnez a2, 1b +.Lset: + sb a1, 0(a3) + addi a2, a2, -1 + addi a3, a3, 1 + bnez a2, .Lset -2: +.Ldone: ret #else - li t1, 15 - move a4, a0 - bleu a2, t1, .Ltiny - and a5, a4, 15 - bnez a5, .Lmisaligned + li REG_TABLE, BYTE_TBL_SZ + mv a3, a0 + + /* If there aren't many bytes, copy them individually to reduce overhead */ + bleu a2, REG_TABLE, .Lcopy_bytes + + and a4, a3, MV_SZ - 1 + beqz a4, .Lword_check + + /* + Jump into the byte table depending on the number of bytes that need to be + written + */ +1: + auipc t0, %pcrel_hi(.Ltable_misaligned) + + /* + Instructions in the tables are forced to be four bytes, so scale count + by 4 + */ +#if __riscv_zba + sh2add t0, a4, t0 +#else + sll t1, a4, 2 + add t0, t0, t1 +#endif -.Laligned: - bnez a1, .Lwordify + /* Save the return address because we aren't exiting the function yet */ + mv t1, ra + jalr t0, %pcrel_lo(1b) -.Lwordified: - and a3, a2, ~15 - and a2, a2, 15 - add a3, a3, a4 + /* Update pointer and count by what was written */ + mv ra, t1 + add a4, a4, -MV_SZ + add a2, a2, a4 + sub a3, a3, a4 + /* Access is now aligned. Check we can copy words. */ + bleu a2, REG_TABLE, .Lcopy_bytes + +.Lword_check: + /* Don't need to splat special case of zero */ + bnez a1, .Lsplat_byte +#if __riscv_zilsd + mv REG_SPLAT, a1 +#endif + j .Lcopy_words_init + +/* + Align labels to four bytes after unconditional jumps to avoid any + penalties when jumping to 32-bit instructions that aren't 4-byte + aligned +*/ +.p2align 2 +.Lsplat_byte: +#if __riscv_zbkb + packh REG_SPLAT, a1, a1 #if __riscv_xlen == 64 -1:sd a1, 0(a4) - sd a1, 8(a4) + packw REG_SPLAT, REG_SPLAT, REG_SPLAT +#endif + pack REG_SPLAT, REG_SPLAT, REG_SPLAT #else -1:sw a1, 0(a4) - sw a1, 4(a4) - sw a1, 8(a4) - sw a1, 12(a4) + and a1, a1, 0xFF + sll t0, a1, 8 + or a1, a1, t0 + sll t0, a1, 16 + or REG_SPLAT, a1, t0 +#if __riscv_xlen == 64 + sll t0, REG_SPLAT, 32 + or REG_SPLAT, REG_SPLAT, t0 +#endif #endif - add a4, a4, 16 - bltu a4, a3, 1b - bnez a2, .Ltiny - ret +.Lcopy_words_init: +#if __riscv_zilsd + /* Odd register of even-odd pair */ + mv a5, REG_SPLAT +#endif + + /* Calculate end address */ + and t0, a2, ~(MV_SZ - 1) + add t1, a3, t0 + + /* + The idea behind the table of word copies is that first we calculate any + remainder of bytes that need to be copied by the table that aren't an + entire table length. That's copied first. After that, runs of the entire + table are performed. + */ + and t0, t0, (WORD_TBL_SZ - 1) * MV_SZ + + /* Skip if there's no remainder */ + beqz t0, .Ltable_bigly + neg t0, t0 + add t0, t0, WORD_TBL_SZ * MV_SZ + + /* Adjust start address with offset */ + sub a3, a3, t0 + +1: + auipc t2, %pcrel_hi(.Ltable_bigly) + +#if MV_SZ == 8 + /* + If eight bytes are being copied with each store, we need to divide + the table offset in half + */ + srl t0, t0, 1 +#endif + + add t2, t2, t0 + jr t2, %pcrel_lo(1b) -.Ltiny: - sub a3, t1, a2 - sll a3, a3, 2 -1:auipc t0, %pcrel_hi(.Ltable) - add a3, a3, t0 +.p2align 2 +.Ltable_bigly: +/* + Force the instructions to be four bytes to avoid an extra instruction + that would be needed to halve the offset for sw +*/ .option push .option norvc -.Ltable_misaligned: - jr a3, %pcrel_lo(1b) -.Ltable: - sb a1,14(a4) - sb a1,13(a4) - sb a1,12(a4) - sb a1,11(a4) - sb a1,10(a4) - sb a1, 9(a4) - sb a1, 8(a4) - sb a1, 7(a4) - sb a1, 6(a4) - sb a1, 5(a4) - sb a1, 4(a4) - sb a1, 3(a4) - sb a1, 2(a4) - sb a1, 1(a4) - sb a1, 0(a4) + RG_ST REG_SPLAT, MV_SZ*0(a3) + RG_ST REG_SPLAT, MV_SZ*1(a3) + RG_ST REG_SPLAT, MV_SZ*2(a3) + RG_ST REG_SPLAT, MV_SZ*3(a3) + RG_ST REG_SPLAT, MV_SZ*4(a3) + RG_ST REG_SPLAT, MV_SZ*5(a3) + RG_ST REG_SPLAT, MV_SZ*6(a3) + RG_ST REG_SPLAT, MV_SZ*7(a3) + RG_ST REG_SPLAT, MV_SZ*8(a3) + RG_ST REG_SPLAT, MV_SZ*9(a3) + RG_ST REG_SPLAT, MV_SZ*10(a3) + RG_ST REG_SPLAT, MV_SZ*11(a3) + RG_ST REG_SPLAT, MV_SZ*12(a3) + RG_ST REG_SPLAT, MV_SZ*13(a3) + RG_ST REG_SPLAT, MV_SZ*14(a3) + RG_ST REG_SPLAT, MV_SZ*15(a3) + RG_ST REG_SPLAT, MV_SZ*16(a3) + RG_ST REG_SPLAT, MV_SZ*17(a3) + RG_ST REG_SPLAT, MV_SZ*18(a3) + RG_ST REG_SPLAT, MV_SZ*19(a3) + RG_ST REG_SPLAT, MV_SZ*20(a3) + RG_ST REG_SPLAT, MV_SZ*21(a3) + RG_ST REG_SPLAT, MV_SZ*22(a3) + RG_ST REG_SPLAT, MV_SZ*23(a3) + RG_ST REG_SPLAT, MV_SZ*24(a3) + RG_ST REG_SPLAT, MV_SZ*25(a3) + RG_ST REG_SPLAT, MV_SZ*26(a3) + RG_ST REG_SPLAT, MV_SZ*27(a3) + RG_ST REG_SPLAT, MV_SZ*28(a3) + RG_ST REG_SPLAT, MV_SZ*29(a3) + RG_ST REG_SPLAT, MV_SZ*30(a3) + RG_ST REG_SPLAT, MV_SZ*31(a3) .option pop - ret -.Lwordify: - and a1, a1, 0xFF - sll a3, a1, 8 - or a1, a1, a3 - sll a3, a1, 16 - or a1, a1, a3 -#if __riscv_xlen == 64 - sll a3, a1, 32 - or a1, a1, a3 + /* Update the pointer and copy data if needed */ + add a3, a3, MV_SZ * WORD_TBL_SZ + bltu a3, t1, .Ltable_bigly + + /* Copy any remaining bytes */ + and a2, a2, MV_SZ - 1 + beqz a2, .Lexit + +#if __riscv_zilsd && __riscv_abi_rve + /* Restore table size if necessary */ + li REG_TABLE, BYTE_TBL_SZ #endif - j .Lwordified - -.Lmisaligned: - sll a3, a5, 2 -1:auipc t0, %pcrel_hi(.Ltable_misaligned) - add a3, a3, t0 - mv t0, ra - jalr a3, %pcrel_lo(1b) - mv ra, t0 - - add a5, a5, -16 - sub a4, a4, a5 - add a2, a2, a5 - bleu a2, t1, .Ltiny - j .Laligned + +.Lcopy_bytes: + auipc t0, %pcrel_hi(.Ltable_tiny) + + sub a2, REG_TABLE, a2 + + /* + Instructions in the tables are forced to be four bytes, so scale count + by 4 + */ +#if __riscv_zba + sh2add t0, a2, t0 +#else + sll a2, a2, 2 + add t0, t0, a2 +#endif + + /* Don't save the return address because we're exiting after the jump */ + jr t0, %pcrel_lo(.Lcopy_bytes) + +.p2align 2 +.Ltable_tiny: +/* + norvc is needed because the immediate is only two bits in size for c.sb, + and without it the table would have a mix of 2- and 4-byte instructions + when Zcb is available +*/ +.option push +.option norvc + sb a1, 30(a3) + sb a1, 29(a3) + sb a1, 28(a3) + sb a1, 27(a3) + sb a1, 26(a3) + sb a1, 25(a3) + sb a1, 24(a3) + sb a1, 23(a3) + sb a1, 22(a3) + sb a1, 21(a3) + sb a1, 20(a3) + sb a1, 19(a3) + sb a1, 18(a3) + sb a1, 17(a3) + sb a1, 16(a3) + sb a1, 15(a3) + sb a1, 14(a3) + sb a1, 13(a3) + sb a1, 12(a3) + sb a1, 11(a3) + sb a1, 10(a3) + sb a1, 9(a3) + sb a1, 8(a3) +#if MV_SZ == 8 +.Ltable_misaligned: +#endif + sb a1, 7(a3) + sb a1, 6(a3) + sb a1, 5(a3) + sb a1, 4(a3) +#if MV_SZ == 4 +.Ltable_misaligned: +#endif + sb a1, 3(a3) + sb a1, 2(a3) + sb a1, 1(a3) + sb a1, 0(a3) +.option pop +.Lexit: + ret #endif - .size memset, .-memset +.size memset, .-memset diff --git a/newlib/libc/machine/riscv/rv_string.h b/newlib/libc/machine/riscv/rv_string.h index 362f66a..dc2a26d 100644 --- a/newlib/libc/machine/riscv/rv_string.h +++ b/newlib/libc/machine/riscv/rv_string.h @@ -20,20 +20,24 @@ // Determine which intrinsics to use based on XLEN and endianness #if __riscv_xlen == 64 - #define __LIBC_RISCV_ZBB_ORC_B(x) __riscv_orc_b_64(x) + #define __LIBC_RISCV_ZBB_ORC_B(x) __riscv_orc_b_64(x) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_ctz_64(x) + #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_ctz_64(x) + #define __LIBC_RISCV_ZBB_CNT_Z_REV(x) __riscv_clz_64(x) #else - #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_clz_64(x) + #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_clz_64(x) + #define __LIBC_RISCV_ZBB_CNT_Z_REV(x) __riscv_ctz_64(x) #endif #else - #define __LIBC_RISCV_ZBB_ORC_B(x) __riscv_orc_b_32(x) + #define __LIBC_RISCV_ZBB_ORC_B(x) __riscv_orc_b_32(x) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_ctz_32(x) + #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_ctz_32(x) + #define __LIBC_RISCV_ZBB_CNT_Z_REV(x) __riscv_clz_32(x) #else - #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_clz_32(x) + #define __LIBC_RISCV_ZBB_CNT_Z(x) __riscv_clz_32(x) + #define __LIBC_RISCV_ZBB_CNT_Z_REV(x) __riscv_ctz_32(x) #endif #endif #endif @@ -82,8 +86,8 @@ static __inline char *__libc_strcpy(char *dst, const char *src, bool ret_start) if (!(*dst++ = src[0])) return dst0; if (!(*dst++ = src[1])) return dst0; if (!(*dst++ = src[2])) return dst0; - if (!(*dst++ = src[3])) return dst0; #if __riscv_xlen == 64 + if (!(*dst++ = src[3])) return dst0; if (!(*dst++ = src[4])) return dst0; if (!(*dst++ = src[5])) return dst0; if (!(*dst++ = src[6])) return dst0; @@ -94,13 +98,13 @@ static __inline char *__libc_strcpy(char *dst, const char *src, bool ret_start) if (!(*dst++ = src[0])) return dst - 1; if (!(*dst++ = src[1])) return dst - 1; if (!(*dst++ = src[2])) return dst - 1; - if (!(*dst++ = src[3])) return dst - 1; #if __riscv_xlen == 64 + if (!(*dst++ = src[3])) return dst - 1; if (!(*dst++ = src[4])) return dst - 1; if (!(*dst++ = src[5])) return dst - 1; if (!(*dst++ = src[6])) return dst - 1; - dst0 = dst; #endif + dst0 = dst; } *dst = 0; @@ -121,4 +125,33 @@ static __inline char *__libc_strcpy(char *dst, const char *src, bool ret_start) } +static __inline uintxlen_t __libc_splat_byte(unsigned char c) +{ + uintxlen_t val; + +#if __riscv_zbkb + asm volatile ("packh %0, %1, %1" + : "=r" (val) + : "r" (c) + ); +#if __riscv_xlen == 64 + asm volatile ("packw %0, %0, %0" + : "+r" (val) + ); +#endif /* __riscv_xlen == 64 */ + asm volatile ("pack %0, %0, %0" + : "+r" (val) + ); +#else /* not __riscv_zbkb */ + val = (c << 8) | c; + val = (val << 16) | val; +#if __riscv_xlen == 64 + val = (val << 32) | val; +#endif /* __riscv_xlen == 64 */ +#endif /* __riscv_zbkb */ + + return val; +} + + #endif /* _RV_STRING_H */ diff --git a/newlib/libc/machine/riscv/setjmp.S b/newlib/libc/machine/riscv/setjmp.S index eef242e..f2b5053 100644 --- a/newlib/libc/machine/riscv/setjmp.S +++ b/newlib/libc/machine/riscv/setjmp.S @@ -16,21 +16,33 @@ .type setjmp, @function setjmp: REG_S ra, 0*SZREG(a0) - REG_S s0, 1*SZREG(a0) - REG_S s1, 2*SZREG(a0) + #if __riscv_xlen == 32 && (__riscv_zilsd) && (__riscv_misaligned_fast) + sd s0, 1*SZREG(a0) + #else + REG_S s0, 1*SZREG(a0) + REG_S s1, 2*SZREG(a0) + #endif -#ifndef __riscv_32e - REG_S s2, 3*SZREG(a0) - REG_S s3, 4*SZREG(a0) - REG_S s4, 5*SZREG(a0) - REG_S s5, 6*SZREG(a0) - REG_S s6, 7*SZREG(a0) - REG_S s7, 8*SZREG(a0) - REG_S s8, 9*SZREG(a0) - REG_S s9, 10*SZREG(a0) - REG_S s10,11*SZREG(a0) - REG_S s11,12*SZREG(a0) - REG_S sp, 13*SZREG(a0) +#ifndef __riscv_abi_rve + #if __riscv_xlen == 32 && (__riscv_zilsd) && (__riscv_misaligned_fast) + sd s2, 3*SZREG(a0) + sd s4, 5*SZREG(a0) + sd s6, 7*SZREG(a0) + sd s8, 9*SZREG(a0) + sd s10,11*SZREG(a0) + #else + REG_S s2, 3*SZREG(a0) + REG_S s3, 4*SZREG(a0) + REG_S s4, 5*SZREG(a0) + REG_S s5, 6*SZREG(a0) + REG_S s6, 7*SZREG(a0) + REG_S s7, 8*SZREG(a0) + REG_S s8, 9*SZREG(a0) + REG_S s9, 10*SZREG(a0) + REG_S s10,11*SZREG(a0) + REG_S s11,12*SZREG(a0) + #endif + REG_S sp, 13*SZREG(a0) #else REG_S sp, 3*SZREG(a0) #endif @@ -59,19 +71,31 @@ setjmp: .type longjmp, @function longjmp: REG_L ra, 0*SZREG(a0) - REG_L s0, 1*SZREG(a0) - REG_L s1, 2*SZREG(a0) -#ifndef __riscv_32e - REG_L s2, 3*SZREG(a0) - REG_L s3, 4*SZREG(a0) - REG_L s4, 5*SZREG(a0) - REG_L s5, 6*SZREG(a0) - REG_L s6, 7*SZREG(a0) - REG_L s7, 8*SZREG(a0) - REG_L s8, 9*SZREG(a0) - REG_L s9, 10*SZREG(a0) - REG_L s10,11*SZREG(a0) - REG_L s11,12*SZREG(a0) + #if __riscv_xlen == 32 && (__riscv_zilsd) && (__riscv_misaligned_fast) + ld s0, 1*SZREG(a0) + #else + REG_L s0, 1*SZREG(a0) + REG_L s1, 2*SZREG(a0) + #endif +#ifndef __riscv_abi_rve + #if __riscv_xlen == 32 && (__riscv_zilsd) && (__riscv_misaligned_fast) + ld s2, 3*SZREG(a0) + ld s4, 5*SZREG(a0) + ld s6, 7*SZREG(a0) + ld s8, 9*SZREG(a0) + ld s10,11*SZREG(a0) + #else + REG_L s2, 3*SZREG(a0) + REG_L s3, 4*SZREG(a0) + REG_L s4, 5*SZREG(a0) + REG_L s5, 6*SZREG(a0) + REG_L s6, 7*SZREG(a0) + REG_L s7, 8*SZREG(a0) + REG_L s8, 9*SZREG(a0) + REG_L s9, 10*SZREG(a0) + REG_L s10,11*SZREG(a0) + REG_L s11,12*SZREG(a0) + #endif REG_L sp, 13*SZREG(a0) #else REG_L sp, 3*SZREG(a0) diff --git a/newlib/libc/machine/riscv/strcmp.S b/newlib/libc/machine/riscv/strcmp.S index cc29b7b..0b1dfc4 100644 --- a/newlib/libc/machine/riscv/strcmp.S +++ b/newlib/libc/machine/riscv/strcmp.S @@ -16,15 +16,15 @@ .type strcmp, @function strcmp: #if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) -1: +.Lcompare: lbu a2, 0(a0) lbu a3, 0(a1) - add a0, a0, 1 - add a1, a1, 1 - bne a2, a3, 2f - bnez a2, 1b + addi a0, a0, 1 + addi a1, a1, 1 + bne a2, a3, .Lreturn_diff + bnez a2, .Lcompare -2: +.Lreturn_diff: sub a0, a2, a3 ret @@ -48,12 +48,16 @@ strcmp: REG_L a2, \i*SZREG(a0) REG_L a3, \i*SZREG(a1) - and t0, a2, a5 - or t1, a2, a5 - add t0, t0, a5 - or t0, t0, t1 + #if __riscv_zbb + orc.b a4, a2 + #else + and a4, a2, a5 + or t1, a2, a5 + add a4, a4, a5 + or a4, a4, t1 + #endif - bne t0, t2, .Lnull\i + bne a4, t2, .Lnull\i .if \i+1-\n bne a2, a3, .Lmismatch .else @@ -95,73 +99,109 @@ strcmp: .Lmismatch: # words don't match, but a2 has no null byte. + #if __riscv_zbb + xor a4, a2, a3 # find differing bits + + # Check system endianness + # If little-endian, use Count Trailing Zeros (ctz) + # If big-endian, use Count Leading Zeros (clz) + # This helps identify the position of the first differing byte between a2 and a3. + + # For example, in little-endian, least significant byte comes first. + # So trailing zeros help find which byte position differs. + + # In big-endian, most significant byte comes first, so leading zeros are used. + # The position will then be used to extract the differing byte. + + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + ctz a5, a4 + #else + clz a5, a4 + #endif + + andi a5, a5, -8 # find position of bit offset to the start of the byte where the first difference occurs + + + # Shift a2 and a3 right by a5 bits to bring the target byte to the LSB, and isolate the byte of interest + srl a2, a2, a5 + and a2, a2, 0xff + + srl a3, a3, a5 + and a3, a3, 0xff + + + sub a0, a2, a3 # Calculate and return the difference in the isolated bytes + ret + + #else + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #if __riscv_xlen == 64 + sll a4, a2, 48 + sll a5, a3, 48 + bne a4, a5, .Lmismatch_upper + sll a4, a2, 32 + sll a5, a3, 32 + bne a4, a5, .Lmismatch_upper + #endif + sll a4, a2, 16 + sll a5, a3, 16 + bne a4, a5, .Lmismatch_upper + + srl a4, a2, 8*SZREG-16 + srl a5, a3, 8*SZREG-16 + sub a0, a4, a5 + and a1, a0, 0xff + bnez a1, .Lfinal_upper_diff + ret -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - -#if __riscv_xlen == 64 - sll a4, a2, 48 - sll a5, a3, 48 - bne a4, a5, .Lmismatch_upper - sll a4, a2, 32 - sll a5, a3, 32 - bne a4, a5, .Lmismatch_upper -#endif - sll a4, a2, 16 - sll a5, a3, 16 - bne a4, a5, .Lmismatch_upper - - srl a4, a2, 8*SZREG-16 - srl a5, a3, 8*SZREG-16 - sub a0, a4, a5 - and a1, a0, 0xff - bnez a1, 1f - ret - -.Lmismatch_upper: - srl a4, a4, 8*SZREG-16 - srl a5, a5, 8*SZREG-16 - sub a0, a4, a5 - and a1, a0, 0xff - bnez a1, 1f - ret - -1:and a4, a4, 0xff - and a5, a5, 0xff - sub a0, a4, a5 - ret - -#else - -#if __riscv_xlen == 64 - srl a4, a2, 48 - srl a5, a3, 48 - bne a4, a5, .Lmismatch_lower - srl a4, a2, 32 - srl a5, a3, 32 - bne a4, a5, .Lmismatch_lower -#endif - srl a4, a2, 16 - srl a5, a3, 16 - bne a4, a5, .Lmismatch_lower - - srl a4, a2, 8 - srl a5, a3, 8 - bne a4, a5, 1f - and a4, a2, 0xff - and a5, a3, 0xff -1:sub a0, a4, a5 - ret - -.Lmismatch_lower: - srl a2, a4, 8 - srl a3, a5, 8 - bne a2, a3, 1f - and a2, a4, 0xff - and a3, a5, 0xff -1:sub a0, a2, a3 - ret - -#endif + .Lmismatch_upper: + srl a4, a4, 8*SZREG-16 + srl a5, a5, 8*SZREG-16 + sub a0, a4, a5 + and a1, a0, 0xff + bnez a1, .Lfinal_upper_diff + ret + + .Lfinal_upper_diff: + and a4, a4, 0xff + and a5, a5, 0xff + sub a0, a4, a5 + ret + #else + #if __riscv_xlen == 64 + srl a4, a2, 48 + srl a5, a3, 48 + bne a4, a5, .Lmismatch_lower + srl a4, a2, 32 + srl a5, a3, 32 + bne a4, a5, .Lmismatch_lower + #endif + srl a4, a2, 16 + srl a5, a3, 16 + bne a4, a5, .Lmismatch_lower + + srl a4, a2, 8 + srl a5, a3, 8 + bne a4, a5, .Lbyte_diff + and a4, a2, 0xff + and a5, a3, 0xff + + .Lbyte_diff: + sub a0, a4, a5 + ret + + .Lmismatch_lower: + srl a2, a4, 8 + srl a3, a5, 8 + bne a2, a3, .Lfinal_lower_diff + and a2, a4, 0xff + and a3, a5, 0xff + + .Lfinal_lower_diff: + sub a0, a2, a3 + ret + #endif + #endif .Lmisaligned: # misaligned @@ -169,10 +209,10 @@ strcmp: lbu a3, 0(a1) add a0, a0, 1 add a1, a1, 1 - bne a2, a3, 1f + bne a2, a3, .Lmisaligned_diff bnez a2, .Lmisaligned -1: +.Lmisaligned_diff: sub a0, a2, a3 ret diff --git a/newlib/libc/machine/riscv/strlen.c b/newlib/libc/machine/riscv/strlen.c index 9bfd2a1..8ab5ce5 100644 --- a/newlib/libc/machine/riscv/strlen.c +++ b/newlib/libc/machine/riscv/strlen.c @@ -9,6 +9,7 @@ http://www.opensource.org/licenses. */ +#include <sys/types.h> #include <string.h> #include <stdint.h> #include "rv_string.h" @@ -38,7 +39,9 @@ size_t strlen(const char *str) asm volatile ("" : "+r"(ps)); /* prevent "optimization" */ str = (const char *)ps; - size_t ret = str - start, sp = sizeof (*ps); + + size_t ret = str - start; + ssize_t sp = sizeof (*ps); #if __riscv_zbb psval = ~__LIBC_RISCV_ZBB_ORC_B(psval); @@ -47,16 +50,16 @@ size_t strlen(const char *str) return ret + (psval >> 3) - sp; #else char c0 = str[0 - sp], c1 = str[1 - sp], c2 = str[2 - sp], c3 = str[3 - sp]; - if (c0 == 0) return ret + 0 - sp; - if (c1 == 0) return ret + 1 - sp; - if (c2 == 0) return ret + 2 - sp; - if (c3 == 0) return ret + 3 - sp; + if (c0 == 0) return ret + 0 - sp; + if (c1 == 0) return ret + 1 - sp; + if (c2 == 0) return ret + 2 - sp; + if (__riscv_xlen == 32 || c3 == 0) return ret + 3 - sp; #if __riscv_xlen == 64 c0 = str[4 - sp], c1 = str[5 - sp], c2 = str[6 - sp]; - if (c0 == 0) return ret + 4 - sp; - if (c1 == 0) return ret + 5 - sp; - if (c2 == 0) return ret + 6 - sp; + if (c0 == 0) return ret + 4 - sp; + if (c1 == 0) return ret + 5 - sp; + if (c2 == 0) return ret + 6 - sp; #endif return ret + 7 - sp; diff --git a/newlib/libc/machine/riscv/xlenint.h b/newlib/libc/machine/riscv/xlenint.h index 86363a8..2d444ff 100644 --- a/newlib/libc/machine/riscv/xlenint.h +++ b/newlib/libc/machine/riscv/xlenint.h @@ -11,4 +11,11 @@ typedef uint32_t uintxlen_t; # error __riscv_xlen must equal 32 or 64 #endif +/* Load/Store length */ +#if __riscv_zilsd +typedef uint64_t uintlslen_t; +#else +typedef uintxlen_t uintlslen_t; +#endif + #endif /* _XLENINT_H */ diff --git a/newlib/libc/posix/ftw.c b/newlib/libc/posix/ftw.c index 79e6358..e3ca85c 100644 --- a/newlib/libc/posix/ftw.c +++ b/newlib/libc/posix/ftw.c @@ -30,7 +30,9 @@ int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int /* The following cast assumes that calling a function with one * argument more than it needs behaves as expected. This is * actually undefined, but works on all real-world machines. */ - return nftw(path, (int (*)())fn, fd_limit, FTW_PHYS); + return nftw(path, + (int (*)(const char *, const struct stat *, int, struct FTW *))fn, + fd_limit, FTW_PHYS); } #endif /* ! HAVE_OPENDIR */ diff --git a/newlib/libc/posix/glob.c b/newlib/libc/posix/glob.c index 20eec02..fe7359b 100644 --- a/newlib/libc/posix/glob.c +++ b/newlib/libc/posix/glob.c @@ -157,10 +157,8 @@ static void qprintf(const char *, Char *); #endif int -glob(pattern, flags, errfunc, pglob) - const char *__restrict pattern; - int flags, (*errfunc)(const char *, int); - glob_t *__restrict pglob; +glob(const char *__restrict pattern, int flags, + int (*errfunc)(const char *, int), glob_t *__restrict pglob) { const u_char *patnext; int c, limit; @@ -215,10 +213,7 @@ glob(pattern, flags, errfunc, pglob) * characters */ static int -globexp1(pattern, pglob, limit) - const Char *pattern; - glob_t *pglob; - int *limit; +globexp1(const Char *pattern, glob_t *pglob, int *limit) { const Char* ptr = pattern; int rv; @@ -241,10 +236,8 @@ globexp1(pattern, pglob, limit) * If it fails then it tries to glob the rest of the pattern and returns. */ static int -globexp2(ptr, pattern, pglob, rv, limit) - const Char *ptr, *pattern; - glob_t *pglob; - int *rv, *limit; +globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, int *rv, + int *limit) { int i; Char *lm, *ls; @@ -348,11 +341,7 @@ globexp2(ptr, pattern, pglob, rv, limit) * expand tilde from the passwd file. */ static const Char * -globtilde(pattern, patbuf, patbuf_len, pglob) - const Char *pattern; - Char *patbuf; - size_t patbuf_len; - glob_t *pglob; +globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) { struct passwd *pwd; char *h; @@ -428,10 +417,7 @@ globtilde(pattern, patbuf, patbuf_len, pglob) * to find no matches. */ static int -glob0(pattern, pglob, limit) - const Char *pattern; - glob_t *pglob; - int *limit; +glob0(const Char *pattern, glob_t *pglob, int *limit) { const Char *qpatnext; int c, err, oldpathc; @@ -517,17 +503,13 @@ glob0(pattern, pglob, limit) } static int -compare(p, q) - const void *p, *q; +compare(const void *p, const void *q) { return(strcmp(*(char **)p, *(char **)q)); } static int -glob1(pattern, pglob, limit) - Char *pattern; - glob_t *pglob; - int *limit; +glob1(Char *pattern, glob_t *pglob, int *limit) { Char pathbuf[MAXPATHLEN]; @@ -544,10 +526,8 @@ glob1(pattern, pglob, limit) * meta characters. */ static int -glob2(pathbuf, pathend, pathend_last, pattern, pglob, limit) - Char *pathbuf, *pathend, *pathend_last, *pattern; - glob_t *pglob; - int *limit; +glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, + glob_t *pglob, int *limit) { struct stat sb; Char *p, *q; @@ -604,10 +584,8 @@ glob2(pathbuf, pathend, pathend_last, pattern, pglob, limit) } static int -glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit) - Char *pathbuf, *pathend, *pathend_last, *pattern, *restpattern; - glob_t *pglob; - int *limit; +glob3(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, + Char *restpattern, glob_t *pglob, int *limit) { struct dirent *dp; DIR *dirp; @@ -620,7 +598,7 @@ glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit) * and dirent.h as taking pointers to differently typed opaque * structures. */ - struct dirent *(*readdirfunc)(); + struct dirent *(*readdirfunc)(void *); if (pathend > pathend_last) return (1); @@ -645,7 +623,7 @@ glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit) if (pglob->gl_flags & GLOB_ALTDIRFUNC) readdirfunc = pglob->gl_readdir; else - readdirfunc = readdir; + readdirfunc = (struct dirent *(*)(void *))readdir; while ((dp = (*readdirfunc)(dirp))) { u_char *sc; Char *dc; @@ -690,10 +668,7 @@ glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit) * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int -globextend(path, pglob, limit) - const Char *path; - glob_t *pglob; - int *limit; +globextend(const Char *path, glob_t *pglob, int *limit) { char **pathv; int i; @@ -745,8 +720,7 @@ globextend(path, pglob, limit) * pattern causes a recursion level. */ static int -match(name, pat, patend) - Char *name, *pat, *patend; +match(Char *name, Char *pat, Char *patend) { int ok, negate_range; Char c, k; @@ -797,8 +771,7 @@ match(name, pat, patend) /* Free allocated data belonging to a glob_t structure. */ void -globfree(pglob) - glob_t *pglob; +globfree(glob_t *pglob) { int i; char **pp; @@ -814,9 +787,7 @@ globfree(pglob) } static DIR * -g_opendir(str, pglob) - Char *str; - glob_t *pglob; +g_opendir(Char *str, glob_t *pglob) { char buf[MAXPATHLEN]; @@ -834,10 +805,7 @@ g_opendir(str, pglob) } static int -g_lstat(fn, sb, pglob) - Char *fn; - struct stat *sb; - glob_t *pglob; +g_lstat(Char *fn, struct stat *sb, glob_t *pglob) { char buf[MAXPATHLEN]; @@ -851,10 +819,7 @@ g_lstat(fn, sb, pglob) } static int -g_stat(fn, sb, pglob) - Char *fn; - struct stat *sb; - glob_t *pglob; +g_stat(Char *fn, struct stat *sb, glob_t *pglob) { char buf[MAXPATHLEN]; @@ -868,9 +833,7 @@ g_stat(fn, sb, pglob) } static Char * -g_strchr(str, ch) - Char *str; - int ch; +g_strchr(Char *str, int ch) { do { if (*str == ch) @@ -880,10 +843,7 @@ g_strchr(str, ch) } static int -g_Ctoc(str, buf, len) - const Char *str; - char *buf; - u_int len; +g_Ctoc(const Char *str, char *buf, u_int len) { while (len--) { @@ -895,9 +855,7 @@ g_Ctoc(str, buf, len) #ifdef DEBUG static void -qprintf(str, s) - const char *str; - Char *s; +qprintf(const char *str, Char *s) { Char *p; diff --git a/newlib/libc/posix/posix_spawn.c b/newlib/libc/posix/posix_spawn.c index 46e4e53..51ad23f 100644 --- a/newlib/libc/posix/posix_spawn.c +++ b/newlib/libc/posix/posix_spawn.c @@ -102,56 +102,13 @@ Supporting OS subroutines required: <<_close>>, <<dup2>>, <<_fcntl>>, #include <stdlib.h> #include <string.h> #include <unistd.h> +#include "posix_spawn.h" /* Only deal with a pointer to environ, to work around subtle bugs with shared libraries and/or small data systems where the user declares his own 'environ'. */ static char ***p_environ = &environ; -struct __posix_spawnattr { - short sa_flags; - pid_t sa_pgroup; - struct sched_param sa_schedparam; - int sa_schedpolicy; - sigset_t sa_sigdefault; - sigset_t sa_sigmask; -}; - -struct __posix_spawn_file_actions { - STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; -}; - -typedef struct __posix_spawn_file_actions_entry { - STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; - enum { - FAE_OPEN, - FAE_DUP2, - FAE_CLOSE, - FAE_CHDIR, - FAE_FCHDIR - } fae_action; - - int fae_fildes; - union { - struct { - char *path; -#define fae_path fae_data.open.path - int oflag; -#define fae_oflag fae_data.open.oflag - mode_t mode; -#define fae_mode fae_data.open.mode - } open; - struct { - int newfildes; -#define fae_newfildes fae_data.dup2.newfildes - } dup2; - char *dir; -#define fae_dir fae_data.dir - int dirfd; -#define fae_dirfd fae_data.dirfd - } fae_data; -} posix_spawn_file_actions_entry_t; - /* * Spawn routines */ diff --git a/newlib/libc/posix/posix_spawn.h b/newlib/libc/posix/posix_spawn.h new file mode 100644 index 0000000..b4cad1e --- /dev/null +++ b/newlib/libc/posix/posix_spawn.h @@ -0,0 +1,54 @@ +#ifndef _POSIX_SPAWN_H_ +#define _POSIX_SPAWN_H_ + +#include <sys/cdefs.h> +#include <sys/sched.h> +#include <sys/signal.h> +#include <sys/types.h> +#include <sys/queue.h> + +struct __posix_spawnattr { + short sa_flags; + pid_t sa_pgroup; + struct sched_param sa_schedparam; + int sa_schedpolicy; + sigset_t sa_sigdefault; + sigset_t sa_sigmask; +}; + +struct __posix_spawn_file_actions { + STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; +}; + +typedef struct __posix_spawn_file_actions_entry { + STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; + enum { + FAE_OPEN, + FAE_DUP2, + FAE_CLOSE, + FAE_CHDIR, + FAE_FCHDIR + } fae_action; + + int fae_fildes; + union { + struct { + char *path; +#define fae_path fae_data.open.path + int oflag; +#define fae_oflag fae_data.open.oflag + mode_t mode; +#define fae_mode fae_data.open.mode + } open; + struct { + int newfildes; +#define fae_newfildes fae_data.dup2.newfildes + } dup2; + char *dir; +#define fae_dir fae_data.dir + int dirfd; +#define fae_dirfd fae_data.dirfd + } fae_data; +} posix_spawn_file_actions_entry_t; + +#endif /* !_POSIX_SPAWN_H_ */ diff --git a/newlib/libc/search/tdelete.c b/newlib/libc/search/tdelete.c index a595200..b12158e 100644 --- a/newlib/libc/search/tdelete.c +++ b/newlib/libc/search/tdelete.c @@ -27,7 +27,7 @@ __RCSID("$NetBSD: tdelete.c,v 1.2 1999/09/16 11:45:37 lukem Exp $"); /* delete node with given key */ void * tdelete (const void *__restrict vkey, /* key to be deleted */ - void **__restrict vrootp, /* address of the root of tree */ + posix_tnode **__restrict vrootp,/* address of the root of tree */ int (*compar)(const void *, const void *)) { node_t **rootp = (node_t **)vrootp; diff --git a/newlib/libc/search/tfind.c b/newlib/libc/search/tfind.c index 670f41f..8bebdac 100644 --- a/newlib/libc/search/tfind.c +++ b/newlib/libc/search/tfind.c @@ -26,7 +26,7 @@ __RCSID("$NetBSD: tfind.c,v 1.2 1999/09/16 11:45:37 lukem Exp $"); /* find a node, or return 0 */ void * tfind (const void *vkey, /* key to be found */ - void **vrootp, /* address of the tree root */ + posix_tnode *const *vrootp, /* address of the tree root */ int (*compar)(const void *, const void *)) { node_t **rootp = (node_t **)vrootp; diff --git a/newlib/libc/search/tsearch.c b/newlib/libc/search/tsearch.c index 82d6944..9be77f1 100644 --- a/newlib/libc/search/tsearch.c +++ b/newlib/libc/search/tsearch.c @@ -26,7 +26,7 @@ __RCSID("$NetBSD: tsearch.c,v 1.3 1999/09/16 11:45:37 lukem Exp $"); /* find or insert datum into search tree */ void * tsearch (const void *vkey, /* key to be located */ - void **vrootp, /* address of tree root */ + posix_tnode **vrootp, /* address of tree root */ int (*compar)(const void *, const void *)) { node_t *q; diff --git a/newlib/libc/search/twalk.c b/newlib/libc/search/twalk.c index 7aec6e4..26d037a 100644 --- a/newlib/libc/search/twalk.c +++ b/newlib/libc/search/twalk.c @@ -50,8 +50,8 @@ trecurse( /* Walk the nodes of a tree */ void -twalk (const void *vroot, /* Root of the tree to be walked */ - void (*action)(const void *, VISIT, int)) +twalk (const posix_tnode *vroot,/* Root of the tree to be walked */ + void (*action)(const posix_tnode *, VISIT, int)) { if (vroot != NULL && action != NULL) trecurse(vroot, action, 0); diff --git a/newlib/libc/stdlib/mbtowc_r.c b/newlib/libc/stdlib/mbtowc_r.c index cab8333..6c3bd3d 100644 --- a/newlib/libc/stdlib/mbtowc_r.c +++ b/newlib/libc/stdlib/mbtowc_r.c @@ -677,6 +677,21 @@ __utf8_mbtowc (struct _reent *r, state->__count = 3; else if (n < (size_t)-1) ++n; + if (n < 4) + return -2; + ch = t[i++]; + if (ch < 0x80 || ch > 0xbf) + { + _REENT_ERRNO(r) = EILSEQ; + return -1; + } + /* Note: Originally we created the low surrogate pair on systems with + wchar_t == UTF-16 *before* checking the 4th byte. This was utterly + wrong, because this failed to check the last byte for being a valid + value for a complete UTF-8 4 byte sequence. As a result, calling + functions happily digested the low surrogate and then got an entirely + different character and handled this separately, thus generating + invalid UTF-16 values. */ if (state->__count == 3 && sizeof(wchar_t) == 2) { /* On systems which have wchar_t being UTF-16 values, the value @@ -695,15 +710,7 @@ __utf8_mbtowc (struct _reent *r, | (wint_t)((state->__value.__wchb[2] & 0x3f) << 6); state->__count = 4; *pwc = 0xd800 | ((tmp - 0x10000) >> 10); - return i; - } - if (n < 4) - return -2; - ch = t[i++]; - if (ch < 0x80 || ch > 0xbf) - { - _REENT_ERRNO(r) = EILSEQ; - return -1; + return 3; } tmp = (wint_t)((state->__value.__wchb[0] & 0x07) << 18) | (wint_t)((state->__value.__wchb[1] & 0x3f) << 12) diff --git a/newlib/libc/stdlib/wctomb_r.c b/newlib/libc/stdlib/wctomb_r.c index 5ea1e13..ec6adfa 100644 --- a/newlib/libc/stdlib/wctomb_r.c +++ b/newlib/libc/stdlib/wctomb_r.c @@ -62,8 +62,8 @@ __utf8_wctomb (struct _reent *r, of the surrogate and proceed to convert the given character. Note to return extra 3 bytes. */ wchar_t tmp; - tmp = (state->__value.__wchb[0] << 16 | state->__value.__wchb[1] << 8) - - (0x10000 >> 10 | 0xd80d); + tmp = (((state->__value.__wchb[0] << 16 | state->__value.__wchb[1] << 8) + - 0x10000) >> 10) | 0xd800; *s++ = 0xe0 | ((tmp & 0xf000) >> 12); *s++ = 0x80 | ((tmp & 0xfc0) >> 6); *s++ = 0x80 | (tmp & 0x3f); diff --git a/newlib/libc/sys/rtems/include/limits.h b/newlib/libc/sys/rtems/include/limits.h index 5e71e4b..8dbf88b 100644 --- a/newlib/libc/sys/rtems/include/limits.h +++ b/newlib/libc/sys/rtems/include/limits.h @@ -56,12 +56,14 @@ #define _POSIX_TZNAME_MAX 3 /* - * Definitions of the following may be omitted if the value is >= stated - * minimum but is indeterminate. + * Definitions of the following may be omitted if the value is >= stated + * minimum but is indeterminate. The following are not defined because + * RTEMS does not have an inherent limit. + * + * - AIO_LISTIO_MAX + * - AIO_MAX */ -#define AIO_LISTIO_MAX 2 -#define AIO_MAX 1 #define AIO_PRIO_DELTA_MAX 0 #define DELAYTIMER_MAX 32 #define MQ_OPEN_MAX 8 diff --git a/newlib/libc/sys/rtems/include/semaphore.h b/newlib/libc/sys/rtems/include/semaphore.h index 939135f..6d756d5 100644 --- a/newlib/libc/sys/rtems/include/semaphore.h +++ b/newlib/libc/sys/rtems/include/semaphore.h @@ -55,6 +55,12 @@ int sem_timedwait(sem_t * __restrict, const struct timespec * __restrict); int sem_trywait(sem_t *); int sem_unlink(const char *); int sem_wait(sem_t *); + +#if (__GNU_VISIBLE || __POSIX_VISIBLE >= 202405) +int sem_clockwait(sem_t * __restrict, __clockid_t, + const struct timespec * __restrict); +#endif /* __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 */ + __END_DECLS #endif /* !_SEMAPHORE_H_ */ diff --git a/newlib/libc/sys/rtems/include/sys/dirent.h b/newlib/libc/sys/rtems/include/sys/dirent.h index 6f8ff42..3e41635 100644 --- a/newlib/libc/sys/rtems/include/sys/dirent.h +++ b/newlib/libc/sys/rtems/include/sys/dirent.h @@ -99,7 +99,7 @@ struct dirent { #define __dirfd(dp) ((dp)->dd_fd) -#if __BSD_VISIBLE +#if __BSD_VISIBLE || __POSIX_VISIBLE >= 202405 /* * File types diff --git a/newlib/libc/sys/rtems/include/sys/poll.h b/newlib/libc/sys/rtems/include/sys/poll.h index cc6ad49..fd2f14d 100644 --- a/newlib/libc/sys/rtems/include/sys/poll.h +++ b/newlib/libc/sys/rtems/include/sys/poll.h @@ -35,6 +35,10 @@ #include <sys/cdefs.h> +#if (__GNU_VISIBLE || __POSIX_VISIBLE >= 202405) +#include <signal.h> +#endif + /* * This file is intended to be compatible with the traditional poll.h. */ @@ -100,6 +104,11 @@ struct pollfd { __BEGIN_DECLS int poll(struct pollfd _pfd[], nfds_t _nfds, int _timeout); +#if (__GNU_VISIBLE || __POSIX_VISIBLE >= 202405) +int ppoll(struct pollfd _pfd[], nfds_t _nfds, + const struct timespec *__restrict _timeout, + const sigset_t *__restrict _newsigmask); +#endif /* __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 */ __END_DECLS #endif /* !_KERNEL */ diff --git a/newlib/libm/machine/riscv/e_sqrt.c b/newlib/libm/machine/riscv/e_sqrt.c index 0c5aaad..ba000d3 100644 --- a/newlib/libm/machine/riscv/e_sqrt.c +++ b/newlib/libm/machine/riscv/e_sqrt.c @@ -35,6 +35,7 @@ #include <math.h> #include "math_config.h" +#include "riscv_math.h" #if defined(__RISCV_HARD_FLOAT) && __RISCV_HARD_FLOAT >= 64 diff --git a/newlib/libm/machine/riscv/ef_sqrt.c b/newlib/libm/machine/riscv/ef_sqrt.c index cc41813..fd7ab4b 100644 --- a/newlib/libm/machine/riscv/ef_sqrt.c +++ b/newlib/libm/machine/riscv/ef_sqrt.c @@ -35,6 +35,7 @@ #include <math.h> #include "math_config.h" +#include "riscv_math.h" #if defined(__RISCV_HARD_FLOAT) && __RISCV_HARD_FLOAT >= 32 diff --git a/winsup/configure.ac b/winsup/configure.ac index 9b9b59d..18adf3d 100644 --- a/winsup/configure.ac +++ b/winsup/configure.ac @@ -69,12 +69,14 @@ DLL_ENTRY="dll_entry" case "$target_cpu" in x86_64) ;; + aarch64) ;; *) AC_MSG_ERROR([Invalid target processor "$target_cpu"]) ;; esac AC_SUBST(DLL_ENTRY) AM_CONDITIONAL(TARGET_X86_64, [test $target_cpu = "x86_64"]) +AM_CONDITIONAL(TARGET_AARCH64, [test $target_cpu = "aarch64"]) AC_ARG_ENABLE(doc, [AS_HELP_STRING([--disable-doc], [do not build documentation])],, diff --git a/winsup/cygserver/cygserver-config b/winsup/cygserver/cygserver-config index 373bfd2..abda186 100755 --- a/winsup/cygserver/cygserver-config +++ b/winsup/cygserver/cygserver-config @@ -89,7 +89,7 @@ _sys="`uname`" _nt=`expr "${_sys}" : "CYGWIN_NT"` # Check for running cygserver processes first. -if ps -ef | grep -v grep | grep -q ${service_name} +if ps -e | grep -v grep | grep -q ${service_name} then echo echo "There is a cygserver (${service_name}) already running. Nothing to do, apparently." diff --git a/winsup/cygwin/Makefile.am b/winsup/cygwin/Makefile.am index 6438a41..31747ac 100644 --- a/winsup/cygwin/Makefile.am +++ b/winsup/cygwin/Makefile.am @@ -78,7 +78,8 @@ LIB_FILES= \ lib/premain1.c \ lib/premain2.c \ lib/premain3.c \ - lib/pseudo-reloc-dummy.c + lib/pseudo-reloc-dummy.c \ + lib/pthreadconst.S FHANDLER_FILES= \ fhandler/base.cc \ @@ -315,6 +316,7 @@ DLL_FILES= \ ipc.cc \ kernel32.cc \ ldap.cc \ + lib/pthreadconst.S \ libstdcxx_wrapper.cc \ loadavg.cc \ lsearch.cc \ diff --git a/winsup/cygwin/create_posix_thread.cc b/winsup/cygwin/create_posix_thread.cc index 8e06099..17bb607 100644 --- a/winsup/cygwin/create_posix_thread.cc +++ b/winsup/cygwin/create_posix_thread.cc @@ -75,7 +75,7 @@ pthread_wrapper (PVOID arg) /* Initialize new _cygtls. */ _my_tls.init_thread (wrapper_arg.stackbase - __CYGTLS_PADSIZE__, (DWORD (*)(void*, void*)) wrapper_arg.func); -#ifdef __x86_64__ +#if defined(__x86_64__) __asm__ ("\n\ leaq %[WRAPPER_ARG], %%rbx # Load &wrapper_arg into rbx \n\ movq (%%rbx), %%r12 # Load thread func into r12 \n\ @@ -99,6 +99,23 @@ pthread_wrapper (PVOID arg) call *%%r12 # Call thread func \n" : : [WRAPPER_ARG] "o" (wrapper_arg), [CYGTLS] "i" (__CYGTLS_PADSIZE__)); +#elif defined(__aarch64__) + /* Sets up a new thread stack, frees the original OS stack, + * and calls the thread function with its arg using AArch64 ABI. */ + __asm__ __volatile__ ("\n\ + ldp x20, x21, [%[WRAPPER_ARG]] // x20 = thread func, x21 = thread arg \n\ + ldp x0, x1, [%[WRAPPER_ARG], #16] // x0 = stackaddr, x1 = stackbase \n\ + sub sp, x1, %[CYGTLS] // sp = stackbase - (CYGTLS) \n\ + mov fp, xzr // clear frame pointer (x29) \n\ + // x0 already has stackaddr \n\ + mov x1, xzr // x1 = 0 (dwSize) \n\ + mov x2, #0x8000 // x2 = MEM_RELEASE \n\ + bl VirtualFree // free original stack \n\ + mov x0, x21 // Move arg into x0 \n\ + blr x20 // call thread function \n" + : : [WRAPPER_ARG] "r" (&wrapper_arg), + [CYGTLS] "r" (__CYGTLS_PADSIZE__) + : "x0", "x1", "x2", "x20", "x21", "x29", "memory"); #else #error unimplemented for this target #endif @@ -206,7 +223,7 @@ class thread_allocator public: thread_allocator () : current (THREAD_STORAGE_HIGH) { - alloc_func = wincap.has_extended_mem_api () ? &_alloc : &_alloc_old; + alloc_func = wincap.has_extended_mem_api () ? &thread_allocator::_alloc : &thread_allocator::_alloc_old; } PVOID alloc (SIZE_T size) { diff --git a/winsup/cygwin/cygwin.sc.in b/winsup/cygwin/cygwin.sc.in index 69526f5..5007a36 100644 --- a/winsup/cygwin/cygwin.sc.in +++ b/winsup/cygwin/cygwin.sc.in @@ -17,7 +17,7 @@ SECTIONS *(SORT(.text$*)) *(.glue_7t) *(.glue_7) -#ifdef __x86_64__ +#if defined(__x86_64__) || defined(__aarch64__) . = ALIGN(8); ___CTOR_LIST__ = .; __CTOR_LIST__ = .; LONG (-1); LONG (-1); *(SORT(.ctors.*)); *(.ctors); *(.ctor); LONG (0); LONG (0); diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc index f4c09be..69c233c 100644 --- a/winsup/cygwin/dcrt0.cc +++ b/winsup/cygwin/dcrt0.cc @@ -1030,7 +1030,7 @@ _dll_crt0 () PVOID stackaddr = create_new_main_thread_stack (allocationbase); if (stackaddr) { -#ifdef __x86_64__ +#if defined(__x86_64__) /* Set stack pointer to new address. Set frame pointer to stack pointer and subtract 32 bytes for shadow space. */ __asm__ ("\n\ @@ -1038,6 +1038,13 @@ _dll_crt0 () movq %%rsp, %%rbp \n\ subq $32,%%rsp \n" : : [ADDR] "r" (stackaddr)); +#elif defined(__aarch64__) + /* Set stack and frame pointers to new address. */ + __asm__ ("\n\ + mov fp, %[ADDR] \n\ + mov sp, fp \n" + : : [ADDR] "r" (stackaddr) + : "memory"); #else #error unimplemented for this target #endif diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc index f029ebb..9b6bb55 100644 --- a/winsup/cygwin/dlfcn.cc +++ b/winsup/cygwin/dlfcn.cc @@ -408,7 +408,8 @@ extern "C" int dladdr (const void *addr, Dl_info *info) { HMODULE hModule; - BOOL ret = GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + BOOL ret = GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT| + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCSTR) addr, &hModule); if (!ret) @@ -420,14 +421,16 @@ dladdr (const void *addr, Dl_info *info) /* Get the module filename. This pathname may be in short-, long- or //?/ format, depending on how it was specified when loaded, but we assume this is always an absolute pathname. */ - WCHAR fname[MAX_PATH]; - DWORD length = GetModuleFileNameW (hModule, fname, MAX_PATH); - if ((length == 0) || (length == MAX_PATH)) + tmp_pathbuf tp; + PWCHAR fname = tp.w_get (); + DWORD length = GetModuleFileNameW (hModule, fname, NT_MAX_PATH); + if ((length == 0) || (length == NT_MAX_PATH)) return 0; /* Convert to a cygwin pathname */ + static_assert (sizeof (info->dli_fname) == PATH_MAX); ssize_t conv = cygwin_conv_path (CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, fname, - info->dli_fname, MAX_PATH); + info->dli_fname, PATH_MAX); if (conv) return 0; diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc index b8f38b5..1369165 100644 --- a/winsup/cygwin/dll_init.cc +++ b/winsup/cygwin/dll_init.cc @@ -633,7 +633,7 @@ dll_list::track_self () static PVOID reserve_at (PCWCHAR name, PVOID here, PVOID dll_base, DWORD dll_size) { - DWORD size; + SIZE_T size; MEMORY_BASIC_INFORMATION mb; if (!VirtualQuery (here, &mb, sizeof (mb))) diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 49fc166..f79978f 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -440,7 +440,7 @@ cygwin_exception::dumpstack () } bool -_cygtls::inside_kernel (CONTEXT *cx) +_cygtls::inside_kernel (CONTEXT *cx, bool inside_cygwin) { int res; MEMORY_BASIC_INFORMATION m; @@ -462,6 +462,8 @@ _cygtls::inside_kernel (CONTEXT *cx) else if (h == hntdll) res = true; /* Calling GetModuleFilename on ntdll.dll can hang */ + else if (h == cygwin_hmodule && inside_cygwin) + res = true; else if (h == user_data->hmodule) res = false; else if (!GetModuleFileNameW (h, checkdir, @@ -595,7 +597,7 @@ try_to_debug () { extern void break_here (); break_here (); - return 1; + return 0; } /* Otherwise, invoke the JIT debugger, if set */ @@ -810,6 +812,8 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, else if (try_to_debug ()) { debugging = 1; + /* If a JIT debugger just attached, replay the exception for the benefit + of that */ return ExceptionContinueExecution; } @@ -912,6 +916,24 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *) } } /* end extern "C" */ +#ifdef __x86_64__ +static LONG CALLBACK +singlestep_handler (EXCEPTION_POINTERS *ep) +{ + if (_my_tls.suspend_on_exception) + { + _my_tls.in_singlestep_handler = true; + RtlWakeAddressSingle ((void *) &_my_tls.in_singlestep_handler); + while (_my_tls.suspend_on_exception) + ; /* Don't call yield() to prevent the thread + from being suspended in the kernel. */ + if (ep->ExceptionRecord->ExceptionCode == (DWORD) STATUS_SINGLE_STEP) + return EXCEPTION_CONTINUE_EXECUTION; + } + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + bool _cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, void *handler, struct sigaction& siga) @@ -925,6 +947,44 @@ _cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, void *handler, interrupted = false; else { +#ifdef __x86_64__ + /* When the Rip points to an instruction that causes an exception, + modifying Rip and calling ResumeThread() may sometimes result in + a crash. To prevent this, advance execution by a single instruction + by setting the trap flag (TF) before calling ResumeThread(). This + will trigger either STATUS_SINGLE_STEP or the exception caused by + the instruction that Rip originally pointed to. By suspending the + targeted thread within singlestep_handler(), Rip no longer points + to the problematic instruction, allowing safe handling of the + interrupt. As a result, Rip can be adjusted appropriately, + and the thread can resume execution without unexpected crashes. */ + if (!inside_kernel (cx, true)) + { + HANDLE h_veh = AddVectoredExceptionHandler (0, singlestep_handler); + cx->EFlags |= 0x100; /* Set TF (setup single step execution) */ + SetThreadContext (*this, cx); + suspend_on_exception = true; + in_singlestep_handler = false; + bool bool_false = false; + NTSTATUS status = STATUS_SUCCESS; + ResumeThread (*this); + while (!in_singlestep_handler && NT_SUCCESS (status)) + { + LARGE_INTEGER timeout; + timeout.QuadPart = -100000ULL; /* 10ms */ + status = RtlWaitOnAddress (&in_singlestep_handler, &bool_false, + sizeof (bool), &timeout); + if (status == STATUS_TIMEOUT) + break; + } + SuspendThread (*this); + GetThreadContext (*this, cx); + RemoveVectoredExceptionHandler (h_veh); + suspend_on_exception = false; + if (!NT_SUCCESS (status) || status == STATUS_TIMEOUT) + return false; /* Not interrupted */ + } +#endif DWORD64 &ip = cx->_CX_instPtr; push (ip); interrupt_setup (si, handler, siga); @@ -1281,6 +1341,7 @@ set_process_mask_delta () else oldmask = _my_tls.sigmask; newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE; + _my_tls.deltamask = 0; sigproc_printf ("oldmask %lx, newmask %lx, deltamask %lx", oldmask, newmask, _my_tls.deltamask); _my_tls.sigmask = newmask; @@ -1503,12 +1564,15 @@ sigpacket::process () if (tl_entry) { tls = tl_entry->thread; + tl_entry->thread->lock (); if (sigismember (&tls->sigwait_mask, si.si_signo)) issig_wait = true; - else if (!sigismember (&tls->sigmask, si.si_signo)) + else if (!sigismember (&tls->sigmask, si.si_signo) + && !sigismember (&tls->deltamask, si.si_signo)) issig_wait = false; else tls = NULL; + tl_entry->thread->unlock (); } } @@ -1756,7 +1820,7 @@ _cygtls::call_signal_handler () int this_errno = saved_errno; reset_signal_arrived (); - incyg = false; + incyg = 0; current_sig = 0; /* Flag that we can accept another signal */ /* We have to fetch the original return address from the signal stack @@ -1869,7 +1933,7 @@ _cygtls::call_signal_handler () } unlock (); - incyg = true; + incyg = 1; set_signal_mask (_my_tls.sigmask, (this_sa_flags & SA_SIGINFO) ? context1.uc_sigmask : this_oldmask); diff --git a/winsup/cygwin/fhandler/base.cc b/winsup/cygwin/fhandler/base.cc index a5d15c7..6c95e2b 100644 --- a/winsup/cygwin/fhandler/base.cc +++ b/winsup/cygwin/fhandler/base.cc @@ -526,8 +526,9 @@ fhandler_base::open (int flags, mode_t mode) ULONG file_attributes = 0; ULONG shared = (get_major () == DEV_TAPE_MAJOR ? 0 : FILE_SHARE_VALID_FLAGS); ULONG create_disposition; + FILE_BASIC_INFORMATION fbi; OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; + IO_STATUS_BLOCK io, io_bi; NTSTATUS status; PFILE_FULL_EA_INFORMATION p = NULL; ULONG plen = 0; @@ -719,16 +720,35 @@ fhandler_base::open (int flags, mode_t mode) goto done; } - if (io.Information == FILE_CREATED) + if (get_device () == FH_FS) { - /* Correct file attributes are needed for later use in, e.g. fchmod. */ - FILE_BASIC_INFORMATION fbi; - - if (!NT_SUCCESS (NtQueryInformationFile (fh, &io, &fbi, sizeof fbi, + /* Fix up file attributes, they are desperately needed later. + + Originally we only did that in the FILE_CREATED case below, but that's + insufficient: + + If two threads try to create the same file at the same time, it's + possible that path_conv::check returns the file as non-existant, i. e., + pc.file_attributes () returns INVALID_FILE_ATTRIBUTES, 0xffffffff. + However, one of the NtCreateFile will beat the other, so only one of + them returns with FILE_CREATED. + + The other fhandler_base::open() will instead run into the O_TRUNC + conditional (further below), blindly check for the SPARSE attribute + and remove that bit. The result is that the attributes will be + 0xfffffdff, i.e., everything but SPARSE. Most annoying is that + pc.isdir() will return TRUE. Hilarity ensues. + + Note that we use a different IO_STATUS_BLOCK, so as not to overwrite + io.Information... */ + if (!NT_SUCCESS (NtQueryInformationFile (fh, &io_bi, &fbi, sizeof fbi, FileBasicInformation))) fbi.FileAttributes = file_attributes | FILE_ATTRIBUTE_ARCHIVE; pc.file_attributes (fbi.FileAttributes); + } + if (io.Information == FILE_CREATED) + { /* Always create files using a NULL SD. Create correct permission bits afterwards, maintaining the owner and group information just like chmod. This is done for two reasons. @@ -752,18 +772,17 @@ fhandler_base::open (int flags, mode_t mode) set_created_file_access (fh, pc, mode); } - /* If you O_TRUNC a file on Linux, the data is truncated, but the EAs are - preserved. If you open a file on Windows with FILE_OVERWRITE{_IF} or - FILE_SUPERSEDE, all streams are truncated, including the EAs. So we don't - use the FILE_OVERWRITE{_IF} flags, but instead just open the file and set - the size of the data stream explicitely to 0. Apart from being more Linux - compatible, this implementation has the pleasant side-effect to be more - than 5% faster than using FILE_OVERWRITE{_IF} (tested on W7 32 bit). */ if ((flags & O_TRUNC) && (flags & O_ACCMODE) != O_RDONLY && io.Information != FILE_CREATED && get_device () == FH_FS) { + /* If you O_TRUNC a file on Linux, the data is truncated, but the EAs are + preserved. If you open a file on Windows with FILE_OVERWRITE{_IF} or + FILE_SUPERSEDE, all streams are truncated, including the EAs. So we + don't use FILE_OVERWRITE{_IF} but just open the file and truncate the + data stream to size 0. Apart from being more Linux compatible, this + has the pleasant side-effect to be more than 5% faster. */ FILE_END_OF_FILE_INFORMATION feofi = { EndOfFile:{ QuadPart:0 } }; status = NtSetInformationFile (fh, &io, &feofi, sizeof feofi, FileEndOfFileInformation); diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc index f162698..1ae4c63 100644 --- a/winsup/cygwin/fhandler/console.cc +++ b/winsup/cygwin/fhandler/console.cc @@ -509,7 +509,7 @@ fhandler_console::cons_master_thread (handle_set_t *p, tty *ttyp) case not_signalled_but_done: case done_with_debugger: processed = true; - ttyp->output_stopped = false; + ttyp->output_stopped &= ~BY_VSTOP; if (ti.c_lflag & NOFLSH) goto remove_record; con.num_processed = 0; @@ -771,6 +771,8 @@ fhandler_console::setup () con.disable_master_thread = true; con.master_thread_suspended = false; con.num_processed = 0; + con.curr_input_mode = tty::restore; + con.curr_output_mode = tty::restore; } } @@ -829,7 +831,7 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, break; case tty::cygwin: flags |= ENABLE_WINDOW_INPUT; - if (con.master_thread_suspended) + if (con.master_thread_suspended || con.disable_master_thread) flags |= ENABLE_PROCESSED_INPUT; if (wincap.has_con_24bit_colors () && !con_is_legacy) flags |= ENABLE_VIRTUAL_TERMINAL_INPUT; @@ -849,11 +851,6 @@ fhandler_console::set_input_mode (tty::cons_mode m, const termios *t, flags |= ENABLE_PROCESSED_INPUT; break; } - if (con.curr_input_mode != tty::cygwin && m == tty::cygwin) - { - prev_input_mode_backup = con.prev_input_mode; - con.prev_input_mode = oflags; - } con.curr_input_mode = m; SetConsoleMode (p->input_handle, flags); if (!(oflags & ENABLE_VIRTUAL_TERMINAL_INPUT) @@ -893,11 +890,6 @@ fhandler_console::set_output_mode (tty::cons_mode m, const termios *t, flags |= DISABLE_NEWLINE_AUTO_RETURN; break; } - if (con.curr_output_mode != tty::cygwin && m == tty::cygwin) - { - prev_output_mode_backup = con.prev_output_mode; - GetConsoleMode (p->output_handle, &con.prev_output_mode); - } con.curr_output_mode = m; acquire_attach_mutex (mutex_timeout); DWORD resume_pid = attach_console (con.owner); @@ -913,13 +905,14 @@ fhandler_console::setup_for_non_cygwin_app () /* Setting-up console mode for non-cygwin app. */ /* If conmode is set to tty::native for non-cygwin apps in background, tty settings of the shell is reflected - to the console mode of the app. So, use tty::restore - for background process instead. */ - tty::cons_mode conmode = - (get_ttyp ()->getpgid ()== myself->pgid) ? tty::native : tty::restore; - set_input_mode (conmode, &tc ()->ti, get_handle_set ()); - set_output_mode (conmode, &tc ()->ti, get_handle_set ()); - set_disable_master_thread (true, this); + to the console mode of the app. So, do not change the + console mode. */ + if (get_ttyp ()->getpgid () == myself->pgid) + { + set_input_mode (tty::native, &tc ()->ti, get_handle_set ()); + set_output_mode (tty::native, &tc ()->ti, get_handle_set ()); + set_disable_master_thread (true, this); + } } void @@ -932,7 +925,7 @@ fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p) /* Cleaning-up console mode for non-cygwin app. */ /* conmode can be tty::restore when non-cygwin app is exec'ed from login shell. */ - tty::cons_mode conmode = cons_mode_on_close (); + tty::cons_mode conmode = cons_mode_on_close (p); set_output_mode (conmode, ti, p); set_input_mode (conmode, ti, p); set_disable_master_thread (con.owner == GetCurrentProcessId ()); @@ -1144,6 +1137,15 @@ fhandler_console::read (void *pv, size_t& buflen) push_process_state process_state (PID_TTYIN); + if (get_ttyp ()->input_stopped && is_nonblocking ()) + { + set_errno (EAGAIN); + buflen = (size_t) -1; + return; + } + while (get_ttyp ()->input_stopped) + cygwait (10); + size_t copied_chars = 0; DWORD timeout = is_nonblocking () ? 0 : @@ -1836,6 +1838,12 @@ fhandler_console::open (int flags, mode_t) handle_set.output_handle = h; release_output_mutex (); + if (con.owner == GetCurrentProcessId ()) + { + GetConsoleMode (get_handle (), &con.prev_input_mode); + GetConsoleMode (get_output_handle (), &con.prev_output_mode); + } + wpbuf.init (); handle_set.input_mutex = input_mutex; @@ -1881,6 +1889,19 @@ fhandler_console::open (int flags, mode_t) setenv ("TERM", "cygwin", 1); } + if (con.curr_input_mode != tty::cygwin) + { + prev_input_mode_backup = con.prev_input_mode; + GetConsoleMode (get_handle (), &con.prev_input_mode); + set_input_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); + } + if (con.curr_output_mode != tty::cygwin) + { + prev_output_mode_backup = con.prev_output_mode; + GetConsoleMode (get_output_handle (), &con.prev_output_mode); + set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set); + } + debug_printf ("opened conin$ %p, conout$ %p", get_handle (), get_output_handle ()); @@ -1991,8 +2012,9 @@ fhandler_console::close (int flag) acquire_output_mutex (mutex_timeout); - if (shared_console_info[unit] && myself->ppid == 1 - && (dev_t) myself->ctty == get_device ()) + if (shared_console_info[unit] && con.curr_input_mode != tty::restore + && (dev_t) myself->ctty == get_device () + && cons_mode_on_close (&handle_set) == tty::restore) { set_output_mode (tty::restore, &get_ttyp ()->ti, &handle_set); set_input_mode (tty::restore, &get_ttyp ()->ti, &handle_set); @@ -2131,6 +2153,7 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) release_output_mutex (); return -1; case FIONREAD: + case TIOCINQ: { DWORD n; int ret = 0; @@ -2183,6 +2206,14 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) return 0; } break; + case TCXONC: + res = this->tcflow ((int)(intptr_t) arg); + release_output_mutex (); + return res; + case TCFLSH: + res = this->tcflush ((int)(intptr_t) arg); + release_output_mutex (); + return res; } release_output_mutex (); @@ -2215,6 +2246,8 @@ int fhandler_console::tcsetattr (int a, struct termios const *t) { get_ttyp ()->ti = *t; + set_input_mode (tty::cygwin, t, &handle_set); + set_output_mode (tty::cygwin, t, &handle_set); return 0; } @@ -4170,8 +4203,8 @@ fhandler_console::write (const void *vsrc, size_t len) void fhandler_console::doecho (const void *str, DWORD len) { - bool stopped = get_ttyp ()->output_stopped; - get_ttyp ()->output_stopped = false; + int stopped = get_ttyp ()->output_stopped; + get_ttyp ()->output_stopped = 0; write (str, len); get_ttyp ()->output_stopped = stopped; } @@ -4702,10 +4735,31 @@ fhandler_console::fstat (struct stat *st) } tty::cons_mode -fhandler_console::cons_mode_on_close () +fhandler_console::cons_mode_on_close (handle_set_t *p) { + int unit = p->unit; if (myself->ppid != 1) /* Execed from normal cygwin process. */ return tty::cygwin; + if (!process_alive (con.owner)) /* The Master process already died. */ + return tty::restore; + if (con.owner == GetCurrentProcessId ()) /* Master process */ + return tty::restore; + + PROCESS_BASIC_INFORMATION pbi; + NTSTATUS status = + NtQueryInformationProcess (GetCurrentProcess (), ProcessBasicInformation, + &pbi, sizeof (pbi), NULL); + if (NT_SUCCESS (status) && cygwin_pid (con.owner) + && !process_alive ((DWORD) pbi.InheritedFromUniqueProcessId)) + /* Execed from normal cygwin process and the parent has been exited. */ + return tty::cygwin; + return tty::restore; /* otherwise, restore */ } + +int +fhandler_console::tcdrain () +{ + return 0; +} diff --git a/winsup/cygwin/fhandler/pipe.cc b/winsup/cygwin/fhandler/pipe.cc index e35d523..2312688 100644 --- a/winsup/cygwin/fhandler/pipe.cc +++ b/winsup/cygwin/fhandler/pipe.cc @@ -326,7 +326,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len) ULONG_PTR nbytes_now = 0; ULONG len1 = (ULONG) (len - nbytes); DWORD select_sem_timeout = 0; - bool real_non_blocking_mode = false; FILE_PIPE_LOCAL_INFORMATION fpli; status = NtQueryInformationFile (get_handle (), &io, @@ -393,7 +392,10 @@ fhandler_pipe::raw_read (void *ptr, size_t& len) status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, ptr, len1, NULL, NULL); if (real_non_blocking_mode) - set_pipe_non_blocking (false); + { + set_pipe_non_blocking (false); + real_non_blocking_mode = false; + } if (isclosed ()) /* A signal handler might have closed the fd. */ { set_errno (EBADF); @@ -443,7 +445,6 @@ ssize_t fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) { size_t nbytes = 0; - ULONG chunk; NTSTATUS status = STATUS_SUCCESS; IO_STATUS_BLOCK io; HANDLE evt; @@ -453,7 +454,6 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) return 0; ssize_t avail = pipe_buf_size; - bool real_non_blocking_mode = false; /* Workaround for native ninja. Native ninja creates pipe with size == 0, and starts cygwin process with that pipe. */ @@ -491,14 +491,14 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) FilePipeLocalInformation); if (NT_SUCCESS (status)) { - if (fpli.WriteQuotaAvailable != 0) + if (fpli.WriteQuotaAvailable == fpli.InboundQuota) avail = fpli.WriteQuotaAvailable; - else /* WriteQuotaAvailable == 0 */ + else /* WriteQuotaAvailable != InboundQuota */ { /* Refer to the comment in select.cc: pipe_data_available(). */ /* NtSetInformationFile() in set_pipe_non_blocking(true) seems to fail with STATUS_PIPE_BUSY if the pipe is not empty. - In this case, the pipe is really full if WriteQuotaAvailable - is zero. Otherwise, the pipe is empty. */ + In this case, WriteQuotaAvailable indicates real pipe space. + Otherwise, the pipe is empty. */ status = fh->set_pipe_non_blocking (true); if (NT_SUCCESS (status)) /* Pipe should be empty because reader is waiting for data. */ @@ -506,9 +506,14 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) fh->set_pipe_non_blocking (false); else if (status == STATUS_PIPE_BUSY) { - /* Full */ - set_errno (EAGAIN); - goto err; + if (fpli.WriteQuotaAvailable == 0) + { + /* Full */ + set_errno (EAGAIN); + goto err; + } + avail = fpli.WriteQuotaAvailable; + status = STATUS_SUCCESS; } } } @@ -540,11 +545,6 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) } } - if (len <= (size_t) avail) - chunk = len; - else - chunk = avail; - if (!(evt = CreateEvent (NULL, false, false, NULL))) { __seterrno (); @@ -561,8 +561,8 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) ULONG len1; DWORD waitret = WAIT_OBJECT_0; - if (left > chunk && !is_nonblocking ()) - len1 = chunk; + if (left > (size_t) avail && !is_nonblocking ()) + len1 = (ULONG) avail; else len1 = (ULONG) left; @@ -650,9 +650,7 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) if (io.Information > 0 || len <= PIPE_BUF || short_write_once) break; /* Independent of being blocking or non-blocking, if we're here, - the pipe has less space than requested. If the pipe is a - non-Cygwin pipe, just try the old strategy of trying a half - write. If the pipe has at + the pipe has less space than requested. If the pipe has at least PIPE_BUF bytes available, try to write all matching PIPE_BUF sized blocks. If it's less than PIPE_BUF, try the next less power of 2 bytes. This is not really the Linux @@ -660,12 +658,13 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) in a very implementation-defined way we can't emulate, but it resembles it closely enough to get useful results. */ avail = pipe_data_available (-1, this, get_handle (), PDA_WRITE); - if (avail < 1) /* error or pipe closed */ + if (avail == PDA_UNKNOWN && real_non_blocking_mode) + avail = len1; + else if (avail == 0 || !PDA_NOERROR (avail)) + /* error or pipe closed */ break; if (avail > len1) /* somebody read from the pipe */ avail = len1; - if (avail == 1) /* 1 byte left or non-Cygwin pipe */ - len1 >>= 1; else if (avail >= PIPE_BUF) len1 = avail & ~(PIPE_BUF - 1); else @@ -706,7 +705,10 @@ fhandler_pipe_fifo::raw_write (const void *ptr, size_t len) } if (real_non_blocking_mode) - ((fhandler_pipe *) this)->set_pipe_non_blocking (false); + { + ((fhandler_pipe *) this)->set_pipe_non_blocking (false); + real_non_blocking_mode = false; + } CloseHandle (evt); if (pipe_mtx) /* pipe_mtx is NULL in the fifo case */ diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc index 3128b92..b882b90 100644 --- a/winsup/cygwin/fhandler/pty.cc +++ b/winsup/cygwin/fhandler/pty.cc @@ -1304,6 +1304,15 @@ fhandler_pty_slave::read (void *ptr, size_t& len) push_process_state process_state (PID_TTYIN); + if (get_ttyp ()->input_stopped && is_nonblocking ()) + { + set_errno (EAGAIN); + len = (size_t) -1; + return; + } + while (get_ttyp ()->input_stopped) + cygwait (10); + if (ptr) /* Indicating not tcflush(). */ mask_switch_to_nat_pipe (true, true); @@ -1650,6 +1659,7 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg) retval = this->tcsetpgrp ((pid_t) (intptr_t) arg); goto out; case FIONREAD: + case TIOCINQ: { DWORD n; if (!bytes_available (n)) @@ -1664,6 +1674,12 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg) } } goto out; + case TCXONC: + retval = this->tcflow ((int)(intptr_t) arg); + goto out; + case TCFLSH: + retval = this->tcflush ((int)(intptr_t) arg); + goto out; default: return fhandler_base::ioctl (cmd, arg); } @@ -2342,6 +2358,7 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg) case TIOCSPGRP: return this->tcsetpgrp ((pid_t) (intptr_t) arg); case FIONREAD: + case TIOCINQ: { DWORD n; if (!bytes_available (n)) @@ -2352,6 +2369,10 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg) *(int *) arg = (int) n; } break; + case TCXONC: + return this->tcflow ((int)(intptr_t) arg); + case TCFLSH: + return this->tcflush ((int)(intptr_t) arg); default: return fhandler_base::ioctl (cmd, arg); } @@ -4194,3 +4215,12 @@ fhandler_pty_common::resume_from_temporarily_attach (DWORD resume_pid) } release_attach_mutex (); } + +int +fhandler_pty_common::tcdrain () +{ + DWORD n; + while (bytes_available (n) && n > 0) + cygwait (10); + return 0; +} diff --git a/winsup/cygwin/fhandler/socket_inet.cc b/winsup/cygwin/fhandler/socket_inet.cc index 22dfed6..5ed0cb0 100644 --- a/winsup/cygwin/fhandler/socket_inet.cc +++ b/winsup/cygwin/fhandler/socket_inet.cc @@ -20,7 +20,12 @@ #undef u_long #define u_long __ms_u_long #include <w32api/ws2tcpip.h> +/* 2025-06-09: win32api headers v13 now define a cmsghdr type which clashes with + our socket.h. Arrange not to see it here. */ +#undef cmsghdr +#define cmsghdr __ms_cmsghdr #include <w32api/mswsock.h> +#undef cmsghdr #include <w32api/mstcpip.h> #include <netinet/tcp.h> #include <netinet/udp.h> diff --git a/winsup/cygwin/fhandler/socket_local.cc b/winsup/cygwin/fhandler/socket_local.cc index 270a1ef..0498edc 100644 --- a/winsup/cygwin/fhandler/socket_local.cc +++ b/winsup/cygwin/fhandler/socket_local.cc @@ -21,7 +21,12 @@ #define u_long __ms_u_long #include "ntsecapi.h" #include <w32api/ws2tcpip.h> +/* 2025-06-09: win32api headers v13 now define a cmsghdr type which clashes with + our socket.h. Arrange not to see it here. */ +#undef cmsghdr +#define cmsghdr __ms_cmsghdr #include <w32api/mswsock.h> +#undef cmsghdr #include <unistd.h> #include <asm/byteorder.h> #include <sys/socket.h> @@ -87,6 +92,8 @@ get_inet_addr_local (const struct sockaddr *in, int inlen, addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); *outlen = sizeof addr; memcpy (out, &addr, *outlen); + if (type) + *type = SOCK_DGRAM; return 0; } diff --git a/winsup/cygwin/fhandler/termios.cc b/winsup/cygwin/fhandler/termios.cc index a3cecdb..19d6220 100644 --- a/winsup/cygwin/fhandler/termios.cc +++ b/winsup/cygwin/fhandler/termios.cc @@ -491,16 +491,16 @@ fhandler_termios::process_stop_start (char c, tty *ttyp) { if (CCEQ (ti.c_cc[VSTOP], c)) { - ttyp->output_stopped = true; + ttyp->output_stopped |= BY_VSTOP; return true; } else if (CCEQ (ti.c_cc[VSTART], c)) { restart_output: - ttyp->output_stopped = false; + ttyp->output_stopped &= ~BY_VSTOP; return true; } - else if ((ti.c_iflag & IXANY) && ttyp->output_stopped) + else if ((ti.c_iflag & IXANY) && (ttyp->output_stopped & BY_VSTOP)) goto restart_output; } if ((ti.c_lflag & ICANON) && (ti.c_lflag & IEXTEN) @@ -540,7 +540,7 @@ fhandler_termios::line_edit (const char *rptr, size_t nread, termios& ti, fallthrough; case not_signalled_but_done: case done_with_debugger: - get_ttyp ()->output_stopped = false; + get_ttyp ()->output_stopped &= ~BY_VSTOP; continue; case not_signalled_with_nat_reader: disable_eof_key = true; @@ -915,3 +915,26 @@ fhandler_termios::get_console_process_id (DWORD pid, bool match, } return res_pri ?: res; } + +int +fhandler_termios::tcflow (int action) +{ + switch (action) + { + case TCOOFF: + get_ttyp ()->output_stopped |= BY_TCFLOW; + return 0; + case TCOON: + get_ttyp ()->output_stopped = 0; + return 0; + case TCIOFF: + get_ttyp ()->input_stopped |= BY_TCFLOW; + return 0; + case TCION: + get_ttyp ()->input_stopped = 0; + return 0; + default: + set_errno (EINVAL); + return -1; + } +} diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 783971b..4abc525 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -187,7 +187,6 @@ frok::child (volatile char * volatile here) ForceCloseHandle1 (fork_info->forker_finished, forker_finished); - pthread::atforkchild (); cygbench ("fork-child"); ld_preload (); fixup_hooks_after_fork (); @@ -199,6 +198,7 @@ frok::child (volatile char * volatile here) CloseHandle (hParent); hParent = NULL; cygwin_finished_initializing = true; + pthread::atforkchild (); return 0; } @@ -660,8 +660,10 @@ dofork (void **proc, bool *with_forkables) ischild = !!setjmp (grouped.ch.jmp); volatile char * volatile stackp; -#ifdef __x86_64__ +#if defined(__x86_64__) __asm__ volatile ("movq %%rsp,%0": "=r" (stackp)); +#elif defined(__aarch64__) + __asm__ volatile ("mov %0, sp" : "=r" (stackp)); #else #error unimplemented for this target #endif diff --git a/winsup/cygwin/include/cygwin/config.h b/winsup/cygwin/include/cygwin/config.h index 2a70832..d9f911d 100644 --- a/winsup/cygwin/include/cygwin/config.h +++ b/winsup/cygwin/include/cygwin/config.h @@ -36,8 +36,13 @@ __attribute__((__gnu_inline__)) extern inline struct _reent *__getreent (void) { register char *ret; -#ifdef __x86_64__ +#if defined(__x86_64__) __asm __volatile__ ("movq %%gs:8,%0" : "=r" (ret)); +#elif defined(__aarch64__) + /* x18 register points to TEB, offset 0x8 points to stack base. + See _TEB structure definition in winsup\cygwin\local_includes\ntdll.h + for more details. */ + __asm __volatile__ ("ldr %0, [x18, #0x8]" : "=r" (ret)); #else #error unimplemented for this target #endif diff --git a/winsup/cygwin/include/cygwin/limits.h b/winsup/cygwin/include/cygwin/limits.h index 204154d..728dfd4 100644 --- a/winsup/cygwin/include/cygwin/limits.h +++ b/winsup/cygwin/include/cygwin/limits.h @@ -43,7 +43,13 @@ details. */ #define __SEM_VALUE_MAX 1147483648 #define __SIGQUEUE_MAX 1024 #define __STREAM_MAX 20 -#define __SYMLOOP_MAX 10 +/* __SYMLOOP_MAX + https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-points + ... There is a limit of 63 reparse points on any given path. + NOTE: The limit can be reduced depending on the length of the + reparse point. For example, if your reparse point targets a fully + qualified path, the limit becomes 31. */ +#define __SYMLOOP_MAX 63 #define __TIMER_MAX 32 #define __TTY_NAME_MAX 32 #define __FILESIZEBITS 64 diff --git a/winsup/cygwin/include/cygwin/time.h b/winsup/cygwin/include/cygwin/time.h index 9b63e9a..d7f9d3f 100644 --- a/winsup/cygwin/include/cygwin/time.h +++ b/winsup/cygwin/include/cygwin/time.h @@ -35,12 +35,6 @@ extern long timezone __asm__ (_SYMSTR (_timezone)); #endif /* __SVID_VISIBLE || __XSI_VISIBLE */ -#if __ISO_C_VISIBLE >= 2011 -#define TIME_UTC 1 - -extern int timespec_get (struct timespec *, int); -#endif - #ifdef __cplusplus } #endif diff --git a/winsup/cygwin/include/pthread.h b/winsup/cygwin/include/pthread.h index 8e29630..01cabee 100644 --- a/winsup/cygwin/include/pthread.h +++ b/winsup/cygwin/include/pthread.h @@ -31,8 +31,6 @@ extern "C" #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_DISABLE 1 #define PTHREAD_CANCELED ((void *)-1) -/* this should be a value that can never be a valid address */ -#define PTHREAD_COND_INITIALIZER (pthread_cond_t)21 #define PTHREAD_CREATE_DETACHED 1 /* the default : joinable */ #define PTHREAD_CREATE_JOINABLE 0 @@ -42,10 +40,6 @@ extern "C" #define PTHREAD_MUTEX_ERRORCHECK 1 #define PTHREAD_MUTEX_NORMAL 2 #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL -/* this should be too low to ever be a valid address */ -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP (pthread_mutex_t)18 -#define PTHREAD_NORMAL_MUTEX_INITIALIZER_NP (pthread_mutex_t)19 -#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (pthread_mutex_t)20 #define PTHREAD_MUTEX_INITIALIZER PTHREAD_NORMAL_MUTEX_INITIALIZER_NP #define PTHREAD_ONCE_INIT { PTHREAD_MUTEX_INITIALIZER, 0 } #if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT >= 0 @@ -55,12 +49,35 @@ extern "C" #endif #define PTHREAD_PROCESS_SHARED 1 #define PTHREAD_PROCESS_PRIVATE 0 -#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t)22 /* process is the default */ #define PTHREAD_SCOPE_PROCESS 0 #define PTHREAD_SCOPE_SYSTEM 1 #define PTHREAD_BARRIER_SERIAL_THREAD (-1) +/* This condition matches the one in <sys/_pthreadtypes.h> */ +#if !defined(__INSIDE_CYGWIN__) || !defined(__cplusplus) +/* Constants for initializer macros */ +extern struct __pthread_mutex_t __pthread_recursive_mutex_initializer_np; +extern struct __pthread_mutex_t __pthread_normal_mutex_initializer_np; +extern struct __pthread_mutex_t __pthread_errorcheck_mutex_initializer_np; +extern struct __pthread_cond_t __pthread_cond_initializer; +extern struct __pthread_rwlock_t __pthread_rwlock_initializer; +#define PTHREAD_COND_INITIALIZER (&__pthread_cond_initializer) +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP (&__pthread_recursive_mutex_initializer_np) +#define PTHREAD_NORMAL_MUTEX_INITIALIZER_NP (&__pthread_normal_mutex_initializer_np) +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (&__pthread_errorcheck_mutex_initializer_np) +#define PTHREAD_RWLOCK_INITIALIZER (&__pthread_rwlock_initializer) +#else +/* Inside the Cygwin DLL's C++ code, using absolute linker symbols sometimes + results in "relocation truncated to fit" errors due to being built with + -mcmodel=small. */ +#define PTHREAD_COND_INITIALIZER (pthread_cond_t)21 +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP (pthread_mutex_t)18 +#define PTHREAD_NORMAL_MUTEX_INITIALIZER_NP (pthread_mutex_t)19 +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (pthread_mutex_t)20 +#define PTHREAD_RWLOCK_INITIALIZER (pthread_rwlock_t)22 +#endif + /* Register Fork Handlers */ int pthread_atfork (void (*)(void), void (*)(void), void (*)(void)); @@ -123,7 +140,7 @@ int pthread_cond_broadcast (pthread_cond_t *); int pthread_cond_destroy (pthread_cond_t *); int pthread_cond_init (pthread_cond_t *, const pthread_condattr_t *); int pthread_cond_signal (pthread_cond_t *); -#if __GNU_VISIBLE +#if __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 int pthread_cond_clockwait (pthread_cond_t *, pthread_mutex_t *, clockid_t, const struct timespec *); #endif @@ -170,7 +187,7 @@ int pthread_mutex_getprioceiling (const pthread_mutex_t *, int *); int pthread_mutex_init (pthread_mutex_t *, const pthread_mutexattr_t *); int pthread_mutex_lock (pthread_mutex_t *); int pthread_mutex_setprioceiling (pthread_mutex_t *, int, int *); -#if __GNU_VISIBLE +#if __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 int pthread_mutex_clocklock (pthread_mutex_t *, clockid_t, const struct timespec *); #endif @@ -202,14 +219,14 @@ int pthread_spin_unlock (pthread_spinlock_t *); int pthread_rwlock_destroy (pthread_rwlock_t *); int pthread_rwlock_init (pthread_rwlock_t *, const pthread_rwlockattr_t *); int pthread_rwlock_rdlock (pthread_rwlock_t *); -#if __GNU_VISIBLE +#if __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 int pthread_rwlock_clockrdlock (pthread_rwlock_t *, clockid_t, const struct timespec *); #endif int pthread_rwlock_timedrdlock (pthread_rwlock_t *, const struct timespec *); int pthread_rwlock_tryrdlock (pthread_rwlock_t *); int pthread_rwlock_wrlock (pthread_rwlock_t *); -#if __GNU_VISIBLE +#if __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 int pthread_rwlock_clockwrlock (pthread_rwlock_t *, clockid_t, const struct timespec *); #endif diff --git a/winsup/cygwin/include/search.h b/winsup/cygwin/include/search.h index f532eae..7c6d7b4 100644 --- a/winsup/cygwin/include/search.h +++ b/winsup/cygwin/include/search.h @@ -39,6 +39,8 @@ typedef struct node } node_t; #endif +typedef void posix_tnode; + struct hsearch_data { struct internal_head *htable; @@ -58,13 +60,13 @@ ENTRY *hsearch (ENTRY, ACTION); int hcreate_r (size_t, struct hsearch_data *); void hdestroy_r (struct hsearch_data *); int hsearch_r (ENTRY, ACTION, ENTRY **, struct hsearch_data *); -void *tdelete (const void * __restrict, void ** __restrict, +void *tdelete (const void * __restrict, posix_tnode ** __restrict, int (*) (const void *, const void *)); void tdestroy (void *, void (*)(void *)); -void *tfind (const void *, void **, +posix_tnode *tfind (const void *, posix_tnode *const *, int (*) (const void *, const void *)); -void *tsearch (const void *, void **, int (*) (const void *, const void *)); -void twalk (const void *, void (*) (const void *, VISIT, int)); +posix_tnode *tsearch (const void *, posix_tnode **, int (*) (const void *, const void *)); +void twalk (const posix_tnode *, void (*) (const posix_tnode *, VISIT, int)); void *lfind (const void *, const void *, size_t *, size_t, int (*) (const void *, const void *)); void *lsearch (const void *, void *, size_t *, size_t, diff --git a/winsup/cygwin/include/semaphore.h b/winsup/cygwin/include/semaphore.h index e20bdc5..6ff462d 100644 --- a/winsup/cygwin/include/semaphore.h +++ b/winsup/cygwin/include/semaphore.h @@ -32,7 +32,7 @@ extern "C" int sem_unlink (const char *__name); int sem_wait (sem_t *__sem); int sem_trywait (sem_t *__sem); -#if __GNU_VISIBLE +#if __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 int sem_clockwait (sem_t *__sem, clockid_t __clock_id, const struct timespec *__abstime); #endif int sem_timedwait (sem_t *__sem, const struct timespec *__abstime); diff --git a/winsup/cygwin/include/sys/poll.h b/winsup/cygwin/include/sys/poll.h index 65822ed..5d58b36 100644 --- a/winsup/cygwin/include/sys/poll.h +++ b/winsup/cygwin/include/sys/poll.h @@ -39,7 +39,7 @@ struct pollfd { typedef unsigned int nfds_t; extern int poll __P ((struct pollfd *fds, nfds_t nfds, int timeout)); -#if __GNU_VISIBLE +#if __GNU_VISIBLE || __POSIX_VISIBLE >= 202405 extern int ppoll __P ((struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask)); diff --git a/winsup/cygwin/include/sys/termios.h b/winsup/cygwin/include/sys/termios.h index d1b4a0a..4c03042 100644 --- a/winsup/cygwin/include/sys/termios.h +++ b/winsup/cygwin/include/sys/termios.h @@ -18,6 +18,7 @@ details. */ #define TIOCMBIC 0x5417 #define TIOCMSET 0x5418 #define TIOCINQ 0x541B +#define TCXONC 0x540A #define TIOCSCTTY 0x540E /* TIOCINQ is utilized instead of FIONREAD which has been diff --git a/winsup/cygwin/lib/pthreadconst.S b/winsup/cygwin/lib/pthreadconst.S new file mode 100644 index 0000000..6e55a83 --- /dev/null +++ b/winsup/cygwin/lib/pthreadconst.S @@ -0,0 +1,17 @@ +#if defined(__i386__) +# define SYM(x) _##x +#else +# define SYM(x) x +#endif + +/* these should all be too low to ever be valid addresses */ +.globl SYM(__pthread_recursive_mutex_initializer_np) +.set __pthread_recursive_mutex_initializer_np, 18 +.globl SYM(__pthread_normal_mutex_initializer_np) +.set __pthread_normal_mutex_initializer_np, 19 +.globl SYM(__pthread_errorcheck_mutex_initializer_np) +.set __pthread_errorcheck_mutex_initializer_np, 20 +.globl SYM(__pthread_cond_initializer) +.set __pthread_cond_initializer, 21 +.globl SYM(__pthread_rwlock_initializer) +.set __pthread_rwlock_initializer, 22 diff --git a/winsup/cygwin/local_includes/cygmalloc.h b/winsup/cygwin/local_includes/cygmalloc.h index 5e1fe81..898ea56 100644 --- a/winsup/cygwin/local_includes/cygmalloc.h +++ b/winsup/cygwin/local_includes/cygmalloc.h @@ -21,7 +21,10 @@ int dlmalloc_trim (size_t); int dlmallopt (int p, int v); void dlmalloc_stats (); +// Already defined for AArch64 in newlib/libc/include/sys/config.h +#ifndef MALLOC_ALIGNMENT #define MALLOC_ALIGNMENT ((size_t)16U) +#endif #if defined (DLMALLOC_VERSION) /* Building malloc.cc */ diff --git a/winsup/cygwin/local_includes/cygtls.h b/winsup/cygwin/local_includes/cygtls.h index 079ada9..9f83c13 100644 --- a/winsup/cygwin/local_includes/cygtls.h +++ b/winsup/cygwin/local_includes/cygtls.h @@ -198,11 +198,13 @@ public: /* Do NOT remove this public: line, it's a marker for gentls_offsets. */ class san *andreas; waitq wq; volatile int current_sig; - unsigned incyg; + volatile unsigned incyg; volatile unsigned stacklock; __tlsstack_t *stackptr; __tlsstack_t stack[TLS_STACK_SIZE]; unsigned initialized; + volatile bool suspend_on_exception; + volatile bool in_singlestep_handler; public: /* Do NOT remove this public: line, it's a marker for gentls_offsets. */ void init_thread (void *, DWORD (*) (void *, void *)); @@ -229,7 +231,7 @@ public: /* Do NOT remove this public: line, it's a marker for gentls_offsets. */ bool interrupt_now (CONTEXT *, siginfo_t&, void *, struct sigaction&); void interrupt_setup (siginfo_t&, void *, struct sigaction&); - bool inside_kernel (CONTEXT *); + bool inside_kernel (CONTEXT *, bool inside_cygwin = false); void signal_debugger (siginfo_t&); #ifdef CYGTLS_HANDLE @@ -242,8 +244,11 @@ public: /* Do NOT remove this public: line, it's a marker for gentls_offsets. */ { while (InterlockedExchange (&stacklock, 1)) { -#ifdef __x86_64__ +#if defined(__x86_64__) __asm__ ("pause"); +#elif defined(__aarch64__) + __asm__ ("dmb ishst\n" + "yield"); #else #error unimplemented for this target #endif @@ -321,7 +326,13 @@ public: address of the _except block to restore the context correctly. See comment preceeding myfault_altstack_handler in exception.cc. */ ret = (DWORD64) _ret; +#if defined(__x86_64__) __asm__ volatile ("movq %%rsp,%0": "=o" (frame)); +#elif defined(__aarch64__) + __asm__ volatile ("mov %0, sp" : "=r" (frame)); +#else +#error unimplemented for this target +#endif } ~san () __attribute__ ((always_inline)) { diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h index 8c71d84..04e2ca4 100644 --- a/winsup/cygwin/local_includes/fhandler.h +++ b/winsup/cygwin/local_includes/fhandler.h @@ -1203,6 +1203,7 @@ class fhandler_pipe_fifo: public fhandler_base protected: size_t pipe_buf_size; HANDLE pipe_mtx; /* Used only in the pipe case */ + bool real_non_blocking_mode; /* Used only in the pipe case */ virtual void release_select_sem (const char *) {}; IMPLEMENT_STATUS_FLAG (bool, isclosed) @@ -1212,6 +1213,8 @@ class fhandler_pipe_fifo: public fhandler_base virtual bool reader_closed () { return false; }; ssize_t raw_write (const void *ptr, size_t len); + + friend ssize_t pipe_data_available (int, fhandler_base *, HANDLE, int); }; class fhandler_pipe: public fhandler_pipe_fifo @@ -1699,9 +1702,9 @@ class fhandler_disk_file: public fhandler_base uint64_t fs_ioc_getflags (); int fs_ioc_setflags (uint64_t); - falloc_allocate (int, off_t, off_t); - falloc_punch_hole (off_t, off_t); - falloc_zero_range (int, off_t, off_t); + int falloc_allocate (int, off_t, off_t); + int falloc_punch_hole (off_t, off_t); + int falloc_zero_range (int, off_t, off_t); public: fhandler_disk_file (); @@ -1979,6 +1982,7 @@ class fhandler_termios: public fhandler_base virtual off_t lseek (off_t, int); pid_t tcgetsid (); virtual int fstat (struct stat *buf); + int tcflow (int); fhandler_termios (void *) {} @@ -2143,12 +2147,12 @@ class dev_console char cons_rabuf[40]; // cannot get longer than char buf[40] in char_command char *cons_rapoi; bool cursor_key_app_mode; - bool disable_master_thread; + volatile bool disable_master_thread; tty::cons_mode curr_input_mode; tty::cons_mode curr_output_mode; DWORD prev_input_mode; DWORD prev_output_mode; - bool master_thread_suspended; + volatile bool master_thread_suspended; int num_processed; /* Number of input events in the current input buffer already processed by cons_master_thread(). */ @@ -2273,6 +2277,7 @@ private: int tcflush (int); int tcsetattr (int a, const struct termios *t); int tcgetattr (struct termios *t); + int tcdrain (); int ioctl (unsigned int cmd, void *); int init (HANDLE, DWORD, mode_t, int64_t = 0); @@ -2368,7 +2373,7 @@ private: void setup_pcon_hand_over (); static void pcon_hand_over_proc (); - static tty::cons_mode cons_mode_on_close (); + static tty::cons_mode cons_mode_on_close (handle_set_t *); friend tty_min * tty_list::get_cttyp (); }; @@ -2391,6 +2396,7 @@ class fhandler_pty_common: public fhandler_termios DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms); void __release_output_mutex (const char *fn, int ln); + int tcdrain (); int close (int flag = -1); off_t lseek (off_t, int); bool bytes_available (DWORD& n); diff --git a/winsup/cygwin/local_includes/ntdll.h b/winsup/cygwin/local_includes/ntdll.h index 97a83d1..1990893 100644 --- a/winsup/cygwin/local_includes/ntdll.h +++ b/winsup/cygwin/local_includes/ntdll.h @@ -490,6 +490,8 @@ typedef struct _FILE_DISPOSITION_INFORMATION_EX // 64 ULONG Flags; } FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX; +#if __MINGW64_VERSION_MAJOR < 13 + typedef struct _FILE_STAT_INFORMATION // 68 { LARGE_INTEGER FileId; @@ -510,6 +512,8 @@ typedef struct _FILE_CASE_SENSITIVE_INFORMATION // 71 ULONG Flags; } FILE_CASE_SENSITIVE_INFORMATION, *PFILE_CASE_SENSITIVE_INFORMATION; +#endif + enum { FILE_LINK_REPLACE_IF_EXISTS = 0x01, FILE_LINK_POSIX_SEMANTICS = 0x02, @@ -1362,7 +1366,8 @@ typedef enum _THREADINFOCLASS ThreadBasicInformation = 0, ThreadTimes = 1, ThreadImpersonationToken = 5, - ThreadQuerySetWin32StartAddress = 9 + ThreadQuerySetWin32StartAddress = 9, + ThreadSuspendCount = 35 } THREADINFOCLASS, *PTHREADINFOCLASS; typedef struct _THREAD_BASIC_INFORMATION @@ -1655,6 +1660,8 @@ extern "C" BOOLEAN); WCHAR RtlUpcaseUnicodeChar (WCHAR); NTSTATUS RtlUpcaseUnicodeString (PUNICODE_STRING, PUNICODE_STRING, BOOLEAN); + VOID RtlWakeAddressSingle (PVOID); + NTSTATUS RtlWaitOnAddress (volatile void *, PVOID, SIZE_T, PLARGE_INTEGER); NTSTATUS RtlWriteRegistryValue (ULONG, PCWSTR, PCWSTR, ULONG, PVOID, ULONG); #ifdef __cplusplus diff --git a/winsup/cygwin/local_includes/select.h b/winsup/cygwin/local_includes/select.h index 43ceb1d..afc05e1 100644 --- a/winsup/cygwin/local_includes/select.h +++ b/winsup/cygwin/local_includes/select.h @@ -143,5 +143,8 @@ ssize_t pipe_data_available (int, fhandler_base *, HANDLE, int); #define PDA_READ 0x00 #define PDA_WRITE 0x01 +#define PDA_ERROR -1 +#define PDA_UNKNOWN -2 +#define PDA_NOERROR(x) (x >= 0) #endif /* _SELECT_H_ */ diff --git a/winsup/cygwin/local_includes/thread.h b/winsup/cygwin/local_includes/thread.h index b349628..cbbbc3f 100644 --- a/winsup/cygwin/local_includes/thread.h +++ b/winsup/cygwin/local_includes/thread.h @@ -221,13 +221,12 @@ public: ~pthread_key (); static void fixup_before_fork () { - keys.for_each (&pthread_key::_fixup_before_fork); + for_each (&pthread_key::_fixup_before_fork); } static void fixup_after_fork () { - keys.fixup_after_fork (); - keys.for_each (&pthread_key::_fixup_after_fork); + for_each (&pthread_key::_fixup_after_fork); } static void run_all_destructors () @@ -246,21 +245,39 @@ public: for (int i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS; ++i) { iterate_dtors_once_more = false; - keys.for_each (&pthread_key::run_destructor); + for_each (&pthread_key::run_destructor); if (!iterate_dtors_once_more) break; } } - /* List support calls */ - class pthread_key *next; private: - static List<pthread_key> keys; + int key_idx; + static class keys_list { + LONG64 seq; + LONG64 busy_cnt; + pthread_key *key; + static bool used (LONG64 seq1) { return (seq1 & 3) != 0; } + static bool ready (LONG64 seq1) { return (seq1 & 3) == 2; } + public: + keys_list () : seq (0), busy_cnt (INT64_MIN), key (NULL) {} + friend class pthread_key; + } keys[PTHREAD_KEYS_MAX]; void _fixup_before_fork (); void _fixup_after_fork (); void (*destructor) (void *); void run_destructor (); void *fork_buf; + static void for_each (void (pthread_key::*callback) ()) { + for (size_t cnt = 0; cnt < PTHREAD_KEYS_MAX; cnt++) + { + if (!keys_list::ready (keys[cnt].seq)) + continue; + if (InterlockedIncrement64 (&keys[cnt].busy_cnt) > 0) + (keys[cnt].key->*callback) (); + InterlockedDecrement64 (&keys[cnt].busy_cnt); + } + } }; class pthread_attr: public verifyable_object diff --git a/winsup/cygwin/local_includes/tty.h b/winsup/cygwin/local_includes/tty.h index 2a047d7..a418ab1 100644 --- a/winsup/cygwin/local_includes/tty.h +++ b/winsup/cygwin/local_includes/tty.h @@ -30,6 +30,9 @@ details. */ #define MIN_CTRL_C_SLOP 50 #endif +#define BY_TCFLOW 2 +#define BY_VSTOP 1 + typedef void *HPCON; #include "devices.h" @@ -43,7 +46,8 @@ class tty_min public: pid_t pgid; - bool output_stopped; /* FIXME: Maybe do this with a mutex someday? */ + volatile int output_stopped; /* FIXME: Maybe do this with a mutex someday? */ + volatile int input_stopped; fh_devices ntty; ULONGLONG last_ctrl_c; /* tick count of last ctrl-c */ bool is_console; diff --git a/winsup/cygwin/mm/cygheap.cc b/winsup/cygwin/mm/cygheap.cc index 4cc8517..3388864 100644 --- a/winsup/cygwin/mm/cygheap.cc +++ b/winsup/cygwin/mm/cygheap.cc @@ -743,6 +743,7 @@ init_cygheap::find_tls (int sig, bool& issig_wait) while (++ix < (int) nthreads) { /* Only pthreads have tid set to non-0. */ + threadlist[ix].thread->lock (); if (!threadlist[ix].thread->tid || !threadlist[ix].thread->initialized) ; @@ -752,13 +753,21 @@ init_cygheap::find_tls (int sig, bool& issig_wait) issig_wait = true; break; } - else if (!t && !sigismember (&(threadlist[ix].thread->sigmask), sig)) + else if (!t && !sigismember (&(threadlist[ix].thread->sigmask), sig) + && !sigismember (&(threadlist[ix].thread->deltamask), sig)) + { t = &cygheap->threadlist[ix]; + break; + } + threadlist[ix].thread->unlock (); } /* Leave with locked mutex. The calling function is responsible for unlocking the mutex. */ if (t) - WaitForSingleObject (t->mutex, INFINITE); + { + threadlist[ix].thread->unlock (); + WaitForSingleObject (t->mutex, INFINITE); + } return t; } diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index 9d7224a..579b1a7 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -18,7 +18,12 @@ details. */ #undef u_long #define u_long __ms_u_long #include <w32api/ws2tcpip.h> +/* 2025-06-09: win32api headers v13 now define a cmsghdr type which clashes with + our socket.h. Arrange not to see it here. */ +#undef cmsghdr +#define cmsghdr __ms_cmsghdr #include <w32api/mswsock.h> +#undef cmsghdr #include <w32api/iphlpapi.h> #define gethostname cygwin_gethostname #include <unistd.h> diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 7a08e97..310876b 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -1855,9 +1855,18 @@ symlink_native (const char *oldpath, path_conv &win32_newpath) while (towupper (*++c_old) == towupper (*++c_new)) ; /* The last component could share a common prefix, so make sure we end - up on the first char after the last common backslash. */ - while (c_old[-1] != L'\\') - --c_old, --c_new; + up on the first char after the last common backslash. + + However, if c_old is a strict prefix of c_new (at a component + boundary), or vice versa, then do not try to find the last common + backslash. */ + if ((!*c_old || *c_old == L'\\') && (!*c_new || *c_new == L'\\')) + c_old += !!*c_old, c_new += !!*c_new; + else + { + while (c_old[-1] != L'\\') + --c_old, --c_new; + } /* 2. Check if prefix is long enough. The prefix must at least points to a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum @@ -1882,8 +1891,10 @@ symlink_native (const char *oldpath, path_conv &win32_newpath) final_oldpath = &final_oldpath_buf; final_oldpath->Buffer = tp.w_get (); PWCHAR e_old = final_oldpath->Buffer; - while (num-- > 0) - e_old = wcpcpy (e_old, L"..\\"); + while (num > 1 || (num == 1 && *c_old)) + e_old = wcpcpy (e_old, L"..\\"), num--; + if (num > 0) + e_old = wcpcpy (e_old, L".."); wcpcpy (e_old, c_old); } } @@ -3911,6 +3922,7 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, int how = what & CCP_CONVFLAGS_MASK; what &= CCP_CONVTYPE_MASK; int ret = -1; + bool prependglobalroot = false; __try { @@ -4019,7 +4031,7 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, { /* Device name points to somewhere else in the NT namespace. Use GLOBALROOT prefix to convert to Win32 path. */ - to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer); + prependglobalroot = true; lsiz += ro_u_globalroot.Length / sizeof (WCHAR); } /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */ @@ -4075,6 +4087,8 @@ cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to, stpcpy ((char *) to, buf); break; case CCP_POSIX_TO_WIN_W: + if (prependglobalroot) + to = (void *) wcpcpy ((PWCHAR) to, ro_u_globalroot.Buffer); wcpcpy ((PWCHAR) to, path); break; } diff --git a/winsup/cygwin/release/3.6.1 b/winsup/cygwin/release/3.6.1 index 07a29ec..e24766d 100644 --- a/winsup/cygwin/release/3.6.1 +++ b/winsup/cygwin/release/3.6.1 @@ -31,3 +31,21 @@ Fixes: - Return EMFILE when opening /dev/ptmx too many times. Addresses: https://cygwin.com/pipermail/cygwin/2025-March/257786.html + +- Move pthread::atforkchild() at the end of fork::child(). This fixes + subprocess failure in cmake (>= 3.29.x). + Addresses: https://cygwin.com/pipermail/cygwin/2025-March/257800.html + Addresses: https://github.com/msys2/msys2-runtime/issues/272 + +- Don't increment DLL reference count in dladdr. + Addresses: https://cygwin.com/pipermail/cygwin/2025-April/257862.html + +- Fix tcsetattr() for console which has been broken sinse cygwin 3.5.5. + +- Fix up cached DOS attributes when trying to create the same file in + two (or more) threads/processes concurrently. + Addresses: https://cygwin.com/pipermail/cygwin/2025-April/257871.html + +- Fix deadlock when calling pthread_key_create in the destructor of + a pthread_key. + Addresses: https://cygwin.com/pipermail/cygwin/2025-March/257705.html diff --git a/winsup/cygwin/release/3.6.2 b/winsup/cygwin/release/3.6.2 new file mode 100644 index 0000000..f6a3548 --- /dev/null +++ b/winsup/cygwin/release/3.6.2 @@ -0,0 +1,42 @@ +Changes: +-------- + +- Update <search.h> to align with POSIX.1-2024, adding the posix_tnode type +and declaring functions using it. + +Fixes: +------ + +- Fix a high latency problem when trying to fetch SID info for SIDs + not resolved by Windows functions anyway. + Addresses: https://cygwin.com/pipermail/cygwin/2025-April/257916.html + +- Fix connect(2) returning WSAEPROTOTYPE on abstract sockets. + Addresses: https://sourceware.org/pipermail/cygwin-patches/2025q2/013638.html + +- Fix the console states after the console is closed. + Addresses: https://cygwin.com/pipermail/cygwin/2025-April/257909.html + +- Fix compilation of sys/unistd.h with std=c90 + Addresses: https://sourceware.org/pipermail/cygwin-patches/2025q2/013644.html + +- Fix setting DOS attributes on devices. + Addresses: https://cygwin.com/pipermail/cygwin/2025-April/257940.html + +- Fix cygwin_conv_path writing to 'to' pointer before size is checked. + Addresses: https://cygwin.com/pipermail/cygwin/2025-April/258068.html + +- Fix handle leak occured when the signal handler calls longjmp(). + https://sourceware.org/pipermail/cygwin/2025-March/257726.html + +- Fix cygserver-config error. + https://cygwin.com/pipermail/cygwin/2025-April/258086.html + +- Fix deadlock for opening both side of a fifo in a process. + Addresses: https://cygwin.com/pipermail/cygwin/2025-May/258138.html + +- Fix infinite exception loop on segmentation fault when strace-ing + Addresses: https://cygwin.com/pipermail/cygwin/2025-May/258144.html + +- Fix size truncation in dll_init reserve_at function. + Addresses: https://cygwin.com/pipermail/cygwin/2025-May/258154.html diff --git a/winsup/cygwin/release/3.6.3 b/winsup/cygwin/release/3.6.3 new file mode 100644 index 0000000..60031e5 --- /dev/null +++ b/winsup/cygwin/release/3.6.3 @@ -0,0 +1,5 @@ +Fixes: +------ + +- Fix "There are no available terminals" error with AzureAD accounts. + Addresses: https://cygwin.com/pipermail/cygwin/2025-May/258214.html diff --git a/winsup/cygwin/release/3.6.4 b/winsup/cygwin/release/3.6.4 new file mode 100644 index 0000000..eb0d92b --- /dev/null +++ b/winsup/cygwin/release/3.6.4 @@ -0,0 +1,23 @@ +Fixes: +------ + +- Fix unexpected crash when SIGSEGV occurs too frequently. + Addresses: https://cygwin.com/pipermail/cygwin/2025-May/258153.html + +- Make pthread initializer macros compatible with C++ constinit. + Addresses: https://cygwin.com/pipermail/cygwin/2025-June/258305.html + +- Fix creating native symlinks to `..` (it used to target `../../<dir>` + instead). + +- Fix CI (stress-ng) for arm64 windows failure. + Addresses: https://cygwin.com/pipermail/cygwin/2025-June/258332.html + +- Fix handling of invalid 4 byte UTF-8 sequences. + Addresses: https://cygwin.com/pipermail/cygwin/2025-June/258358.html + +- Fix SSH hang with non-cygwin pipe reader. + Addresses: https://github.com/git-for-windows/git/issues/5682 + +- Fix unexpected blocking mode change by pipe_data_available() + Addresses: https://github.com/git-for-windows/git/issues/5682#issuecomment-2997428207 diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index bb141b0..a7e82a0 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -601,7 +601,7 @@ pipe_data_available (int fd, fhandler_base *fh, HANDLE h, int mode) if (mode == PDA_READ && PeekNamedPipe (h, NULL, 0, NULL, &nbytes_in_pipe, NULL)) return nbytes_in_pipe; - return -1; + return PDA_ERROR; } IO_STATUS_BLOCK iosb = {{0}, 0}; @@ -618,48 +618,52 @@ pipe_data_available (int fd, fhandler_base *fh, HANDLE h, int mode) access on the write end. */ select_printf ("fd %d, %s, NtQueryInformationFile failed, status %y", fd, fh->get_name (), status); - return (mode == PDA_WRITE) ? 1 : -1; + return (mode == PDA_WRITE) ? PDA_UNKNOWN : PDA_ERROR; } if (mode == PDA_WRITE) { /* If there is anything available in the pipe buffer then signal - that. This means that a pipe could still block since you could - be trying to write more to the pipe than is available in the - buffer but that is the hazard of select(). - - Note that WriteQuotaAvailable is unreliable. - - Usually WriteQuotaAvailable on the write side reflects the space - available in the inbound buffer on the read side. However, if a - pipe read is currently pending, WriteQuotaAvailable on the write side - is decremented by the number of bytes the read side is requesting. - So it's possible (even likely) that WriteQuotaAvailable is 0, even - if the inbound buffer on the read side is not full. This can lead to - a deadlock situation: The reader is waiting for data, but select - on the writer side assumes that no space is available in the read - side inbound buffer. - - Consequentially, there are two possibilities when WriteQuotaAvailable - is 0. One is that the buffer is really full. The other is that the - reader is currently trying to read the pipe and it is pending. - In the latter case, the fact that the reader cannot read the data - immediately means that the pipe is empty. In the former case, - NtSetInformationFile() in set_pipe_non_blocking(true) will fail - with STATUS_PIPE_BUSY, while it succeeds in the latter case. - Therefore, we can distinguish these cases by calling set_pipe_non_ - blocking(true). If it returns success, the pipe is empty, so we - return the pipe buffer size. Otherwise, we return 0. */ - if (fh->get_device () == FH_PIPEW && fpli.WriteQuotaAvailable == 0) + that. This means that a pipe could still block since you could + be trying to write more to the pipe than is available in the + buffer but that is the hazard of select(). + + Note that WriteQuotaAvailable is unreliable. + + Usually WriteQuotaAvailable on the write side reflects the space + available in the inbound buffer on the read side. However, if a + pipe read is currently pending, WriteQuotaAvailable on the write side + is decremented by the number of bytes the read side is requesting. + So it's possible (even likely) that WriteQuotaAvailable is less than + actual space available in the pipe, even if the inbound buffer is + empty. This can lead to a deadlock situation: The reader is waiting + for data, but select on the writer side assumes that no space is + available in the read side inbound buffer. + + Consequentially, there are two possibilities when WriteQuotaAvailable + is less than pipe size. One is that the buffer is really not empty. + The other is that the reader is currently trying to read the pipe + and it is pending. + In the latter case, the fact that the reader cannot read the data + immediately means that the pipe is empty. In the former case, + NtSetInformationFile() in set_pipe_non_blocking(!orig_mode) will + fail with STATUS_PIPE_BUSY, while it succeeds in the latter case. + Therefore, we can distinguish these cases by calling set_pipe_non_ + blocking(true). If it returns success, the pipe is empty, so we + return the pipe buffer size. Otherwise, we return the value of + WriteQuotaAvailable as is. */ + if (fh->get_device () == FH_PIPEW + && fpli.WriteQuotaAvailable < fpli.InboundQuota) { + bool orig_mode = ((fhandler_pipe *) fh)->real_non_blocking_mode; NTSTATUS status = - ((fhandler_pipe *) fh)->set_pipe_non_blocking (true); + ((fhandler_pipe *) fh)->set_pipe_non_blocking (!orig_mode); if (status == STATUS_PIPE_BUSY) - return 0; /* Full */ + return fpli.WriteQuotaAvailable; /* Not empty */ else if (!NT_SUCCESS (status)) /* We cannot know actual write pipe space. */ - return 1; - /* Restore pipe mode to blocking mode */ - ((fhandler_pipe *) fh)->set_pipe_non_blocking (false); + return PDA_UNKNOWN; + /* Restore pipe mode to original blocking mode */ + ((fhandler_pipe *) fh)->set_pipe_non_blocking (orig_mode); /* Empty */ fpli.WriteQuotaAvailable = fpli.InboundQuota; } @@ -681,7 +685,7 @@ pipe_data_available (int fd, fhandler_base *fh, HANDLE h, int mode) return fpli.ReadDataAvailable; } if (fpli.NamedPipeState & FILE_PIPE_CLOSING_STATE) - return -1; + return PDA_ERROR; return 0; } @@ -731,7 +735,7 @@ peek_pipe (select_record *s, bool from_select) if (n == 0 && fh->get_echo_handle ()) n = pipe_data_available (s->fd, fh, fh->get_echo_handle (), PDA_READ); - if (n < 0) + if (n == PDA_ERROR) { select_printf ("read: %s, n %d", fh->get_name (), n); if (s->except_selected) @@ -772,8 +776,8 @@ out: } ssize_t n = pipe_data_available (s->fd, fh, h, PDA_WRITE); select_printf ("write: %s, n %d", fh->get_name (), n); - gotone += s->write_ready = (n > 0); - if (n < 0 && s->except_selected) + gotone += s->write_ready = (n > 0 || n == PDA_UNKNOWN); + if (n == PDA_ERROR && s->except_selected) gotone += s->except_ready = true; } return gotone; @@ -986,7 +990,7 @@ out: ssize_t n = pipe_data_available (s->fd, fh, fh->get_handle (), PDA_WRITE); select_printf ("write: %s, n %d", fh->get_name (), n); gotone += s->write_ready = (n > 0); - if (n < 0 && s->except_selected) + if (n == PDA_ERROR && s->except_selected) gotone += s->except_ready = true; } return gotone; @@ -1412,7 +1416,7 @@ out: ssize_t n = pipe_data_available (s->fd, fh, h, PDA_WRITE); select_printf ("write: %s, n %d", fh->get_name (), n); gotone += s->write_ready = (n > 0); - if (n < 0 && s->except_selected) + if (n == PDA_ERROR && s->except_selected) gotone += s->except_ready = true; } return gotone; diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index fc28be9..3618879 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -611,7 +611,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) bool communing = si.si_signo == __SIGCOMMUNE; pack.wakeup = NULL; - bool wait_for_completion; + bool wait_for_completion = false; if (!(its_me = p == NULL || p == myself || p == myself_nowait)) { /* It is possible that the process is not yet ready to receive messages @@ -762,13 +762,10 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) memcpy (p, si._si_commune._si_str, n); p += n; } - unsigned cw_mask; - cw_mask = pack.si.si_signo == __SIGFLUSHFAST ? 0 : cw_sig_restart; - char mtx_name[MAX_PATH]; shared_name (mtx_name, "sig_send", p->pid); mtx = CreateMutex (&sec_none_nih, FALSE, mtx_name); - cygwait (mtx, INFINITE, cw_mask); + WaitForSingleObject (mtx, INFINITE); if (its_me && (si.si_signo == __SIGFLUSHFAST || si.si_signo == __SIGFLUSH)) { @@ -791,7 +788,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) CloseHandle (mtx); ResetEvent (sigflush_done_evt); SetEvent (sigflush_evt); - cygwait (sigflush_done_evt, INFINITE, cw_mask); + WaitForSingleObject (sigflush_done_evt, INFINITE); rc = 0; goto out; } @@ -807,8 +804,8 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) if (!res || packsize == nb) break; ReleaseMutex (mtx); - cygwait (NULL, 10, cw_mask); - cygwait (mtx, INFINITE, cw_mask); + Sleep (10); + WaitForSingleObject (mtx, INFINITE); res = 0; } ReleaseMutex (mtx); @@ -843,7 +840,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls) if (wait_for_completion) { sigproc_printf ("Waiting for pack.wakeup %p", pack.wakeup); - rc = cygwait (pack.wakeup, WSSC, cw_mask); + rc = WaitForSingleObject (pack.wakeup, WSSC); ForceCloseHandle (pack.wakeup); } else @@ -874,6 +871,11 @@ out: } if (pack.wakeup) ForceCloseHandle (pack.wakeup); + + /* Handle signals here if it was not handled yet */ + if (wait_for_completion && pack.si.si_signo != __SIGFLUSHFAST) + _my_tls.call_signal_handler (); + if (si.si_signo != __SIGPENDING && si.si_signo != __SIGPENDINGALL) /* nothing */; else if (!rc) diff --git a/winsup/cygwin/strfuncs.cc b/winsup/cygwin/strfuncs.cc index 66667bd..cb7911c 100644 --- a/winsup/cygwin/strfuncs.cc +++ b/winsup/cygwin/strfuncs.cc @@ -23,7 +23,7 @@ details. */ is affected as well, but we can't transform it as long as we accept Win32 paths as input. */ static const WCHAR tfx_chars[] = { - 0xf000 | 0, 0xf000 | 1, 0xf000 | 2, 0xf000 | 3, + 0, 0xf000 | 1, 0xf000 | 2, 0xf000 | 3, 0xf000 | 4, 0xf000 | 5, 0xf000 | 6, 0xf000 | 7, 0xf000 | 8, 0xf000 | 9, 0xf000 | 10, 0xf000 | 11, 0xf000 | 12, 0xf000 | 13, 0xf000 | 14, 0xf000 | 15, @@ -62,7 +62,7 @@ static const WCHAR tfx_chars[] = { converting back space and dot on filesystems only supporting DOS filenames. */ static const WCHAR tfx_rev_chars[] = { - 0xf000 | 0, 0xf000 | 1, 0xf000 | 2, 0xf000 | 3, + 0, 0xf000 | 1, 0xf000 | 2, 0xf000 | 3, 0xf000 | 4, 0xf000 | 5, 0xf000 | 6, 0xf000 | 7, 0xf000 | 8, 0xf000 | 9, 0xf000 | 10, 0xf000 | 11, 0xf000 | 12, 0xf000 | 13, 0xf000 | 14, 0xf000 | 15, @@ -109,7 +109,7 @@ transform_chars_af_unix (PWCHAR out, const char *path, __socklen_t len) { len -= sizeof (__sa_family_t); for (const unsigned char *p = (const unsigned char *) path; len-- > 0; ++p) - *out++ = (*p <= 0x7f) ? tfx_chars[*p] : *p; + *out++ = (*p <= 0x7f) ? (*p == 0) ? 0xf000 : tfx_chars[*p] : *p; return out; } diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index c93bf4c..d6a2c2d 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -1472,11 +1472,6 @@ open (const char *unix_path, int flags, ...) mode = va_arg (ap, mode_t); va_end (ap); - cygheap_fdnew fd; - - if (fd < 0) - __leave; /* errno already set */ - /* When O_PATH is specified in flags, flag bits other than O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */ if (flags & O_PATH) @@ -1577,6 +1572,12 @@ open (const char *unix_path, int flags, ...) if ((flags & O_TMPFILE) && !fh->pc.isremote ()) try_to_bin (fh->pc, fh->get_handle (), DELETE, FILE_OPEN_FOR_BACKUP_INTENT); + + cygheap_fdnew fd; + + if (fd < 0) + __leave; /* errno already set */ + fd = fh; if (fd <= 2) set_std_handle (fd); diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index 9ee9650..510e2be 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -1666,27 +1666,49 @@ pthread_rwlock::_fixup_after_fork () /* pthread_key */ /* static members */ /* This stores pthread_key information across fork() boundaries */ -List<pthread_key> pthread_key::keys; +pthread_key::keys_list pthread_key::keys[PTHREAD_KEYS_MAX]; /* non-static members */ -pthread_key::pthread_key (void (*aDestructor) (void *)):verifyable_object (PTHREAD_KEY_MAGIC), destructor (aDestructor) +pthread_key::pthread_key (void (*aDestructor) (void *)) : + verifyable_object (PTHREAD_KEY_MAGIC), destructor (aDestructor) { tls_index = TlsAlloc (); if (tls_index == TLS_OUT_OF_INDEXES) magic = 0; else - keys.insert (this); + for (size_t cnt = 0; cnt < PTHREAD_KEYS_MAX; cnt++) + { + LONG64 seq = keys[cnt].seq; + if (!keys_list::used (seq) + && InterlockedCompareExchange64 (&keys[cnt].seq, + seq + 1, seq) == seq) + { + keys[cnt].key = this; + keys[cnt].busy_cnt = 0; + key_idx = cnt; + InterlockedIncrement64 (&keys[key_idx].seq); + break; + } + } } pthread_key::~pthread_key () { - /* We may need to make the list code lock the list during operations - */ if (magic != 0) { - keys.remove (this); - TlsFree (tls_index); + LONG64 seq = keys[key_idx].seq; + if (keys_list::ready (seq) + && InterlockedCompareExchange64 (&keys[key_idx].seq, + seq + 1, seq) == seq) + { + while (InterlockedCompareExchange64 (&keys[key_idx].busy_cnt, + INT64_MIN, 0) > 0) + yield (); + keys[key_idx].key = NULL; + InterlockedIncrement64 (&keys[key_idx].seq); + TlsFree (tls_index); + } } } @@ -1946,7 +1968,12 @@ pthread_spinlock::lock () else if (spins < FAST_SPINS_LIMIT) { ++spins; +#if defined(__x86_64__) __asm__ volatile ("pause":::); +#elif defined(__aarch64__) + __asm__ volatile ("dmb ishst\n" + "yield":::); +#endif } else { diff --git a/winsup/cygwin/times.cc b/winsup/cygwin/times.cc index a89980d..3c3ba92 100644 --- a/winsup/cygwin/times.cc +++ b/winsup/cygwin/times.cc @@ -490,7 +490,8 @@ clock_settime (clockid_t clk_id, const struct timespec *tp) return -1; } - if (clk_id != CLOCK_REALTIME_COARSE && clk_id != CLOCK_REALTIME) + if ((clk_id != CLOCK_REALTIME_COARSE && clk_id != CLOCK_REALTIME) + || tp->tv_nsec < 0) /* Otherwise -999...-1 would be accepted */ { set_errno (EINVAL); return -1; diff --git a/winsup/cygwin/uinfo.cc b/winsup/cygwin/uinfo.cc index 27dc289..ffe71ee 100644 --- a/winsup/cygwin/uinfo.cc +++ b/winsup/cygwin/uinfo.cc @@ -1983,6 +1983,23 @@ pwdgrp::fetch_account_from_windows (fetch_user_arg_t &arg, cyg_ldap *pldap) break; case SID_arg: sid = *arg.sid; + + /* SIDs we want to filter out before hitting LookupAccountSidW. + If the latency of the AD connection is high, LookupAccountSidW + might take a long time before returning with ERROR_NONE_MAPPED. */ + + /* Capability SIDs, just drop out, we don't handle them */ + if (sid_id_auth (sid) == 15 /* SECURITY_APP_PACKAGE_AUTHORITY */ + && sid_sub_auth (sid, 0) == SECURITY_CAPABILITY_BASE_RID) + return NULL; + /* IIS APPPOOL */ + if (sid_id_auth (sid) == 5 /* SECURITY_NT_AUTHORITY */ + && sid_sub_auth (sid, 0) == SECURITY_APPPOOL_ID_BASE_RID) + break; + /* Samba user/group SIDs */ + if (sid_id_auth (sid) == 22) + break; + ret = LookupAccountSidW (NULL, sid, name, &nlen, dom, &dlen, &acc_type); if (!ret && cygheap->dom.member_machine () diff --git a/winsup/doc/dll.xml b/winsup/doc/dll.xml index f036976..66d65f7 100644 --- a/winsup/doc/dll.xml +++ b/winsup/doc/dll.xml @@ -19,26 +19,53 @@ variables, etc. All these are merged together, like if you were building one big object files, and put into the dll. They are not put into your .exe at all.</para> -<para>The exports contains a list of functions and variables that the +<para>The exports is a list of functions and variables that the dll makes available to other programs. Think of this as the list of -"global" symbols, the rest being hidden. Normally, you'd create this -list by hand with a text editor, but it's possible to do it -automatically from the list of functions in your code. The -<filename>dlltool</filename> program creates the exports section of -the dll from your text file of exported symbols.</para> - -<para>The import library is a regular UNIX-like -<filename>.a</filename> library, but it only contains the tiny bit of -information needed to tell the OS how your program interacts with -("imports") the dll. This information is linked into your -<filename>.exe</filename>. This is also generated by -<filename>dlltool</filename>.</para> +"public" symbols, the rest being hidden. + +<footnote> + <para> + Note that <filename>ld</filename>'s default behaviour is to export all + global symbols, if there otherwise wouldn't be any exported symbols + (i.e. because you haven't specified a def file or made any export + annotations). (See <code>--export-all-symbols</code> in the + <filename>ld</filename> man page for more details.) + </para> +</footnote> + +This list can be in a module definition (.def) file, which you can write by hand +with a text editor, but it's also possible to have it generated automatically +from the functions and variables in your code, by annotating the declarations +with <code>__attribute__ ((dllexport))</code>. + +<footnote> + <para> + If you're making these annotations on the declarations in a header which is + also installed to be included by users of your library, you probably want to + use macros to do the right thing and increase portability. See <ulink + url="https://gcc.gnu.org/wiki/Visibility">this example</ulink> for details. + </para> +</footnote> + +</para> + +<para>The import library is a regular UNIX-like <filename>.a</filename> library, +but it only contains the tiny bit of information ("a stub") needed to tell the +OS how your program interacts with ("imports") the dll. This information is +linked into your <filename>.exe</filename>. +</para> + +<para> + Refer to the <ulink + url="https://sourceware.org/binutils/docs/ld/WIN32.html">section of the ld + manual</ulink> discussing Win32 PE specifics for more details. +</para> <sect2 id="dll-build"><title>Building DLLs</title> -<para>This page gives only a few simple examples of gcc's DLL-building +<para>This page gives only a few simple examples of gcc's DLL-building capabilities. To begin an exploration of the many additional options, -see the gcc documentation and website, currently at +see the gcc documentation and website, currently at <ulink url="http://gcc.gnu.org/">http://gcc.gnu.org/</ulink> </para> @@ -49,8 +76,8 @@ For this example, we'll use a single file <filename>mydll.c</filename> for the contents of the dll (<filename>mydll.dll</filename>).</para> -<para>Fortunately, with the latest gcc and binutils the process for building a dll -is now pretty simple. Say you want to build this minimal function in mydll.c:</para> +<para>Say you want to build this minimal function in +<filename>mydll.c</filename>:</para> <screen> #include <stdio.h> @@ -59,28 +86,44 @@ int hello() { printf ("Hello World!\n"); -} +} </screen> -<para>First compile mydll.c to object code:</para> +<para>First compile <filename>mydll.c</filename> to the object +<filename>mydll.o</filename>:</para> <screen>gcc -c mydll.c</screen> <para>Then, tell gcc that it is building a shared library:</para> -<screen>gcc -shared -o mydll.dll mydll.o</screen> +<screen>gcc -shared -o mydll.dll mydll.o -Wl,--out-implib libmydll.a</screen> + +<para> + That's it! You now have the dll (<filename>mydll.dll</filename>) and the + import library (<filename>libmydll.a</filename>). + +<footnote> + <para> + In fact, <code>--out-implib</code> is optional in this simple example, + because <filename>ld</filename> can automatically generate import stubs when + told to link directly to a .dll. (See <code>--enable-auto-import</code> in + the <filename>ld</filename> man page for more details.) + </para> +</footnote> + +</para> <para> -That's it! To finish up the example, you can now link to the -dll with a simple program: +To finish up the example, you can now link to the dll with a simple program, +<filename>myprog.c</filename>: </para> <screen> -int +int main () { hello (); -} +} </screen> <para> @@ -89,35 +132,84 @@ Then link to your dll with a command like: <screen>gcc -o myprog myprog.c -L./ -lmydll</screen> -<para>However, if you are building a dll as an export library, -you will probably want to use the complete syntax:</para> +<para> + Try it out: +</para> + +<screen> +$ ./myprog +Hello World! +</screen> + +<para>However, if you are building a dll for installation, +you will probably want to use a more complex syntax:</para> <screen>gcc -shared -o cyg${module}.dll \ -Wl,--out-implib=lib${module}.dll.a \ - -Wl,--export-all-symbols \ - -Wl,--enable-auto-import \ - -Wl,--whole-archive ${old_libs} \ - -Wl,--no-whole-archive ${dependency_libs}</screen> + -Wl,--whole-archive ${objs_libs} -Wl,--no-whole-archive \ + ${dependency_libs}</screen> -<para> +<itemizedlist spacing="compact"> +<listitem> The name of your library is <literal>${module}</literal>, prefixed with <literal>cyg</literal> for the DLL and <literal>lib</literal> for the -import library. Cygwin DLLs use the <literal>cyg</literal> prefix to -differentiate them from native-Windows MinGW DLLs, see -<ulink url="http://mingw.org">the MinGW website</ulink> for more details. -<literal>${old_libs}</literal> are all -your object files, bundled together in static libs or single object -files and the <literal>${dependency_libs}</literal> are import libs you -need to link against, e.g -<userinput>'-lpng -lz -L/usr/local/special -lmyspeciallib'</userinput>. +import library. Cygwin DLLs use the <literal>cyg</literal> prefix to +differentiate them from native-Windows MinGW DLLs. +</listitem> +<listitem> +<literal>${objs_libs}</literal> are all your object files, bundled together in +static libs or single object files +</listitem> +<listitem> +<literal>${dependency_libs}</literal> are static or import libs you need to link +against, e.g <userinput>'-lpng -lz -L/usr/local/special -lmyspeciallib' +</userinput>. +</listitem> +</itemizedlist> + +<para> + When the import library is installed into <filename>/usr/lib</filename>, it + can be linked to with just <code>-l${module}</code>. The dll itself is + installed into <filename>/usr/bin</filename> so it can be found on + <code>PATH</code> by the loader when a linked .exe is run. +</para> + +</sect2> + +<sect2 id="dll-tool"><title>dlltool</title> + +<para> +Historically, the process for building a dll with <filename>gcc</filename> and +<filename>binutils</filename> wasn't so simple, and the +<filename>dlltool</filename> tool was used: </para> + +<itemizedlist spacing="compact"> + <listitem> + <para> + To create the exports section of the dll, from the module definition file + or by scanning object files. + </para> + </listitem> + + <listitem> + <para> + To generate the import library. + </para> + </listitem> +</itemizedlist> + +<para> + (See the <filename>dlltool</filename> man page for more details.) +</para> + </sect2> -<sect2 id="dll-link"><title>Linking Against DLLs</title> +<sect2 id="dll-link"><title>Linking Against Foreign DLLs</title> <para>If you have an existing DLL already, you need to build a Cygwin-compatible import library. If you have the source to compile -the DLL, see <xref linkend="dll-build"></xref> for details on having +the DLL, see <xref linkend="dll-build"></xref> for details on having <filename>gcc</filename> build one for you. If you do not have the source or a supplied working import library, you can get most of the way by creating a .def file with these commands (you might need to diff --git a/winsup/doc/faq-using.xml b/winsup/doc/faq-using.xml index e5e4479..111702c 100644 --- a/winsup/doc/faq-using.xml +++ b/winsup/doc/faq-using.xml @@ -855,6 +855,11 @@ possible to preset the sparse attribute with <literal>chattr</literal>. ---a-S-------- 2/is_sparse ---a-S-------- 2/maybe_sparse ---a-S-------- 2/not_sparse + $ lssparse -H 0/is_sparse # from cygutils-extra package + Hole range[1]: offset=0x0, length=0x100000 + Data range[2]: offset=0x100000, length=0x4 + $ lssparse -H 0/not_sparse + Data range[1]: offset=0x0, length=0x100004 </screen> <para>With <literal>sparse</literal> mount option or a SSD, all <literal>?/maybe_sparse</literal> files would be sparse. diff --git a/winsup/doc/path.xml b/winsup/doc/path.xml index f56614b..9665f6b 100644 --- a/winsup/doc/path.xml +++ b/winsup/doc/path.xml @@ -33,12 +33,12 @@ <refsect1 id="func-cygwin-conv-path-desc"> <title>Description</title> -<para>Use this function to convert POSIX paths in -<parameter>from</parameter> to Win32 paths in <parameter>to</parameter> -or, vice versa, Win32 paths in <parameter>from</parameter> to POSIX paths -in <parameter>to</parameter>. <parameter>what</parameter> -defines the direction of this conversion and can be any of the below -values.</para> +<para>Use this function to convert NUL-terminated POSIX paths in +<parameter>from</parameter> to NUL-terminated Win32 paths in +<parameter>to</parameter> or, vice versa, NUL-terminated Win32 paths in +<parameter>from</parameter> to NUL-terminated POSIX paths in +<parameter>to</parameter>. <parameter>what</parameter> defines the +direction of this conversion and can be any of the below values.</para> <programlisting> CCP_POSIX_TO_WIN_A /* from is char *posix, to is char *win32 */ @@ -62,7 +62,8 @@ default.</para> <para><parameter>size</parameter> is the size of the buffer pointed to by <parameter>to</parameter> in bytes. If <parameter>size</parameter> -is 0, <function>cygwin_conv_path</function> just returns the required +is 0, <parameter>to</parameter> may be NULL and +<function>cygwin_conv_path</function> just returns the required buffer size in bytes. Otherwise, it returns 0 on success, or -1 on error and errno is set to one of the below values.</para> @@ -73,6 +74,12 @@ error and errno is set to one of the below values.</para> of what == CCP_POSIX_TO_WIN_A, longer than MAX_PATH. ENOSPC size is less than required for the conversion. </programlisting> + +<para>In the event of an error, the memory at <parameter>to</parameter> is +not modified unless the error is <constant>EFAULT</constant> writing to +the memory at <parameter>to</parameter>, which may happen if +<parameter>size</parameter> is incorrectly specified.</para> + </refsect1> <refsect1 id="func-cygwin-conv-path-example"> diff --git a/winsup/testsuite/Makefile.am b/winsup/testsuite/Makefile.am index 8f2967a..20e06b9 100644 --- a/winsup/testsuite/Makefile.am +++ b/winsup/testsuite/Makefile.am @@ -311,6 +311,12 @@ check_PROGRAMS = \ winsup.api/pthread/self2 \ winsup.api/pthread/threadidafterfork \ winsup.api/pthread/tsd1 \ + winsup.api/posix_spawn/chdir \ + winsup.api/posix_spawn/errors \ + winsup.api/posix_spawn/fds \ + winsup.api/posix_spawn/signals \ + winsup.api/posix_spawn/spawnp \ + winsup.api/posix_spawn/win32 \ winsup.api/samples/sample-fail \ winsup.api/samples/sample-pass # winsup.api/ltp/ulimit01 is omitted as we don't have <ulimit.h> @@ -344,7 +350,7 @@ XFAIL_TESTS = \ LOG_COMPILER = $(srcdir)/cygrun.sh export runtime_root=$(abs_builddir)/testinst/bin -export cygrun=$(builddir)/mingw/cygrun +export mingwtestdir=$(builddir)/mingw # Set up things in the Cygwin 'installation' at testsuite/testinst/ to provide # things which tests need to work diff --git a/winsup/testsuite/cygrun.sh b/winsup/testsuite/cygrun.sh index bf1d5cc..f1673e4 100755 --- a/winsup/testsuite/cygrun.sh +++ b/winsup/testsuite/cygrun.sh @@ -11,7 +11,7 @@ export PATH="$runtime_root:${PATH}" if [ "$1" = "./mingw/cygload" ] then windows_runtime_root=$(cygpath -m $runtime_root) - $cygrun "$exe -v -cygwin $windows_runtime_root/cygwin1.dll" + $mingwtestdir/cygrun "$exe -v -cygwin $windows_runtime_root/cygwin1.dll" else - cygdrop $cygrun $exe + cygdrop $mingwtestdir/cygrun $exe fi diff --git a/winsup/testsuite/mingw/Makefile.am b/winsup/testsuite/mingw/Makefile.am index 772e734..25300a1 100644 --- a/winsup/testsuite/mingw/Makefile.am +++ b/winsup/testsuite/mingw/Makefile.am @@ -16,7 +16,7 @@ override CC = @MINGW_CC@ override CXX = @MINGW_CXX@ AM_CPPFLAGS = -noinst_PROGRAMS = cygrun cygload +noinst_PROGRAMS = cygrun cygload winchild cygrun_SOURCES = \ ../cygrun.c @@ -24,3 +24,8 @@ cygrun_SOURCES = \ cygload_SOURCES = \ ../winsup.api/cygload.cc cygload_LDFLAGS=-static -Wl,-e,cygloadCRTStartup + +winchild_SOURCES = \ + ../winsup.api/posix_spawn/winchild.c +winchild_LDFLAGS=-municode +winchild_LDADD=-lntdll diff --git a/winsup/testsuite/stress/cygstress b/winsup/testsuite/stress/cygstress new file mode 100755 index 0000000..55412ef --- /dev/null +++ b/winsup/testsuite/stress/cygstress @@ -0,0 +1,613 @@ +#!/bin/bash +# +# Run stress-ng on Cygwin +# +# Copyright 2025 Christian Franke +# +# SPDX-License-Identifier: BSD-3-Clause +# + +set -e -o pipefail # pipefail is required + +usage() +{ + cat <<EOF +Usage: ${0##*/} [OPTION...] {CI|WORK|FAIL|test...} + + -n print commands only (dry-run) + -f force execution of tests tagged 'heavy' or 'admin' + -c LIST set CPU affinity to LIST + -s PATH stress-ng executable [default: stress-ng] + -t N run each test for at least N seconds [default: 5] + -v print stress-ng output always [default: on error only] + -w N start N workers for each test [default: 2] + + CI run all tests tagged CI + WORK run all tests tagged WORKS + FAIL run all tests tagged FAILS + test... run individual test(s) (may require '-f') +EOF + exit 1 +} + +# Tags: +# WORKS: works on Cygwin (3.7.0-0.51.gd35cc82b5ec1) +# WORKS,CI: possibly suitable subset for Cygwin CI test. +# FAILS: fails on Cygwin, see "TODO Cygwin" for details. +# heavy: heavy resource usage, may work, hang, freeze desktop, require reset, ... +# admin: requires administrator, may work or not, may be 'heavy' or not. +# -----: unsupported due to missing API, library, declaration, ... + +stress_tests=' +# TEST [ARGS] # Tag # Comment + access # FAILS # TODO undecided: "access 004 on chmod mode 400 failed: 13 (Permission denied)" + acl # WORKS,CI # (fixed in stress-ng 0.18.12) + affinity # WORKS + af-alg # ----- # requires AF_ALG + aio # WORKS + aiol # ----- # requires io_setup(2), io_submit(2), ... + alarm # WORKS,CI + apparmor # ----- + atomic # WORKS + + bad-altstack # WORKS + bad-ioctl # ----- + besselmath # WORKS + bigheap # heavy + binderfs # ----- + bind-mount # ----- + bitonicsort # WORKS + bitops # WORKS + branch # WORKS + brk # heavy # allocates memory until OOM + bsearch # WORKS + bubblesort # WORKS + + cache # WORKS + cacheline # WORKS + cachehammer # WORKS + cap # ----- + cgroup # ----- + chattr # ----- + chdir # WORKS,CI + chmod # WORKS,CI + chown # FAILS # TODO undecided: "fchown failed, errno=22 (Invalid argument)" + chroot # admin + clock # WORKS,CI # (fixed in stress-ng 0.18.12: "timer_create failed for timer ... + # ... ''CLOCK_THREAD_CPUTIME_ID'', errno=134") + clone # ----- + close # FAILS # TODO Cygwin: close(2) is not thread-safe + context # WORKS,CI # (fixed in Cygwin 3.6.0: signals lost after swapcontext) + copy-file # ----- + cpu # WORKS + cpu-online # ----- + cpu-sched # FAILS # TODO undecided: "child died: signal 9 ''SIGKILL''" + # (fixed in Cygwin 3.6.0: signals lost after SIGSTOP) + crypt # WORKS # uses libcrypt + cyclic # admin + + daemon # WORKS + dccp # ----- + dekker # WORKS + dentry # WORKS + dev # FAILS # TODO Cygwin: "*** fatal error in forked process - pthread_mutex::_fixup_after_fork () ... + # ... doesn''t understand PROCESS_SHARED mutex''s" + dev-shm # ----- + dfp # WORKS + dir # WORKS + dirdeep # heavy # creates deep dir tree + dirmany # heavy # creates many dirs/files + dnotify # ----- + dup # WORKS,CI + dynlib # ----- + + easy-opcode # WORKS + eigen # WORKS + efivar # ----- + enosys # ----- + env # heavy # creates very large environment until OOM + epoll # ----- + eventfd # ----- + exec # WORKS,CI + exit-group # ----- + expmath # WORKS + + factor # WORKS # uses libgmp + fallocate # WORKS,CI + fanotify # ----- + far-branch # WORKS + fault # WORKS + fcntl # FAILS # TODO this script: fixed in stress-ng >0.18.12: "F_SETLKW (F_WRLCK) failed: ... + # ... errno=45 (Resource deadlock avoided)" + # (fixed in Cygwin 3.6.1: "ftruncate failed, errno=21 (Is a directory)") + fd-fork # WORKS,CI + fd-race # ----- # TODO stress-ng: drop restriction to Linux + # TODO Cygwin: close(2) is not thread-safe, see also "close" + fibsearch # WORKS + fiemap # ----- + fifo # WORKS + file-ioctl # WORKS,CI + filename # FAILS # TODO Cygwin: creates files Cygwin cannot remove later, please see: + # https://sourceware.org/pipermail/cygwin/2024-September/256451.html + filename --filename-opts posix # WORKS,CI # restricts filenames to POSIX charset +# filerace # WORKS # TODO this script: added in stress-ng >0.18.12 + flipflop # WORKS + flock # WORKS,CI + flushcache # WORKS + fma # WORKS + fork # WORKS,CI + forkheavy # heavy # forks until process table is full + fp # WORKS,CI + fp-error # WORKS,CI + fpunch # FAILS # TODO this script: fixed in stress-ng >0.18.12 + fractal # WORKS + fsize # heavy # creates large files until disk is full + fstat # WORKS,CI + full # ----- # requires pread/pwrite() working on /dev/full + funccall # WORKS + funcret # WORKS + futex # ----- + + get # WORKS + getdent # ----- + getrandom # ----- + goto # WORKS + gpu # ----- + + handle # ----- # requires name/open_by_handle_at(2) + hash # WORKS + hdd # WORKS + heapsort # WORKS # uses libbsd + hrtimers # WORKS,CI # (fixed in Cygwin 3.5.7: "timer_delete failed") + hsearch # WORKS + hyperbolic # WORKS + + icache # WORKS + icmp-flood # ----- # requires "struct icmphdr", ... in <netinet/*.h> + idle-page # ----- # requires /sys/kernel/mm/page_idle/bitmap + inode-flags # ----- + inotify # ----- # requires inotify_*(2) + insertionsort # WORKS + intmath # WORKS + io # WORKS + iomix # WORKS + ioport # ----- + ioprio # ----- + io-uring # ----- + ipsec-mb # ----- # requires libipsec-mb + itimer # WORKS,CI + + jpeg # WORKS # uses libjpeg + judy # ----- # requires libJudy (ORPHANED) + + kcmp # ----- + key # ----- + kill # WORKS,CI + klog # ----- + kvm # ----- + + l1cache # ----- # requires /sys/devices/system/cpu + l1cache --l1cache-line-size 32768 --l1cache-ways 8 --l1cache-sets 1 # WORKS + landlock # ----- + lease # ----- + led # ----- + link # WORKS,CI + list # WORKS + llc-affinity # WORKS + loadavg # WORKS + locka # WORKS + lockbus # WORKS + lockf # WORKS,CI # (fixed in Cygwin 3.5.5: "NtCreateEvent(lock): 0xC000003" and ... + # "can''t handle more than 910 locks per file") + lockmix # WORKS + lockofd # ----- + logmath # WORKS,CI # (fixed in Cygwin 3.5.5: signal handler destroys long double values) + longjmp # WORKS,CI # (fixed in Cygwin 3.5.5: signals lost during setjmp or longjmp) + loop # ----- + lsearch # WORKS + lsm # ----- + + madvise # WORKS + malloc # WORKS,CI + matrix # WORKS + matrix-3d # WORKS + mcontend # WORKS + membarrier # ----- + memcpy # WORKS,CI # (fixed in Cygwin 3.6.1: crash due to set DF in signal handler) + memfd # ----- + memhotplug # ----- + memrate # WORKS + memthrash # WORKS + mergesort # WORKS # uses libbsd + metamix # FAILS # TODO Cygwin: "fdatasync on ./tmp-stress-ng-metamix-*/... failed, errno=13" + mincore # ----- + min-nanosleep # WORKS + misaligned # WORKS + mknod # ----- + mlock # WORKS + mlockmany # heavy # requires --pathological + mmap # WORKS,CI + mmapaddr # WORKS + mmapfork # WORKS + mmapfiles # WORKS + mmapfixed # WORKS + mmaphuge # ----- + mmapmany # WORKS + mmaptorture # heavy + module # ----- + monte-carlo # WORKS + mpfr # WORKS # uses libmpfr + mprotect # FAILS # TODO Cygwin: crashes or hangs and then ignores SIGKILL + mq # FAILS # TODO undecided: "fail: ... mq_[timed]receive failed, errno=1" + # (fixed in Cygwin 3.5.6: crash on invalid mq fd) + mremap # ----- + mseal # ----- + msg # WORKS + msync # WORKS + msyncmany # WORKS + mtx # WORKS + munmap # ----- + mutex # WORKS + + nanosleep # FAILS # TODO undecided: "detected 1 unexpected nanosleep underruns" + netdev # ----- + netlink-proc # ----- + netlink-task # ----- + nice # heavy # TODO Cygwin: may change nice value of other processes + nop # WORKS + null # WORKS + numa # ----- + + oom-pipe # ----- + opcode # ----- + open # WORKS,CI + + pagemove # ----- + pageswap # ----- + pci # ----- + personality # ----- + peterson # WORKS + physmmap # ----- + physpage # ----- + pidfd # ----- + ping-sock # ----- + pipe # WORKS,CI + pipeherd # heavy # many forks, may freeze desktop + pkey # ----- + plugin # ----- + poll # WORKS + powmath # WORKS + prctl # ----- + prefetch # WORKS + prime # WORKS # uses libgmp + prio-inv # ----- # requires <pthread_nt.h> + priv-instr # FAILS # TODO Cygwin: crashes or hangs, please see: + # https://sourceware.org/pipermail/cygwin/2025-March/257726.html + procfs # ----- # TODO stress-ng: support /proc subset of Cygwin + pseek # WORKS,CI # (fixed in Cygwin 3.5.5: "pread: Bad file descriptor") + pthread # WORKS,CI + ptr-chase # WORKS + ptrace # ----- + pty # WORKS,CI # (fixed in Cygwin 3.7.0: implement tcdrain/tcflow/TIOCINQ for pty) + # (fixed in Cygwin 3.6.1: "No pty allocated, errno=0") + qsort # WORKS + quota # ----- + + race-sched # WORKS + radixsort # WORKS # uses libbsd + ramfs # ----- + rawdev # ----- + randlist # WORKS + rawsock # ----- + rawpkt # ----- # requires <linux/if_packet.h>, ... + rawudp # ----- + rdrand # WORKS + readahead # ----- + reboot # ----- + regex # WORKS + regs # WORKS + remap # ----- + rename # WORKS,CI + resched # heavy + resources # heavy + revio # WORKS + ring-pipe # WORKS + rlimit # heavy + rmap # WORKS + rotate # WORKS + rseq # ----- + rtc # ----- # requires /dev/rtc + + schedmix # WORKS + schedpolicy # WORKS,CI + sctp # ----- + seal # ----- + seccomp # ----- + secretmem # ----- + seek # WORKS,CI + sem # FAILS # TODO Cygwin: "instance 0 corrupted bogo-ops counter, 556328 vs 556327" + sem-sysv # FAILS # TODO Cygwin: "aborted early, out of system resources" + sendfile # ----- # requires sendfile(2) + session # WORKS + set # FAILS # TODO stress-ng: "setrlimit failed, ..., expected -EPERM, instead got errno=22 (Invalid argument)" + shellsort # WORKS + shm # WORKS,CI + shm-sysv # ----- # requires shmat(2), smdt(2) + sigabrt # WORKS,CI + sigbus # FAILS # TODO Cygwin: "ftruncate file to a single page failed, errno=13 (Permission denied)" + sigchld # FAILS # TODO Cygwin: hangs + sigfd # ----- # TODO Cygwin: "stressor terminated with unexpected signal 11 ''SIGSEGV''" + # (fixed in stress-ng >0.18.12: drop restriction to glibc) + sigfpe # FAILS # TODO undecided: "got SIGFPE error 15 (FPE_INTDEV), expecting 20 (FPE_FLTRES)" + sighup # WORKS,CI + sigill # FAILS # TODO Cygwin: "terminated on signal: 11", possibly similar to "priv-instr" + sigio # ----- # requires O_ASYNC + signal # WORKS,CI + signest # FAILS # TODO Cygwin: "terminated on signal: 11" + sigpending # WORKS,CI + sigpipe # WORKS,CI + sigq # WORKS,CI + sigrt # WORKS,CI + sigsegv # FAILS # TODO Cygwin: crashes or hangs, possibly similar to "priv-instr" + sigsuspend # WORKS,CI + sigtrap # WORKS,CI + sigurg # WORKS,CI + sigvtalrm # WORKS,CI + sigxcpu # FAILS # TODO stress-ng: "setrlimit failed, errno=22 (Invalid argument)" + sigxfsz # FAILS # TODO stress-ng: "setrlimit failed, errno=22 (Invalid argument)" + skiplist # WORKS + sleep # WORKS,CI + smi # ----- + sock # WORKS + sockabuse # FAILS # TODO undecided: "recv failed, errno=113 (Software caused connection abort)" + sockdiag # ----- + sockfd # ----- + sockmany # heavy + sockpair # WORKS + softlockup # admin + sparsematrix # WORKS + spawn # heavy # TODO Cygwin: "NNN spawns failed (100.00%)", may crash other processes + spinmem # WORKS + splice # ----- + stack # heavy + stackmmap # WORKS + statmount # ----- + str # WORKS + stream # WORKS # (fixed in stress-ng >0.18.12: --stream-l3-size set correctly) + swap # ----- + switch # WORKS + symlink # WORKS,CI + sync-file # ----- + syncload # WORKS + sysbadaddr # heavy + syscall # FAILS # TODO Cygwin: "terminated on signal: 11" + sysinfo # WORKS + sysinval # ----- + sysfs # ----- + + tee # ----- # requires tee(2) + timer # WORKS,CI # TODO undecided: "1 timer settime calls failed" + timerfd # heavy # TODO undecided: may freeze desktop + time-warp # WORKS + tlb-shootdown # heavy + tmpfs # ----- # requires tmpfs filesystem + touch # WORKS + tree # WORKS # (fixed in Cygwin 3.6.1: crash due to set DF in signal handler, see also "memcpy") + trig # WORKS + tsc # WORKS + tsearch # WORKS + tun # ----- + + udp # WORKS + udp-flood # ----- # requires AF_PACKET + umask # WORKS + umount # ----- + unlink # WORKS,CI + unshare # ----- + uprobe # ----- + urandom # WORKS + userfaultfd # ----- + usersyscall # ----- + utime # WORKS,CI + + vdso # ----- + veccmp # WORKS + vecfp # WORKS + vecmath # WORKS + vecshuf # WORKS + vecwide # WORKS + verity # ----- + vfork # WORKS + vforkmany # heavy # forks until process table is full + vm # WORKS,CI + vm-addr # WORKS + vm-rw # ----- + vm-segv # WORKS + vm-splice # ----- + vma # WORKS + vnni # WORKS + + wait # FAILS # TODO Cygwin: hangs in few cases + waitcpu # WORKS + watchdog # ----- + wcs # WORKS + workload # WORKS,CI # (fixed in Cygwin 3.5.5: hang in mq_send/receive) + + x86cpuid # WORKS + x86syscall # ----- + xattr # FAILS # TODO Cygwin: "fsetxattr succeeded unexpectedly, created ... + # "... attribute with size greater than permitted size, errno=61" + yield # WORKS + + zero # WORKS,CI + zlib # WORKS # uses libz + zombie # WORKS,CI +' + +stress_ng="stress-ng" +timeout=5; workers=2 +dryrun=false; force=false; verbose=false +taskset= + +while :; do case $1 in + -c) shift; taskset=$1 ;; + -f) force=true ;; + -n) dryrun=true ;; + -s) shift; stress_ng=$1 ;; + -t) shift; timeout=$1 ;; + -v) verbose=true ;; + -w) shift; workers=$1 ;; + -*) usage ;; + *) break ;; +esac; shift || usage; done + +run_ci=false; run_work=false; run_fail=false +run_tests= + +while [ $# -ge 1 ]; do case $1 in + CI) run_ci=true ;; WORK) run_work=true ;; FAIL) run_fail=true ;; + [a-z]*[a-z]) run_tests+=" $1" ;; + *) usage ;; +esac; shift; done +$run_ci || $run_work || $run_fail || [ ${run_tests:+t} ] || usage + +command -V "$stress_ng" >/dev/null || exit 1 + +# SIGKILL may not work if stress-ng hangs. +# Use Windows 'taskkill' as no 'killall --force' is available. +command -V taskkill >/dev/null || exit 1 + +stress_ng_name=${stress_ng##*/} +tempdir=${TMP:-/tmp} +logdir=${LOGDIR:-/tmp/logdir} + +mkdir -p "$logdir" + +find_stress() +{ + local p=$(procps -C "$stress_ng_name" -o pid,ppid,s,pri,ni,tt,start,time,args --sort pid) + test "$(wc -l <<< "$p")" -gt 1 || return 1 + echo "$p" +} + +stop_stress() +{ + echo '$' taskkill /F /T /IM "${stress_ng_name}.exe" + taskkill /F /T /IM "${stress_ng_name}.exe" ||: +} + +total=0 +fails=0 + +# stress TEST [OPTION...] +stress() +{ + local name=$1 + shift || return 1 + + local td="$tempdir/stress-ng.$$.$total.d" + local logfile="$logdir/$name" + local cmd=("$stress_ng" -v -M --oomable --timestamp --verify --temp-path "$td" -t "$timeout") + test -z "$taskset" || cmd+=(--taskset "$taskset") + cmd+=(--"$name" "$workers" "$@") + + if $dryrun || $verbose; then + echo '$' "${cmd[@]}" + ! $dryrun || return 0 + fi + + ( + t=$(date +%s); : $((t += timeout + 30)); sleep 1 + while [ "$(date +%s)" -lt "$t" ]; do sleep 1; done + # Another delay to let 'exit 0' occur before 'kill $watchdog' + ( sleep 1; stop_stress ) & + exit 0 + ) & + local watchdog=$! + trap "kill $watchdog 2>/dev/null ||:; exit 130" SIGINT SIGTERM + + mkdir "$td" + local rc=0 + if $verbose; then + # This requires '-o pipefail' + "${cmd[@]}" 2>&1 | tee "$logfile" || rc=$? + else + "${cmd[@]}" >"$logfile" 2>&1 || rc=$? + fi + + kill $watchdog 2>/dev/null ||: + trap - SIGINT SIGTERM + + local errs= + if wait $watchdog; then + sleep 2 + errs=", command hangs" + fi + + local p + if p=$(find_stress); then + errs+=", processes left" + stop_stress + sleep 2 + fi + + rmdir "$td" 2>/dev/null || errs+=", files left in '$td'" + + ! grep -Eqv '^(stress-ng|info):' "$logfile" || errs+=", unexpected output" + + if [ "${rc}${errs:+t}" != "0" ]; then + $verbose || cat "$logfile" + echo ">>> FAILURE: $name" "$@" "(exit status ${rc}${errs})" + ! [ ${p:+t} ] || echo "$p" + echo + return 1 + fi + echo ">>> SUCCESS: $name" "$@" + ! $verbose || echo + return 0 +} + +if p=$(find_stress); then + echo "*** Other $stress_ng_name processes are still running:" + echo "$p" + $dryrun || exit 1 +fi + +while read; do + args=${REPLY#*|} + name=${args%% *} + run_this=false + for t in $run_tests; do if [ "$t" = "$name" ]; then + run_this=true; break + fi; done + + tag=${REPLY%%|*} + case $tag in + FAILS) $run_this || $run_fail || continue ;; + WORKS) $run_this || $run_work || continue ;; + WORKS,CI) $run_this || $run_work || $run_ci || continue ;; + -----) $run_this || continue ;; + admin|heavy) + $run_this || continue + if ! $force; then + echo ">>> SKIPPED: $name (tagged '$tag', use '-f' to override)"; echo + continue + fi ;; + *) echo "*** syntax error: '$REPLY'"; exit 1 ;; + esac + + : $((++total)) + stress $args ||: $((++fails)) +done <<<"$( + sed -E \ + -e 's/^ *([-0-9a-z]+)( +-[^#]*[^ #])? +# *(FAILS|WORKS(,CI)?|admin|heavy|-----) *(#.*)?$/\3|\1\2/' \ + -e '/^ *(#.*)?$/d' \ + <<<"$stress_tests" +)" + +if [ $fails -ne 0 ]; then + echo ">>> FAILURE: $fails of $total stress test(s) failed" + exit 1 +fi +echo ">>> SUCCESS: All $total stress test(s) succeeded" +exit 0 diff --git a/winsup/testsuite/winsup.api/cygload.cc b/winsup/testsuite/winsup.api/cygload.cc index afd3ee9..08372a3 100644 --- a/winsup/testsuite/winsup.api/cygload.cc +++ b/winsup/testsuite/winsup.api/cygload.cc @@ -82,6 +82,13 @@ cygwin::padding::padding () "movl %%fs:4, %0" :"=r"(stackbase) ); +# elif __aarch64__ + // x18 register points to TEB. See _TEB structure definition in + // winsup\cygwin\local_includes\ntdll.h + __asm__ volatile ( + "ldr %0, [x18, #0x8]" + :"=r" (stackbase) + ); # else # error Unknown architecture # endif diff --git a/winsup/testsuite/winsup.api/posix_spawn/chdir.c b/winsup/testsuite/winsup.api/posix_spawn/chdir.c new file mode 100644 index 0000000..0951d2d --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/chdir.c @@ -0,0 +1,158 @@ +#include "test.h" +#include <fcntl.h> +#include <limits.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Linux is behind the times a bit (also needs the *chdir_np functions) */ +#ifndef O_SEARCH +# define O_SEARCH O_PATH +#endif + +int handle_child (char *expected) +{ + char buf[PATH_MAX + 1]; + + nullError (getcwd (buf, sizeof (buf))); + testAssertMsg (!strcmp (buf, expected), "cwd '%s' != expected '%s'", + buf, expected); + + return 0; +} + +int handle_childfds (char *expectedcwd, char *expectedfd0, char *expectedfd1) +{ + char buf[PATH_MAX + 1]; + ssize_t ret; + + testAssert (handle_child (expectedcwd) == 0); + + negError (ret = readlink ("/dev/fd/0", buf, PATH_MAX)); + testAssertMsg (ret < PATH_MAX, "Path too long for PATH_MAX buffer"); + buf[ret] = '\0'; + testAssertMsg (!strcmp (buf, expectedfd0), "fd 0 '%s' != expected '%s'", + buf, expectedfd0); + + negError (ret = readlink ("/dev/fd/1", buf, PATH_MAX)); + testAssertMsg (ret < PATH_MAX, "Path too long for PATH_MAX buffer"); + buf[ret] = '\0'; + testAssertMsg (!strcmp (buf, expectedfd1), "fd 1 '%s' != expected '%s'", + buf, expectedfd1); + + return 0; +} + +static char tmpcwd[] = "tmpcwd.XXXXXX"; +static char tmppath[] = "tmpfile.XXXXXX"; +static char tmppath2[sizeof (tmpcwd) + 9] = {0}; + +static void cleanup_tmpfiles (void) +{ + if (tmppath2[0]) + unlink (tmppath2); + rmdir (tmpcwd); + unlink (tmppath); +} + +int main (int argc, char **argv) +{ + posix_spawn_file_actions_t fa; + pid_t pid; + int status; + int fd; + char buf[PATH_MAX + 1]; + char buffd0[PATH_MAX + 1]; + char buffd1[PATH_MAX + 1]; + char *childargv[] = {"chdir", "--child", buf, NULL, NULL, NULL}; + + /* unbuffer stdout */ + setvbuf(stdout, NULL, _IONBF, 0); + + if (argc == 3 && !strcmp (argv[1], "--child")) + return handle_child (argv[2]); + else if (argc == 5 && !strcmp (argv[1], "--child")) + return handle_childfds (argv[2], argv[3], argv[4]); + + /* make a directory and a couple of files for testing */ + nullError (mkdtemp (tmpcwd)); + atexit (cleanup_tmpfiles); + + negError (fd = mkstemp (tmppath)); + negError (close (fd)); + + stpcpy (stpcpy (stpcpy (tmppath2, tmpcwd), "/"), "tmpfile2"); + negError (fd = open (tmppath2, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)); + negError (close (fd)); + + + /* ensure cwd is inherited by default */ + nullError (getcwd (buf, sizeof (buf))); + stpcpy (stpcpy (stpcpy (buffd0, buf), "/"), tmppath); + errCode (posix_spawn (&pid, MYSELF, NULL, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + + /* test posix_spawn_file_actions_addchdir */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addchdir_np (&fa, tmpcwd)); + + strcat (buf, "/"); + strcat (buf, tmpcwd); + stpcpy (stpcpy (stpcpy (buffd1, buf), "/"), "tmpfile2"); + + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_addfchdir */ + negError (fd = open (tmpcwd, O_SEARCH|O_DIRECTORY|O_CLOEXEC, 0755)); + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addfchdir_np (&fa, fd)); + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + negError (close (fd)); + + /* test posix_spawn_file_actions_addchdir + addopen */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 0, tmppath, O_RDONLY, 0644)); + errCode (posix_spawn_file_actions_addchdir_np (&fa, tmpcwd)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, "tmpfile2", O_WRONLY, 0644)); + childargv[3] = buffd0; + childargv[4] = buffd1; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_addfchdir + addopen */ + negError (fd = open (tmpcwd, O_SEARCH|O_DIRECTORY|O_CLOEXEC, 0755)); + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 0, tmppath, O_RDONLY, 0644)); + errCode (posix_spawn_file_actions_addfchdir_np (&fa, fd)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, "tmpfile2", O_WRONLY, 0644)); + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_addfchdir + adddup2 of directory fd */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_adddup2 (&fa, fd, 0)); + errCode (posix_spawn_file_actions_addfchdir_np (&fa, fd)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, "tmpfile2", O_WRONLY, 0644)); + childargv[3] = buf; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + negError (close (fd)); + + return 0; +} diff --git a/winsup/testsuite/winsup.api/posix_spawn/errors.c b/winsup/testsuite/winsup.api/posix_spawn/errors.c new file mode 100644 index 0000000..3fbc2cb --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/errors.c @@ -0,0 +1,66 @@ +#include "test.h" +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +static char tmppath[] = "pspawn.XXXXXX"; +static const char exit0[] = "exit 0\n"; + +void cleanup_tmpfile (void) +{ + unlink (tmppath); +} + +int main (void) +{ + posix_spawn_file_actions_t fa; + pid_t pid; + int fd; + char *childargv[] = {"ls", NULL}; + char tmpsub[sizeof (tmppath) + 3]; + char *p; + + /* unbuffer stdout */ + setvbuf(stdout, NULL, _IONBF, 0); + + negError (fd = mkstemp (tmppath)); + atexit (cleanup_tmpfile); + negError (write (fd, exit0, sizeof (exit0) - 1)); + negError (close (fd)); + + /* expected ENOENT: posix_spawn without full path */ + errCodeExpected (ENOENT, + posix_spawn (&pid, childargv[0], NULL, NULL, childargv, environ)); + + /* expected EACCES: posix_spawn with path to non-executable file */ + errCodeExpected (EACCES, + posix_spawn (&pid, tmppath, NULL, NULL, childargv, environ)); + + negError (chmod (tmppath, 0755)); + + /* expected ENOEXEC: posix_spawn with unrecognized file format */ + errCodeExpected (ENOEXEC, + posix_spawn (&pid, tmppath, NULL, NULL, childargv, environ)); + + p = stpcpy (tmpsub, tmppath); + p = stpcpy (p, "/ls"); + +#ifndef __CYGWIN__ + /* Cygwin returns ENOENT rather than ENOTDIR here */ + /* expected ENOTDIR: posix_spawn with file as non-leaf entry in path */ + errCodeExpected (ENOTDIR, + posix_spawn (&pid, tmpsub, NULL, NULL, childargv, environ)); +#endif + + /* expected ENOENT: relative path after chdir */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addchdir_np (&fa, "/tmp")); + errCodeExpected (ENOENT, + posix_spawn (&pid, tmppath, &fa, NULL, childargv, environ)); + errCode (posix_spawn_file_actions_destroy (&fa)); + + return 0; +} diff --git a/winsup/testsuite/winsup.api/posix_spawn/fds.c b/winsup/testsuite/winsup.api/posix_spawn/fds.c new file mode 100644 index 0000000..98ce36f --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/fds.c @@ -0,0 +1,124 @@ +#include "test.h" +#include <fcntl.h> +#include <limits.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int handle_child (char *devfd, char *target) +{ + char buf[PATH_MAX]; + ssize_t ret; + + ret = readlink (devfd, buf, PATH_MAX); + if (ret < 0) + { + int err = errno; + if (err == ENOENT && !strcmp (target, "<ENOENT>")) + return 0; + error_at_line (1, err, __FILE__, __LINE__ - 6, + "ret = readlink (devfd, buf, PATH_MAX)"); + } + testAssertMsg (ret < PATH_MAX, "Path too long for PATH_MAX buffer"); + buf[ret] = '\0'; + if (strcmp (target, buf)) + error_at_line (1, 0, __FILE__, __LINE__ - 12, + "Target '%s' != expected '%s'", buf, target); + + return 0; +} + +int main (int argc, char **argv) +{ + posix_spawn_file_actions_t fa; + pid_t pid; + int status; + int fd, fdcloexec; + char buf[16]; + char *childargv[] = {"fds", "--child", buf, "", NULL}; + + /* unbuffer stdout */ + setvbuf(stdout, NULL, _IONBF, 0); + + if (argc == 4 && !strcmp (argv[1], "--child")) + return handle_child (argv[2], argv[3]); + + /* open file descriptors to test inheritance */ + negError (fd = open ("/dev/null", O_RDONLY, 0644)); + negError (fdcloexec = open ("/dev/full", O_RDONLY|O_CLOEXEC, 0644)); + + /* ensure fd is inherited by default */ + sprintf (buf, "/dev/fd/%d", fd); + childargv[3] = "/dev/null"; + errCode (posix_spawn (&pid, MYSELF, NULL, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + + /* ensure CLOEXEC fd is closed */ + sprintf (buf, "/dev/fd/%d", fdcloexec); + childargv[3] = "<ENOENT>"; + errCode (posix_spawn (&pid, MYSELF, NULL, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + + /* test posix_spawn_file_actions_addopen */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 0, "/dev/zero", O_RDONLY, + 0644)); + strcpy (buf, "/dev/fd/0"); + childargv[3] = "/dev/zero"; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_adddup2 */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_adddup2 (&fa, fd, 0)); + childargv[3] = "/dev/null"; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_adddup2 with CLOEXEC fd */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_adddup2 (&fa, fdcloexec, 0)); + childargv[3] = "/dev/full"; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_adddup2 with out to err */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, "/dev/zero", O_WRONLY, + 0644)); + errCode (posix_spawn_file_actions_adddup2 (&fa, 1, 2)); + strcpy (buf, "/dev/fd/2"); + childargv[3] = "/dev/zero"; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_addclose */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addclose (&fa, fd)); + sprintf (buf, "/dev/fd/%d", fd); + childargv[3] = "<ENOENT>"; + errCode (posix_spawn (&pid, MYSELF, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* TODO: test new fds (open or dup2) not 0 through 2 */ + /* TODO: test error cases */ + + negError (close (fd)); + negError (close (fdcloexec)); + + return 0; +} diff --git a/winsup/testsuite/winsup.api/posix_spawn/signals.c b/winsup/testsuite/winsup.api/posix_spawn/signals.c new file mode 100644 index 0000000..f64404d --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/signals.c @@ -0,0 +1,82 @@ +#include "test.h" +#include <signal.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int handle_child (char *arg) +{ + struct sigaction sa; + sigset_t mask; + int ret; + + negError (sigaction (SIGUSR1, NULL, &sa)); + negError (sigprocmask (SIG_SETMASK, NULL, &mask)); + negError (ret = sigismember (&mask, SIGUSR2)); + + if (!strcmp (arg, "inherited")) + { + testAssert (sa.sa_handler == SIG_IGN); + testAssertMsg (ret, "SIGUSR2 not masked"); + } + else + { + testAssert (sa.sa_handler == SIG_DFL); + testAssertMsg (!ret, "SIGUSR2 masked"); + } + + return 0; +} + +int main (int argc, char **argv) +{ + posix_spawnattr_t sa; + pid_t pid; + int status; + sigset_t sigusr1mask, sigusr2mask, emptymask; + char *childargv[] = {"signal", "--child", "inherited", NULL}; + + /* unbuffer stdout */ + setvbuf(stdout, NULL, _IONBF, 0); + + if (argc == 3 && !strcmp (argv[1], "--child")) + return handle_child (argv[2]); + + negError (sigemptyset (&sigusr1mask)); + negError (sigaddset (&sigusr1mask, SIGUSR1)); + negError (sigemptyset (&sigusr2mask)); + negError (sigaddset (&sigusr2mask, SIGUSR2)); + negError (sigemptyset (&emptymask)); + + /* set all signals to default */ + for (int i = 1; i < NSIG; ++i) + if (i != SIGKILL && i != SIGSTOP) + signal (i, SIG_DFL); + + /* change some signal states to test signal-related posix_spawn flags */ + sigError (signal (SIGUSR1, SIG_IGN)); + negError (sigprocmask (SIG_SETMASK, &sigusr2mask, NULL)); + + /* ensure ignored and blocked signals are inherited by default */ + errCode (posix_spawn (&pid, MYSELF, NULL, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + + errCode (posix_spawnattr_init (&sa)); + errCode (posix_spawnattr_setsigmask (&sa, &emptymask)); + errCode (posix_spawnattr_setsigdefault (&sa, &sigusr1mask)); + errCode (posix_spawnattr_setflags (&sa, + POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)); + + /* ensure setsigmask and setsigdefault work */ + childargv[2] = "spawnattr"; + errCode (posix_spawn (&pid, MYSELF, NULL, &sa, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + + errCode (posix_spawnattr_destroy (&sa)); + + return 0; +} diff --git a/winsup/testsuite/winsup.api/posix_spawn/spawnp.c b/winsup/testsuite/winsup.api/posix_spawn/spawnp.c new file mode 100644 index 0000000..c7bee87 --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/spawnp.c @@ -0,0 +1,25 @@ +#include "test.h" +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int main (void) +{ + pid_t pid; + int status; + /* the test installation has very limited binaries on the PATH, but sh is one + of them and 'true' should be a builtin */ + char *childargv[] = {"sh", "-c", "true", NULL}; + char *childenv[] = {NULL}; + + /* unbuffer stdout */ + setvbuf(stdout, NULL, _IONBF, 0); + + /* can posix_spawnp find a program even with an empty environment? */ + errCode (posix_spawnp (&pid, childargv[0], NULL, NULL, childargv, childenv)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + + return 0; +} diff --git a/winsup/testsuite/winsup.api/posix_spawn/test.h b/winsup/testsuite/winsup.api/posix_spawn/test.h new file mode 100644 index 0000000..5c56ed1 --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/test.h @@ -0,0 +1,53 @@ +#ifndef _POSIX_SPAWN_TEST_H_ +#define _POSIX_SPAWN_TEST_H_ + +#define _GNU_SOURCE +#include <errno.h> +#include <error.h> +#include <signal.h> +#include <sys/wait.h> + +#define negError(x) do { \ + if ((x) < 0) \ + error_at_line(1, errno, __FILE__, __LINE__, "%s", #x); \ +} while (0) + +#define nullError(x) do { \ + if (!(x)) \ + error_at_line(1, errno, __FILE__, __LINE__, "%s", #x); \ +} while (0) + +#define sigError(x) do { \ + if ((x) == SIG_ERR) \ + error_at_line(1, errno, __FILE__, __LINE__, "%s", #x); \ +} while (0) + +#define errCodeExpected(expected, x) do { \ + int _errcode = (x); \ + if (_errcode != (expected)) \ + error_at_line(1, _errcode, __FILE__, __LINE__, "%s", #x); \ +} while (0) + +#define errCode(x) errCodeExpected(0, x) + +#define exitStatus(status, expectedExitCode) do { \ + if (WIFSIGNALED ((status))) \ + error_at_line (128 + WTERMSIG ((status)), 0, __FILE__, __LINE__ - 2, \ + "child terminated with signal %d", WTERMSIG ((status))); \ + else if (WIFEXITED ((status)) && WEXITSTATUS ((status)) != (expectedExitCode)) \ + error_at_line (WEXITSTATUS ((status)), 0, __FILE__, __LINE__ - 2, \ + "child exited with code %d", WEXITSTATUS ((status))); \ +} while (0) + +/* first vararg to testAssertMsg must be string msg */ +#define testAssertMsg(cond, ...) do { \ + if (!(cond)) \ + error_at_line (1, 0, __FILE__, __LINE__, __VA_ARGS__); \ +} while (0); + +#define testAssert(cond) testAssertMsg(cond, "%s", #cond) + +#define MYSELF "/proc/self/exe" + +#endif /* _POSIX_SPAWN_TEST_H_ */ + diff --git a/winsup/testsuite/winsup.api/posix_spawn/win32.c b/winsup/testsuite/winsup.api/posix_spawn/win32.c new file mode 100644 index 0000000..8998c43 --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/win32.c @@ -0,0 +1,181 @@ +#include "test.h" +#include <dlfcn.h> +#include <fcntl.h> +#include <limits.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/cygwin.h> +#include <unistd.h> + +char * find_winchild (void) +{ + static const char winchild[] = "/winchild"; + char *mingwtestdir = getenv ("mingwtestdir"); + if (!mingwtestdir) + { + Dl_info dli; + if (dladdr (&find_winchild, &dli)) + { + ssize_t i = strlen (dli.dli_fname) - 1; + for (int slashes = 0; i >= 0 && slashes < 3; --i) + if (dli.dli_fname[i] == '/') + slashes++; + stpcpy (stpcpy (dli.dli_fname + i + 1, "/mingw"), winchild); + return realpath (dli.dli_fname, NULL); + } + else + { + return realpath ("../../mingw/winchild", NULL); + } + } + else + { + char *ret, *tmp = malloc (strlen (mingwtestdir) + sizeof (winchild)); + stpcpy (stpcpy (tmp, mingwtestdir), winchild); + ret = realpath (tmp, NULL); + free (tmp); + return ret; + } +} + +static char tmppath[] = "pspawn.XXXXXX"; +static char tmpcwd[] = "tmpcwd.XXXXXX"; +static char tmppath2[sizeof (tmpcwd) + 9] = {0}; + +static void cleanup_tmpfiles (void) +{ + if (tmppath2[0]) + unlink (tmppath2); + rmdir (tmpcwd); + unlink (tmppath); +} + +int main (void) +{ + posix_spawn_file_actions_t fa; + pid_t pid; + int status; + int fd, fdcloexec, cwdfd; + char *childargv[] = {"winchild", NULL, NULL, NULL}; + char *winchild = find_winchild (); + + /* unbuffer stdout */ + setvbuf(stdout, NULL, _IONBF, 0); + + /* temp regular file */ + negError (fd = mkstemp (tmppath)); + atexit (cleanup_tmpfiles); + negError (close (fd)); + + /* temp directory */ + nullError (mkdtemp (tmpcwd)); + + /* temp file within temp directory */ + stpcpy (stpcpy (stpcpy (tmppath2, tmpcwd), "/"), "tmpfile2"); + negError (fd = open (tmppath2, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR)); + negError (close (fd)); + + /* open file descriptors to test inheritance */ + negError (fd = open ("/dev/null", O_RDONLY, 0644)); + negError (fdcloexec = open ("/dev/full", O_RDONLY|O_CLOEXEC, 0644)); + + /* test posix_spawn_file_actions_addopen */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 0, "/dev/zero", O_RDONLY, + 0644)); + childargv[1] = "0"; + childargv[2] = "\\Device\\Null"; + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_adddup2 */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_adddup2 (&fa, fd, 0)); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_adddup2 with CLOEXEC fd */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_adddup2 (&fa, fdcloexec, 0)); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_adddup2 with out to err */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, "/dev/zero", O_WRONLY, + 0644)); + errCode (posix_spawn_file_actions_adddup2 (&fa, 1, 2)); + childargv[1] = "2"; + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_addopen with real file */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, tmppath, O_WRONLY, 0644)); + childargv[1] = "1"; + childargv[2] = cygwin_create_path (CCP_POSIX_TO_WIN_A|CCP_ABSOLUTE, tmppath); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + free (childargv[2]); + + /* test posix_spawn_file_actions_addchdir */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addchdir (&fa, tmpcwd)); + childargv[1] = "CWD"; + childargv[2] = cygwin_create_path (CCP_POSIX_TO_WIN_A|CCP_ABSOLUTE, tmpcwd); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + + /* test posix_spawn_file_actions_addfchdir */ + negError (cwdfd = open (tmpcwd, O_SEARCH|O_DIRECTORY|O_CLOEXEC, 0755)); + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addfchdir (&fa, cwdfd)); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + free (childargv[2]); + + /* test posix_spawn_file_actions_addfchdir followed by addopen */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_addfchdir (&fa, cwdfd)); + errCode (posix_spawn_file_actions_addopen (&fa, 1, "tmpfile2", O_WRONLY, 0644)); + childargv[1] = "1"; + childargv[2] = cygwin_create_path (CCP_POSIX_TO_WIN_A|CCP_ABSOLUTE, tmppath2); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + free (childargv[2]); + + /* test posix_spawn_file_actions_adddup2 of directory handle */ + errCode (posix_spawn_file_actions_init (&fa)); + errCode (posix_spawn_file_actions_adddup2 (&fa, cwdfd, 0)); + childargv[1] = "0"; + childargv[2] = cygwin_create_path (CCP_POSIX_TO_WIN_A|CCP_ABSOLUTE, tmpcwd); + errCode (posix_spawn (&pid, winchild, &fa, NULL, childargv, environ)); + negError (waitpid (pid, &status, 0)); + exitStatus (status, 0); + errCode (posix_spawn_file_actions_destroy (&fa)); + free (childargv[2]); + + negError (close (cwdfd)); + negError (close (fd)); + negError (close (fdcloexec)); + + return 0; +} diff --git a/winsup/testsuite/winsup.api/posix_spawn/winchild.c b/winsup/testsuite/winsup.api/posix_spawn/winchild.c new file mode 100644 index 0000000..6fdfa00 --- /dev/null +++ b/winsup/testsuite/winsup.api/posix_spawn/winchild.c @@ -0,0 +1,130 @@ +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <winternl.h> +#include <ctype.h> +#include <stdio.h> + + +int wmain (int argc, wchar_t **argv) +{ + if (argc != 3) + { + fwprintf (stderr, L"Usage: %ls handle expected\n", argv[0]); + return 1; + } + + if (!wcscmp (argv[1], L"CWD")) + { + LPWSTR buffer; + DWORD len = GetCurrentDirectoryW (0, NULL); + if (len == 0) + { + fwprintf (stderr, L"%ls: GetCurrentDirectory failed with error %lu\n", + argv[0], GetLastError ()); + return 2; + } + buffer = malloc (len * sizeof (WCHAR)); + if (GetCurrentDirectoryW (len, buffer) != len - 1) + { + fwprintf (stderr, L"%ls: GetCurrentDirectory failed with error %lu\n", + argv[0], GetLastError ()); + return 2; + } + if (wcscmp (argv[2], buffer)) + { + fwprintf (stderr, L"%ls: CWD '%ls' != expected '%ls'\n", + argv[0], buffer, argv[2]); + free (buffer); + return 4; + } + free (buffer); + } + else if (iswdigit (argv[1][0]) && !argv[1][1]) + { + HANDLE stdhandle; + DWORD nStdHandle; + switch (argv[1][0]) + { + case L'0': + nStdHandle = STD_INPUT_HANDLE; + break; + case L'1': + nStdHandle = STD_OUTPUT_HANDLE; + break; + case L'2': + nStdHandle = STD_ERROR_HANDLE; + break; + default: + fwprintf (stderr, L"%ls: Unknown handle '%ls'\n", argv[0], argv[1]); + return 1; + } + + stdhandle = GetStdHandle (nStdHandle); + if (stdhandle == INVALID_HANDLE_VALUE) + { + fwprintf (stderr, L"%ls: Failed getting standard handle %ls: %lu\n", + argv[0], argv[1], GetLastError ()); + return 2; + } + else if (stdhandle == NULL) + { + if (wcscmp (argv[2], L"<CLOSED>")) + { + fwprintf (stderr, + L"%ls: Handle %ls name '%ls' != expected '%ls'\n", + argv[0], argv[1], L"<CLOSED>", argv[2]); + return 4; + } + } + else + { + LPWSTR buf, win32path; + buf = malloc (65536); + if (!GetFinalPathNameByHandleW (stdhandle, buf, + 65536 / sizeof (WCHAR), + FILE_NAME_OPENED|VOLUME_NAME_DOS)) + { + POBJECT_NAME_INFORMATION pinfo = (POBJECT_NAME_INFORMATION) buf; + DWORD err = GetLastError (); + ULONG len; + NTSTATUS status = NtQueryObject (stdhandle, ObjectNameInformation, + pinfo, 65536, &len); + if (!NT_SUCCESS (status)) + { + fwprintf (stderr, + L"%ls: NtQueryObject for handle %ls failed: 0x%08x\n", + argv[0], argv[1], status); + free (buf); + return 3; + } + + pinfo->Name.Buffer[pinfo->Name.Length / sizeof (WCHAR)] = L'\0'; + win32path = pinfo->Name.Buffer; + } + else + { + static const WCHAR prefix[] = L"\\\\?\\"; + win32path = buf; + if (!wcsncmp (win32path, prefix, + sizeof (prefix) / sizeof (WCHAR) - 1)) + win32path += sizeof (prefix) / sizeof (WCHAR) - 1; + } + + if (wcscmp (win32path, argv[2])) + { + fwprintf (stderr, + L"%ls: Handle %ls name '%ls' != expected '%ls'\n", + argv[0], argv[1], win32path, argv[2]); + free (buf); + return 4; + } + free (buf); + } + } + else + { + fwprintf (stderr, L"%ls: Unknown handle '%ls'\n", argv[0], argv[1]); + return 1; + } + return 0; +} diff --git a/winsup/testsuite/winsup.api/pthread/cpu_relax.h b/winsup/testsuite/winsup.api/pthread/cpu_relax.h index 1936dc5..c31ef8c 100644 --- a/winsup/testsuite/winsup.api/pthread/cpu_relax.h +++ b/winsup/testsuite/winsup.api/pthread/cpu_relax.h @@ -4,7 +4,8 @@ #if defined(__x86_64__) || defined(__i386__) // Check for x86 architectures #define CPU_RELAX() __asm__ volatile ("pause" :::) #elif defined(__aarch64__) || defined(__arm__) // Check for ARM architectures - #define CPU_RELAX() __asm__ volatile ("yield" :::) + #define CPU_RELAX() __asm__ volatile ("dmb ishst\n" \ + "yield" :::) #else #error unimplemented for this target #endif diff --git a/winsup/utils/kill.cc b/winsup/utils/kill.cc index bcabcd4..1e6ab5c 100644 --- a/winsup/utils/kill.cc +++ b/winsup/utils/kill.cc @@ -372,7 +372,9 @@ main (int argc, char **argv) case '?': if (gotasig) /* this is a negative pid, go ahead */ { - --optind; + /* Reset optind because it points to the next argument if and + only if the pid has one digit. */ + optind = av - argv; goto out; } optreset = 1; |