summaryrefslogtreecommitdiff
path: root/usr/src/common
diff options
context:
space:
mode:
authorJohn Sonnenschein <johns@joyent.com>2012-11-07 22:04:45 +0000
committerJohn Sonnenschein <johns@joyent.com>2012-11-07 22:04:45 +0000
commitad2a40e16d2bee59cfe84f89db3ce92b9f15adff (patch)
treebc8e23c4588b29e4602a54b72dbe2f1d8786468d /usr/src/common
parentb5f683274309a9c6f46eea8ce5d0cca514d977a8 (diff)
parent7c45eec5760d4195b6b85770e7ac51c380adfb8a (diff)
downloadillumos-joyent-ad2a40e16d2bee59cfe84f89db3ce92b9f15adff.tar.gz
Illumos sync
Diffstat (limited to 'usr/src/common')
-rw-r--r--usr/src/common/saveargs/saveargs.c268
-rw-r--r--usr/src/common/saveargs/saveargs.h52
-rw-r--r--usr/src/common/saveargs/tests/README9
-rw-r--r--usr/src/common/saveargs/tests/functional/Makefile45
-rw-r--r--usr/src/common/saveargs/tests/functional/align.c32
-rw-r--r--usr/src/common/saveargs/tests/functional/basic.c32
-rw-r--r--usr/src/common/saveargs/tests/functional/big-struct-ret-and-spill.c38
-rw-r--r--usr/src/common/saveargs/tests/functional/big-struct-ret.c38
-rw-r--r--usr/src/common/saveargs/tests/functional/small-struct-ret-and-spill.c37
-rw-r--r--usr/src/common/saveargs/tests/functional/small-struct-ret.c37
-rw-r--r--usr/src/common/saveargs/tests/functional/stack-spill.c32
-rw-r--r--usr/src/common/saveargs/tests/functional/test.sh37
-rw-r--r--usr/src/common/saveargs/tests/testmatch/Makefile41
-rw-r--r--usr/src/common/saveargs/tests/testmatch/data.s396
-rw-r--r--usr/src/common/saveargs/tests/testmatch/testmatch.c137
-rw-r--r--usr/src/common/smbsrv/smb_match.c322
-rw-r--r--usr/src/common/smbsrv/smb_xdr.c24
17 files changed, 1433 insertions, 144 deletions
diff --git a/usr/src/common/saveargs/saveargs.c b/usr/src/common/saveargs/saveargs.c
new file mode 100644
index 0000000000..c75d07e5b9
--- /dev/null
+++ b/usr/src/common/saveargs/saveargs.c
@@ -0,0 +1,268 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * The Sun Studio and GCC (patched for opensolaris/illumos) compilers
+ * implement a argument saving scheme on amd64 via the -Wu,save-args or
+ * options. When the option is specified, INTEGER type function arguments
+ * passed via registers will be saved on the stack immediately after %rbp, and
+ * will not be modified through out the life of the routine.
+ *
+ * +--------+
+ * %rbp --> | %rbp |
+ * +--------+
+ * -0x8(%rbp) | %rdi |
+ * +--------+
+ * -0x10(%rbp) | %rsi |
+ * +--------+
+ * -0x18(%rbp) | %rdx |
+ * +--------+
+ * -0x20(%rbp) | %rcx |
+ * +--------+
+ * -0x28(%rbp) | %r8 |
+ * +--------+
+ * -0x30(%rbp) | %r9 |
+ * +--------+
+ *
+ *
+ * For example, for the following function,
+ *
+ * void
+ * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
+ * {
+ * ...
+ * }
+ *
+ * Disassembled code will look something like the following:
+ *
+ * pushq %rbp
+ * movq %rsp, %rbp
+ * subq $imm8, %rsp **
+ * movq %rdi, -0x8(%rbp)
+ * movq %rsi, -0x10(%rbp)
+ * movq %rdx, -0x18(%rbp)
+ * movq %rcx, -0x20(%rbp)
+ * movq %r8, -0x28(%rbp)
+ * movq %r9, -0x30(%rbp)
+ * ...
+ * or
+ * pushq %rbp
+ * movq %rsp, %rbp
+ * subq $imm8, %rsp **
+ * movq %r9, -0x30(%rbp)
+ * movq %r8, -0x28(%rbp)
+ * movq %rcx, -0x20(%rbp)
+ * movq %rdx, -0x18(%rbp)
+ * movq %rsi, -0x10(%rbp)
+ * movq %rdi, -0x8(%rbp)
+ * ...
+ * or
+ * pushq %rbp
+ * movq %rsp, %rbp
+ * pushq %rdi
+ * pushq %rsi
+ * pushq %rdx
+ * pushq %rcx
+ * pushq %r8
+ * pushq %r9
+ *
+ * **: The space being reserved is in addition to what the current
+ * function prolog already reserves.
+ *
+ * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function
+ * looking for each argument saving instruction we would expect to see. We
+ * loop byte-by-byte, rather than doing anything smart about insn lengths,
+ * only deviating from this when we know we have our insn, and can skip the
+ * rest of it.
+ *
+ * If there are odd number of arguments to a function, additional space is
+ * reserved on the stack to maintain 16-byte alignment. For example,
+ *
+ * argc == 0: no argument saving.
+ * argc == 3: save 3, but space for 4 is reserved
+ * argc == 7: save 6.
+ */
+
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <saveargs.h>
+
+/*
+ * Size of the instruction sequence arrays. It should correspond to
+ * the maximum number of arguments passed via registers.
+ */
+#define INSTR_ARRAY_SIZE 6
+
+#define INSTR1(ins, off) (ins[(off)])
+#define INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
+#define INSTR3(ins, off) \
+ (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
+#define INSTR4(ins, off) \
+ (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
+ (ins[(off) + 3] << 24))
+
+/*
+ * Sun Studio 10 patch implementation saves %rdi first;
+ * GCC 3.4.3 Sun branch implementation saves them in reverse order.
+ */
+static const uint32_t save_instr[INSTR_ARRAY_SIZE] = {
+ 0xf87d8948, /* movq %rdi, -0x8(%rbp) */
+ 0xf0758948, /* movq %rsi, -0x10(%rbp) */
+ 0xe8558948, /* movq %rdx, -0x18(%rbp) */
+ 0xe04d8948, /* movq %rcx, -0x20(%rbp) */
+ 0xd845894c, /* movq %r8, -0x28(%rbp) */
+ 0xd04d894c /* movq %r9, -0x30(%rbp) */
+};
+
+static const uint16_t save_instr_push[] = {
+ 0x57, /* pushq %rdi */
+ 0x56, /* pushq %rsi */
+ 0x52, /* pushq %rdx */
+ 0x51, /* pushq %rcx */
+ 0x5041, /* pushq %r8 */
+ 0x5141 /* pushq %r9 */
+};
+
+/*
+ * If the return type of a function is a structure greater than 16 bytes in
+ * size, %rdi will contain the address to which it should be stored, and
+ * arguments will begin at %rsi. Studio will push all of the normal argument
+ * registers anyway, GCC will start pushing at %rsi, so we need a separate
+ * pattern.
+ */
+static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
+ 0xf8758948, /* movq %rsi,-0x8(%rbp) */
+ 0xf0558948, /* movq %rdx,-0x10(%rbp) */
+ 0xe84d8948, /* movq %rcx,-0x18(%rbp) */
+ 0xe045894c, /* movq %r8,-0x20(%rbp) */
+ 0xd84d894c /* movq %r9,-0x28(%rbp) */
+};
+
+static const uint8_t save_fp_pushes[] = {
+ 0x55, /* pushq %rbp */
+ 0xcc /* int $0x3 */
+};
+#define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
+
+static const uint32_t save_fp_movs[] = {
+ 0x00e58948, /* movq %rsp,%rbp, encoding 1 */
+ 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */
+};
+#define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
+
+static int
+has_saved_fp(uint8_t *ins, int size)
+{
+ int i, j;
+ uint32_t n;
+ int found_push = 0;
+
+ for (i = 0; i < size; i++) {
+ if (found_push == 0) {
+ n = INSTR1(ins, i);
+ for (j = 0; j <= NUM_FP_PUSHES; j++)
+ if (save_fp_pushes[j] == n) {
+ found_push = 1;
+ break;
+ }
+ } else {
+ n = INSTR3(ins, i);
+ for (j = 0; j <= NUM_FP_MOVS; j++)
+ if (save_fp_movs[j] == n)
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+int
+saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
+{
+ int i, j;
+ uint32_t n;
+
+ argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
+
+ if (!has_saved_fp(ins, size))
+ return (SAVEARGS_NO_ARGS);
+
+ /*
+ * Compare against Sun Studio implementation
+ */
+ for (i = 4, j = 0; i <= size - 4; i++) {
+ n = INSTR4(ins, i);
+
+ if (n == save_instr[j]) {
+ i += 3;
+ if (++j >= argc)
+ return (start_index ? SAVEARGS_STRUCT_ARGS :
+ SAVEARGS_TRAD_ARGS);
+ }
+ }
+
+ /*
+ * Compare against GCC implementation
+ */
+ for (i = 4, j = argc - 1; i <= size - 4; i++) {
+ n = INSTR4(ins, i);
+
+ if (n == save_instr[j]) {
+ i += 3;
+ if (--j < start_index)
+ return (SAVEARGS_TRAD_ARGS);
+ }
+ }
+
+ /*
+ * Compare against GCC push-based implementation
+ */
+ for (i = 4, j = start_index; i <= size - 2; i += 1) {
+ n = (i >= (8 - start_index)) ? INSTR2(ins, i) : INSTR1(ins, i);
+
+ if (n == save_instr_push[j]) {
+ if (i >= (8 - start_index))
+ i += 1;
+ if (++j >= argc)
+ return (SAVEARGS_TRAD_ARGS);
+ }
+ }
+
+ /* Look for a GCC-style returned structure */
+ if (start_index != 0) {
+ for (i = 4, j = argc - 2; i <= size - 4; i++) {
+ n = INSTR4(ins, i);
+
+ if (n == save_instr_sr[j]) {
+ i += 3;
+ if (--j >= (argc - 1))
+ return (SAVEARGS_TRAD_ARGS);
+ }
+ }
+ }
+
+ return (SAVEARGS_NO_ARGS);
+}
diff --git a/usr/src/common/saveargs/saveargs.h b/usr/src/common/saveargs/saveargs.h
new file mode 100644
index 0000000000..c20707d615
--- /dev/null
+++ b/usr/src/common/saveargs/saveargs.h
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SAVEARGS_H
+#define _SAVEARGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * The longest instruction sequence in bytes before all 6 arguments are
+ * saved on the stack. This value depends on compiler implementation,
+ * therefore it should be examined periodically to guarantee accuracy.
+ */
+#define SAVEARGS_INSN_SEQ_LEN 256
+
+#define SAVEARGS_NO_ARGS 0 /* no saved arguments */
+#define SAVEARGS_TRAD_ARGS 1 /* traditionally located arguments */
+#define SAVEARGS_STRUCT_ARGS 2 /* struct return addr pushed as arg0 */
+
+int saveargs_has_args(uint8_t *, size_t, uint_t, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SAVEARGS_H */
diff --git a/usr/src/common/saveargs/tests/README b/usr/src/common/saveargs/tests/README
new file mode 100644
index 0000000000..d97be601f6
--- /dev/null
+++ b/usr/src/common/saveargs/tests/README
@@ -0,0 +1,9 @@
+testmatch:
+
+ A stub program that tests the saveargs matcher against a variety of
+ function prologues (assembled from data.s)
+
+functional:
+
+ Actually test the full chunk of the (libproc) side of the code, running
+ pstack on the range of test apps.
diff --git a/usr/src/common/saveargs/tests/functional/Makefile b/usr/src/common/saveargs/tests/functional/Makefile
new file mode 100644
index 0000000000..d8810eb49a
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/Makefile
@@ -0,0 +1,45 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012, Richard Lowe.
+#
+
+include $(SRC)/Makefile.master
+include $(SRC)/Makefile.master.64
+
+.KEEP_STATE:
+
+PROGS = align \
+ basic \
+ big-struct-ret \
+ big-struct-ret-and-spill \
+ small-struct-ret \
+ small-struct-ret-and-spill \
+ stack-spill
+
+CFLAGS += $(CTF_FLAGS)
+CFLAGS64 += $(CTF_FLAGS)
+
+%: %.c
+ $(LINK.c) -o $@ $< -lc
+ $(CTFCONVERT) -L VERSION $@
+
+all: $(PROGS)
+
+install: all
+
+clean:
+ $(RM) $(PROGS)
+
+clobber: clean
+
+FRC:
diff --git a/usr/src/common/saveargs/tests/functional/align.c b/usr/src/common/saveargs/tests/functional/align.c
new file mode 100644
index 0000000000..731ca5eccb
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/align.c
@@ -0,0 +1,32 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+int
+test(long a, long b, long c, long d, long e)
+{
+ printf("%ld %ld %ld %ld %ld\n", a, b, c, d, e);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4, 5);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/basic.c b/usr/src/common/saveargs/tests/functional/basic.c
new file mode 100644
index 0000000000..4a6a3819a6
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/basic.c
@@ -0,0 +1,32 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+int
+test(long a, long b, long c, long d)
+{
+ printf("%ld %ld %ld %ld\n", a, b, c, d);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/big-struct-ret-and-spill.c b/usr/src/common/saveargs/tests/functional/big-struct-ret-and-spill.c
new file mode 100644
index 0000000000..4823346299
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/big-struct-ret-and-spill.c
@@ -0,0 +1,38 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+struct foo {
+ long a;
+ long b;
+ long c;
+};
+
+struct foo
+test(long a, long b, long c, long d, long e, long f, long g, long h)
+{
+ printf("%ld %ld %ld %ld %ld %ld %ld %ld\n", a, b, c, d, e, f, g, h);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4, 5, 6, 7, 8);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/big-struct-ret.c b/usr/src/common/saveargs/tests/functional/big-struct-ret.c
new file mode 100644
index 0000000000..0953622f0d
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/big-struct-ret.c
@@ -0,0 +1,38 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+struct foo {
+ long a;
+ long b;
+ long c;
+};
+
+struct foo
+test(long a, long b, long c, long d)
+{
+ printf("%ld %ld %ld %ld\n", a, b, c, d);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/small-struct-ret-and-spill.c b/usr/src/common/saveargs/tests/functional/small-struct-ret-and-spill.c
new file mode 100644
index 0000000000..6cbc0cde47
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/small-struct-ret-and-spill.c
@@ -0,0 +1,37 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+struct foo {
+ long a;
+ long b;
+};
+
+struct foo
+test(long a, long b, long c, long d, long e, long f, long g, long h)
+{
+ printf("%ld %ld %ld %ld %ld %ld %ld %ld\n", a, b, c, d, e, f, g, h);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4, 5, 6, 7, 8);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/small-struct-ret.c b/usr/src/common/saveargs/tests/functional/small-struct-ret.c
new file mode 100644
index 0000000000..3731365f11
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/small-struct-ret.c
@@ -0,0 +1,37 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+struct foo {
+ long a;
+ long b;
+};
+
+struct foo
+test(long a, long b, long c, long d)
+{
+ printf("%ld %ld %ld %ld\n", a, b, c, d);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/stack-spill.c b/usr/src/common/saveargs/tests/functional/stack-spill.c
new file mode 100644
index 0000000000..4324d60c27
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/stack-spill.c
@@ -0,0 +1,32 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+int
+test(long a, long b, long c, long d, long e, long f, long g, long h)
+{
+ printf("%ld %ld %ld %ld %ld %ld %ld %ld\n", a, b, c, d, e, f, g, h);
+ for (;;)
+ sleep(60);
+}
+
+int
+main(int argc, char **argv)
+{
+ test(1, 2, 3, 4, 5, 6, 7, 8);
+ return (0);
+}
diff --git a/usr/src/common/saveargs/tests/functional/test.sh b/usr/src/common/saveargs/tests/functional/test.sh
new file mode 100644
index 0000000000..dea1059b2c
--- /dev/null
+++ b/usr/src/common/saveargs/tests/functional/test.sh
@@ -0,0 +1,37 @@
+#! /usr/bin/ksh
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012, Richard Lowe.
+#
+
+function tester {
+ prog=${1}
+ pattern=${2}
+
+ ./$prog >/dev/null &
+ pid=$!
+ if (/usr/bin/amd64/pstack $pid | /usr/xpg4/bin/grep -q "${pattern}"); then
+ echo "pass: ${prog}"
+ else
+ echo "FAIL: ${prog}"
+ fi
+ kill $pid
+}
+
+tester align "test (1, 2, 3, 4, 5)"
+tester basic "test (1, 2, 3, 4)"
+tester big-struct-ret "test (1, 2, 3, 4)"
+tester big-struct-ret-and-spill "test (1, 2, 3, 4, 5, 6, 7, 8)"
+tester small-struct-ret "test (1, 2, 3, 4)"
+tester small-struct-ret-and-spill "test (1, 2, 3, 4, 5, 6, 7, 8)"
+tester stack-spill "test (1, 2, 3, 4, 5, 6, 7, 8)"
diff --git a/usr/src/common/saveargs/tests/testmatch/Makefile b/usr/src/common/saveargs/tests/testmatch/Makefile
new file mode 100644
index 0000000000..2277b64c60
--- /dev/null
+++ b/usr/src/common/saveargs/tests/testmatch/Makefile
@@ -0,0 +1,41 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2012, Richard Lowe.
+#
+
+include $(SRC)/Makefile.master
+include $(SRC)/Makefile.master.64
+
+.KEEP_STATE:
+
+OBJECTS = testmatch.o saveargs.o data.o
+PROG = testmatch
+
+CPPFLAGS += -I$(SRC)/common/saveargs
+ASFLAGS += -P
+AS_CPPFLAGS += -D_ASM
+
+%.o: $(SRC)/common/saveargs/%.c
+ $(COMPILE.c) -o $@ $<
+
+$(PROG): $(OBJECTS)
+ $(LINK.c) -o $@ $(OBJECTS) -lc
+
+clean:
+ $(RM) $(OBJECTS) $(PROG)
+
+clobber: clean
+
+all: $(PROG)
+
+install: all
diff --git a/usr/src/common/saveargs/tests/testmatch/data.s b/usr/src/common/saveargs/tests/testmatch/data.s
new file mode 100644
index 0000000000..4d2660b24a
--- /dev/null
+++ b/usr/src/common/saveargs/tests/testmatch/data.s
@@ -0,0 +1,396 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#define FUNC(x) \
+ .text; \
+ .align 16; \
+ .globl x; \
+ .type x, @function; \
+x:
+
+#define SET_SIZE(x, x_size) \
+ .size x, [.-x]; \
+ .globl x_size; \
+ .type x_size, @object; \
+x_size:
+
+/*
+ * Extracted versions of the functional tests
+ *
+ * Named of the form <compiler>-<prologue style>-<nature of test>
+ * basic -- A regular function
+ * align -- odd number of arguments needing save-area
+ * alignment
+ * big-struct-ret -- returns a > 16byte structure by value
+ * big-struct-ret-and-spill -- returns a > 16byte structure by value and
+ * spills args to the stack
+ * small-struct-ret -- returns a < 16byte structure by value
+ * small-struct-ret-and-spill -- returns a < 16byte structure by value and
+ * spills args to the stack
+ * stack-spill -- spills arguments to the stack
+ */
+FUNC(gcc_mov_align)
+pushq %rbp
+movq %rsp, %rbp
+movq %rbx, -0x38(%rbp)
+movq %r8, -0x28(%rbp)
+movq %rcx, -0x20(%rbp)
+movq %rdx, -0x18(%rbp)
+movq %rsi, -0x10(%rbp)
+movq %rdi, -0x8(%rbp)
+subq $0x70, %rsp
+SET_SIZE(gcc_mov_align, gcc_mov_align_end)
+
+FUNC(gcc_mov_basic)
+pushq %rbp
+movq %rsp, %rbp
+movq %rbx,-0x28(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x50,%rsp
+SET_SIZE(gcc_mov_basic, gcc_mov_basic_end)
+
+FUNC(gcc_mov_big_struct_ret)
+pushq %rbp
+movq %rsp,%rbp
+movq %rbx,-0x28(%rbp)
+movq %r8,-0x20(%rbp)
+movq %rcx,-0x18(%rbp)
+movq %rdx,-0x10(%rbp)
+movq %rsi,-0x8(%rbp)
+subq $0x50,%rsp
+SET_SIZE(gcc_mov_big_struct_ret, gcc_mov_big_struct_ret_end)
+
+FUNC(gcc_mov_big_struct_ret_and_spill)
+pushq %rbp
+movq %rsp,%rbp
+movq %rbx,-0x38(%rbp)
+movq %r9,-0x28(%rbp)
+movq %r8,-0x20(%rbp)
+movq %rcx,-0x18(%rbp)
+movq %rdx,-0x10(%rbp)
+movq %rsi,-0x8(%rbp)
+subq $0x90,%rsp
+SET_SIZE(gcc_mov_big_struct_ret_and_spill, gcc_mov_big_struct_ret_and_spill_end)
+
+FUNC(gcc_mov_small_struct_ret)
+pushq %rbp
+movq %rsp,%rbp
+movq %rbx,-0x28(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x50,%rsp
+SET_SIZE(gcc_mov_small_struct_ret, gcc_mov_small_struct_ret_end)
+
+FUNC(gcc_mov_small_struct_ret_and_spill)
+pushq %rbp
+movq %rsp,%rbp
+movq %rbx,-0x38(%rbp)
+movq %r9,-0x30(%rbp)
+movq %r8,-0x28(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x90,%rsp
+SET_SIZE(gcc_mov_small_struct_ret_and_spill, gcc_mov_small_struct_ret_and_spill_end)
+
+FUNC(gcc_mov_stack_spill)
+pushq %rbp
+movq %rsp,%rbp
+movq %rbx,-0x38(%rbp)
+movq %r9,-0x30(%rbp)
+movq %r8,-0x28(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x90,%rsp
+SET_SIZE(gcc_mov_stack_spill, gcc_mov_stack_spill_end)
+
+FUNC(gcc_push_align)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rdi
+pushq %rsi
+pushq %rdx
+pushq %rcx
+pushq %r8
+subq $0x8,%rsp
+subq $0x30,%rsp
+SET_SIZE(gcc_push_align, gcc_push_align_end)
+
+FUNC(gcc_push_basic)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rdi
+pushq %rsi
+pushq %rdx
+pushq %rcx
+subq $0x20,%rsp
+SET_SIZE(gcc_push_basic, gcc_push_basic_end)
+
+FUNC(gcc_push_big_struct_ret)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rsi
+pushq %rdx
+pushq %rcx
+pushq %r8
+subq $0x30,%rsp
+SET_SIZE(gcc_push_big_struct_ret, gcc_push_big_struct_ret_end)
+
+FUNC(gcc_push_big_struct_ret_and_spill)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rsi
+pushq %rdx
+pushq %rcx
+pushq %r8
+pushq %r9
+subq $0x8,%rsp
+subq $0x50,%rsp
+SET_SIZE(gcc_push_big_struct_ret_and_spill, gcc_push_big_struct_ret_and_spill_end)
+
+FUNC(gcc_push_small_struct_ret)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rdi
+pushq %rsi
+pushq %rdx
+pushq %rcx
+subq $0x20,%rsp
+SET_SIZE(gcc_push_small_struct_ret, gcc_push_small_struct_ret_end)
+
+FUNC(gcc_push_small_struct_ret_and_spill)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rdi
+pushq %rsi
+pushq %rdx
+pushq %rcx
+pushq %r8
+pushq %r9
+subq $0x50,%rsp
+SET_SIZE(gcc_push_small_struct_ret_and_spill, gcc_push_small_struct_ret_and_spill_end)
+
+FUNC(gcc_push_stack_spill)
+pushq %rbp
+movq %rsp,%rbp
+pushq %rdi
+pushq %rsi
+pushq %rdx
+pushq %rcx
+pushq %r8
+pushq %r9
+subq $0x50,%rsp
+SET_SIZE(gcc_push_stack_spill, gcc_push_stack_spill_end)
+
+FUNC(ss_mov_align)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x30,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %r8,-0x28(%rbp)
+SET_SIZE(ss_mov_align, ss_mov_align_end)
+
+FUNC(ss_mov_basic)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x20,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+SET_SIZE(ss_mov_basic, ss_mov_basic_end)
+
+FUNC(ss_mov_big_struct_ret)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x30,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %r8,-0x28(%rbp)
+SET_SIZE(ss_mov_big_struct_ret, ss_mov_big_struct_ret_end)
+
+FUNC(ss_mov_big_struct_ret_and_spill)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x50,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %r8,-0x28(%rbp)
+movq %r9,-0x30(%rbp)
+SET_SIZE(ss_mov_big_struct_ret_and_spill, ss_mov_big_struct_ret_and_spill_end)
+
+FUNC(ss_mov_small_struct_ret)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x20,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+SET_SIZE(ss_mov_small_struct_ret, ss_mov_small_struct_ret_end)
+
+FUNC(ss_mov_small_struct_ret_and_spill)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x50,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %r8,-0x28(%rbp)
+movq %r9,-0x30(%rbp)
+SET_SIZE(ss_mov_small_struct_ret_and_spill, ss_mov_small_struct_ret_and_spill_end)
+
+FUNC(ss_mov_stack_spill)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x50,%rsp
+movq %rdi,-0x8(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %r8,-0x28(%rbp)
+movq %r9,-0x30(%rbp)
+SET_SIZE(ss_mov_stack_spill, ss_mov_stack_spill_end)
+
+/* DTrace instrumentation */
+FUNC(dtrace_instrumented)
+int $0x3
+movq %rsp, %rbp
+movq %rbx,-0x28(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x50,%rsp
+SET_SIZE(dtrace_instrumented, dtrace_instrumented_end)
+
+/*
+ * System functions with special characteristics, be they non-initial FP save,
+ * gaps between FP save and argument saving, or gaps between saved arguments.
+ */
+FUNC(kmem_alloc)
+leaq -0x1(%rdi),%rax
+pushq %rbp
+movq %rax,%rdx
+movq %rsp,%rbp
+subq $0x30,%rsp
+shrq $0x3,%rdx
+movq %r12,-0x28(%rbp)
+movq %rbx,-0x30(%rbp)
+cmpq $0x1ff,%rdx
+movq %r13,-0x20(%rbp)
+movq %r14,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+movq %rdi,%r12
+SET_SIZE(kmem_alloc, kmem_alloc_end)
+
+FUNC(uts_kill)
+pushq %rbp
+movq %rsp,%rbp
+subq $0x50,%rsp
+movq %rbx,-0x28(%rbp)
+leaq -0x50(%rbp),%rbx
+movq %r12,-0x20(%rbp)
+movq %r13,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movl %edi,%r12d
+movq %rdi,-0x8(%rbp)
+SET_SIZE(uts_kill, uts_kill_end)
+
+FUNC(av1394_ic_bitreverse)
+movq %rdi,%rdx
+movq $0x5555555555555555,%rax
+movq $0x3333333333333333,%rcx
+shrq $0x1,%rdx
+pushq %rbp
+andq %rax,%rdx
+andq %rdi,%rax
+addq %rax,%rax
+movq %rsp,%rbp
+subq $0x10,%rsp
+orq %rdx,%rax
+movq %rdi,-0x8(%rbp)
+SET_SIZE(av1394_ic_bitreverse, av1394_ic_bitreverse_end)
+
+/* Problematic functions which should not match */
+
+FUNC(no_fp) /* No frame pointer */
+movq %rdi, %rsi
+movq %rsi, %rdi
+movq %rbx,-0x28(%rbp)
+movq %rcx,-0x20(%rbp)
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x50,%rsp
+SET_SIZE(no_fp, no_fp_end)
+
+/* Small structure return, but with an SSE type (thus forcing it to the stack) */
+FUNC(small_struct_ret_w_float)
+pushq %rbp
+movq %rsp,%rbp
+movq %rdi,-0x8(%rbp)
+subq $0x30,%rsp
+SET_SIZE(small_struct_ret_w_float, small_struct_ret_w_float_end)
+
+/* Big structure return, but with an SSE type */
+FUNC(big_struct_ret_w_float)
+pushq %rbp
+movq %rsp,%rbp
+movq %rsi,-0x8(%rbp)
+subq $0x50,%rsp
+movq %rsi,-0x48(%rbp)
+movq -0x48(%rbp),%rax
+movq %rax,%rsi
+movl $0x400f60,%edi
+movl $0x0,%eax
+movl $0x1770,%edi
+movl $0x0,%eax
+leave
+ret
+SET_SIZE(big_struct_ret_w_float, big_struct_ret_w_float_end)
+
+FUNC(big_struct_arg_by_value)
+pushq %rbp
+movq %rsp,%rbp
+movq %rdi,-0x8(%rbp)
+subq $0x40,%rsp
+SET_SIZE(big_struct_arg_by_value, big_struct_arg_by_value_end)
+
+FUNC(small_struct_arg_by_value)
+pushq %rbp
+movq %rsp,%rbp
+movq %rdx,-0x18(%rbp)
+movq %rsi,-0x10(%rbp)
+movq %rdi,-0x8(%rbp)
+subq $0x50,%rsp
+SET_SIZE(small_struct_arg_by_value, small_struct_arg_by_value_end)
diff --git a/usr/src/common/saveargs/tests/testmatch/testmatch.c b/usr/src/common/saveargs/tests/testmatch/testmatch.c
new file mode 100644
index 0000000000..b670a67bbd
--- /dev/null
+++ b/usr/src/common/saveargs/tests/testmatch/testmatch.c
@@ -0,0 +1,137 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2012, Richard Lowe.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <saveargs.h>
+
+#define DEF_TEST(name) \
+ extern uint8_t name[]; \
+ extern int name##_end
+
+#define SIZE_OF(name) ((caddr_t)&name##_end - (caddr_t)&name)
+
+#define TEST_GOOD(name, argc) \
+ if (saveargs_has_args(name, SIZE_OF(name), argc, 0) != 0) \
+ printf("Pass: %s\n", #name); \
+ else \
+ printf("FAIL: %s\n", #name);
+
+#define TEST_GOOD_STRUCT(name, argc) \
+ if (saveargs_has_args(name, SIZE_OF(name), argc, 1) != 0) \
+ printf("Pass: %s\n", #name); \
+ else \
+ printf("FAIL: %s\n", #name);
+
+#define TEST_BAD(name, argc) \
+ if (saveargs_has_args(name, SIZE_OF(name), argc, 0) == 0) \
+ printf("Pass: %s\n", #name); \
+ else \
+ printf("FAIL: %s\n", #name);
+
+#define TEST_BAD_STRUCT(name, argc) \
+ if (saveargs_has_args(name, SIZE_OF(name), argc, 1) == 0) \
+ printf("Pass: %s\n", #name); \
+ else \
+ printf("FAIL: %s\n", #name);
+
+DEF_TEST(gcc_mov_align);
+DEF_TEST(gcc_mov_basic);
+DEF_TEST(gcc_mov_big_struct_ret);
+DEF_TEST(gcc_mov_big_struct_ret_and_spill);
+DEF_TEST(gcc_mov_small_struct_ret);
+DEF_TEST(gcc_mov_small_struct_ret_and_spill);
+DEF_TEST(gcc_mov_stack_spill);
+
+DEF_TEST(gcc_push_align);
+DEF_TEST(gcc_push_basic);
+DEF_TEST(gcc_push_big_struct_ret);
+DEF_TEST(gcc_push_big_struct_ret_and_spill);
+DEF_TEST(gcc_push_small_struct_ret);
+DEF_TEST(gcc_push_small_struct_ret_and_spill);
+DEF_TEST(gcc_push_stack_spill);
+
+DEF_TEST(ss_mov_align);
+DEF_TEST(ss_mov_basic);
+DEF_TEST(ss_mov_big_struct_ret);
+DEF_TEST(ss_mov_big_struct_ret_and_spill);
+DEF_TEST(ss_mov_small_struct_ret);
+DEF_TEST(ss_mov_small_struct_ret_and_spill);
+DEF_TEST(ss_mov_stack_spill);
+
+DEF_TEST(dtrace_instrumented);
+DEF_TEST(kmem_alloc);
+DEF_TEST(uts_kill);
+DEF_TEST(av1394_ic_bitreverse);
+
+DEF_TEST(small_struct_ret_w_float);
+DEF_TEST(big_struct_ret_w_float);
+
+/*
+ * Functions which should not match
+ *
+ * no_fp -- valid save-args sequence with no saved FP
+ * big_struct_arg_by_value -- function with big struct passed by value
+ * small_struct_arg_by_value -- function with small struct passed by value
+ */
+DEF_TEST(no_fp);
+DEF_TEST(big_struct_arg_by_value);
+DEF_TEST(small_struct_arg_by_value);
+
+int
+main(int argc, char **argv)
+{
+ TEST_GOOD(kmem_alloc, 2);
+ TEST_GOOD(uts_kill, 2);
+ TEST_GOOD(av1394_ic_bitreverse, 1);
+ TEST_GOOD(dtrace_instrumented, 4);
+ TEST_GOOD_STRUCT(big_struct_ret_w_float, 1);
+ TEST_BAD(no_fp, 5);
+
+ TEST_GOOD(gcc_mov_align, 5);
+ TEST_GOOD(gcc_push_align, 5);
+ TEST_GOOD(ss_mov_align, 5);
+
+ TEST_GOOD(gcc_mov_basic, 4);
+ TEST_GOOD(gcc_push_basic, 4);
+ TEST_GOOD(ss_mov_basic, 4);
+
+ TEST_GOOD_STRUCT(gcc_mov_big_struct_ret, 4);
+ TEST_GOOD_STRUCT(gcc_push_big_struct_ret, 4);
+ TEST_GOOD_STRUCT(ss_mov_big_struct_ret, 4);
+
+ TEST_GOOD_STRUCT(gcc_mov_big_struct_ret_and_spill, 8);
+ TEST_GOOD_STRUCT(gcc_push_big_struct_ret_and_spill, 8);
+ TEST_GOOD_STRUCT(ss_mov_big_struct_ret_and_spill, 8);
+
+ TEST_GOOD(gcc_mov_small_struct_ret, 4);
+ TEST_GOOD(gcc_push_small_struct_ret, 4);
+ TEST_GOOD(ss_mov_small_struct_ret, 4);
+
+ TEST_GOOD(gcc_mov_small_struct_ret_and_spill, 8);
+ TEST_GOOD(gcc_push_small_struct_ret_and_spill, 8);
+ TEST_GOOD(ss_mov_small_struct_ret_and_spill, 8);
+
+ TEST_GOOD(gcc_mov_stack_spill, 8);
+ TEST_GOOD(gcc_push_stack_spill, 8);
+ TEST_GOOD(ss_mov_stack_spill, 8);
+
+ TEST_BAD(big_struct_arg_by_value, 2);
+ TEST_BAD(small_struct_arg_by_value, 2);
+
+ TEST_BAD(small_struct_ret_w_float, 1);
+
+ return (0);
+}
diff --git a/usr/src/common/smbsrv/smb_match.c b/usr/src/common/smbsrv/smb_match.c
index 4e1f174fcf..1a81af96b3 100644
--- a/usr/src/common/smbsrv/smb_match.c
+++ b/usr/src/common/smbsrv/smb_match.c
@@ -18,8 +18,10 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _KERNEL
@@ -38,193 +40,225 @@
*/
#define SMB_MATCH_DEPTH_MAX 32
-#define SMB_CRC_POLYNOMIAL 0xD8B5D8B5
+struct match_priv {
+ int depth;
+ boolean_t ci;
+};
+
+static int smb_match_private(const char *, const char *, struct match_priv *);
-static int smb_match_private(const char *, const char *, int *);
-static int smb_match_ci_private(const char *, const char *, int *);
+static const char smb_wildcards[] = "*?<>\"";
/*
- * smb_match
+ * Return B_TRUE if pattern contains wildcards
*/
boolean_t
-smb_match(char *patn, char *str)
+smb_contains_wildcards(const char *pattern)
{
- int depth = 0;
- return (smb_match_private(patn, str, &depth) == 1);
+ return (strpbrk(pattern, smb_wildcards) != NULL);
}
/*
- * The '*' character matches multiple characters.
- * The '?' character matches a single character.
- *
- * If the pattern has trailing '?'s then it matches the specified number
- * of characters or less. For example, "x??" matches "xab", "xa" and "x",
- * but not "xabc".
- *
- * Returns:
- * 1 match
- * 0 no-match
- * -1 no-match, too many wildcards in pattern
+ * NT-compatible file name match function. [MS-FSA 3.1.4.4]
+ * Returns TRUE if there is a match.
*/
-static int
-smb_match_private(const char *patn, const char *str, int *depth)
+boolean_t
+smb_match(const char *p, const char *s, boolean_t ci)
{
+ struct match_priv priv;
int rc;
- for (;;) {
- switch (*patn) {
- case '\0':
- return (*str == '\0');
-
- case '?':
- if (*str != 0) {
- str++;
- patn++;
- continue;
- } else {
- return (0);
- }
- /*NOTREACHED*/
-
- case '*':
- patn += strspn(patn, "*");
- if (*patn == '\0')
- return (1);
-
- if ((*depth)++ >= SMB_MATCH_DEPTH_MAX)
- return (-1);
+ /*
+ * Optimize common patterns that match everything:
+ * ("*", "<\"*") That second one is the converted
+ * form of "*.*" after smb_convert_wildcards() does
+ * its work on it for an old LM client. Note that a
+ * plain "*.*" never gets this far.
+ */
+ if (p[0] == '*' && p[1] == '\0')
+ return (B_TRUE);
+ if (p[0] == '<' && p[1] == '\"' && p[2] == '*' && p[3] == '\0')
+ return (B_TRUE);
- while (*str) {
- rc = smb_match_private(patn, str, depth);
- if (rc != 0)
- return (rc);
- str++;
- }
- return (0);
+ /*
+ * Match string ".." as if "." This is Windows behavior
+ * (not mentioned in MS-FSA) that was determined using
+ * the Samba masktest program.
+ */
+ if (s[0] == '.' && s[1] == '.' && s[2] == '\0')
+ s++;
- default:
- if (*str != *patn)
- return (0);
- str++;
- patn++;
- continue;
- }
+ /*
+ * Optimize simple patterns (no wildcards)
+ */
+ if (NULL == strpbrk(p, smb_wildcards)) {
+ if (ci)
+ rc = smb_strcasecmp(p, s, 0);
+ else
+ rc = strcmp(p, s);
+ return (rc == 0);
}
- /*NOTREACHED*/
-}
-/*
- * smb_match_ci
- */
-boolean_t
-smb_match_ci(char *patn, char *str)
-{
- int depth = 0;
-
- return (smb_match_ci_private(patn, str, &depth) == 1);
+ /*
+ * Do real wildcard match.
+ */
+ priv.depth = 0;
+ priv.ci = ci;
+ rc = smb_match_private(p, s, &priv);
+ return (rc == 1);
}
/*
- * The '*' character matches multiple characters.
- * The '?' character matches a single character.
+ * Internal file name match function. [MS-FSA 3.1.4.4]
+ * This does the full expression evaluation.
*
- * If the pattern has trailing '?'s then it matches the specified number
- * of characters or less. For example, "x??" matches "xab", "xa" and "x",
- * but not "xabc".
+ * '*' matches zero of more of any characters.
+ * '?' matches exactly one of any character.
+ * '<' matches any string up through the last dot or EOS.
+ * '>' matches any one char not a dot, dot at EOS, or EOS.
+ * '"' matches a dot, or EOS.
*
* Returns:
- * 1 match
- * 0 no-match
- * -1 no-match, too many wildcards in pattern
+ * 1 match
+ * 0 no-match
+ * -1 no-match, error (illseq, too many wildcards in pattern, ...)
+ *
+ * Note that both the pattern and the string are in multi-byte form.
+ *
+ * The implementation of this is quite tricky. First note that it
+ * can call itself recursively, though it limits the recursion depth.
+ * Each switch case in the while loop can basically do one of three
+ * things: (a) return "Yes, match", (b) return "not a match", or
+ * continue processing the match pattern. The cases for wildcards
+ * that may match a variable number of characters ('*' and '<') do
+ * recursive calls, looking for a match of the remaining pattern,
+ * starting at the current and later positions in the string.
*/
static int
-smb_match_ci_private(const char *patn, const char *str, int *depth)
+smb_match_private(const char *pat, const char *str, struct match_priv *priv)
{
- const char *p;
- smb_wchar_t wc1, wc2;
- int nbytes1, nbytes2;
+ const char *limit;
+ char pc; /* current pattern char */
int rc;
+ smb_wchar_t wcpat, wcstr; /* current wchar in pat, str */
+ int nbpat, nbstr; /* multi-byte length of it */
+
+ if (priv->depth >= SMB_MATCH_DEPTH_MAX)
+ return (-1);
/*
- * "<" is a special pattern that matches only those names that do
- * NOT have an extension. "." and ".." are ok.
+ * Advance over one multi-byte char, used in cases like
+ * '?' or '>' where "match one character" needs to be
+ * interpreted as "match one multi-byte sequence".
+ *
+ * This macro needs to consume the semicolon following
+ * each place it appears, so this is carefully written
+ * as an if/else with a missing semicolon at the end.
*/
- if (strcmp(patn, "<") == 0) {
- if ((strcmp(str, ".") == 0) || (strcmp(str, "..") == 0))
- return (1);
- if (strchr(str, '.') == 0)
- return (1);
- return (0);
- }
+#define ADVANCE(str) \
+ if ((nbstr = smb_mbtowc(NULL, str, MTS_MB_CHAR_MAX)) < 1) \
+ return (-1); \
+ else \
+ str += nbstr /* no ; */
- for (;;) {
- switch (*patn) {
- case '\0':
- return (*str == '\0');
+ /*
+ * We move pat forward in each switch case so that the
+ * default case can move it by a whole multi-byte seq.
+ */
+ while ((pc = *pat) != '\0') {
+ switch (pc) {
- case '?':
- if (*str != 0) {
- str++;
- patn++;
+ case '?': /* exactly one of any character */
+ pat++;
+ if (*str != '\0') {
+ ADVANCE(str);
continue;
- } else {
- p = patn;
- p += strspn(p, "?");
- return ((*p == '\0') ? 1 : 0);
}
- /*NOTREACHED*/
-
- case '*':
- patn += strspn(patn, "*");
- if (*patn == '\0')
- return (1);
-
- if ((*depth)++ >= SMB_MATCH_DEPTH_MAX)
- return (-1);
+ /* EOS: no-match */
+ return (0);
- while (*str) {
- rc = smb_match_ci_private(patn, str, depth);
+ case '*': /* zero or more of any characters */
+ pat++;
+ /* Optimize '*' at end of pattern. */
+ if (*pat == '\0')
+ return (1); /* match */
+ while (*str != '\0') {
+ priv->depth++;
+ rc = smb_match_private(pat, str, priv);
+ priv->depth--;
if (rc != 0)
- return (rc);
- str++;
+ return (rc); /* match */
+ ADVANCE(str);
}
- return (0);
-
- default:
- nbytes1 = smb_mbtowc(&wc1, patn, MTS_MB_CHAR_MAX);
- nbytes2 = smb_mbtowc(&wc2, str, MTS_MB_CHAR_MAX);
- if ((nbytes1 == -1) || (nbytes2 == -1))
- return (-1);
+ continue;
- if (wc1 != wc2) {
- wc1 = smb_tolower(wc1);
- wc2 = smb_tolower(wc2);
- if (wc1 != wc2)
- return (0);
+ case '<': /* any string up through the last dot or EOS */
+ pat++;
+ if ((limit = strrchr(str, '.')) != NULL)
+ limit++;
+ while (*str != '\0' && str != limit) {
+ priv->depth++;
+ rc = smb_match_private(pat, str, priv);
+ priv->depth--;
+ if (rc != 0)
+ return (rc); /* match */
+ ADVANCE(str);
}
-
- patn += nbytes1;
- str += nbytes2;
continue;
- }
- }
- /*NOTREACHED*/
-}
-uint32_t
-smb_crc_gen(uint8_t *buf, size_t len)
-{
- uint32_t crc = SMB_CRC_POLYNOMIAL;
- uint8_t *p;
- int i;
+ case '>': /* anything not a dot, dot at EOS, or EOS */
+ pat++;
+ if (*str == '.') {
+ if (str[1] == '\0') {
+ /* dot at EOS */
+ str++; /* ADVANCE over '.' */
+ continue;
+ }
+ /* dot NOT at EOS: no-match */
+ return (0);
+ }
+ if (*str != '\0') {
+ /* something not a dot */
+ ADVANCE(str);
+ continue;
+ }
+ continue;
- for (p = buf, i = 0; i < len; ++i, ++p) {
- crc = (crc ^ (uint32_t)*p) + (crc << 12);
+ case '\"': /* dot, or EOS */
+ pat++;
+ if (*str == '.') {
+ str++; /* ADVANCE over '.' */
+ continue;
+ }
+ if (*str == '\0') {
+ continue;
+ }
+ /* something else: no-match */
+ return (0);
- if (crc == 0 || crc == 0xFFFFFFFF)
- crc = SMB_CRC_POLYNOMIAL;
+ default: /* not a wildcard */
+ nbpat = smb_mbtowc(&wcpat, pat, MTS_MB_CHAR_MAX);
+ nbstr = smb_mbtowc(&wcstr, str, MTS_MB_CHAR_MAX);
+ /* make sure we advance */
+ if (nbpat < 1 || nbstr < 1)
+ return (-1);
+ if (wcpat == wcstr) {
+ pat += nbpat;
+ str += nbstr;
+ continue;
+ }
+ if (priv->ci) {
+ wcpat = smb_tolower(wcpat);
+ wcstr = smb_tolower(wcstr);
+ if (wcpat == wcstr) {
+ pat += nbpat;
+ str += nbstr;
+ continue;
+ }
+ }
+ return (0); /* no-match */
+ }
}
-
- return (crc);
+ return (*str == '\0');
}
diff --git a/usr/src/common/smbsrv/smb_xdr.c b/usr/src/common/smbsrv/smb_xdr.c
index 33013b20b9..fc9153394d 100644
--- a/usr/src/common/smbsrv/smb_xdr.c
+++ b/usr/src/common/smbsrv/smb_xdr.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/sunddi.h>
@@ -734,3 +735,26 @@ smb_shr_execinfo_xdr(XDR *xdrs, smb_shr_execinfo_t *objp)
return (TRUE);
}
+
+/*
+ * The smbsrv ioctl callers include a CRC of the XDR encoded data,
+ * and kmod ioctl handler checks it. Both use this function. This
+ * is not really XDR related, but this is as good a place as any.
+ */
+#define SMB_CRC_POLYNOMIAL 0xD8B5D8B5
+uint32_t
+smb_crc_gen(uint8_t *buf, size_t len)
+{
+ uint32_t crc = SMB_CRC_POLYNOMIAL;
+ uint8_t *p;
+ int i;
+
+ for (p = buf, i = 0; i < len; ++i, ++p) {
+ crc = (crc ^ (uint32_t)*p) + (crc << 12);
+
+ if (crc == 0 || crc == 0xFFFFFFFF)
+ crc = SMB_CRC_POLYNOMIAL;
+ }
+
+ return (crc);
+}