aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/cygwin.yml176
-rw-r--r--libgloss/doc/porting.texi2
-rw-r--r--libgloss/riscv/internal_syscall.h2
-rw-r--r--libgloss/rs6000/mbx-print.c2
-rw-r--r--libgloss/rs6000/mvme-print.c3
-rw-r--r--libgloss/rs6000/mvme-read.c2
-rw-r--r--libgloss/rs6000/sim-abort.c3
-rw-r--r--libgloss/rs6000/sim-inbyte.c1
-rw-r--r--libgloss/rs6000/sim-print.c2
-rw-r--r--libgloss/rs6000/sim-sbrk.c1
-rw-r--r--newlib/MAINTAINERS1
-rw-r--r--newlib/Makefile.in41
-rwxr-xr-xnewlib/doc/makedocbook.py4
-rw-r--r--newlib/libc/ctype/ctype_.c9
-rw-r--r--newlib/libc/include/pthread.h12
-rw-r--r--newlib/libc/include/search.h10
-rw-r--r--newlib/libc/include/stdlib.h5
-rw-r--r--newlib/libc/include/sys/reent.h3
-rw-r--r--newlib/libc/include/sys/unistd.h2
-rw-r--r--newlib/libc/include/time.h5
-rw-r--r--newlib/libc/machine/riscv/Makefile.inc2
-rw-r--r--newlib/libc/machine/riscv/memchr.c152
-rw-r--r--newlib/libc/machine/riscv/memcpy-asm.S12
-rw-r--r--newlib/libc/machine/riscv/memcpy.c163
-rw-r--r--newlib/libc/machine/riscv/memmove.S28
-rw-r--r--newlib/libc/machine/riscv/memrchr.c172
-rw-r--r--newlib/libc/machine/riscv/memset.S349
-rw-r--r--newlib/libc/machine/riscv/rv_string.h51
-rw-r--r--newlib/libc/machine/riscv/setjmp.S78
-rw-r--r--newlib/libc/machine/riscv/strcmp.S198
-rw-r--r--newlib/libc/machine/riscv/strlen.c19
-rw-r--r--newlib/libc/machine/riscv/xlenint.h7
-rw-r--r--newlib/libc/posix/ftw.c4
-rw-r--r--newlib/libc/posix/glob.c90
-rw-r--r--newlib/libc/posix/posix_spawn.c45
-rw-r--r--newlib/libc/posix/posix_spawn.h54
-rw-r--r--newlib/libc/search/tdelete.c2
-rw-r--r--newlib/libc/search/tfind.c2
-rw-r--r--newlib/libc/search/tsearch.c2
-rw-r--r--newlib/libc/search/twalk.c4
-rw-r--r--newlib/libc/stdlib/mbtowc_r.c25
-rw-r--r--newlib/libc/stdlib/wctomb_r.c4
-rw-r--r--newlib/libc/sys/rtems/include/limits.h10
-rw-r--r--newlib/libc/sys/rtems/include/semaphore.h6
-rw-r--r--newlib/libc/sys/rtems/include/sys/dirent.h2
-rw-r--r--newlib/libc/sys/rtems/include/sys/poll.h9
-rw-r--r--newlib/libm/machine/riscv/e_sqrt.c1
-rw-r--r--newlib/libm/machine/riscv/ef_sqrt.c1
-rw-r--r--winsup/configure.ac2
-rwxr-xr-xwinsup/cygserver/cygserver-config2
-rw-r--r--winsup/cygwin/Makefile.am4
-rw-r--r--winsup/cygwin/create_posix_thread.cc21
-rw-r--r--winsup/cygwin/cygwin.sc.in2
-rw-r--r--winsup/cygwin/dcrt0.cc9
-rw-r--r--winsup/cygwin/dlfcn.cc13
-rw-r--r--winsup/cygwin/dll_init.cc2
-rw-r--r--winsup/cygwin/exceptions.cc74
-rw-r--r--winsup/cygwin/fhandler/base.cc45
-rw-r--r--winsup/cygwin/fhandler/console.cc104
-rw-r--r--winsup/cygwin/fhandler/pipe.cc52
-rw-r--r--winsup/cygwin/fhandler/pty.cc30
-rw-r--r--winsup/cygwin/fhandler/socket_inet.cc5
-rw-r--r--winsup/cygwin/fhandler/socket_local.cc7
-rw-r--r--winsup/cygwin/fhandler/termios.cc31
-rw-r--r--winsup/cygwin/fork.cc6
-rw-r--r--winsup/cygwin/include/cygwin/config.h7
-rw-r--r--winsup/cygwin/include/cygwin/limits.h8
-rw-r--r--winsup/cygwin/include/cygwin/time.h6
-rw-r--r--winsup/cygwin/include/pthread.h39
-rw-r--r--winsup/cygwin/include/search.h10
-rw-r--r--winsup/cygwin/include/semaphore.h2
-rw-r--r--winsup/cygwin/include/sys/poll.h2
-rw-r--r--winsup/cygwin/include/sys/termios.h1
-rw-r--r--winsup/cygwin/lib/pthreadconst.S17
-rw-r--r--winsup/cygwin/local_includes/cygmalloc.h3
-rw-r--r--winsup/cygwin/local_includes/cygtls.h17
-rw-r--r--winsup/cygwin/local_includes/fhandler.h18
-rw-r--r--winsup/cygwin/local_includes/ntdll.h9
-rw-r--r--winsup/cygwin/local_includes/select.h3
-rw-r--r--winsup/cygwin/local_includes/thread.h31
-rw-r--r--winsup/cygwin/local_includes/tty.h6
-rw-r--r--winsup/cygwin/mm/cygheap.cc13
-rw-r--r--winsup/cygwin/net.cc5
-rw-r--r--winsup/cygwin/path.cc26
-rw-r--r--winsup/cygwin/release/3.6.118
-rw-r--r--winsup/cygwin/release/3.6.242
-rw-r--r--winsup/cygwin/release/3.6.35
-rw-r--r--winsup/cygwin/release/3.6.423
-rw-r--r--winsup/cygwin/select.cc84
-rw-r--r--winsup/cygwin/sigproc.cc20
-rw-r--r--winsup/cygwin/strfuncs.cc6
-rw-r--r--winsup/cygwin/syscalls.cc11
-rw-r--r--winsup/cygwin/thread.cc41
-rw-r--r--winsup/cygwin/times.cc3
-rw-r--r--winsup/cygwin/uinfo.cc17
-rw-r--r--winsup/doc/dll.xml174
-rw-r--r--winsup/doc/faq-using.xml5
-rw-r--r--winsup/doc/path.xml21
-rw-r--r--winsup/testsuite/Makefile.am8
-rwxr-xr-xwinsup/testsuite/cygrun.sh4
-rw-r--r--winsup/testsuite/mingw/Makefile.am7
-rwxr-xr-xwinsup/testsuite/stress/cygstress613
-rw-r--r--winsup/testsuite/winsup.api/cygload.cc7
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/chdir.c158
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/errors.c66
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/fds.c124
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/signals.c82
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/spawnp.c25
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/test.h53
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/win32.c181
-rw-r--r--winsup/testsuite/winsup.api/posix_spawn/winchild.c130
-rw-r--r--winsup/testsuite/winsup.api/pthread/cpu_relax.h3
-rw-r--r--winsup/utils/kill.cc4
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 &lt;stdio.h&gt;
@@ -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;