Hi Keith,
Attached is a patch which will fix the kdb symbol completion
problem I described yesterday. It replaces most of the symbol
table handling with the version from the port I did last fall.
I have not looked at the code yet to check for the buffer
overflow you described. As long as the buffers are larger
than the longest symbol in the kernel/module symbol tables
I would not expect a problem.
Jim Houston - Concurrent Computer Corp.
--
diff -urN -Xdontdiff linux-2.6.8.1.orig/include/linux/module.h
linux-2.6.8.1/include/linux/module.h
--- linux-2.6.8.1.orig/include/linux/module.h 2004-08-23 16:38:06.000000000 -0400
+++ linux-2.6.8.1/include/linux/module.h 2004-09-29 13:33:11.249170728 -0400
@@ -437,6 +437,11 @@
unsigned long *symbolsize,
unsigned long *offset,
char **modname);
+#ifdef CONFIG_KDB
+/* Kdb access to symbol table. */
+const char *module_symbol_index(unsigned int index,
+ unsigned long *value, const char **modname);
+#endif /* CONFIG_KDB */
/* For extable.c to search modules' exception tables. */
const struct exception_table_entry *search_module_extables(unsigned long addr);
@@ -518,6 +523,15 @@
return 0;
}
+#ifdef CONFIG_KDB
+/* Kdb access to symbol table. */
+static inline const char *module_symbol_index(unsigned int index,
+ unsigned long *value, const char **modname)
+{
+ return NULL;
+}
+#endif /* CONFIG_KDB */
+
static inline int register_module_notifier(struct notifier_block * nb)
{
/* no events will happen anyway, so this can always succeed */
diff -urN -Xdontdiff linux-2.6.8.1.orig/kdb/kdbsupport.c linux-2.6.8.1/kdb/kdbsupport.c
--- linux-2.6.8.1.orig/kdb/kdbsupport.c 2004-09-02 15:18:00.000000000 -0400
+++ linux-2.6.8.1/kdb/kdbsupport.c 2004-09-29 13:22:21.707916008 -0400
@@ -27,14 +27,6 @@
#include <linux/kdb.h>
#include <linux/kdbprivate.h>
-#ifdef CONFIG_MODULES
-extern struct list_head *kdb_modules;
-#endif
-
-/* These will be re-linked against their real values during the second link stage */
-extern unsigned long kallsyms_addresses[] __attribute__((weak));;
-extern unsigned long kallsyms_num_syms __attribute__((weak));
-extern char kallsyms_names[] __attribute__((weak));
/*
* Symbol table functions.
@@ -46,7 +38,7 @@
* Return the address of the given symbol.
*
* Parameters:
- * symname Character string containing symbol name
+ * symname Character string containing symbol name
* symtab Structure to receive results
* Outputs:
* Returns:
@@ -57,55 +49,12 @@
* Remarks:
*/
-int
-kdbgetsymval(const char *symname, kdb_symtab_t *symtab)
-{
- int i;
- char *name = kallsyms_names;
- char namebuf[128];
- if (KDB_DEBUG(AR))
- kdb_printf("kdbgetsymval: symname=%s, symtab=%p\n", symname, symtab);
- memset(symtab, 0, sizeof(*symtab));
-
- namebuf[127] = 0;
- namebuf[0] = 0;
- for (i = 0; i < kallsyms_num_syms; i++) {
- unsigned prefix = *name++;
- strncpy(namebuf + prefix, name, 127 - prefix);
- if (strcmp(namebuf, symname) == 0) {
- /* found */
- symtab->sym_start = kallsyms_addresses[i];
- if (KDB_DEBUG(AR))
- kdb_printf("kdbgetsymval: returns 1,
symtab->sym_start=0x%lx\n", symtab->sym_start);
- return(1);
- }
- name += strlen(name) + 1;
- }
-#ifdef CONFIG_MODULES
- {
- struct module *mod;
- /* look into modules */
- list_for_each_entry(mod, kdb_modules, list) {
- for (i = 1; i < mod->num_symtab; i++) {
- if (mod->symtab[i].st_shndx == SHN_UNDEF)
- continue;
- name = mod->strtab + mod->symtab[i].st_name;
- if (strcmp(name, symname) == 0) {
- /* found */
- symtab->sym_start = mod->symtab[i].st_value;
- if (KDB_DEBUG(AR))
- kdb_printf("kdbgetsymval: returns 1,
symtab->sym_start=0x%lx\n", symtab->sym_start);
- return(1);
- }
- }
- }
- }
-#endif /* CONFIG_MODULES */
- if (KDB_DEBUG(AR))
- kdb_printf("kdbgetsymval: returns 0\n");
- return(0);
-}
+/* These will be re-linked against their real values during the second link stage */
+extern unsigned long kallsyms_addresses[] __attribute__((weak));
+extern unsigned long kallsyms_num_syms __attribute__((weak));
+extern char kallsyms_names[] __attribute__((weak));
+extern char _end;
/*
* kdbnearsym
@@ -114,8 +63,8 @@
* less than 'addr'.
*
* Parameters:
- * addr Address to check for symbol near
- * symtab Structure to receive results
+ * addr Address to check for symbol near
+ * symtab Structure to receive results
* Outputs:
* Returns:
* 0 No sections contain this address, symtab zero filled
@@ -123,189 +72,164 @@
* Locking:
* None.
* Remarks:
- * 2.6 kallsyms has a "feature" where it unpacks the name into a string.
- * If that string is reused before the caller expects it then the caller
- * sees its string change without warning. To avoid cluttering up the
- * main kdb code with lots of kdb_strdup, tests and kfree calls, kdbnearsym
- * maintains an LRU list of the last few unique strings. The list is sized
- * large enough to hold active strings, no kdb caller of kdbnearsym makes
- * more than ~20 later calls before using a saved value.
*/
int
kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
{
- int ret;
- unsigned long symbolsize;
- unsigned long offset;
- static char *knt[100]; /* kdb name table, arbitrary size */
-#define knt1_size 128 /* must be >= kallsyms table size */
- char *knt1 = kmalloc(knt1_size, GFP_ATOMIC);
+ const char *name;
+ char *modname;
+ long size, offset;
+ int h, l , m;
- if (!knt1) {
- kdb_printf("kdbnearsym: addr=0x%lx cannot kmalloc knt1\n", addr);
+ if (!kallsyms_addresses) {
+ kdb_printf("kdbnearsym: no symbol table\n");
return 0;
}
-
- if (KDB_DEBUG(AR))
- kdb_printf("kdbnearsym: addr=0x%lx, symtab=%p\n", addr, symtab);
-
+ name = NULL;
+ if (addr > (unsigned long)&_end) {
+ name = module_address_lookup(addr, &size, &offset, &modname);
+ } else if (addr > PAGE_OFFSET) {
+ l = 0;
+ h = kallsyms_num_syms-1;
+ m = (h+l)/2;
+ while (h > l+1) {
+ if (kallsyms_addresses[m] > addr)
+ h = m;
+ else
+ l = m;
+ m = (h+l)/2;
+ }
+ if (kallsyms_addresses[h] <= addr)
+ l = h;
+ h = l+1;
+ name = kallsyms_names;
+ for (m = 0; m < l ; m++)
+ name += strlen(name)+1;
+ size = kallsyms_addresses[h] - kallsyms_addresses[l];
+ offset = addr - kallsyms_addresses[l];
+ modname = NULL;
+ }
memset(symtab, 0, sizeof(*symtab));
- symtab->sym_name = kallsyms_lookup(addr, &symbolsize , &offset, (char
**)(&symtab->mod_name), knt1);
+ if (!name)
+ return 0;
+ symtab->sym_name = name;
symtab->sym_start = addr - offset;
- symtab->sym_end = symtab->sym_start + symbolsize;
- ret = symtab->sym_name != NULL && *(symtab->sym_name) != '\0';
+ symtab->sym_end = addr - offset + size;
+ symtab->mod_name = modname ? modname : "kernel";
+ return 1;
+}
- if (ret) {
- int i;
- /* Another 2.6 kallsyms "feature". Sometimes the sym_name is
- * set but the buffer passed into kallsyms_lookup is not used,
- * so it contains garbage. The caller has to work out which
- * buffer needs to be saved.
- *
- * What was Rusty smoking when he wrote that code?
- */
- if (symtab->sym_name != knt1) {
- strncpy(knt1, symtab->sym_name, knt1_size);
- knt1[knt1_size-1] = '\0';
- }
- for (i = 0; i < ARRAY_SIZE(knt); ++i) {
- if (knt[i] && strcmp(knt[i], knt1) == 0)
- break;
- }
- if (i >= ARRAY_SIZE(knt)) {
- memcpy(knt, knt+1, sizeof(knt[0])*(ARRAY_SIZE(knt)-1));
- i = ARRAY_SIZE(knt)-1;
- } else {
- kfree(knt1);
- knt1 = knt[i];
- memcpy(knt+i, knt+i+1, sizeof(knt[0])*(ARRAY_SIZE(knt)-i-1));
- i = ARRAY_SIZE(knt) - 1;
- }
- knt[i] = knt1;
- symtab->sym_name = knt[i];
- }
+#define first_symbol(index, string, value, module) \
+ index = 0; \
+ string = kallsyms_names; \
+ module = NULL; \
+ value = kallsyms_addresses[0];
- if (symtab->mod_name == NULL)
- symtab->mod_name = "kernel";
- if (KDB_DEBUG(AR))
- kdb_printf("kdbnearsym: returns %d symtab->sym_start=0x%lx,
symtab->mod_name=%p, symtab->sym_name=%p (%s)\n", ret, symtab->sym_start,
symtab->mod_name, symtab->sym_name, symtab->sym_name);
- return ret;
-}
+#define next_symbol(index, string, value, module) \
+ if (++index < kallsyms_num_syms) { \
+ string += strlen(string)+1; \
+ value = kallsyms_addresses[index]; \
+ } else { \
+ string = module_symbol_index(index-kallsyms_num_syms, &value,
&module); \
+ }
-/*
- * kallsyms_symbol_complete
- *
- * Parameters:
- * prefix_name prefix of a symbol name to lookup
- * Returns:
- * Number of symbols which match the given prefix.
- */
+/* paramter prefix_name is a buffer provided by the caller, it must ends with '\0'. */
+/* return the extra string together with the given prefix of a symbol name. */
+/* return 0 means no prefix string is found. */
+/* return >0 means prefix string is found. */
+/* Prefix of a symbol name to lookup */
int kallsyms_symbol_complete(char *prefix_name)
{
- char *name = kallsyms_names;
- int i;
- char namebuf[128];
- int prefix_len = strlen(prefix_name);
- int number = 0;
-
- /* look into kernel symbols */
-
- for (i=0; i < kallsyms_num_syms; i++) {
- unsigned prefix = *name++;
- strncpy(namebuf + prefix, name, 127 - prefix);
- if (strncmp(namebuf, prefix_name, prefix_len) == 0) {
- /* found */
- ++number;
- }
- name += strlen(name) + 1;
- }
-#ifdef CONFIG_MODULES
- {
- struct module *mod;
- /* look into modules symbols */
- list_for_each_entry(mod, kdb_modules, list) {
- for (i = 1; i < mod->num_symtab; i++) {
- if (mod->symtab[i].st_shndx == SHN_UNDEF)
- continue;
- name = mod->strtab + mod->symtab[i].st_name;
- if (strncmp(name, prefix_name, prefix_len) == 0) {
- /* found */
- ++number;
+ int prefix_len=strlen(prefix_name);
+ int last_pos=0;
+ int number=0;
+ const char *p, *mod;
+ unsigned long value;
+ int i, j;
+
+ first_symbol(i, p, value, mod);
+ while (p) {
+ if (strncmp(p, prefix_name,prefix_len) == 0) {
+ if (number++ == 0) {
+ last_pos = strlen(p);
+ strncpy(prefix_name, p, last_pos+1);
+ } else {
+ for (j = prefix_len ; j < last_pos; j++) {
+ if (!p[j] || p[j] != prefix_name[j]) {
+ last_pos = j;
+ prefix_name[j] = '\0';
+ break;
+ }
+ }
}
}
+ next_symbol(i, p, value, mod);
}
- }
-#endif /* CONFIG_MODULES */
+
return number;
}
-/*
- * kallsyms_symbol_next
- *
- * Parameters:
- * prefix_name prefix of a symbol name to lookup
- * flag 0 means search from the head, 1 means continue search.
- * Returns:
- * 1 if a symbol matches the given prefix.
- * 0 if no string found
- */
+/* paramter prefix_name is a buffer provided by the caller, it must ends with '\0'. */
+/* parameter flag = 0 means search from the head, flag = 1 means continue search. */
+/* return a symbol string which matches the given prefix. */
+/* return 0 means no prefix string is found. */
+/* return >0 means prefix string is found. */
+
+static unsigned int current_index;
+static const char *current_string;
-int kallsyms_symbol_next(char *prefix_name, int flag)
+/* Prefix of a symbol name to lookup */
+/* Indicate if search from the head */
+int kallsyms_symbol_next( char *prefix_name, int flag)
{
- int prefix_len = strlen(prefix_name);
- char namebuf[128];
- static int i;
- static char *name;
- static struct module *mod;
-
- if (flag) {
- /* continue searching */
- i++;
- name += strlen(name) + 1;
- } else {
- /* new search */
- i = 0;
- name = kallsyms_names;
- mod = (struct module *)NULL;
+ int prefix_len=strlen(prefix_name);
+ unsigned long value;
+ const char *p, *mod;
+
+
+ if(!flag) {
+ first_symbol(current_index, current_string, value, mod);
}
- if (mod == (struct module *)NULL) {
- /* look into kernel symbols */
- for (; i < kallsyms_num_syms; i++) {
- unsigned prefix = *name++;
- strncpy(namebuf + prefix, name, 127 - prefix);
- if (strncmp(namebuf, prefix_name, prefix_len) == 0) {
- /* found */
- strncpy(prefix_name, namebuf, strlen(namebuf)+1);
- return(1);
- }
- name += strlen(name) + 1;
+ while ((p = current_string)) {
+ next_symbol(current_index, current_string, value, mod);
+ if (strncmp(p, prefix_name,prefix_len) == 0) {
+ strncpy(prefix_name, p, strlen(p)+1);
+ return 1;
}
-#ifdef CONFIG_MODULES
- /* not found */
- i = 1;
- mod = list_entry(kdb_modules->next, struct module, list);
- }
- /* look into modules */
- for (; &mod->list != kdb_modules; mod =
- list_entry(mod->list.next, struct module, list))
- for (; i < mod->num_symtab; i++) {
- if (mod->symtab[i].st_shndx == SHN_UNDEF)
- continue;
- name = mod->strtab + mod->symtab[i].st_name;
- if (strncmp(name, prefix_name, prefix_len) == 0) {
- /* found */
- strncpy(prefix_name, name, strlen(name)+1);
- return(1);
- }
- }
-#else /* CONFIG_MODULES */
}
-#endif /* CONFIG_MODULES */
- return(0);
+ return 0;
+}
+
+int
+kdbgetsymval(const char *symname, kdb_symtab_t *symtab)
+{
+ unsigned long value;
+ const char *p, *mod;
+ int i;
+
+ if (!kallsyms_addresses) {
+ kdb_printf("kdbgetsymval: no symbol table\n");
+ return 0;
+ }
+ first_symbol(i, p, value, mod);
+ while (p) {
+ if (strcmp(p, symname) == 0)
+ break;
+ next_symbol (i, p, value, mod);
+ }
+ if (!p)
+ return 0;
+ /*
+ * Use the value based lookup to find the symbol's size.
+ */
+ i = kdbnearsym(value, symtab);
+ /* return the name we matched. */
+ symtab->sym_name = p;
+ return(i);
}
#if defined(CONFIG_SMP)
Binary files linux-2.6.8.1.orig/kernel/config_data.gz and
linux-2.6.8.1/kernel/config_data.gz differ
diff -urN -Xdontdiff linux-2.6.8.1.orig/kernel/kallsyms.c
linux-2.6.8.1/kernel/kallsyms.c
--- linux-2.6.8.1.orig/kernel/kallsyms.c 2004-09-02 15:18:00.000000000 -0400
+++ linux-2.6.8.1/kernel/kallsyms.c 2004-09-29 12:20:44.000000000 -0400
@@ -14,11 +14,7 @@
#include <linux/err.h>
#include <linux/proc_fs.h>
-#ifdef CONFIG_KDB
-#define kdb 1
-#else
-#define kdb 0
-#endif
+static int stem_compression = 1;
/* These will be re-linked against their real values during the second link stage */
extern unsigned long kallsyms_addresses[] __attribute__((weak));
@@ -27,7 +23,6 @@
/* Defined by the linker script. */
extern char _stext[], _etext[], _sinittext[], _einittext[];
-extern char _end[]; /* for CONFIG_KDB */
static inline int is_kernel_inittext(unsigned long addr)
{
@@ -44,12 +39,6 @@
return 0;
}
-/* kdb treats all kernel addresses as valid, including data */
-static inline int is_kernel(unsigned long addr)
-{
- return (addr >= (unsigned long)_stext && addr <= (unsigned long)_end);
-}
-
/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name)
{
@@ -82,30 +71,26 @@
namebuf[KSYM_NAME_LEN] = 0;
namebuf[0] = 0;
- if ((kdb && is_kernel(addr)) ||
- (!kdb && (is_kernel_text(addr) || is_kernel_inittext(addr)))) {
+
+ if (is_kernel_text(addr) || is_kernel_inittext(addr)) {
unsigned long symbol_end;
char *name = kallsyms_names;
/* They're sorted, we could be clever here, but who cares? */
for (i = 0; i < kallsyms_num_syms; i++) {
- if (kallsyms_addresses[i] > kallsyms_addresses[best] &&
+ if (kallsyms_addresses[i] >= kallsyms_addresses[best] &&
kallsyms_addresses[i] <= addr)
best = i;
}
+
/* Grab name */
- for (i = 0; i <= best; i++) {
- unsigned prefix = *name++;
- strncpy(namebuf + prefix, name, KSYM_NAME_LEN - prefix);
- name += strlen(name) + 1;
- }
/* At worst, symbol ends at end of section. */
if (is_kernel_inittext(addr))
symbol_end = (unsigned long)_einittext;
else
- symbol_end = kdb ? (unsigned long)_end : (unsigned long)_etext;
+ symbol_end = (unsigned long)_etext;
/* Search for next non-aliased symbol */
for (i = best+1; i < kallsyms_num_syms; i++) {
@@ -118,7 +103,18 @@
*symbolsize = symbol_end - kallsyms_addresses[best];
*modname = NULL;
*offset = addr - kallsyms_addresses[best];
- return namebuf;
+ if (stem_compression) {
+ for (i = 0; i <= best; i++) {
+ unsigned prefix = *name++;
+ strncpy(namebuf + prefix, name, KSYM_NAME_LEN -
prefix);
+ name += strlen(name) + 1;
+ }
+ return namebuf;
+ }
+ for (i = 0; i < best; i++)
+ name += strlen(name)+1;
+ strncpy(namebuf, name, KSYM_NAME_LEN);
+ return name;
}
return module_address_lookup(addr, symbolsize, offset, modname);
@@ -185,7 +181,11 @@
/* First char of each symbol name indicates prefix length
shared with previous name (stem compression). */
- stemlen = kallsyms_names[off++];
+ if (stem_compression) {
+ stemlen = kallsyms_names[off++];
+ } else {
+ stemlen = 0;
+ }
strlcpy(iter->name+stemlen, kallsyms_names + off,
KSYM_NAME_LEN+1-stemlen);
@@ -318,6 +318,8 @@
entry = create_proc_entry("kallsyms", 0444, NULL);
if (entry)
entry->proc_fops = &kallsyms_operations;
+
+ stem_compression = kallsyms_names ? (*kallsyms_names == 0) : 0;
return 0;
}
__initcall(kallsyms_init);
diff -urN -Xdontdiff linux-2.6.8.1.orig/kernel/module.c linux-2.6.8.1/kernel/module.c
--- linux-2.6.8.1.orig/kernel/module.c 2004-09-02 15:18:00.000000000 -0400
+++ linux-2.6.8.1/kernel/module.c 2004-09-29 12:20:44.000000000 -0400
@@ -38,6 +38,10 @@
#include <asm/semaphore.h>
#include <asm/cacheflush.h>
+#ifdef CONFIG_KDB
+#include <linux/kdb.h>
+#endif
+
#if 0
#define DEBUGP printk
#else
@@ -2038,6 +2042,30 @@
}
#endif /* CONFIG_KALLSYMS */
+#ifdef CONFIG_KDB
+/*
+ * Allow the debugger to iterate over the symbols for all of
+ * the currently loaded modules. The enumeration of the
+ * symbols will change when ever a module loaded or unloaded.
+ */
+const char *module_symbol_index(unsigned int index,
+ unsigned long *value, const char **modname)
+{
+ struct module *mod;
+
+ list_for_each_entry(mod, &modules, list) {
+ if (index < mod->num_symtab) {
+ *value = mod->symtab[index].st_value;
+ *modname = mod->name;
+ return mod->strtab + mod->symtab[index].st_name;
+ }
+ index -= mod->num_symtab;
+ }
+ return NULL;
+
+}
+#endif
+
/* Called by the /proc file system to return a list of modules. */
static void *m_start(struct seq_file *m, loff_t *pos)
{
diff -urN -Xdontdiff linux-2.6.8.1.orig/Makefile linux-2.6.8.1/Makefile
--- linux-2.6.8.1.orig/Makefile 2004-09-02 15:18:00.000000000 -0400
+++ linux-2.6.8.1/Makefile 2004-09-29 12:24:14.000000000 -0400
@@ -577,7 +577,12 @@
endef
quiet_cmd_kallsyms = KSYM $@
+# Disable stem compression and add data symbols for kdb
+ifdef CONFIG_KDB
+cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) --no-stem-compression --all-symbols > $@
+else
cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) $(foreach
x,$(CONFIG_KALLSYMS_ALL),--all-symbols) > $@
+endif
.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
$(call if_changed_dep,as_o_S)
diff -urN -Xdontdiff linux-2.6.8.1.orig/scripts/kallsyms.c
linux-2.6.8.1/scripts/kallsyms.c
--- linux-2.6.8.1.orig/scripts/kallsyms.c 2004-08-23 16:38:09.000000000 -0400
+++ linux-2.6.8.1/scripts/kallsyms.c 2004-09-29 12:20:44.000000000 -0400
@@ -12,6 +12,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <unistd.h>
struct sym_entry {
unsigned long long addr;
@@ -24,11 +25,12 @@
static int size, cnt;
static unsigned long long _stext, _etext, _sinittext, _einittext;
static int all_symbols = 0;
+static int stem_compression = 1;
static void
usage(void)
{
- fprintf(stderr, "Usage: kallsyms [--all-symbols] < in.map > out.S\n");
+ fprintf(stderr, "Usage: kallsyms [--no-stem-compression][--all-symbols] <
in.map > out.S\n");
exit(1);
}
@@ -131,7 +133,7 @@
for (i = 0; i < cnt; i++) {
if (!symbol_valid(&table[i]))
continue;
-
+
printf("\tPTR\t%#llx\n", table[i].addr);
valid++;
}
@@ -153,10 +155,16 @@
if (!symbol_valid(&table[i]))
continue;
- for (k = 0; table[i].sym[k] && table[i].sym[k] == prev[k]; ++k)
- ;
+ if (stem_compression) {
+ for (k = 0; table[i].sym[k] && table[i].sym[k] == prev[k]; ++k)
+ ;
+
+ printf("\t.byte 0x%02x\n\t.asciz\t\"%s\"\n",
+ k, table[i].sym + k);
+ } else {
+ printf("\t.asciz\t\"%s\"\n", table[i].sym);
+ }
- printf("\t.byte 0x%02x\n\t.asciz\t\"%s\"\n", k, table[i].sym + k);
prev = table[i].sym;
}
printf("\n");
@@ -165,10 +173,13 @@
int
main(int argc, char **argv)
{
- if (argc == 2 && strcmp(argv[1], "--all-symbols") == 0)
- all_symbols = 1;
- else if (argc != 1)
- usage();
+ while(++argv,--argc) {
+ if (strcmp(*argv, "--all-symbols") == 0)
+ all_symbols = 1;
+ else if (strcmp(*argv, "--no-stem-compression") == 0)
+ stem_compression = 0;
+ else usage();
+ }
read_map(stdin);
write_src();
---------------------------
Use http://oss.sgi.com/ecartis to modify your settings or to unsubscribe.