DEVEL15-linux-allow-disabling-syscall-probing-20090602
[openafs.git] / src / afs / LINUX / osi_probe.c
index fc9c394..02bd9fd 100644 (file)
 #include <afsconfig.h>
 #include "afs/param.h"
 #endif
+#if defined(ENABLE_LINUX_SYSCALL_PROBING) && defined(EXPORTED_INIT_MM)
+#ifdef AFS_LINUX24_ENV
 #include <linux/module.h> /* early to avoid printf->printk mapping */
+#ifdef AFS_LINUX26_ENV
+#include <scsi/scsi.h> /* for scsi_command_size */
+#endif
 #ifndef OSI_PROBE_STANDALONE
 #include "afs/sysincludes.h"
 #include "afsincludes.h"
 #endif
 #include <linux/version.h>
+#include <linux/sched.h>
+#ifdef CONFIG_H_EXISTS
 #include <linux/config.h>
+#endif
 #include <linux/linkage.h>
 #include <linux/init.h>
 #include <linux/unistd.h>
 #include <linux/mm.h>
 
-#ifdef AFS_AMD64_LINUX20_ENV
-#include <asm/ia32_unistd.h>
+#if defined(AFS_PPC64_LINUX26_ENV)
+#include <asm/abs_addr.h>
 #endif
 
-#ifdef HAVE_KERNEL_LINUX_SYSCALL_H
-#include <linux/syscall.h>
+#ifdef AFS_AMD64_LINUX20_ENV
+#include <asm/ia32_unistd.h>
 #endif
 
 /* number of syscalls */
 #endif
 
 /* lower bound of valid kernel text pointers */
-//#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#ifdef AFS_IA64_LINUX20_ENV
+#define ktxt_lower_bound (((unsigned long)&kernel_thread )  & 0xfff00000L)
+#elif defined(AFS_PPC64_LINUX20_ENV)
+#define ktxt_lower_bound (KERNELBASE)
+#else
 #define ktxt_lower_bound (((unsigned long)&kernel_thread )  & ~0xfffffL)
-//#else
-//#define ktxt_lower_bound (((unsigned long)&empty_zero_page) & ~0xfffffL)
-//#endif
+#endif
 
 /* On SPARC64 and S390X, sys_call_table contains 32-bit entries
  * even though pointers are 64 bit quantities.
 #define PROBETYPE long
 #endif
 
+#if defined(AFS_S390X_LINUX20_ENV) && !defined(AFS_S390X_LINUX26_ENV) 
+#define _SS(x) ((x) << 1)
+#define _SX(x) ((x) &~ 1)
+#else
+#define _SS(x) (x)
+#define _SX(x) (x)
+#endif
+
+/* Older Linux doesn't have __user. The sys_read prototype needs it. */
+#ifndef __user
+#define __user
+#endif
 
 /* Allow the user to specify sys_call_table addresses */
 static unsigned long sys_call_table_addr[4] = { 0,0,0,0 };
+#if defined(module_param_array) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param_array(sys_call_table_addr, long, NULL, 0);
+#else
 MODULE_PARM(sys_call_table_addr, "1-4l");
+#endif
 MODULE_PARM_DESC(sys_call_table_addr, "Location of system call tables");
 
 /* If this is set, we are more careful about avoiding duplicate matches */
 static int probe_carefully = 1;
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param(probe_carefully, int, 0);
+#else
 MODULE_PARM(probe_carefully, "i");
