Discussion:
[PATCH] ARM: fixed *jmp() functions
Enrico Scholz
2011-03-12 20:40:10 UTC
Permalink
With soft-fp, the previous __sigsetjmp() implementation modifies a
buffer pointing 48 bytes before the correct one. Was broken at
2001-03-16 by the "add several functions to make busybox compile and
partially link" commit...

Nevertheless, there are further patches required which save the
co-processor registers (e.g. the iwmmxt ones) of recent cpus. The 'lfm'
in the __SOFTFP__ clause is very ancient and will not work with recent
hard-fp capable cpus.

Patch adds testsuite programs for sigjmp() + sigsetjmp() functionality.

Signed-off-by: Enrico Scholz <***@informatik.tu-chemnitz.de>
---
arm/__longjmp.S | 5 ++-
arm/setjmp.S | 8 ++-
test/Makefile | 4 +-
test/runtests.sh | 2 +-
test/setjmp.c | 106 +++++++++++++++++++++++++++++++++++++++++
test/sigsetjmp.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 258 insertions(+), 7 deletions(-)
create mode 100644 test/setjmp.c
create mode 100644 test/sigsetjmp.c

diff --git a/arm/__longjmp.S b/arm/__longjmp.S
index 31307bd..7b71af9 100644
--- a/arm/__longjmp.S
+++ b/arm/__longjmp.S
@@ -6,6 +6,9 @@ __longjmp:
movs r0, r1
moveq r0, #1
#ifndef __SOFTFP__
- lfm f4, 4, [ip], #48
+ lfm f4, 4, [ip, #10*4]
+#endif
+#ifdef __IWMMXT__
+# warning "sigjmp will not restore iwmmxt coprocessor registers"
#endif
ldmia ip, {r4-r11, sp, pc}
diff --git a/arm/setjmp.S b/arm/setjmp.S
index 6b850d4..bceda66 100644
--- a/arm/setjmp.S
+++ b/arm/setjmp.S
@@ -8,10 +8,12 @@ __setjmp:
__sigsetjmp:
.weak sigsetjmp
sigsetjmp:
+ stmia r0, {r4-r11, sp, lr}
#ifndef __SOFTFP__
- sfm f4, 4, [r0], #48
+ sfm f4, 4, [r0, #10*4]
+#endif
+#ifdef __IWMMXT__
+# warning "setjmp will not save iwmmxt coprocessor registers"
#endif
- stmia r0, {r4-r11, sp, lr}
- sub r0, r0, #48
b __sigjmp_save

diff --git a/test/Makefile b/test/Makefile
index 2e406f3..862ba75 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -12,8 +12,8 @@ fputc ftw fwrite getaddrinfo getenv getgrnam gethostbyaddr gethostbyname \
gethostbyname_r getmntent getopt getpass getpwnam getservbyname getservbyport getusershell \
glob grent hasmntopt hello iconv if_nameindex ltostr malloc-debugger md5_testharness \
memccpy memchr memcmp memrchr memusage mktime mmap_test pipe printf printftest \
-protoent prototypes putenv pwent rand48 read1 readdir regex select sendfile servent siglist \
-speed spent sprintf sscanf stdarg strcasecmp strcmp strncat strncpy strptime strrchr \
+protoent prototypes putenv pwent rand48 read1 readdir regex select sendfile servent setjmp siglist \
+sigsetjmp speed spent sprintf sscanf stdarg strcasecmp strcmp strncat strncpy strptime strrchr \
strstr strtol sysenter ungetc utime waitpid

test: $(TESTPROGRAMS)
diff --git a/test/runtests.sh b/test/runtests.sh
index d6fb19b..4bea244 100644
--- a/test/runtests.sh
+++ b/test/runtests.sh
@@ -1,6 +1,6 @@
SUBDIRS="dirent inet stdio string stdlib time"

-TESTPROGRAMS="adjtime alarm argv atexit bsearch byteswap calloc confstr empty fadvise flush fputc ffs fnmatch ftw fwrite getaddrinfo getenv getdelim getgrnam gethostbyaddr gethostbyname gethostbyname_r getmntent getopt getpwnam getservbyname getservbyport getusershell glob grent hasmntopt hello iconv if_nameindex ltostr malloc-debugger md5_testharness memccpy memchr memcmp memrchr memusage mktime mmap_test pipe printf printftest protoent prototypes putenv pwent rand48 readdir regex select sendfile servent siglist speed spent sprintf sscanf stdarg strcasecmp strcmp strncat strncpy strptime strrchr strstr strtol sysenter ungetc utime waitpid"
+TESTPROGRAMS="adjtime alarm argv atexit bsearch byteswap calloc confstr empty fadvise flush fputc ffs fnmatch ftw fwrite getaddrinfo getenv getdelim getgrnam gethostbyaddr gethostbyname gethostbyname_r getmntent getopt getpwnam getservbyname getservbyport getusershell glob grent hasmntopt hello iconv if_nameindex ltostr malloc-debugger md5_testharness memccpy memchr memcmp memrchr memusage mktime mmap_test pipe printf printftest protoent prototypes putenv pwent rand48 readdir regex select sendfile servent setjmp siglist sigsetjmp speed spent sprintf sscanf stdarg strcasecmp strcmp strncat strncpy strptime strrchr strstr strtol sysenter ungetc utime waitpid"

STDIN="read1"
PASS="getpass"
diff --git a/test/setjmp.c b/test/setjmp.c
new file mode 100644
index 0000000..15951e5
--- /dev/null
+++ b/test/setjmp.c
@@ -0,0 +1,106 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+static int Xmemcmp(void const volatile *a, void const volatile *b, size_t l)
+{
+ return memcmp((void const *)a, (void const *)b, l);
+}
+
+int main(void)
+{
+ char volatile a[8] = "testbufA";
+ jmp_buf env;
+ char volatile b[8] = "testbufB";
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+
+ /* Test 1: not calling longjmp */
+ if (setjmp(env) == 0) {
+ char volatile somebuf[128];
+
+ memset((void *)somebuf, 0xde, sizeof somebuf);
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+ } else
+ assert(0);
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+
+ /* Test 2: calling longjmp */
+ switch (setjmp(env)) {
+ case 0: {
+ char volatile somebuf[128];
+
+ memset((void *)somebuf, 0xde, sizeof somebuf);
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+ longjmp(env, 23);
+
+ a[0] = 'X';
+ b[0] = 'X';
+ }
+
+ case 23:
+ break;
+
+ default:
+ assert(0);
+ }
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+
+ /* Test 3: calling longjmp again with dirty env */
+ switch (setjmp(env)) {
+ case 0: {
+ char volatile somebuf[128];
+
+ memset((void *)somebuf, 0xde, sizeof somebuf);
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+ longjmp(env, 23);
+
+ a[0] = 'X';
+ b[0] = 'X';
+ }
+
+ case 23:
+ break;
+
+ default:
+ assert(0);
+ }
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+
+ /* Test 4: not calling longjmp, but dirty env */
+ if (setjmp(env) == 0) {
+ char volatile somebuf[128];
+
+ memset((void *)somebuf, 0xde, sizeof somebuf);
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+ } else
+ assert(0);
+
+ assert(Xmemcmp(a, "testbufA", 8) == 0);
+ assert(Xmemcmp(b, "testbufB", 8) == 0);
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/sigsetjmp.c b/test/sigsetjmp.c
new file mode 100644
index 0000000..3fa71bb
--- /dev/null
+++ b/test/sigsetjmp.c
@@ -0,0 +1,140 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#define TEST_PATTERN \
+ "0123456789abcdefghijklmnopqrstuv" \
+ "ZYXWVUTSRQPONMLKJIHGFEDCBA987654" \
+ "456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "vutsrqponmlkjihgfedcba9876543210" \
+ "0123456789ABCDEFGHIJKLMNOPQRSTUV" \
+ "zyxwvutsrqponmlkjihgfedcba987654" \
+ "456789abcdefghijklmnopqrstuvwxyz" \
+ "VUTSRQPONMLKJIHGFEDCBA987654321" \
+
+static struct {
+ char volatile a[256];
+ sigjmp_buf env;
+ char volatile b[256];
+} sigenv = {
+ .a = TEST_PATTERN "<",
+ .b = TEST_PATTERN ">",
+};
+
+static int volatile sig_seen;
+
+#define VALIDATE_BUFFERS(_sig_exp) do { \
+ assert(Xmemcmp(sigenv.a, TEST_PATTERN "<", sizeof sigenv.a) == 0); \
+ assert(Xmemcmp(sigenv.b, TEST_PATTERN ">", sizeof sigenv.b) == 0); \
+ assert(sig_seen == (_sig_exp)); \
+ } while (0)
+
+static int Xmemcmp(void const volatile *a, void const volatile *b, size_t l)
+{
+ return memcmp((void const *)a, (void const *)b, l);
+}
+
+static void do_test(int sig_num, int do_save, int block_sig)
+{
+ int rc;
+ sigset_t block_set;
+ sigset_t cur_set;
+
+ printf("%s(%d,%d,%d)... ", __func__, sig_num, do_save, block_sig);
+ fflush(stdout);
+
+ VALIDATE_BUFFERS(0);
+
+ sigemptyset(&block_set);
+ assert(sigprocmask(SIG_SETMASK, NULL, &cur_set) == 0);
+
+ /* verify that tested signal is not blocked */
+ if (sig_num != 0)
+ assert(!sigismember(&cur_set, sig_num));
+
+ /* verify that blocked signal is not already blocked and fill signal set */
+ if (block_sig != 0) {
+ assert(!sigismember(&cur_set, block_sig));
+ sigaddset(&block_set, block_sig);
+ }
+
+ sig_seen = 0;
+ rc = sigsetjmp(sigenv.env, do_save);
+ if (rc == 0) {
+ char volatile somebuf[128];
+
+ memset((void *)somebuf, 0x42, sizeof somebuf);
+ VALIDATE_BUFFERS(0);
+
+ /* modify signal mask */
+ if (block_sig != 0)
+ assert(sigprocmask(SIG_BLOCK, &block_set, NULL) == 0);
+
+ /* raise a signal which triggers a siglongjmp */
+ if (sig_num != 0) {
+ raise(sig_num);
+ sigenv.a[0] = 'X';
+ sigenv.b[0] = 'X';
+ assert(0);
+ }
+ } else if (rc != sig_num)
+ /* sigsetjmp() returned with an unexpected value */
+ assert(0);
+
+ VALIDATE_BUFFERS(sig_num);
+ sig_seen = 0;
+
+ /* check whether current signal mask contains the blocked signal; it should
+ be there iff sigsetjmp() was triggered and sigmask was saved. */
+ if (block_sig != 0) {
+ sigset_t cur_set;
+ assert(sigprocmask(SIG_SETMASK, NULL, &cur_set) == 0);
+
+ if (do_save && rc != 0)
+ assert(!sigismember(&cur_set, block_sig));
+ else {
+ assert( sigismember(&cur_set, block_sig));
+ sigprocmask(SIG_UNBLOCK, &block_set, NULL);
+ }
+ }
+
+ printf(" ok\n");
+};
+
+static void sig_handler(int num)
+{
+ assert(sig_seen == 0);
+ sig_seen = num;
+ siglongjmp(sigenv.env, num);
+}
+
+int main(void)
+{
+ struct sigaction sigact = {
+ .sa_handler = sig_handler,
+ .sa_flags = SA_NODEFER, /* raised signal will be in blocked mask else */
+ };
+
+ /* verify our assumptions about the memory layout */
+ assert(sizeof sigenv.a == 256);
+ assert(sizeof sigenv.b == 256);
+ assert(offsetof(__typeof__(sigenv), env) == sizeof sigenv.a);
+ assert(offsetof(__typeof__(sigenv), b) == sizeof sigenv.a + sizeof sigenv.env);
+
+ sigaction(SIGBUS, &sigact, NULL);
+ sigaction(SIGUSR1, &sigact, NULL);
+
+ do_test(0, 0, 0);
+ do_test(0, 0, SIGUSR1);
+ do_test(0, 1, 0);
+ do_test(0, 1, SIGUSR1);
+ do_test(SIGBUS, 0, 0);
+ do_test(SIGBUS, 0, SIGUSR1);
+ do_test(SIGBUS, 1, 0);
+ do_test(SIGBUS, 1, SIGUSR1);
+
+ return EXIT_SUCCESS;
+}
--
1.7.4
Enrico Scholz
2011-04-03 10:07:24 UTC
Permalink
Post by Enrico Scholz
Nevertheless, there are further patches required which save the
co-processor registers (e.g. the iwmmxt ones) of recent cpus.
fwiw, basic neon and fpv4 support (e.g. for omap3) is added by

https://github.com/ensc/dietlibc/commit/e1910c6289f754dcbf863d309ed8b72452a5de74

Patch enhances testsuite to check whether fpu registers are restored.
Patch requires <arm-features.h> added by

https://github.com/ensc/dietlibc/commit/031c299c386e522413d22300d24a494077d51986



Enrico

Loading...