+#endif
 MODULE_PARM_DESC(probe_carefully, "Probe for system call tables carefully");
 
 static int probe_ignore_syscalls[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+#if defined(module_param_array) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param_array(probe_ignore_syscalls, int, NULL, 0);
+#else
 MODULE_PARM(probe_ignore_syscalls, "1-8i");
+#endif
 MODULE_PARM_DESC(probe_ignore_syscalls, "Syscalls to ignore in table checks");
 
 #ifdef OSI_PROBE_DEBUG
@@ -121,21 +155,38 @@ MODULE_PARM_DESC(probe_ignore_syscalls, "Syscalls to ignore in table checks");
  * 0x0010 - detail - check_harder
  * 0x0020 - detail - check_harder/zapped
  * 0x0040 - automatically ignore setgroups and afs_syscall
+ * 0x0080 - detail - check_table_readable
  */
 static int probe_debug = 0x41;
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param(probe_debug, int, 0);
+#else
 MODULE_PARM(probe_debug, "i");
+#endif
 MODULE_PARM_DESC(probe_debug, "Debugging level");
 
 static unsigned long probe_debug_addr[4] = { 0,0,0,0 };
+#if defined(module_param_array) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param_array(probe_debug_addr, long, NULL, 0);
+#else
 MODULE_PARM(probe_debug_addr, "1-4l");
+#endif
 MODULE_PARM_DESC(probe_debug_addr, "Debug range starting locations");
 
 static unsigned long probe_debug_range = 0;
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param(probe_debug_range, long, 0);
+#else
 MODULE_PARM(probe_debug_range, "l");
+#endif
 MODULE_PARM_DESC(probe_debug_range, "Debug range length");
 
 static unsigned long probe_debug_tag = 0;
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
+module_param(probe_debug_tag, long, 0);
+#else
 MODULE_PARM(probe_debug_tag, "l");
+#endif
 MODULE_PARM_DESC(probe_debug_tag, "Debugging output start tag");
 #endif
 
@@ -143,7 +194,16 @@ MODULE_PARM_DESC(probe_debug_tag, "Debugging output start tag");
 /* Weak references are our friends.  They are supported by the in-kernel
  * linker in Linux 2.6 and by all versions of modutils back to 2.2pre1.
  * A weak reference not satisified by the kernel will have value zero.
+ *
+ * Unfortunately, weak references to functions don't work right on
+ * IA64; specifically, if you actually try to make a call through
+ * such a reference, and the symbol doesn't exist in the kernel, then
+ * the module relocation code will oops.  A workaround for this is
+ * probably possible, but the use of kallsyms_* is of limited value,
+ * so I'm not bothing with the effort for now.
+ * -- jhutz, 10-Feb-2005
  */
+#ifdef OSI_PROBE_KALLSYMS
 extern int kallsyms_symbol_to_address(char *name, unsigned long *token,
                                      char **mod_name,
                                      unsigned long *mod_start,
@@ -167,21 +227,28 @@ extern int kallsyms_address_to_symbol(unsigned long address,
                                      unsigned long *sym_start,
                                      unsigned long *sym_end
                                     ) __attribute__((weak));
+#endif
 
 extern SYSCALLTYPE sys_call_table[] __attribute__((weak));
 extern SYSCALLTYPE ia32_sys_call_table[] __attribute__((weak));
 extern SYSCALLTYPE sys_call_table32[] __attribute__((weak));
+extern SYSCALLTYPE sys_call_table_emu[] __attribute__((weak));
 
+extern asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count) __attribute__((weak));
 extern asmlinkage long sys_close(unsigned int) __attribute__((weak));
+#if defined(EXPORTED_SYS_CHDIR)
 extern asmlinkage long sys_chdir(const char *) __attribute__((weak));
-extern asmlinkage int  sys_write(unsigned int, const char *, size_t) __attribute__((weak));
+#endif
+extern asmlinkage ssize_t sys_write(unsigned int, const char *, size_t) __attribute__((weak));
 #ifdef AFS_LINUX26_ENV
 extern asmlinkage long sys_wait4(pid_t, int *, int, struct rusage *) __attribute__((weak));
 #else
 extern asmlinkage long sys_wait4(pid_t, unsigned int *, int, struct rusage *) __attribute__((weak));
 #endif
-extern asmlinkage int  sys_exit (int) __attribute__((weak));
+extern asmlinkage long sys_exit (int) __attribute__((weak));
+#if defined(EXPORTED_SYS_OPEN)
 extern asmlinkage long sys_open (const char *, int, int) __attribute__((weak));
+#endif
 extern asmlinkage long sys_ioctl(unsigned int, unsigned int, unsigned long) __attribute__((weak));
 
 
@@ -217,6 +284,11 @@ typedef struct {
     unsigned long try_base_mask;    /* base address bits to force to zero */
     unsigned long try_length;       /* default length for scan */
 
+    unsigned long alt_try_sect_sym;     /* symbol in section to try scanning */
+    unsigned long alt_try_base;         /* default base address for scan */
+    unsigned long alt_try_base_mask;    /* base address bits to force to zero */
+    unsigned long alt_try_length;       /* default length for scan */
+
     int n_zapped_syscalls;          /* number of unimplemented system calls */
     int *zapped_syscalls;           /* list of unimplemented system calls */
 
@@ -229,6 +301,10 @@ typedef struct {
     int debug_ignore_NR[4];         /* syscalls to ignore for debugging */
 } probectl;
 
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+static int check_access(unsigned long, int);
+static int check_table_readable(probectl *, PROBETYPE *);
+#endif
 
 
 /********** Probing Configuration: sys_call_table **********/
@@ -237,14 +313,20 @@ typedef struct {
 /* On PPC64 and SPARC64, we need to omit the ones that might match both tables */
 static tryctl main_try[] = {
 #if !defined(AFS_PPC64_LINUX20_ENV) && !defined(AFS_SPARC64_LINUX20_ENV)
+#if defined(EXPORTED_SYS_CHDIR)
     { "scan: close+chdir+write", __NR_close, &sys_close, __NR_chdir, &sys_chdir, __NR_write, &sys_write },
 #endif
+#endif
     { "scan: close+wait4",       __NR_close, &sys_close, __NR_wait4, &sys_wait4, -1,         0          },
 #if !defined(AFS_PPC64_LINUX20_ENV) && !defined(AFS_SPARC64_LINUX20_ENV)
+#if defined(EXPORTED_SYS_CHDIR)
     { "scan: close+chdir",       __NR_close, &sys_close, __NR_chdir, &sys_chdir, -1,         0          },
 #endif
+#endif
     { "scan: close+ioctl",       __NR_close, &sys_close, __NR_ioctl, &sys_ioctl, -1,         0          },
+#if defined(EXPORTED_SYS_OPEN)
     { "scan: exit+open",         __NR_exit,  &sys_exit,  __NR_open,  &sys_open,  -1,         0          },
+#endif
     { 0 }
 };
 
@@ -306,7 +388,7 @@ static int main_zapped_syscalls[] = {
  * corresponding __NR macros are not defined, so the tests above fail.
  * Instead, we just have to know the numbers for these.
  */
-#ifdef AFS_S390_LINUX20_ENV
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
     /* break, stty, gtty, ftime, prof, lock, mpx */
     17, 31, 32, 35, 44, 53, 56,
 #endif
@@ -385,8 +467,8 @@ static int main_zapped_syscalls[] = {
  * mmap2 and fstat64 are implemented only for 32-bit calls
  */
 #ifdef AFS_PPC64_LINUX20_ENV
-    __NR_mmap2,
-    __NR_fstat64,
+    /* _mmap2, _fstat64 */
+    192, 197,
 #endif /* AFS_PPC64_LINUX20_ENV */
 
 /* Similarly for S390X, with lcown16 and fstat64 */
@@ -400,6 +482,13 @@ static int main_zapped_syscalls[] = {
 
 /* unique syscalls for try_harder */
 static int main_unique_syscalls[] = {
+#if defined(AFS_SPARC64_LINUX24_ENV) || defined(AFS_SPARC_LINUX24_ENV)
+    /* 
+     * On SPARC, we need some additional unique calls to make sure
+     * we don't match the SunOS-compatibility table.
+     */
+    __NR_sgetmask, __NR_ssetmask,
+#endif
     __NR_exit, __NR_mount, __NR_read, __NR_write,
     __NR_open, __NR_close, __NR_unlink
 };
@@ -428,8 +517,11 @@ static probectl main_probe = {
     main_try,                     /* array of combinations to try */
 
     /* symbol in section to try scanning */
-#if defined(AFS_SPARC64_LINUX20_ENV)
+#if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
     (unsigned long)&sys_close,
+#elif defined(AFS_AMD64_LINUX26_ENV)
+    /* On this platform, it's in a different section! */
+    (unsigned long)&generic_ro_fops,
 #elif defined(AFS_AMD64_LINUX20_ENV)
     /* On this platform, it's in a different section! */
     (unsigned long)&tasklist_lock,
@@ -444,13 +536,30 @@ static probectl main_probe = {
     (unsigned long)(&sys_close),
     0xfffff,
     0x10000,
+#elif   defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+    /* bleah; this is so suboptimal */
+    (unsigned long)(&sys_close),
+    0xfffff,
+    0x20000,
 #elif   defined(AFS_IA64_LINUX20_ENV)
-    (unsigned long)(&sys_close - 0x180000),
+    (unsigned long)(&init_mm),
+    0x1fffff,
+    0x30000,
+#elif defined(AFS_AMD64_LINUX26_ENV)
+    (unsigned long)(&generic_ro_fops) - 0x30000,
     0,
-    (0x180000 / sizeof(unsigned long *)),
+    0x6000,
 #elif defined(AFS_AMD64_LINUX20_ENV)
-    (unsigned long)(&tasklist_lock - 0x1000),
+    (unsigned long)(&tasklist_lock) - 0x30000,
     0,
+    0x6000,
+#elif defined(AFS_PPC64_LINUX26_ENV)
+    (unsigned long)(&do_signal),
+    0xfff,
+    0x400,
+#elif defined(AFS_PPC_LINUX20_ENV) || defined(AFS_PPC_LINUX20_ENV)
+    (unsigned long)&init_mm,
+    0xffff,
     16384,
 #else
     (unsigned long)&init_mm,
@@ -458,6 +567,15 @@ static probectl main_probe = {
     16384,
 #endif
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
+    0x3ffff,
+    0x40000,
+#else
+    0, 0, 0, 0,
+#endif
+
     /* number and list of unimplemented system calls */
     ((sizeof(main_zapped_syscalls)/sizeof(main_zapped_syscalls[0])) - 1),
     main_zapped_syscalls,
@@ -498,8 +616,10 @@ static probectl main_probe = {
 
 /* syscall pairs/triplets to probe */
 static tryctl ia32_try[] = {
+#if defined(EXPORTED_SYS_CHDIR)
     { "scan: close+chdir+write", __NR_ia32_close, &sys_close, __NR_ia32_chdir, &sys_chdir,        __NR_ia32_write, &sys_write },
     { "scan: close+chdir",       __NR_ia32_close, &sys_close, __NR_ia32_chdir, &sys_chdir,        -1,              0          },
+#endif
     { 0 }
 };
 
@@ -545,6 +665,16 @@ static probectl ia32_probe = {
     0,
     (0x180000 / sizeof(unsigned long *)),
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
+    0x3ffff,
+    0x40000,
+#else
+    0, 0, 0, 0,
+#endif
+
+
     /* number and list of unimplemented system calls */
     ((sizeof(ia32_zapped_syscalls)/sizeof(ia32_zapped_syscalls[0])) - 1),
     ia32_zapped_syscalls,
@@ -570,8 +700,25 @@ static probectl *probe_list[] = {
 };
 
 
+/********** Probing Configuration: IA64 **********/
+#elif defined(AFS_IA64_LINUX20_ENV)
+struct fptr {
+    void *ip;
+    unsigned long gp;
+};
+
+/* no 32-bit support on IA64 for now */
+static probectl *probe_list[] = {
+    &main_probe
+};
+
+
 /********** Probing Configuration: ppc64, sparc64 sys_call_table32 **********/
 #elif defined(AFS_PPC64_LINUX20_ENV) || defined(AFS_SPARC64_LINUX20_ENV)
+struct fptr {
+    void *ip;
+    unsigned long gp;
+};
 
 /* 
  * syscall pairs/triplets to probe
@@ -600,7 +747,15 @@ static int sct32_zapped_syscalls[] = {
 /* mmap2 and fstat64 are implemented only for 32-bit calls */
 static int sct32_unique_syscalls[] = {
 #ifdef AFS_PPC64_LINUX20_ENV
-    __NR_mmap2, __NR_fstat64,
+    /* _mmap2, _fstat64 */
+    192, 197,
+#endif
+#ifdef AFS_SPARC64_LINUX24_ENV
+    /* 
+     * On SPARC, we need some additional unique calls to make sure
+     * we don't match the SunOS-compatibility table.
+     */
+    __NR_sgetmask, __NR_ssetmask,
 #endif
     __NR_exit, __NR_mount, __NR_read, __NR_write,
     __NR_open, __NR_close, __NR_unlink
@@ -639,12 +794,25 @@ static probectl sct32_probe = {
     (unsigned long)(&sys_close),
     0xfffff,
     0x10000,
+#elif defined(AFS_PPC64_LINUX26_ENV)
+    (unsigned long)(&do_signal),
+    0xfff,
+    0x400,
 #else
     (unsigned long)&init_mm,
     0,
     16384,
 #endif
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
+    0x3ffff,
+    0x40000,
+#else
+    0, 0, 0, 0,
+#endif
+
     /* number and list of unimplemented system calls */
     ((sizeof(sct32_zapped_syscalls)/sizeof(sct32_zapped_syscalls[0])) - 1),
     sct32_zapped_syscalls,
@@ -675,7 +843,11 @@ static probectl *probe_list[] = {
 
 
 /********** Probing Configuration: s390x sys_call_table_emu **********/
-#elif defined(AFS_S390X_LINUX20_ENV)
+/* We only actually need to do this on s390x_linux26 and later.
+ * On earlier versions, the two tables were interleaved and so
+ * have related base addresses.
+ */
+#elif defined(AFS_S390X_LINUX26_ENV)
 
 /* syscall pairs/triplets to probe */
 /* nothing worthwhile is exported, so this is empty */
@@ -718,14 +890,23 @@ static probectl emu_probe = {
     emu_try,                      /* array of combinations to try */
 
     /* symbol in section to try scanning */
-    (unsigned long)&init_mm,
+    (unsigned long)&sys_close,
 
     /* default base address for scan */
     /* base address bits to force to zero */
     /* default length for scan */
-    (unsigned long)&init_mm,
-    0,
-    16384,
+    (unsigned long)&sys_close,
+    0xfffff,
+    0x20000,
+
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
+    0x3ffff,
+    0x40000,
+#else
+    0, 0, 0, 0,
+#endif
 
     /* number and list of unimplemented system calls */
     ((sizeof(emu_zapped_syscalls)/sizeof(emu_zapped_syscalls[0])) - 1),
@@ -773,17 +954,22 @@ static int check_table(probectl *P, PROBETYPE *ptr)
     PROBETYPE *x;
     int i, j;
 
-    for (x = ptr, i = 0; i < NR_syscalls; i++, x++) {
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+    i = check_table_readable(P, ptr);
+    if (i >= 0) return i;
+#endif
+
+    for (x = ptr, i = 0; i < _SS(NR_syscalls); i++, x++) {
 #ifdef OSI_PROBE_DEBUG
        if (probe_debug & 0x0040) {
            for (j = 0; j < 4; j++) {
-               if (P->debug_ignore_NR[j] == i) break;
+               if (_SS(P->debug_ignore_NR[j]) == _SX(i + P->offset)) break;
            }
            if (j < 4) continue;
        }
 #endif
        for (j = 0; j < 8; j++) {
-           if (probe_ignore_syscalls[j] == i) break;
+           if (_SS(probe_ignore_syscalls[j]) == _SX(i) + P->offset) break;
        }
        if (j < 8) continue;
        if (*x <= ktxt_lower_bound) {
@@ -802,17 +988,20 @@ static int check_table(probectl *P, PROBETYPE *ptr)
     return -1;
 }
 
-static void *try(probectl *P, tryctl *T, PROBETYPE *ptr,
+static void *try(probectl *P, tryctl *T, PROBETYPE *aptr,
                 unsigned long datalen)
 {
+#ifdef OSI_PROBE_KALLSYMS
     char *mod_name, *sec_name, *sym_name;
     unsigned long mod_start, mod_end;
     unsigned long sec_start, sec_end;
     unsigned long sym_start, sym_end;
+#endif
     unsigned long offset, ip1, ip2, ip3;
     int ret;
+    PROBETYPE *ptr;
 
-#ifdef AFS_IA64_LINUX20_ENV
+#if defined(AFS_IA64_LINUX20_ENV) || defined(AFS_PPC64_LINUX20_ENV)
     ip1 = T->fn1 ? (unsigned long)((struct fptr *)T->fn1)->ip : 0;
     ip2 = T->fn2 ? (unsigned long)((struct fptr *)T->fn2)->ip : 0;
     ip3 = T->fn3 ? (unsigned long)((struct fptr *)T->fn3)->ip : 0;
@@ -831,11 +1020,30 @@ static void *try(probectl *P, tryctl *T, PROBETYPE *ptr,
     if (!ip1 || !ip2 || (T->NR3 >= 0 && !ip3))
        return 0;
 
-    for (offset = 0; offset < datalen; offset++, ptr++) {
+    for (offset = 0; offset < datalen; offset++, aptr++) {
+#if defined(AFS_PPC64_LINUX20_ENV)
+       ptr = (PROBETYPE*)(*aptr);
+       if ((unsigned long)ptr <= KERNELBASE) {
+               continue;
+       }
+#else
+       ptr = aptr;
+#endif
+       if ((unsigned long)ptr < init_mm.start_code ||
+#if defined(AFS_AMD64_LINUX20_ENV)
+               (unsigned long)ptr > init_mm.brk)
+#else
+               (unsigned long)ptr > init_mm.end_data)
+#endif
+       {
+/*          printk("address 0x%lx (from 0x%lx %d) is out of range in check_table. wtf?\n", (unsigned long)x, (unsigned long)ptr, i);*/
+            continue;
+       }
+
        ret = check_table(P, ptr);
        if (ret >= 0) {
            /* return value is number of entries to skip */
-           ptr    += ret;
+           aptr    += ret;
            offset += ret;
            continue;
        }
@@ -844,14 +1052,15 @@ static void *try(probectl *P, tryctl *T, PROBETYPE *ptr,
        if ((probe_debug & 0x0002) && DEBUG_IN_RANGE(P,ptr))
            printk("<7>try 0x%lx\n", (unsigned long)ptr);
 #endif
-       if (ptr[T->NR1 - P->offset] != ip1)                    continue;
-       if (ptr[T->NR2 - P->offset] != ip2)        continue;
-       if (ip3 && ptr[T->NR3 - P->offset] != ip3) continue;
+       if (ptr[_SS(T->NR1 - P->offset)] != ip1)        continue;
+       if (ptr[_SS(T->NR2 - P->offset)] != ip2)        continue;
+       if (ip3 && ptr[_SS(T->NR3 - P->offset)] != ip3) continue;
 
 #ifdef OSI_PROBE_DEBUG
        if (probe_debug & 0x0002)
            printk("<7>try found 0x%lx\n", (unsigned long)ptr);
 #endif
+#ifdef OSI_PROBE_KALLSYMS
        if (kallsyms_address_to_symbol) {
            ret = kallsyms_address_to_symbol((unsigned long)ptr,
                                             &mod_name, &mod_start, &mod_end,
@@ -859,6 +1068,7 @@ static void *try(probectl *P, tryctl *T, PROBETYPE *ptr,
                                             &sym_name, &sym_start, &sym_end);
            if (!ret || strcmp(sym_name, P->symbol)) continue;
        }
+#endif
        /* XXX should we make sure there is only one match? */
        return (void *)ptr;
     }
@@ -871,9 +1081,14 @@ static int check_harder(probectl *P, PROBETYPE *p)
     unsigned long ip1;
     int i, s;
 
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+    i = check_table_readable(P, p);
+    if (i >= 0) return 0;
+#endif
+
     /* Check zapped syscalls */
     for (i = 1; i < P->n_zapped_syscalls; i++) {
-       if (p[P->zapped_syscalls[i]] != p[P->zapped_syscalls[0]]) {
+       if (p[_SS(P->zapped_syscalls[i])] != p[_SS(P->zapped_syscalls[0])]) {
 #ifdef OSI_PROBE_DEBUG
            if ((probe_debug & 0x0020) && DEBUG_IN_RANGE(P,p))
                printk("<7>check_harder 0x%lx zapped failed i=%d\n", (unsigned long)p, i);
@@ -885,7 +1100,8 @@ static int check_harder(probectl *P, PROBETYPE *p)
     /* Check unique syscalls */
     for (i = 0; i < P->n_unique_syscalls; i++) {
        for (s = 0; s < NR_syscalls; s++) {
-           if (p[s] == p[P->unique_syscalls[i]] && s != P->unique_syscalls[i]) {
+           if (p[_SS(s)] == p[_SS(P->unique_syscalls[i])]
+               && s != P->unique_syscalls[i]) {
 #ifdef OSI_PROBE_DEBUG
                if ((probe_debug & 0x0010) && DEBUG_IN_RANGE(P,p))
                    printk("<7>check_harder 0x%lx unique failed i=%d s=%d\n", (unsigned long)p, i, s);
@@ -895,13 +1111,13 @@ static int check_harder(probectl *P, PROBETYPE *p)
        }
     }
 
-#ifdef AFS_IA64_LINUX20_ENV
+#if defined(AFS_IA64_LINUX20_ENV) || defined(AFS_PPC64_LINUX20_ENV)
     ip1 = P->verify_fn ? (unsigned long)((struct fptr *)(P->verify_fn))->ip : 0;
 #else
     ip1 = (unsigned long)(P->verify_fn);
 #endif
 
-    if (ip1 && p[P->verifyNR - P->offset] != ip1) {
+    if (ip1 && p[_SS(P->verifyNR - P->offset)] != ip1) {
 #ifdef OSI_PROBE_DEBUG
        if ((probe_debug & 0x0010) && DEBUG_IN_RANGE(P,p))
            printk("<7>check_harder 0x%lx verify failed\n", (unsigned long)p);
@@ -918,10 +1134,12 @@ static int check_harder(probectl *P, PROBETYPE *p)
 
 static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
 {
+#ifdef OSI_PROBE_KALLSYMS
     char *mod_name, *sec_name, *sym_name;
     unsigned long mod_start, mod_end;
     unsigned long sec_start, sec_end;
     unsigned long sym_start, sym_end;
+#endif
     unsigned long offset;
     void *match = 0;
     int ret;
@@ -931,6 +1149,16 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
        printk("<7>osi_probe: %s                      try_harder\n", P->symbol);
 #endif
     for (offset = 0; offset < datalen; offset++, ptr++) {
+        if ((unsigned long)ptr < init_mm.start_code ||
+#if defined(AFS_AMD64_LINUX20_ENV)
+               (unsigned long)ptr > init_mm.brk)
+#else
+               (unsigned long)ptr > init_mm.end_data)
+#endif
+       {
+/*          printk("address 0x%lx (from 0x%lx %d) is out of range in check_table. wtf?\n", (unsigned long)x, (unsigned long)ptr, i);*/
+            continue;
+       }
        ret = check_table(P, ptr);
         if (ret >= 0) {
             /* return value is number of entries to skip */
@@ -951,6 +1179,7 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
            printk("<7>try_harder found 0x%lx\n", (unsigned long)ptr);
 #endif
 
+#ifdef OSI_PROBE_KALLSYMS
        if (kallsyms_address_to_symbol) {
            ret = kallsyms_address_to_symbol((unsigned long)ptr,
                                             &mod_name, &mod_start, &mod_end,
@@ -958,6 +1187,7 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
                                             &sym_name, &sym_start, &sym_end);
            if (!ret || strcmp(sym_name, P->symbol)) continue;
        }
+#endif
 
        if (match) {
 #ifdef OSI_PROBE_DEBUG
@@ -980,33 +1210,95 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
     if (probe_debug & 0x0001) {                                                              \
        printk("<7>osi_probe: %s = 0x%016lx %s\n", P->symbol, (unsigned long)(x), (m)); \
     }                                                                                      \
-    if ((x)) {                                                                             \
+    if ((x) && ((int)(x)) != -ENOENT) {                                                    \
        *method = (m);                                                                     \
         final_answer = (void *)(x);                                                        \
     }                                                                                      \
 } while (0)
 #else
 #define check_result(x,m) do {  \
-    if ((x)) {                  \
+    if ((x) && ((int)(x)) != -ENOENT) { \
         *method = (m);          \
         return (void *)(x);     \
     }                           \
 } while (0)
 #endif
+static void *scan_for_syscall_table(probectl *P, PROBETYPE *B, unsigned long L)
+{
+    tryctl *T;
+    void *answer;
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+    void *answer2;
+#endif
+#ifdef OSI_PROBE_DEBUG
+    void *final_answer = 0;
+#endif
+#ifdef OSI_PROBE_DEBUG
+    if (probe_debug & 0x0007)
+       printk("<7>osi_probe: %s                      base=0x%lx, len=0x%lx\n",
+              P->symbol, (unsigned long)B, L);
+    if (probe_debug & 0x0009) {
+       printk("<7>osi_probe: %s                      ktxt_lower_bound=0x%lx\n",
+              P->symbol, ktxt_lower_bound);
+       printk("<7>osi_probe: %s                      NR_syscalls=%d\n",
+              P->symbol, NR_syscalls);
+    }
+#endif
+
+    for (T = P->trylist; T->name; T++) {
+       answer = try(P, T, B, L);
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+       answer2 = try(P, T, (PROBETYPE *)(2 + (void *)B), L);
+#ifdef OSI_PROBE_DEBUG
+       if (probe_debug & 0x0003) {
+           printk("<7>osi_probe: %s = 0x%016lx %s (even)\n",
+                  P->symbol, (unsigned long)(answer), T->name);
+           printk("<7>osi_probe: %s = 0x%016lx %s (odd)\n",
+                  P->symbol, (unsigned long)(answer2), T->name);
+       }
+#endif
+       if (answer && answer2) answer = 0;
+       else if (answer2) answer = answer2;
+#endif
+       if (answer)
+           return answer;
+    }
+
+    /* XXX more checks here */
+
+    answer = try_harder(P, B, L);
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+    answer2 = try_harder(P, (PROBETYPE *)(2 + (void *)B), L);
+#ifdef OSI_PROBE_DEBUG
+    if (probe_debug & 0x0005) {
+       printk("<7>osi_probe: %s = 0x%016lx pattern scan (even)\n",
+              P->symbol, (unsigned long)(answer));
+       printk("<7>osi_probe: %s = 0x%016lx pattern scan (odd)\n",
+              P->symbol, (unsigned long)(answer2));
+    }
+#endif
+    if (answer && answer2) answer = 0;
+    else if (answer2) answer = answer2;
+#endif
+    return answer;
+}
+
 static void *do_find_syscall_table(probectl *P, char **method)
 {
+#ifdef OSI_PROBE_KALLSYMS
     char *mod_name, *sec_name, *sym_name;
     unsigned long mod_start, mod_end;
     unsigned long sec_start, sec_end;
     unsigned long sym_start, sym_end;
+    unsigned long token;
+    int ret;
+#endif
     PROBETYPE *B;
-    unsigned long token, L;
-    tryctl *T;
+    unsigned long L;
     void *answer;
 #ifdef OSI_PROBE_DEBUG
     void *final_answer = 0;
 #endif
-    int ret;
 
     *method = "not found";
 
@@ -1014,6 +1306,7 @@ static void *do_find_syscall_table(probectl *P, char **method)
     check_result(P->weak_answer, "exported");
 
     /* ask the kernel to do the name lookup, if it's willing */
+#ifdef OSI_PROBE_KALLSYMS
     if (kallsyms_symbol_to_address) {
        token = 0;
         sym_start = 0;
@@ -1028,6 +1321,7 @@ static void *do_find_syscall_table(probectl *P, char **method)
        } while (ret);
         check_result(sym_start, "kallsyms_symbol_to_address");
     }
+#endif
 
     /* Maybe a little birdie told us */
     check_result(P->parm_answer,  "module parameter");
@@ -1037,6 +1331,7 @@ static void *do_find_syscall_table(probectl *P, char **method)
     B = (PROBETYPE *)((P->try_base) & ~(P->try_base_mask));
     L = P->try_length;
     /* Now, see if the kernel will tell us something better than the default */
+#ifdef OSI_PROBE_KALLSYMS
     if (kallsyms_address_to_symbol) {
        ret = kallsyms_address_to_symbol(P->try_sect_sym,
                                         &mod_name, &mod_start, &mod_end,
@@ -1047,29 +1342,29 @@ static void *do_find_syscall_table(probectl *P, char **method)
            L = (sec_end - sec_start) / sizeof(unsigned long);
        }
     }
-
-#ifdef OSI_PROBE_DEBUG
-    if (probe_debug & 0x0007)
-       printk("<7>osi_probe: %s                      base=0x%lx, len=0x%lx\n",
-              P->symbol, (unsigned long)B, L);
-    if (probe_debug & 0x0009) {
-       printk("<7>osi_probe: %s                      ktxt_lower_bound=0x%lx\n",
-              P->symbol, ktxt_lower_bound);
-       printk("<7>osi_probe: %s                      NR_syscalls=%d\n",
-              P->symbol, NR_syscalls);
+#endif
+   
+    answer = scan_for_syscall_table(P, B, L);
+    check_result(answer, "pattern scan");
+    B = (PROBETYPE *)((P->alt_try_base) & ~(P->alt_try_base_mask));
+    L = P->alt_try_length;
+    /* Now, see if the kernel will tell us something better than the default */
+#ifdef OSI_PROBE_KALLSYMS
+    if (kallsyms_address_to_symbol && P->alt_try_sect_sym) {
+       ret = kallsyms_address_to_symbol(P->alt_try_sect_sym,
+                                        &mod_name, &mod_start, &mod_end,
+                                        &sec_name, &sec_start, &sec_end,
+                                        &sym_name, &sym_start, &sym_end);
+       if (ret) {
+           B = (PROBETYPE *)sec_start;
+           L = (sec_end - sec_start) / sizeof(unsigned long);
+       }
     }
 #endif
-
-    for (T = P->trylist; T->name; T++) {
-       answer = try(P, T, B, L);
-        check_result(answer, T->name);
+    if (B && L) {
+       answer = scan_for_syscall_table(P, B, L);
+       check_result(answer, "pattern scan");
     }
-
-    /* XXX more checks here */
-
-    answer = try_harder(P, B, L);
-    check_result(answer, "pattern scan");
-
 #ifdef OSI_PROBE_DEBUG
     return final_answer;
 #else
@@ -1077,6 +1372,62 @@ static void *do_find_syscall_table(probectl *P, char **method)
 #endif
 }
 
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+static int check_access(unsigned long address, int mode) 
+{ 
+    pgd_t *pgd = pgd_offset_k(address);
+#ifdef PUD_SIZE
+    pud_t *pud;
+#endif
+    pmd_t *pmd;
+    pte_t *pte;
+
+    if (pgd_none(*pgd))
+       return 0;
+#ifdef PUD_SIZE
+    pud = pud_offset(pgd, address);
+    if (pud_none(*pud))
+       return 0;
+    pmd = pmd_offset(pud, address);
+#else
+    pmd = pmd_offset(pgd, address);
+#endif
+    if (pmd_none(*pmd))
+       return 0;
+    if (pmd_large(*pmd))
+       pte = (pte_t *)pmd;
+    else
+       pte = pte_offset_kernel(pmd, address);
+    if (pte_none(*pte) || !pte_present(*pte))
+       return 0;
+    if (mode && !pte_write(*pte))
+       return 0;
+    return 1;
+}
+
+static int check_table_readable(probectl *P, PROBETYPE *ptr)
+{
+    PROBETYPE *next_page;
+    int i = 0, delta;
+
+    while (i < _SS(NR_syscalls)) {
+       next_page = (PROBETYPE *)PAGE_ALIGN((unsigned long)(ptr+1));
+       delta = next_page - ptr;
+       if (!check_access((unsigned long)ptr, 0)) {
+#ifdef OSI_PROBE_DEBUG
+           if (probe_debug & 0x0080)
+               printk("<7>osi_probe: %s                      0x%016lx not readable; delta=0x%lx\n",
+                      P->symbol, (unsigned long)ptr, delta);
+#endif
+           return delta - 1;
+       }
+       ptr += delta;
+       i += delta;
+    }
+    return -1;
+}
+#endif
+
 void *osi_find_syscall_table(int which)
 {
     probectl *P;
@@ -1101,6 +1452,13 @@ void *osi_find_syscall_table(int which)
        return 0;
     }
     printk("Found %s at 0x%lx (%s)\n", P->desc, (unsigned long)answer, method);
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+    if (!check_access((unsigned long)answer, 1)) {
+       printk("Address 0x%lx is not writable.\n", (unsigned long)answer);
+       printk("System call hooks will not be installed; proceeding anyway\n");
+       return 0;
+    }
+#endif
     return answer;
 }
 
@@ -1123,3 +1481,10 @@ void osi_probe_exit(void) { }
 module_init(osi_probe_init);
 module_exit(osi_probe_exit);
 #endif
+#endif
+#else
+void *osi_find_syscall_table(int which)
+{
+    return 0;
+}
+#endif /* EXPORTED_INIT_MM */